mirror of
https://github.com/freebsd/freebsd-src
synced 2024-09-29 13:15:05 +00:00
In kern_linkat() and kern_renameat(), do not call namei(9) while
holding a write reference on the filesystem. Try to get write reference in unblocked way after all vnodes are resolved; if failed, drop all locks and retry after waiting for suspension end. The VFS_UNMOUNT() methods for UFS and tmpfs try to establish suspension on unmount, while covered vnode is locked by VFS, which prevents namei() from stepping over the mount point. The thread doing namei() sleeps on the covered vnode lock, owning the write ref. Reported by: bdrewery Tested by: bdrewery (previous version), pho Sponsored by: The FreeBSD Foundation MFC after: 1 week
This commit is contained in:
parent
83e78a7f6e
commit
2c6fbcbed6
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=272130
|
@ -1551,10 +1551,10 @@ kern_linkat(struct thread *td, int fd1, int fd2, char *path1, char *path2,
|
|||
cap_rights_t rights;
|
||||
int error;
|
||||
|
||||
again:
|
||||
bwillwrite();
|
||||
NDINIT_AT(&nd, LOOKUP, follow | AUDITVNODE1, segflg, path1, fd1, td);
|
||||
|
||||
again:
|
||||
if ((error = namei(&nd)) != 0)
|
||||
return (error);
|
||||
NDFREE(&nd, NDF_ONLY_PNBUF);
|
||||
|
@ -1563,50 +1563,65 @@ kern_linkat(struct thread *td, int fd1, int fd2, char *path1, char *path2,
|
|||
vrele(vp);
|
||||
return (EPERM); /* POSIX */
|
||||
}
|
||||
if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0) {
|
||||
vrele(vp);
|
||||
return (error);
|
||||
}
|
||||
NDINIT_ATRIGHTS(&nd, CREATE, LOCKPARENT | SAVENAME | AUDITVNODE2,
|
||||
segflg, path2, fd2, cap_rights_init(&rights, CAP_LINKAT), td);
|
||||
if ((error = namei(&nd)) == 0) {
|
||||
if (nd.ni_vp != NULL) {
|
||||
NDFREE(&nd, NDF_ONLY_PNBUF);
|
||||
if (nd.ni_dvp == nd.ni_vp)
|
||||
vrele(nd.ni_dvp);
|
||||
else
|
||||
vput(nd.ni_dvp);
|
||||
vrele(nd.ni_vp);
|
||||
error = EEXIST;
|
||||
} else if ((error = vn_lock(vp, LK_EXCLUSIVE)) == 0) {
|
||||
vrele(vp);
|
||||
return (EEXIST);
|
||||
} else if (nd.ni_dvp->v_mount != vp->v_mount) {
|
||||
/*
|
||||
* Check for cross-device links. No need to
|
||||
* recheck vp->v_type, since it cannot change
|
||||
* for non-doomed vnode.
|
||||
* Cross-device link. No need to recheck
|
||||
* vp->v_type, since it cannot change, except
|
||||
* to VBAD.
|
||||
*/
|
||||
if (nd.ni_dvp->v_mount != vp->v_mount)
|
||||
error = EXDEV;
|
||||
else
|
||||
error = can_hardlink(vp, td->td_ucred);
|
||||
if (error == 0)
|
||||
NDFREE(&nd, NDF_ONLY_PNBUF);
|
||||
vput(nd.ni_dvp);
|
||||
vrele(vp);
|
||||
return (EXDEV);
|
||||
} else if ((error = vn_lock(vp, LK_EXCLUSIVE)) == 0) {
|
||||
error = can_hardlink(vp, td->td_ucred);
|
||||
#ifdef MAC
|
||||
if (error == 0)
|
||||
error = mac_vnode_check_link(td->td_ucred,
|
||||
nd.ni_dvp, vp, &nd.ni_cnd);
|
||||
if (error == 0)
|
||||
#endif
|
||||
error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd);
|
||||
if (error != 0) {
|
||||
vput(vp);
|
||||
vput(nd.ni_dvp);
|
||||
NDFREE(&nd, NDF_ONLY_PNBUF);
|
||||
return (error);
|
||||
}
|
||||
error = vn_start_write(vp, &mp, V_NOWAIT);
|
||||
if (error != 0) {
|
||||
vput(vp);
|
||||
vput(nd.ni_dvp);
|
||||
NDFREE(&nd, NDF_ONLY_PNBUF);
|
||||
error = vn_start_write(NULL, &mp,
|
||||
V_XSLEEP | PCATCH);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
goto again;
|
||||
}
|
||||
error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd);
|
||||
VOP_UNLOCK(vp, 0);
|
||||
vput(nd.ni_dvp);
|
||||
vn_finished_write(mp);
|
||||
NDFREE(&nd, NDF_ONLY_PNBUF);
|
||||
} else {
|
||||
vput(nd.ni_dvp);
|
||||
NDFREE(&nd, NDF_ONLY_PNBUF);
|
||||
vrele(vp);
|
||||
vn_finished_write(mp);
|
||||
goto again;
|
||||
}
|
||||
NDFREE(&nd, NDF_ONLY_PNBUF);
|
||||
}
|
||||
vrele(vp);
|
||||
vn_finished_write(mp);
|
||||
return (error);
|
||||
}
|
||||
|
||||
|
@ -3519,6 +3534,7 @@ kern_renameat(struct thread *td, int oldfd, char *old, int newfd, char *new,
|
|||
cap_rights_t rights;
|
||||
int error;
|
||||
|
||||
again:
|
||||
bwillwrite();
|
||||
#ifdef MAC
|
||||
NDINIT_ATRIGHTS(&fromnd, DELETE, LOCKPARENT | LOCKLEAF | SAVESTART |
|
||||
|
@ -3539,14 +3555,6 @@ kern_renameat(struct thread *td, int oldfd, char *old, int newfd, char *new,
|
|||
VOP_UNLOCK(fromnd.ni_vp, 0);
|
||||
#endif
|
||||
fvp = fromnd.ni_vp;
|
||||
if (error == 0)
|
||||
error = vn_start_write(fvp, &mp, V_WAIT | PCATCH);
|
||||
if (error != 0) {
|
||||
NDFREE(&fromnd, NDF_ONLY_PNBUF);
|
||||
vrele(fromnd.ni_dvp);
|
||||
vrele(fvp);
|
||||
goto out1;
|
||||
}
|
||||
NDINIT_ATRIGHTS(&tond, RENAME, LOCKPARENT | LOCKLEAF | NOCACHE |
|
||||
SAVESTART | AUDITVNODE2, pathseg, new, newfd,
|
||||
cap_rights_init(&rights, CAP_LINKAT), td);
|
||||
|
@ -3559,11 +3567,30 @@ kern_renameat(struct thread *td, int oldfd, char *old, int newfd, char *new,
|
|||
NDFREE(&fromnd, NDF_ONLY_PNBUF);
|
||||
vrele(fromnd.ni_dvp);
|
||||
vrele(fvp);
|
||||
vn_finished_write(mp);
|
||||
goto out1;
|
||||
}
|
||||
tdvp = tond.ni_dvp;
|
||||
tvp = tond.ni_vp;
|
||||
error = vn_start_write(fvp, &mp, V_NOWAIT);
|
||||
if (error != 0) {
|
||||
NDFREE(&fromnd, NDF_ONLY_PNBUF);
|
||||
NDFREE(&tond, NDF_ONLY_PNBUF);
|
||||
if (tvp != NULL)
|
||||
vput(tvp);
|
||||
if (tdvp == tvp)
|
||||
vrele(tdvp);
|
||||
else
|
||||
vput(tdvp);
|
||||
vrele(fromnd.ni_dvp);
|
||||
vrele(fvp);
|
||||
vrele(tond.ni_startdir);
|
||||
if (fromnd.ni_startdir != NULL)
|
||||
vrele(fromnd.ni_startdir);
|
||||
error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
goto again;
|
||||
}
|
||||
if (tvp != NULL) {
|
||||
if (fvp->v_type == VDIR && tvp->v_type != VDIR) {
|
||||
error = ENOTDIR;
|
||||
|
|
Loading…
Reference in a new issue