FPPS4/sys/vfs/vfs_cache.pas

380 lines
7.3 KiB
Plaintext

unit vfs_cache;
{$mode ObjFPC}{$H+}
{$CALLING SysV_ABI_CDecl}
interface
uses
vnode;
function vn_fullpath1(vp,rdir:p_vnode;buf:PChar;retbuf:PPChar;buflen:DWORD):Integer;
function vn_fullpath(vn:p_vnode;retbuf,freebuf:PPChar):Integer;
function vn_fullpath_global(vn:p_vnode;retbuf,freebuf:PPChar):Integer;
function vn_path_to_global_path(vp:p_vnode;path:PChar;pathlen:DWORD):Integer;
function vn_vptocnp(vp:pp_vnode;buf:PChar;buflen:PDWORD):Integer;
function sys___getcwd(buf:PChar;buflen:DWORD):Integer;
implementation
uses
sysutils,
systm,
errno,
vuio,
vmount,
vnamei,
vfiledesc,
vfs_subr,
vfs_vnops,
vfs_lookup,
vnode_if,
kern_thr,
kern_mtx;
{
* Retrieve the full filesystem path that correspond to a vnode from the name
* cache (if available)
}
function vn_fullpath(vn:p_vnode;retbuf,freebuf:PPChar):Integer;
var
buf:PChar;
rdir:p_vnode;
error,vfslocked:Integer;
begin
//if (disablefullpath) then Exit(ENODEV);
if (vn=nil) then
begin
Exit(EINVAL);
end;
buf:=AllocMem(MAXPATHLEN);
FILEDESC_SLOCK(@fd_table);
rdir:=fd_table.fd_rdir;
VREF(rdir);
FILEDESC_SUNLOCK(@fd_table);
error:=vn_fullpath1(vn, rdir, buf, retbuf, MAXPATHLEN);
vfslocked:=VFS_LOCK_GIANT(rdir^.v_mount);
vrele(rdir);
VFS_UNLOCK_GIANT(vfslocked);
if (error=0) then
freebuf^:=buf
else
FreeMem(buf);
Exit(error);
end;
{
* This function is similar to vn_fullpath, but it attempts to lookup the
* pathname relative to the global root mount point. This is required for the
* auditing sub-system, as audited pathnames must be absolute, relative to the
* global root mount point.
}
function vn_fullpath_global(vn:p_vnode;retbuf,freebuf:PPChar):Integer;
var
buf:PChar;
error:Integer;
begin
//if (disablefullpath) then Exit(ENODEV);
if (vn=nil) then
begin
Exit(EINVAL);
end;
buf:=AllocMem(MAXPATHLEN);
error:=vn_fullpath1(vn, rootvnode, buf, retbuf, MAXPATHLEN);
if (error=0) then
freebuf^:=buf
else
FreeMem(buf);
Exit(error);
end;
{
* This function updates path string to vnode's full global path
* and checks the size of the new path string against the pathlen argument.
*
* Requires a locked, referenced vnode and GIANT lock held.
* Vnode is re-locked on success or ENODEV, otherwise unlocked.
*
* If sysctl debug.disablefullpath is set, ENODEV is returned,
* vnode is left locked and path remain untouched.
*
* If vp is a directory, the call to vn_fullpath_global() always succeeds
* because it falls back to the ".." lookup if the namecache lookup fails.
}
function vn_path_to_global_path(vp:p_vnode;path:PChar;pathlen:DWORD):Integer;
label
_out;
var
nd:t_nameidata;
vp1:p_vnode;
rpath,fbuf:PChar;
error,vfslocked:Integer;
begin
VFS_ASSERT_GIANT(vp^.v_mount);
ASSERT_VOP_ELOCKED(vp, 'vn_path_to_global_path');
{ Return ENODEV if sysctl debug.disablefullpath==1 }
//if (disablefullpath) then Exit(ENODEV);
{ Construct global filesystem path from vp. }
VOP_UNLOCK(vp, 0);
error:=vn_fullpath_global(vp, @rpath, @fbuf);
if (error<>0) then
begin
vrele(vp);
Exit(error);
end;
if (strlen(rpath) >= pathlen) then
begin
vrele(vp);
error:=ENAMETOOLONG;
goto _out;
end;
{
* Re-lookup the vnode by path to detect a possible rename.
* As a side effect, the vnode is relocked.
* If vnode was renamed, return ENOENT.
}
NDINIT(@nd, LOOKUP, FOLLOW or LOCKLEAF or MPSAFE or AUDITVNODE1, UIO_SYSSPACE, path, curkthread);
error:=nd_namei(@nd);
if (error<>0) then
begin
vrele(vp);
goto _out;
end;
vfslocked:=NDHASGIANT(@nd);
NDFREE(@nd, NDF_ONLY_PNBUF);
vp1:=nd.ni_vp;
vrele(vp);
if (vp1=vp) then
begin
strcopy(path, rpath);
end else
begin
vput(vp1);
error:=ENOENT;
end;
VFS_UNLOCK_GIANT(vfslocked);
_out:
FreeMem(fbuf);
Exit(error);
end;
function vn_vptocnp(vp:pp_vnode;buf:PChar;buflen:PDWORD):Integer;
var
dvp:p_vnode;
error,vfslocked:Integer;
begin
//no cache
vfslocked:=VFS_LOCK_GIANT((vp^)^.v_mount);
vn_lock(vp^, LK_SHARED or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
error:=VOP_VPTOCNP(vp^, @dvp, buf, PInteger(buflen));
vput(vp^);
VFS_UNLOCK_GIANT(vfslocked);
if (error<>0) then
begin
Exit(error);
end;
vp^:=dvp;
if ((dvp^.v_iflag and VI_DOOMED)<>0) then
begin
{ forced unmount }
vfslocked:=VFS_LOCK_GIANT(dvp^.v_mount);
vrele(dvp);
VFS_UNLOCK_GIANT(vfslocked);
error:=ENOENT;
Exit(error);
end;
{
* *vp has its use count incremented still.
}
Exit(0);
end;
{
* The magic behind kern___getcwd() and vn_fullpath().
}
function vn_fullpath1(vp,rdir:p_vnode;buf:PChar;retbuf:PPChar;buflen:DWORD):Integer;
var
error,slash_prefixed,vfslocked:Integer;
vp1:p_vnode;
begin
Dec(buflen);
buf[buflen]:=#0;
error:=0;
slash_prefixed:=0;
vref(vp);
if (vp^.v_type<>VDIR) then
begin
error:=vn_vptocnp(@vp, buf, @buflen);
if (error<>0) then
begin
Exit(error);
end;
if (buflen=0) then
begin
vfslocked:=VFS_LOCK_GIANT(vp^.v_mount);
vrele(vp);
VFS_UNLOCK_GIANT(vfslocked);
Exit(ENOMEM);
end;
Dec(buflen);
buf[buflen]:='/';
slash_prefixed:=1;
end;
while (vp<>rdir) and (vp<>rootvnode) do
begin
if ((vp^.v_vflag and VV_ROOT)<>0) then
begin
if ((vp^.v_iflag and VI_DOOMED)<>0) then
begin { forced unmount }
vfslocked:=VFS_LOCK_GIANT(vp^.v_mount);
vrele(vp);
VFS_UNLOCK_GIANT(vfslocked);
error:=ENOENT;
break;
end;
vp1:=p_mount(vp^.v_mount)^.mnt_vnodecovered;
vref(vp1);
vfslocked:=VFS_LOCK_GIANT(vp^.v_mount);
vrele(vp);
VFS_UNLOCK_GIANT(vfslocked);
vp:=vp1;
continue;
end;
if (vp^.v_type<>VDIR) then
begin
vfslocked:=VFS_LOCK_GIANT(vp^.v_mount);
vrele(vp);
VFS_UNLOCK_GIANT(vfslocked);
error:=ENOTDIR;
break;
end;
error:=vn_vptocnp(@vp, buf, @buflen);
if (error<>0) then
begin
break;
end;
if (buflen=0) then
begin
vfslocked:=VFS_LOCK_GIANT(vp^.v_mount);
vrele(vp);
VFS_UNLOCK_GIANT(vfslocked);
error:=ENOMEM;
break;
end;
Dec(buflen);
buf[buflen]:='/';
slash_prefixed:=1;
end;
if (error<>0) then
begin
Exit(error);
end;
if (slash_prefixed=0) then
begin
if (buflen=0) then
begin
vfslocked:=VFS_LOCK_GIANT(vp^.v_mount);
vrele(vp);
VFS_UNLOCK_GIANT(vfslocked);
Exit(ENOMEM);
end;
Dec(buflen);
buf[buflen]:='/';
end;
vfslocked:=VFS_LOCK_GIANT(vp^.v_mount);
vrele(vp);
VFS_UNLOCK_GIANT(vfslocked);
retbuf^:=buf + buflen;
Exit(0);
end;
function kern___getcwd(buf:PChar;bufseg:uio_seg;buflen:DWORD):Integer;
var
bp,tmpbuf:PChar;
cdir,rdir:p_vnode;
error,vfslocked:Integer;
begin
//if (disablecwd) then Exit(ENODEV);
if (buflen < 2) then Exit(EINVAL);
if (buflen > MAXPATHLEN) then
begin
buflen:=MAXPATHLEN;
end;
tmpbuf:=AllocMem(buflen);
FILEDESC_SLOCK(@fd_table);
cdir:=fd_table.fd_cdir;
VREF(cdir);
rdir:=fd_table.fd_rdir;
VREF(rdir);
FILEDESC_SUNLOCK(@fd_table);
error:=vn_fullpath1(cdir, rdir, tmpbuf, @bp, buflen);
vfslocked:=VFS_LOCK_GIANT(rdir^.v_mount);
vrele(rdir);
VFS_UNLOCK_GIANT(vfslocked);
vfslocked:=VFS_LOCK_GIANT(cdir^.v_mount);
vrele(cdir);
VFS_UNLOCK_GIANT(vfslocked);
if (error=0) then
begin
if (bufseg=UIO_SYSSPACE) then
Move(bp^, buf^, strlen(bp) + 1)
else
error:=copyout(bp, buf, strlen(bp) + 1);
end;
FreeMem(tmpbuf);
Exit(error);
end;
{ Implementation of the getcwd syscall. }
function sys___getcwd(buf:PChar;buflen:DWORD):Integer;
begin
//priv_check(td,683);
Exit(EPERM);
Exit(kern___getcwd(buf, UIO_USERSPACE, buflen));
end;
end.