]> Zhao Yanbai Git Server - minix.git/commitdiff
VFS: change 'last_dir' to match locking assumption
authorThomas Veerman <thomas@minix3.org>
Tue, 20 Nov 2012 13:14:31 +0000 (13:14 +0000)
committerThomas Veerman <thomas@minix3.org>
Mon, 26 Nov 2012 15:20:18 +0000 (15:20 +0000)
new_node makes the assumption that when it does last_dir on a path, a
successive advance would not yield a lock on a vmnt, because last_dir
already locked the vmnt. This is true except when last_dir resolves
to a directory on the parent vmnt of the file that was the result of
advance. For example,
 # cd /
 # echo foo > home
where home is on a different (sub) partition than / is (default
install). last_dir would resolve to / and advance would resolve to
/home.

With this change, last_dir resolves to the root node on the /home
partition, making the assumption valid again.

servers/vfs/open.c
servers/vfs/path.c

index 0a0edf80f7968fe8f83e39d8249c676cd903b762..bd945371c269efba68a87f8dbf3a17acb2be6913 100644 (file)
@@ -444,9 +444,9 @@ static struct vnode *new_node(struct lookup *resolve, int oflags, mode_t bits)
                else
                        err_code = r;
 
+               unlock_vmnt(dir_vmp);
                unlock_vnode(dirp);
                unlock_vnode(vp);
-               unlock_vmnt(dir_vmp);
                put_vnode(dirp);
                return(NULL);
        }
index c19f1e1353f74465cadbcaa555eb708f4b105dd9..63de98f8000860623cd5d813ba0b33dccb027773 100644 (file)
@@ -264,7 +264,9 @@ struct fproc *rfp;
        symlink.l_vmnt_lock = VMNT_READ;
        sym_vp = advance(res_vp, &symlink, rfp);
 
-       if (sym_vp != NULL && S_ISLNK(sym_vp->v_mode)) {
+       if (sym_vp == NULL) break;
+
+       if (S_ISLNK(sym_vp->v_mode)) {
                /* Last component is a symlink, but if we've been asked to not
                 * resolve it, return now.
                 */
@@ -310,6 +312,41 @@ struct fproc *rfp;
 
                        continue;
                }
+       } else {
+               symloop = 0;    /* Not a symlink, so restart counting */
+
+               /* If we're crossing a mount point, return root node of mount
+                * point on which the file resides. That's the 'real' last
+                * dir that holds the file we're looking for.
+                */
+               if (sym_vp->v_fs_e != res_vp->v_fs_e) {
+                       assert(sym_vmp != NULL);
+
+                       /* Unlock final file, it might have wrong lock types */
+                       unlock_vnode(sym_vp);
+                       unlock_vmnt(sym_vmp);
+                       put_vnode(sym_vp);
+                       sym_vp = NULL;
+
+                       /* Also unlock and release erroneous result */
+                       unlock_vnode(*resolve->l_vnode);
+                       unlock_vmnt(*resolve->l_vmp);
+                       put_vnode(res_vp);
+
+                       /* Relock vmnt and vnode with correct lock types */
+                       lock_vmnt(sym_vmp, resolve->l_vmnt_lock);
+                       lock_vnode(sym_vmp->m_root_node, resolve->l_vnode_lock);
+                       res_vp = sym_vmp->m_root_node;
+                       dup_vnode(res_vp);
+                       *resolve->l_vnode = res_vp;
+                       *resolve->l_vmp = sym_vmp;
+
+                       /* We've effectively resolved the final component, so
+                        * change it to current directory to prevent future
+                        * 'advances' of returning erroneous results.
+                        */
+                       strlcpy(dir_entry, ".", NAME_MAX+1);
+               }
        }
        break;
   } while (symloop < SYMLOOP_MAX);