summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormckusick <mckusick@FreeBSD.org>2013-03-20 17:57:00 +0000
committermckusick <mckusick@FreeBSD.org>2013-03-20 17:57:00 +0000
commit9904f3d96892171a40469b83d51181e130737660 (patch)
tree3142bd0a78d267ce0b86f3c2972bc8ca6b0764c0
parenta88f1a3d131b117c073d9babaa2cf03d4b2d232a (diff)
downloadFreeBSD-src-9904f3d96892171a40469b83d51181e130737660.zip
FreeBSD-src-9904f3d96892171a40469b83d51181e130737660.tar.gz
When renaming a directory from one parent directory to another,
we need to call ufs_checkpath() to walk from our new location to the root of the filesystem to ensure that we do not encounter ourselves along the way. Until now, we accomplished this by reading the ".." entries of each directory in our path until we reached the root (or encountered an error). This change tries to avoid the I/O of reading the ".." entries by first looking them up in the name cache and only doing the I/O when the name cache lookup fails. Reviewed by: kib Tested by: Peter Holm MFC after: 4 weeks
-rw-r--r--sys/kern/vfs_cache.c22
-rw-r--r--sys/sys/vnode.h2
-rw-r--r--sys/ufs/ufs/ufs_lookup.c47
3 files changed, 54 insertions, 17 deletions
diff --git a/sys/kern/vfs_cache.c b/sys/kern/vfs_cache.c
index aea71af..9eecc09 100644
--- a/sys/kern/vfs_cache.c
+++ b/sys/kern/vfs_cache.c
@@ -1359,6 +1359,28 @@ vn_fullpath1(struct thread *td, struct vnode *vp, struct vnode *rdir,
return (0);
}
+struct vnode *
+vn_dir_dd_ino(struct vnode *vp)
+{
+ struct namecache *ncp;
+ struct vnode *ddvp;
+
+ ASSERT_VOP_LOCKED(vp, "vn_dir_dd_ino");
+ CACHE_RLOCK();
+ TAILQ_FOREACH(ncp, &(vp->v_cache_dst), nc_dst) {
+ if ((ncp->nc_flag & NCF_ISDOTDOT) != 0)
+ continue;
+ ddvp = ncp->nc_dvp;
+ VI_LOCK(ddvp);
+ CACHE_RUNLOCK();
+ if (vget(ddvp, LK_INTERLOCK | LK_SHARED | LK_NOWAIT, curthread))
+ return (NULL);
+ return (ddvp);
+ }
+ CACHE_RUNLOCK();
+ return (NULL);
+}
+
int
vn_commname(struct vnode *vp, char *buf, u_int buflen)
{
diff --git a/sys/sys/vnode.h b/sys/sys/vnode.h
index bc16acf..42bfb65 100644
--- a/sys/sys/vnode.h
+++ b/sys/sys/vnode.h
@@ -622,6 +622,8 @@ int vn_fullpath(struct thread *td, struct vnode *vn,
char **retbuf, char **freebuf);
int vn_fullpath_global(struct thread *td, struct vnode *vn,
char **retbuf, char **freebuf);
+struct vnode *
+ vn_dir_dd_ino(struct vnode *vp);
int vn_commname(struct vnode *vn, char *buf, u_int buflen);
int vn_path_to_global_path(struct thread *td, struct vnode *vp,
char *path, u_int pathlen);
diff --git a/sys/ufs/ufs/ufs_lookup.c b/sys/ufs/ufs/ufs_lookup.c
index 02fda9f..7068565 100644
--- a/sys/ufs/ufs/ufs_lookup.c
+++ b/sys/ufs/ufs/ufs_lookup.c
@@ -1387,13 +1387,29 @@ ufs_dirempty(ip, parentino, cred)
}
static int
-ufs_dir_dd_ino(struct vnode *vp, struct ucred *cred, ino_t *dd_ino)
+ufs_dir_dd_ino(struct vnode *vp, struct ucred *cred, ino_t *dd_ino,
+ struct vnode **dd_vp)
{
struct dirtemplate dirbuf;
+ struct vnode *ddvp;
int error, namlen;
+ ASSERT_VOP_LOCKED(vp, "ufs_dir_dd_ino");
if (vp->v_type != VDIR)
return (ENOTDIR);
+ /*
+ * First check to see if we have it in the name cache.
+ */
+ if ((ddvp = vn_dir_dd_ino(vp)) != NULL) {
+ KASSERT(ddvp->v_mount == vp->v_mount,
+ ("ufs_dir_dd_ino: Unexpected mount point crossing"));
+ *dd_ino = VTOI(ddvp)->i_number;
+ *dd_vp = ddvp;
+ return (0);
+ }
+ /*
+ * Have to read the directory.
+ */
error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf,
sizeof (struct dirtemplate), (off_t)0, UIO_SYSSPACE,
IO_NODELOCKED | IO_NOMACCHECK, cred, NOCRED, NULL, NULL);
@@ -1411,6 +1427,7 @@ ufs_dir_dd_ino(struct vnode *vp, struct ucred *cred, ino_t *dd_ino)
dirbuf.dotdot_name[1] != '.')
return (ENOTDIR);
*dd_ino = dirbuf.dotdot_ino;
+ *dd_vp = NULL;
return (0);
}
@@ -1435,7 +1452,7 @@ ufs_checkpath(ino_t source_ino, ino_t parent_ino, struct inode *target, struct u
if (target->i_number == ROOTINO)
return (0);
for (;;) {
- error = ufs_dir_dd_ino(vp, cred, &dd_ino);
+ error = ufs_dir_dd_ino(vp, cred, &dd_ino, &vp1);
if (error != 0)
break;
if (dd_ino == source_ino) {
@@ -1446,22 +1463,16 @@ ufs_checkpath(ino_t source_ino, ino_t parent_ino, struct inode *target, struct u
break;
if (dd_ino == parent_ino)
break;
- error = VFS_VGET(mp, dd_ino, LK_SHARED | LK_NOWAIT, &vp1);
- if (error != 0) {
- *wait_ino = dd_ino;
- break;
- }
- /* Recheck that ".." still points to vp1 after relock of vp */
- error = ufs_dir_dd_ino(vp, cred, &dd_ino);
- if (error != 0) {
- vput(vp1);
- break;
- }
- /* Redo the check of ".." if directory was reparented */
- if (dd_ino != VTOI(vp1)->i_number) {
- vput(vp1);
- continue;
+ if (vp1 == NULL) {
+ error = VFS_VGET(mp, dd_ino, LK_SHARED | LK_NOWAIT,
+ &vp1);
+ if (error != 0) {
+ *wait_ino = dd_ino;
+ break;
+ }
}
+ KASSERT(dd_ino == VTOI(vp1)->i_number,
+ ("directory %d reparented\n", VTOI(vp1)->i_number));
if (vp != tvp)
vput(vp);
vp = vp1;
@@ -1469,6 +1480,8 @@ ufs_checkpath(ino_t source_ino, ino_t parent_ino, struct inode *target, struct u
if (error == ENOTDIR)
panic("checkpath: .. not a directory\n");
+ if (vp1 != NULL)
+ vput(vp1);
if (vp != tvp)
vput(vp);
return (error);
OpenPOWER on IntegriCloud