diff options
author | mm <mm@FreeBSD.org> | 2012-01-15 12:08:20 +0000 |
---|---|---|
committer | mm <mm@FreeBSD.org> | 2012-01-15 12:08:20 +0000 |
commit | 9f44ed5ca827811dd8729bb0ab4397c71161fa9c (patch) | |
tree | 7cb3e09e5396eca584100f0f0377b0dd5a33d1ba /sys/kern/vfs_cache.c | |
parent | 4642718fb42bef8b06538195461ee680c22cd1f9 (diff) | |
download | FreeBSD-src-9f44ed5ca827811dd8729bb0ab4397c71161fa9c.zip FreeBSD-src-9f44ed5ca827811dd8729bb0ab4397c71161fa9c.tar.gz |
Introduce vn_path_to_global_path()
This function updates path string to vnode's full global path and checks
the size of the new path string against the pathlen argument.
In vfs_domount(), sys_unmount() and kern_jail_set() this new function
is used to update the supplied path argument to the respective global path.
Unbreaks jailed zfs(8) with enforce_statfs set to 1.
Reviewed by: kib
MFC after: 1 month
Diffstat (limited to 'sys/kern/vfs_cache.c')
-rw-r--r-- | sys/kern/vfs_cache.c | 74 |
1 files changed, 74 insertions, 0 deletions
diff --git a/sys/kern/vfs_cache.c b/sys/kern/vfs_cache.c index 3c8338b..9783e80 100644 --- a/sys/kern/vfs_cache.c +++ b/sys/kern/vfs_cache.c @@ -45,6 +45,7 @@ __FBSDID("$FreeBSD$"); #include <sys/kernel.h> #include <sys/lock.h> #include <sys/malloc.h> +#include <sys/fcntl.h> #include <sys/mount.h> #include <sys/namei.h> #include <sys/proc.h> @@ -1278,3 +1279,76 @@ vn_commname(struct vnode *vp, char *buf, u_int buflen) buf[l] = '\0'; return (0); } + +/* + * 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 + */ +int +vn_path_to_global_path(struct thread *td, struct vnode *vp, char *path, + u_int pathlen) +{ + struct nameidata nd; + struct vnode *vp1; + char *rpath, *fbuf; + int error, vfslocked; + + VFS_ASSERT_GIANT(vp->v_mount); + ASSERT_VOP_ELOCKED(vp, __func__); + + /* Return ENODEV if sysctl debug.disablefullpath==1 */ + if (disablefullpath) + return (ENODEV); + + /* Construct global filesystem path from vp. */ + VOP_UNLOCK(vp, 0); + error = vn_fullpath_global(td, vp, &rpath, &fbuf); + + if (error != 0) { + vrele(vp); + return (error); + } + + if (strlen(rpath) >= pathlen) { + vrele(vp); + error = ENAMETOOLONG; + goto out; + } + + /* + * 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 | LOCKLEAF | MPSAFE | AUDITVNODE1, + UIO_SYSSPACE, path, td); + error = namei(&nd); + if (error != 0) { + vrele(vp); + goto out; + } + vfslocked = NDHASGIANT(&nd); + NDFREE(&nd, NDF_ONLY_PNBUF); + vp1 = nd.ni_vp; + vrele(vp); + if (vp1 == vp) + strcpy(path, rpath); + else { + vput(vp1); + error = ENOENT; + } + VFS_UNLOCK_GIANT(vfslocked); + +out: + free(fbuf, M_TEMP); + return (error); +} |