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
|
else
|
||||||
err_code = r;
|
err_code = r;
|
||||||
|
|
||||||
|
unlock_vmnt(dir_vmp);
|
||||||
unlock_vnode(dirp);
|
unlock_vnode(dirp);
|
||||||
unlock_vnode(vp);
|
unlock_vnode(vp);
|
||||||
unlock_vmnt(dir_vmp);
|
|
||||||
put_vnode(dirp);
|
put_vnode(dirp);
|
||||||
return(NULL);
|
return(NULL);
|
||||||
}
|
}
|
||||||
|
|
|
@ -264,7 +264,9 @@ struct fproc *rfp;
|
||||||
symlink.l_vmnt_lock = VMNT_READ;
|
symlink.l_vmnt_lock = VMNT_READ;
|
||||||
sym_vp = advance(res_vp, &symlink, rfp);
|
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
|
/* Last component is a symlink, but if we've been asked to not
|
||||||
* resolve it, return now.
|
* resolve it, return now.
|
||||||
*/
|
*/
|
||||||
|
@ -310,6 +312,41 @@ struct fproc *rfp;
|
||||||
|
|
||||||
continue;
|
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;
|
break;
|
||||||
} while (symloop < SYMLOOP_MAX);
|
} while (symloop < SYMLOOP_MAX);
|
||||||
|
|
Loading…
Reference in a new issue