FPPS4/sys/vfs/vfs_lookup.pas

1230 lines
28 KiB
Plaintext

unit vfs_lookup;
{$mode ObjFPC}{$H+}
{$CALLING SysV_ABI_CDecl}
interface
uses
sysutils,
kern_param,
systm,
vcapability,
vuio,
vfiledesc,
vfcntl,
vnode,
vnode_if,
vnamei,
vmount,
kern_mtx,
kern_thr,
kern_descrip;
function nd_namei(ndp:p_nameidata):Integer;
function nd_lookup(ndp:p_nameidata):Integer;
procedure NDFREE(ndp:p_nameidata;flags:Integer);
procedure nameiinit; //SYSINIT(vfs, SI_SUB_VFS, SI_ORDER_SECOND, nameiinit, NULL);
implementation
uses
errno,
vfs_subr,
vfs_vnops;
//
var
dead_vnodeops:vop_vector; external;
//
var
vp_crossmp:p_vnode;
lookup_shared:Integer=1;
procedure nameiinit;
begin
getnewvnode('crossmp', nil, @dead_vnodeops, @vp_crossmp);
//vn_lock(vp_crossmp, LK_EXCLUSIVE);
//VN_LOCK_ASHARE(vp_crossmp);
//VOP_UNLOCK(vp_crossmp, 0);
end;
{
* Convert a pathname into a pointer to a locked vnode.
*
* The FOLLOW flag is set when symbolic links are to be followed
* when they occur at the end of the name translation process.
* Symbolic links are always followed for all other pathname
* components other than the last.
*
* The segflg defines whether the name is to be copied from user
* space or kernel space.
*
* Overall outline of namei:
*
* copy in name
* get starting directory
* while (!done and !error) begin
* call lookup to search path.
* if symbolic link, massage name in buffer and continue
* end;
}
procedure namei_cleanup_cnp(cnp:p_componentname); inline;
begin
FreeMem(cnp^.cn_pnbuf);
end;
function zalloc_namei:Pointer; inline;
begin
Result:=AllocMem(kern_param.MAXPATHLEN);
end;
function nd_namei(ndp:p_nameidata):Integer; public;
var
cp:PChar; { pointer into pathname argument }
dp:p_vnode; { the directory we are searching }
aiov:iovec; { uio for reading symbolic links }
auio:t_uio;
error, linklen:Integer;
cnp:p_componentname;
td:p_kthread;
vfslocked:Integer;
begin
cnp:=@ndp^.ni_cnd;
td:=cnp^.cn_thread;
Assert(((cnp^.cn_flags and MPSAFE)<>0) or mtx_owned(VFS_Giant),'NOT MPSAFE and Giant not held');
Assert((cnp^.cn_nameiop and (not OPMASK))=0,'namei: nameiop contaminated with flags');
Assert((cnp^.cn_flags and OPMASK)=0,'namei: flags contaminated with nameiops');
if (lookup_shared=0) then
begin
cnp^.cn_flags:=cnp^.cn_flags and (not LOCKSHARED);
end;
{ We will set this ourselves if we need it. }
cnp^.cn_flags:=cnp^.cn_flags and (not TRAILINGSLASH);
{
* Get a buffer for the name to be translated, and copy the
* name into the buffer.
}
if ((cnp^.cn_flags and HASBUF)=0) then
begin
cnp^.cn_pnbuf:=zalloc_namei;
end;
if (ndp^.ni_segflg=UIO_SYSSPACE) then
begin
error:=copystr (ndp^.ni_dirp, cnp^.cn_pnbuf, MAXPATHLEN, @ndp^.ni_pathlen);
end else
begin
error:=copyinstr(ndp^.ni_dirp, cnp^.cn_pnbuf, MAXPATHLEN, @ndp^.ni_pathlen);
end;
{
* Don't allow empty pathnames.
}
if (error=0) and (cnp^.cn_pnbuf^=#0) then
begin
error:=ENOENT;
end;
if (error<>0) then
begin
namei_cleanup_cnp(cnp);
ndp^.ni_vp:=nil;
Exit(error);
end;
ndp^.ni_loopcnt:=0;
{
* Get starting point for the translation.
}
FILEDESC_SLOCK(@fd_table);
ndp^.ni_rootdir:=fd_table.fd_rdir;
ndp^.ni_topdir :=fd_table.fd_jdir;
dp:=nil;
if (cnp^.cn_pnbuf[0]<>'/') then
begin
if (ndp^.ni_startdir<>nil) then
begin
dp:=ndp^.ni_startdir;
error:=0;
end else
if (ndp^.ni_dirfd<>AT_FDCWD) then
begin
error:=fgetvp_rights(ndp^.ni_dirfd,
ndp^.ni_rightsneeded or CAP_LOOKUP,
@ndp^.ni_baserights,
@dp);
end;
if (error<>0) or (dp<>nil) then
begin
FILEDESC_SUNLOCK(@fd_table);
if (error=0) and (dp^.v_type<>VDIR) then
begin
vfslocked:=VFS_LOCK_GIANT(dp^.v_mount);
vrele(dp);
VFS_UNLOCK_GIANT(vfslocked);
error:=ENOTDIR;
end;
end;
if (error<>0) then
begin
namei_cleanup_cnp(cnp);
Exit(error);
end;
end;
if (dp=nil) then
begin
dp:=fd_table.fd_cdir;
VREF(dp);
FILEDESC_SUNLOCK(@fd_table);
if (ndp^.ni_startdir<>nil) then
begin
vfslocked:=VFS_LOCK_GIANT(ndp^.ni_startdir^.v_mount);
vrele(ndp^.ni_startdir);
VFS_UNLOCK_GIANT(vfslocked);
end;
end;
vfslocked:=VFS_LOCK_GIANT(dp^.v_mount);
while (true) do
begin
{
* Check if root directory should replace current directory.
* Done at start of translation and after symbolic link.
}
cnp^.cn_nameptr:=cnp^.cn_pnbuf;
if (cnp^.cn_nameptr^='/') then
begin
vrele(dp);
VFS_UNLOCK_GIANT(vfslocked);
if (ndp^.ni_strictrelative<>0) then
begin
namei_cleanup_cnp(cnp);
Exit(ENOTCAPABLE);
end;
while (cnp^.cn_nameptr^='/') do
begin
Inc(cnp^.cn_nameptr);
Dec(ndp^.ni_pathlen);
end;
dp:=ndp^.ni_rootdir;
vfslocked:=VFS_LOCK_GIANT(dp^.v_mount);
VREF(dp);
end;
if (vfslocked<>0) then
begin
ndp^.ni_cnd.cn_flags:=ndp^.ni_cnd.cn_flags or GIANTHELD;
end;
ndp^.ni_startdir:=dp;
error:=nd_lookup(ndp);
if (error<>0) then
begin
namei_cleanup_cnp(cnp);
Exit(error);
end;
vfslocked:=ord((ndp^.ni_cnd.cn_flags and GIANTHELD)<>0);
ndp^.ni_cnd.cn_flags:=ndp^.ni_cnd.cn_flags and (not GIANTHELD);
{
* If not a symbolic link, we're done.
}
if ((cnp^.cn_flags and ISSYMLINK)=0) then
begin
if ((cnp^.cn_flags and (SAVENAME or SAVESTART))=0) then
begin
namei_cleanup_cnp(cnp);
end else
begin
cnp^.cn_flags:=cnp^.cn_flags or HASBUF;
end;
if ((cnp^.cn_flags and MPSAFE)=0) then
begin
VFS_UNLOCK_GIANT(vfslocked);
end else
if (vfslocked<>0) then
begin
ndp^.ni_cnd.cn_flags:=ndp^.ni_cnd.cn_flags or GIANTHELD;
end;
Exit(0);
end;
Inc(ndp^.ni_loopcnt);
if ((ndp^.ni_loopcnt-1)>=MAXSYMLINKS) then
begin
error:=ELOOP;
break;
end;
//if ((cnp^.cn_flags and NOMACCHECK)=0) then
//begin
// error:=mac_vnode_check_readlink(td^.td_ucred, ndp^.ni_vp);
// if (error<>0) then
// break;
//end;
if (ndp^.ni_pathlen>1) then
cp:=zalloc_namei
else
cp:=cnp^.cn_pnbuf;
aiov.iov_base :=cp;
aiov.iov_len :=MAXPATHLEN;
auio.uio_iov :=@aiov;
auio.uio_iovcnt:=1;
auio.uio_offset:=0;
auio.uio_rw :=UIO_READ;
auio.uio_segflg:=UIO_SYSSPACE;
auio.uio_td :=td;
auio.uio_resid :=MAXPATHLEN;
error:=VOP_READLINK(ndp^.ni_vp, @auio);
if (error<>0) then
begin
if (ndp^.ni_pathlen > 1) then
begin
FreeMem(cp);
end;
break;
end;
linklen:=MAXPATHLEN - auio.uio_resid;
if (linklen=0) then
begin
if (ndp^.ni_pathlen>1) then
begin
FreeMem(cp);
end;
error:=ENOENT;
break;
end;
if (linklen + ndp^.ni_pathlen >= MAXPATHLEN) then
begin
if (ndp^.ni_pathlen > 1) then
begin
FreeMem(cp);
end;
error:=ENAMETOOLONG;
break;
end;
if (ndp^.ni_pathlen > 1) then
begin
Move(ndp^.ni_next^,(cp + linklen)^,ndp^.ni_pathlen);
FreeMem(cnp^.cn_pnbuf);
cnp^.cn_pnbuf:=cp;
end else
begin
cnp^.cn_pnbuf[linklen]:=#0;
end;
Inc(ndp^.ni_pathlen,linklen);
vput(ndp^.ni_vp);
dp:=ndp^.ni_dvp;
end;
namei_cleanup_cnp(cnp);
vput(ndp^.ni_vp);
ndp^.ni_vp:=nil;
vrele(ndp^.ni_dvp);
VFS_UNLOCK_GIANT(vfslocked);
Exit(error);
end;
function compute_cn_lkflags(mp:p_mount;lkflags,cnflags:Integer):Integer;
begin
if (mp=nil) then
begin
lkflags:=lkflags and (not LK_SHARED);
lkflags:=lkflags or LK_EXCLUSIVE;
Exit(lkflags);
end;
if (((lkflags and LK_SHARED)<>0) and
(((mp^.mnt_kern_flag and MNTK_LOOKUP_SHARED)=0) or
(((cnflags and ISDOTDOT)<>0) and
((mp^.mnt_kern_flag and MNTK_LOOKUP_EXCL_DOTDOT)<>0)))) then
begin
lkflags:=lkflags and (not LK_SHARED);
lkflags:=lkflags or LK_EXCLUSIVE;
end;
Exit(lkflags);
end;
function needs_exclusive_leaf(mp:p_mount;flags:Integer):Integer;
begin
{
* Intermediate nodes can use shared locks, we only need to
* force an exclusive lock for leaf nodes.
}
if ((flags and (ISLASTCN or LOCKLEAF))<>(ISLASTCN or LOCKLEAF)) then
begin
Exit(0);
end;
{ Always use exclusive locks if LOCKSHARED isn't set. }
if ((flags and LOCKSHARED)=0) then
begin
Exit(1);
end;
{
* For lookups during open(), if the mount point supports
* extended shared operations, then use a shared lock for the
* leaf node, otherwise use an exclusive lock.
}
if ((flags and ISOPEN)<>0) then
begin
if (mp<>nil) then
if ((mp^.mnt_kern_flag and MNTK_EXTENDED_SHARED)<>0) then
begin
Exit(0)
end;
Exit(1);
end;
{
* Lookup requests outside of open() that specify LOCKSHARED
* only need a shared lock on the leaf vnode.
}
Exit(0);
end;
{
* Search a pathname.
* This is a very central and rather complicated routine.
*
* The pathname is pointed to by ni_ptr and is of length ni_pathlen.
* The starting directory is taken from ni_startdir. The pathname is
* descended until done, or a symbolic link is encountered. The variable
* ni_more is clear if the path is completed; it is set to one if a
* symbolic link needing interpretation is encountered.
*
* The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on
* whether the name is to be looked up, created, renamed, or deleted.
* When CREATE, RENAME, or DELETE is specified, information usable in
* creating, renaming, or deleting a directory entry may be calculated.
* If flag has LOCKPARENT or'ed into it, the parent directory is returned
* locked. If flag has WANTPARENT or'ed into it, the parent directory is
* returned unlocked. Otherwise the parent directory is not returned. If
* the target of the pathname exists and LOCKLEAF is or'ed into the flag
* the target is returned locked, otherwise it is returned unlocked.
* When creating or renaming and LOCKPARENT is specified, the target may not
* be '.'. When deleting and LOCKPARENT is specified, the target may be '.'.
*
* Overall outline of lookup:
*
* dirloop:
* identify next component of name at ndp^.ni_ptr
* handle degenerate case where name is nil string
* if .. and crossing mount points and on mounted filesys, find parent
* call VOP_LOOKUP routine for next component name
* directory vnode returned in ni_dvp, unlocked unless LOCKPARENT set
* component vnode returned in ni_vp (if it exists), locked.
* if result vnode is mounted on and crossing mount points,
* find mounted on vnode
* if more components of name, do next level at dirloop
* Exitthe answer in ni_vp, locked if LOCKLEAF set
* if LOCKPARENT set, Exitlocked parent in ni_dvp
* if WANTPARENT set, Exitunlocked parent in ni_dvp
}
function nd_lookup(ndp:p_nameidata):Integer; public;
var
cp :PChar ; { pointer into pathname argument }
dp :p_vnode; { the directory we are searching }
tdp :p_vnode; { saved dp }
mp :p_mount; { mount table entry }
docache :Integer; {=0 do not cache last component }
_wantparent :Integer; { 1 => wantparent or lockparent flag }
_rdonly :Integer; { lookup read-only flag bit }
error :Integer;
dpunlocked :Integer; { dp has already been unlocked }
cnp :p_componentname;
vfslocked :Integer; { VFS Giant state for child }
dvfslocked :Integer; { VFS Giant state for parent }
tvfslocked :Integer;
lkflags_save :Integer;
ni_dvp_unlocked:Integer;
label
dirloop,
bad,
success,
nextname,
unionlookup,
bad2;
begin
dp:=nil;
error:=0;
dpunlocked:=0;
cnp:=@ndp^.ni_cnd;
{
* Setup: break out flag bits into variables.
}
dvfslocked:=ord((ndp^.ni_cnd.cn_flags and GIANTHELD)<>0);
vfslocked:=0;
ni_dvp_unlocked:=0;
ndp^.ni_cnd.cn_flags:=ndp^.ni_cnd.cn_flags and (not GIANTHELD);
_wantparent:=cnp^.cn_flags and (LOCKPARENT or WANTPARENT);
Assert((cnp^.cn_nameiop=LOOKUP) or (wantparent<>0),'CREATE, DELETE, RENAME require LOCKPARENT or WANTPARENT.');
docache:=(cnp^.cn_flags and NOCACHE) xor NOCACHE;
if (cnp^.cn_nameiop=DELETE) or
((_wantparent and cnp^.cn_nameiop<>CREATE) and
(cnp^.cn_nameiop<>LOOKUP)) then
begin
docache:=0;
end;
_rdonly:=cnp^.cn_flags and RDONLY;
cnp^.cn_flags:=cnp^.cn_flags and (not ISSYMLINK);
ndp^.ni_dvp:=nil;
{
* We use shared locks until we hit the parent of the last cn then
* we adjust based on the requesting flags.
}
if (lookup_shared<>0) then
cnp^.cn_lkflags:=LK_SHARED
else
cnp^.cn_lkflags:=LK_EXCLUSIVE;
dp:=ndp^.ni_startdir;
ndp^.ni_startdir:=nil;
vn_lock(dp,compute_cn_lkflags(dp^.v_mount,cnp^.cn_lkflags or LK_RETRY,cnp^.cn_flags),{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
dirloop:
{
* Search a new directory.
*
* The last component of the filename is left accessible via
* cnp^.cn_nameptr for callers that need the name. Callers needing
* the name set the SAVENAME flag. When done, they assume
* responsibility for freeing the pathname buffer.
}
cnp^.cn_consume:=0;
cp:=cnp^.cn_nameptr;
while (cp^<>#0) and (cp^<>'/') do Inc(cp);
cnp^.cn_namelen:=cp - cnp^.cn_nameptr;
if (cnp^.cn_namelen > NAME_MAX) then
begin
error:=ENAMETOOLONG;
goto bad;
end;
Dec(ndp^.ni_pathlen,cnp^.cn_namelen);
ndp^.ni_next:=cp;
{
* Replace multiple slashes by a single slash and trailing slashes
* by a nil. This must be done before VOP_LOOKUP() because some
* fs's don't know about trailing slashes. Remember if there were
* trailing slashes to handle symlinks, existing non-directories
* and non-existing files that won't be directories specially later.
}
while ((cp^='/') and ((cp[1]='/') or (cp[1]=#0))) do
begin
Inc(cp);
Dec(ndp^.ni_pathlen);
if (cp^=#0) then
begin
ndp^.ni_next^:=#0;
cnp^.cn_flags:=cnp^.cn_flags or TRAILINGSLASH;
end;
end;
ndp^.ni_next:=cp;
cnp^.cn_flags:=cnp^.cn_flags or MAKEENTRY;
if (cp^=#0) and (docache=0) then
begin
cnp^.cn_flags:=cnp^.cn_flags and (not MAKEENTRY);
end;
if (cnp^.cn_namelen=2) and
(cnp^.cn_nameptr[1]='.') and
(cnp^.cn_nameptr[0]='.') then
cnp^.cn_flags:=cnp^.cn_flags or ISDOTDOT
else
cnp^.cn_flags:=cnp^.cn_flags and (not ISDOTDOT);
if (ndp^.ni_next^=#0) then
cnp^.cn_flags:=cnp^.cn_flags or ISLASTCN
else
cnp^.cn_flags:=cnp^.cn_flags and (not ISLASTCN);
if ((cnp^.cn_flags and ISLASTCN)<>0) and
(cnp^.cn_namelen=1) and
(cnp^.cn_nameptr[0]='.') and
((cnp^.cn_nameiop=DELETE) or (cnp^.cn_nameiop=RENAME)) then
begin
error:=EINVAL;
goto bad;
end;
{
* Check for degenerate name (e.g. / or '')
* which is a way of talking about a directory,
* e.g. like '/.' or '.'.
}
if (cnp^.cn_nameptr[0]=#0) then
begin
if (dp^.v_type<>VDIR) then
begin
error:=ENOTDIR;
goto bad;
end;
if (cnp^.cn_nameiop<>LOOKUP) then
begin
error:=EISDIR;
goto bad;
end;
if (_wantparent<>0) then
begin
ndp^.ni_dvp:=dp;
VREF(dp);
end;
ndp^.ni_vp:=dp;
if ((cnp^.cn_flags and (LOCKPARENT or LOCKLEAF))=0) then
begin
VOP_UNLOCK(dp, 0);
end;
{ XXX This should probably move to the top of function. }
if ((cnp^.cn_flags and SAVESTART)<>0) then
begin
Assert(false,'lookup: SAVESTART');
end;
goto success;
end;
{
* Handle '..': five special cases.
* 0. If doing a capability lookup, return ENOTCAPABLE (this is a
* fairly conservative design choice, but it's the only one that we
* are satisfied guarantees the property we're looking for).
* 1. Exitan error if this is the last component of
* the name and the operation is DELETE or RENAME.
* 2. If at root directory (e.g. after chroot)
* or at absolute root directory
* then ignore it so can't get out.
* 3. If this vnode is the root of a mounted
* filesystem, then replace it with the
* vnode which was mounted on so we take the
* .. in the other filesystem.
* 4. If the vnode is the top directory of
* the jail or chroot, don't let them out.
}
if ((cnp^.cn_flags and ISDOTDOT)<>0) then
begin
if (ndp^.ni_strictrelative<>0) then
begin
error:=ENOTCAPABLE;
goto bad;
end;
if ((cnp^.cn_flags and ISLASTCN)<>0) and
((cnp^.cn_nameiop=DELETE) or (cnp^.cn_nameiop=RENAME)) then
begin
error:=EINVAL;
goto bad;
end;
while (true) do
begin
if (dp=ndp^.ni_rootdir) or
(dp=ndp^.ni_topdir) or
(dp=rootvnode) or
(((dp^.v_vflag and VV_ROOT)<>0) and
((cnp^.cn_flags and NOCROSSMOUNT)<>0)) then
begin
ndp^.ni_dvp:=dp;
ndp^.ni_vp :=dp;
vfslocked:=VFS_LOCK_GIANT(dp^.v_mount);
VREF(dp);
goto nextname;
end;
if ((dp^.v_vflag and VV_ROOT)=0) then
begin
break;
end;
if ((dp^.v_iflag and VI_DOOMED)<>0) then { forced unmount }
begin
error:=ENOENT;
goto bad;
end;
tdp:=dp;
dp:=p_mount(dp^.v_mount)^.mnt_vnodecovered;
tvfslocked:=dvfslocked;
dvfslocked:=VFS_LOCK_GIANT(dp^.v_mount);
VREF(dp);
vput(tdp);
VFS_UNLOCK_GIANT(tvfslocked);
vn_lock(dp,compute_cn_lkflags(dp^.v_mount,cnp^.cn_lkflags or LK_RETRY,ISDOTDOT),{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
end;
end;
{
* We now have a segment name to search for, and a directory to search.
}
unionlookup:
//if ((cnp^.cn_flags and NOMACCHECK)=0) begin
// error:=mac_vnode_check_lookup(cnp^.cn_thread^.td_ucred, dp, cnp);
// if (error)
// goto bad;
//end;
ndp^.ni_dvp:=dp;
ndp^.ni_vp :=nil;
ASSERT_VOP_LOCKED(dp, 'lookup');
Assert(vfslocked=0,'lookup: vfslocked %d');
{
* If we have a shared lock we may need to upgrade the lock for the
* last operation.
}
if (dp<>vp_crossmp) and
(VOP_ISLOCKED(dp)=LK_SHARED) and
((cnp^.cn_flags and ISLASTCN)<>0) and ((cnp^.cn_flags and LOCKPARENT)<>0) then
begin
vn_lock(dp, LK_UPGRADE or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
end;
if ((dp^.v_iflag and VI_DOOMED)<>0) then
begin
error:=ENOENT;
goto bad;
end;
{
* If we're looking up the last component and we need an exclusive
* lock, adjust our lkflags.
}
if (needs_exclusive_leaf(dp^.v_mount, cnp^.cn_flags)<>0) then
begin
cnp^.cn_lkflags:=LK_EXCLUSIVE;
end;
lkflags_save:=cnp^.cn_lkflags;
cnp^.cn_lkflags:=compute_cn_lkflags(dp^.v_mount, cnp^.cn_lkflags, cnp^.cn_flags);
error:=VOP_LOOKUP(dp, @ndp^.ni_vp, cnp);
if (error<>0) then
begin
cnp^.cn_lkflags:=lkflags_save;
Assert(ndp^.ni_vp=nil, 'leaf should be empty');
if (error=ENOENT) and
((dp^.v_vflag and VV_ROOT)<>0) and
(dp^.v_mount<>nil) then
if ((p_mount(dp^.v_mount)^.mnt_flag and MNT_UNION)<>0) then
begin
tdp:=dp;
dp:=p_mount(dp^.v_mount)^.mnt_vnodecovered;
tvfslocked:=dvfslocked;
dvfslocked:=VFS_LOCK_GIANT(dp^.v_mount);
VREF(dp);
vput(tdp);
VFS_UNLOCK_GIANT(tvfslocked);
vn_lock(dp,compute_cn_lkflags(dp^.v_mount, cnp^.cn_lkflags or LK_RETRY, cnp^.cn_flags),{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
goto unionlookup;
end;
if (error<>EJUSTRETURN) then
begin
goto bad;
end;
{
* At this point, we know we're at the end of the
* pathname. If creating / renaming, we can consider
* allowing the file or directory to be created / renamed,
* provided we're not on a read-only filesystem.
}
if (_rdonly<>0) then
begin
error:=EROFS;
goto bad;
end;
{ trailing slash only allowed for directories }
if ((cnp^.cn_flags and TRAILINGSLASH)<>0) and
((cnp^.cn_flags and WILLBEDIR)=0) then
begin
error:=ENOENT;
goto bad;
end;
if ((cnp^.cn_flags and LOCKPARENT)=0) then
begin
VOP_UNLOCK(dp, 0);
end;
{
* We Exit with ni_vp nil to indicate that the entry
* doesn't currently exist, leaving a pointer to the
* (possibly locked) directory vnode in ndp^.ni_dvp.
}
if ((cnp^.cn_flags and SAVESTART)<>0) then
begin
ndp^.ni_startdir:=ndp^.ni_dvp;
VREF(ndp^.ni_startdir);
end;
goto success;
end else
begin
cnp^.cn_lkflags:=lkflags_save;
end;
{
* Take into account any additional components consumed by
* the underlying filesystem.
}
if (cnp^.cn_consume > 0) then
begin
Inc(cnp^.cn_nameptr,cnp^.cn_consume);
Inc(ndp^.ni_next ,cnp^.cn_consume);
Dec(ndp^.ni_pathlen,cnp^.cn_consume);
cnp^.cn_consume:=0;
end;
dp:=ndp^.ni_vp;
vfslocked:=VFS_LOCK_GIANT(dp^.v_mount);
{
* Check to see if the vnode has been mounted on;
* if so find the root of the mounted filesystem.
}
mp:=dp^.v_mountedhere;
while (dp^.v_type=VDIR) and
(mp<>nil) and
((cnp^.cn_flags and NOCROSSMOUNT)=0) do
begin
if (vfs_busy(mp, 0)<>0) then
begin
continue;
end;
vput(dp);
VFS_UNLOCK_GIANT(vfslocked);
vfslocked:=VFS_LOCK_GIANT(mp);
if (dp<>ndp^.ni_dvp) then
vput(ndp^.ni_dvp)
else
vrele(ndp^.ni_dvp);
VFS_UNLOCK_GIANT(dvfslocked);
dvfslocked:=0;
vref(vp_crossmp);
ndp^.ni_dvp:=vp_crossmp;
error:=VFS_ROOT(mp, compute_cn_lkflags(mp, cnp^.cn_lkflags, cnp^.cn_flags), @tdp);
vfs_unbusy(mp);
if (vn_lock(vp_crossmp, LK_SHARED or LK_NOWAIT,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%})<>0) then
begin
Assert(False,'vp_crossmp exclusively locked or reclaimed');
end;
if (error<>0) then
begin
dpunlocked:=1;
goto bad2;
end;
dp:=tdp;
ndp^.ni_vp:=tdp;
mp:=dp^.v_mountedhere;
end;
{
* Check for symbolic link
}
if (dp^.v_type=VLNK) and
(((cnp^.cn_flags and FOLLOW)<>0) or
((cnp^.cn_flags and TRAILINGSLASH)<>0) or
(ndp^.ni_next^='/')) then
begin
cnp^.cn_flags:=cnp^.cn_flags or ISSYMLINK;
if ((dp^.v_iflag and VI_DOOMED)<>0) then
begin
{
* We can't know whether the directory was mounted with
* NOSYMFOLLOW, so we can't follow safely.
}
error:=ENOENT;
goto bad2;
end;
if ((p_mount(dp^.v_mount)^.mnt_flag and MNT_NOSYMFOLLOW)<>0) then
begin
error:=EACCES;
goto bad2;
end;
{
* Symlink code always expects an unlocked dvp.
}
if (ndp^.ni_dvp<>ndp^.ni_vp) then
begin
VOP_UNLOCK(ndp^.ni_dvp, 0);
ni_dvp_unlocked:=1;
end;
goto success;
end;
nextname:
{
* Not a symbolic link that we will follow. Continue with the
* next component if there is any; otherwise, we're done.
}
Assert(((cnp^.cn_flags and ISLASTCN)<>0) or (ndp^.ni_next^='/'),'lookup: invalid path state.');
if (ndp^.ni_next^='/') then
begin
cnp^.cn_nameptr:=ndp^.ni_next;
while (cnp^.cn_nameptr^='/') do
begin
Inc(cnp^.cn_nameptr);
Dec(ndp^.ni_pathlen);
end;
if (ndp^.ni_dvp<>dp) then
vput(ndp^.ni_dvp)
else
vrele(ndp^.ni_dvp);
VFS_UNLOCK_GIANT(dvfslocked);
dvfslocked:=vfslocked; { dp becomes dvp in dirloop }
vfslocked:=0;
goto dirloop;
end;
{
* If we're processing a path with a trailing slash,
* check that the end result is a directory.
}
if ((cnp^.cn_flags and TRAILINGSLASH)<>0) and (dp^.v_type<>VDIR) then
begin
error:=ENOTDIR;
goto bad2;
end;
{
* Disallow directory write attempts on read-only filesystems.
}
if (_rdonly<>0) and
((cnp^.cn_nameiop=DELETE) or (cnp^.cn_nameiop=RENAME)) then
begin
error:=EROFS;
goto bad2;
end;
if ((cnp^.cn_flags and SAVESTART)<>0) then
begin
ndp^.ni_startdir:=ndp^.ni_dvp;
VREF(ndp^.ni_startdir);
end;
if (_wantparent=0) then
begin
ni_dvp_unlocked:=2;
if (ndp^.ni_dvp<>dp) then
vput(ndp^.ni_dvp)
else
vrele(ndp^.ni_dvp);
VFS_UNLOCK_GIANT(dvfslocked);
dvfslocked:=0;
end else
if ((cnp^.cn_flags and LOCKPARENT)=0) and (ndp^.ni_dvp<>dp) then
begin
VOP_UNLOCK(ndp^.ni_dvp, 0);
ni_dvp_unlocked:=1;
end;
if ((cnp^.cn_flags and LOCKLEAF)=0) then
begin
VOP_UNLOCK(dp, 0);
end;
success:
{
* Because of lookup_shared we may have the vnode shared locked, but
* the caller may want it to be exclusively locked.
}
if (needs_exclusive_leaf(dp^.v_mount, cnp^.cn_flags)<>0) and
(VOP_ISLOCKED(dp)<>LK_EXCLUSIVE) then
begin
vn_lock(dp, LK_UPGRADE or LK_RETRY,{$INCLUDE %FILE%},{$INCLUDE %LINENUM%});
if ((dp^.v_iflag and VI_DOOMED)<>0) then
begin
error:=ENOENT;
goto bad2;
end;
end;
if (vfslocked<>0) and (dvfslocked<>0) then
begin
VFS_UNLOCK_GIANT(dvfslocked); { Only need one }
end;
if (vfslocked<>0) or (dvfslocked<>0) then
begin
ndp^.ni_cnd.cn_flags:=ndp^.ni_cnd.cn_flags or GIANTHELD;
end;
Exit(0);
bad2:
if (ni_dvp_unlocked<>2) then
begin
if (dp<>ndp^.ni_dvp) and (ni_dvp_unlocked=0) then
vput(ndp^.ni_dvp)
else
vrele(ndp^.ni_dvp);
end;
bad:
if (dpunlocked=0) then
begin
vput(dp);
end;
VFS_UNLOCK_GIANT(vfslocked);
VFS_UNLOCK_GIANT(dvfslocked);
ndp^.ni_cnd.cn_flags:=ndp^.ni_cnd.cn_flags and (not GIANTHELD);
ndp^.ni_vp:=nil;
Exit(error);
end;
{
* relookup - lookup a path name component
* Used by lookup to re-acquire things.
}
{
int
relookup(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp)
begin
struct vnode *dp:=0; { the directory we are searching }
int _wantparent; { 1 => wantparent or lockparent flag }
int _rdonly; { lookup read-only flag bit }
int error:=0;
Assert(cnp^.cn_flags and ISLASTCN,
('relookup: Not given last component.'));
{
* Setup: break out flag bits into variables.
}
_wantparent:=cnp^.cn_flags and (LOCKPARENT or WANTPARENT);
Assert(_wantparent, ('relookup: parent not wanted.'));
_rdonly:=cnp^.cn_flags and RDONLY;
cnp^.cn_flags:= and ~ISSYMLINK;
dp:=dvp;
cnp^.cn_lkflags:=LK_EXCLUSIVE;
vn_lock(dp, LK_EXCLUSIVE or LK_RETRY);
{
* Search a new directory.
*
* The last component of the filename is left accessible via
* cnp^.cn_nameptr for callers that need the name. Callers needing
* the name set the SAVENAME flag. When done, they assume
* responsibility for freeing the pathname buffer.
}
{
* Check for '' which represents the root directory after slash
* removal.
}
if (cnp^.cn_nameptr[0]='\0') begin
{
* Support only LOOKUP for '/' because lookup()
* can't succeed for CREATE, DELETE and RENAME.
}
Assert(cnp^.cn_nameiop=LOOKUP, ('nameiop must be LOOKUP'));
Assert(dp^.v_type=VDIR, ('dp is not a directory'));
if (!(cnp^.cn_flags and LOCKLEAF))
VOP_UNLOCK(dp, 0);
*vpp:=dp;
{ XXX This should probably move to the top of function. }
if (cnp^.cn_flags and SAVESTART)
panic('lookup: SAVESTART');
Exit(0);
end;
if (cnp^.cn_flags and ISDOTDOT)
panic ('relookup: lookup on dot-dot');
{
* We now have a segment name to search for, and a directory to search.
}
if ((error:=VOP_LOOKUP(dp, vpp, cnp))<>0) begin
Assert(*vpp=nil, ('leaf should be empty'));
if (error<>EJUSTRETURN)
goto bad;
{
* If creating and at end of pathname, then can consider
* allowing file to be created.
}
if (_rdonly) begin
error:=EROFS;
goto bad;
end;
{ ASSERT(dvp=ndp^.ni_startdir) }
if (cnp^.cn_flags and SAVESTART)
VREF(dvp);
if ((cnp^.cn_flags and LOCKPARENT)=0)
VOP_UNLOCK(dp, 0);
{
* We Exitwith ni_vp nil to indicate that the entry
* doesn't currently exist, leaving a pointer to the
* (possibly locked) directory vnode in ndp^.ni_dvp.
}
Exit(0);
end;
dp:=*vpp;
{
* Disallow directory write attempts on read-only filesystems.
}
if (_rdonly and
(cnp^.cn_nameiop=DELETE or cnp^.cn_nameiop=RENAME)) begin
if (dvp=dp)
vrele(dvp);
else
vput(dvp);
error:=EROFS;
goto bad;
end;
{
* Set the parent lock/ref state to the requested state.
}
if ((cnp^.cn_flags and LOCKPARENT)=0 and dvp<>dp) begin
if (_wantparent)
VOP_UNLOCK(dvp, 0);
else
vput(dvp);
end; else if (!_wantparent)
vrele(dvp);
{
* Check for symbolic link
}
Assert(dp^.v_type<>VLNK or !(cnp^.cn_flags and FOLLOW),
('relookup: symlink found.\n'));
{ ASSERT(dvp=ndp^.ni_startdir) }
if (cnp^.cn_flags and SAVESTART)
VREF(dvp);
if ((cnp^.cn_flags and LOCKLEAF)=0)
VOP_UNLOCK(dp, 0);
Exit(0);
bad:
vput(dp);
*vpp:=nil;
Exit(error);
end;
}
{
* Free data allocated by namei(); see namei(9) for details.
}
procedure NDFREE(ndp:p_nameidata;flags:Integer); public;
var
unlock_dvp:Integer;
unlock_vp :Integer;
begin
unlock_dvp:=0;
unlock_vp:=0;
if ((flags and NDF_NO_FREE_PNBUF)=0) and
((ndp^.ni_cnd.cn_flags and HASBUF<>0)) then
begin
FreeMem(ndp^.ni_cnd.cn_pnbuf);
ndp^.ni_cnd.cn_flags:=ndp^.ni_cnd.cn_flags and (not HASBUF);
end;
if ((flags and NDF_NO_VP_UNLOCK)=0) and
((ndp^.ni_cnd.cn_flags and LOCKLEAF)<>0) and
(ndp^.ni_vp<>nil) then
begin
unlock_vp:=1;
end;
if (((flags and NDF_NO_VP_RELE)=0) and (ndp^.ni_vp<>nil)) then
begin
if (unlock_vp<>0) then
begin
vput(ndp^.ni_vp);
unlock_vp:=0;
end else
begin
vrele(ndp^.ni_vp);
end;
ndp^.ni_vp:=nil;
end;
if (unlock_vp<>0) then
begin
VOP_UNLOCK(ndp^.ni_vp, 0);
end;
if ((flags and NDF_NO_DVP_UNLOCK)=0) and
((ndp^.ni_cnd.cn_flags and LOCKPARENT)<>0) and
(ndp^.ni_dvp<>ndp^.ni_vp) then
begin
unlock_dvp:=1;
end;
if ((flags and NDF_NO_DVP_RELE)=0) and
((ndp^.ni_cnd.cn_flags and (LOCKPARENT or WANTPARENT))<>0) then
begin
if (unlock_dvp<>0) then
begin
vput(ndp^.ni_dvp);
unlock_dvp:=0;
end else
begin
vrele(ndp^.ni_dvp);
end;
ndp^.ni_dvp:=nil;
end;
if (unlock_dvp<>0) then
begin
VOP_UNLOCK(ndp^.ni_dvp, 0);
end;
if ((flags and NDF_NO_STARTDIR_RELE)=0) and
((ndp^.ni_cnd.cn_flags and SAVESTART)<>0) then
begin
vrele(ndp^.ni_startdir);
ndp^.ni_startdir:=nil;
end;
end;
end.