VFS: change 'last_dir' to match locking assumption
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.
This commit is contained in:
parent
d67db9b39c
commit
de83b2a9d9
2 changed files with 39 additions and 2 deletions
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue