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.