Changeset View
Changeset View
Standalone View
Standalone View
sys/ufs/ufs/ufs_vnops.c
Show First 20 Lines • Show All 1,000 Lines • ▼ Show 20 Lines | ufs_remove(ap) | ||||
struct vnode *vp = ap->a_vp; | struct vnode *vp = ap->a_vp; | ||||
struct vnode *dvp = ap->a_dvp; | struct vnode *dvp = ap->a_dvp; | ||||
int error; | int error; | ||||
struct thread *td; | struct thread *td; | ||||
td = curthread; | td = curthread; | ||||
ip = VTOI(vp); | ip = VTOI(vp); | ||||
if ((ip->i_flags & (NOUNLINK | IMMUTABLE | APPEND)) || | if ((ip->i_flags & (NOUNLINK | IMMUTABLE | APPEND)) || | ||||
(VTOI(dvp)->i_flags & APPEND)) { | (VTOI(dvp)->i_flags & APPEND)) | ||||
error = EPERM; | return (EPERM); | ||||
goto out; | if (DOINGSOFTDEP(dvp)) { | ||||
error = softdep_prelink(dvp, vp, true); | |||||
if (error != 0) { | |||||
MPASS(error == ERELOOKUP); | |||||
return (error); | |||||
} | } | ||||
} | |||||
#ifdef UFS_GJOURNAL | #ifdef UFS_GJOURNAL | ||||
ufs_gjournal_orphan(vp); | ufs_gjournal_orphan(vp); | ||||
#endif | #endif | ||||
error = ufs_dirremove(dvp, ip, ap->a_cnp->cn_flags, 0); | error = ufs_dirremove(dvp, ip, ap->a_cnp->cn_flags, 0); | ||||
if (ip->i_nlink <= 0) | if (ip->i_nlink <= 0) | ||||
vp->v_vflag |= VV_NOSYNC; | vp->v_vflag |= VV_NOSYNC; | ||||
if ((ip->i_flags & SF_SNAPSHOT) != 0) { | if ((ip->i_flags & SF_SNAPSHOT) != 0) { | ||||
/* | /* | ||||
* Avoid deadlock where another thread is trying to | * Avoid deadlock where another thread is trying to | ||||
* update the inodeblock for dvp and is waiting on | * update the inodeblock for dvp and is waiting on | ||||
* snaplk. Temporary unlock the vnode lock for the | * snaplk. Temporary unlock the vnode lock for the | ||||
* unlinked file and sync the directory. This should | * unlinked file and sync the directory. This should | ||||
* allow vput() of the directory to not block later on | * allow vput() of the directory to not block later on | ||||
* while holding the snapshot vnode locked, assuming | * while holding the snapshot vnode locked, assuming | ||||
* that the directory hasn't been unlinked too. | * that the directory hasn't been unlinked too. | ||||
*/ | */ | ||||
VOP_UNLOCK(vp); | VOP_UNLOCK(vp); | ||||
(void) VOP_FSYNC(dvp, MNT_WAIT, td); | (void) VOP_FSYNC(dvp, MNT_WAIT, td); | ||||
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); | vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); | ||||
} | } | ||||
out: | |||||
return (error); | return (error); | ||||
} | } | ||||
static void | static void | ||||
print_bad_link_count(const char *funcname, struct vnode *dvp) | print_bad_link_count(const char *funcname, struct vnode *dvp) | ||||
{ | { | ||||
struct inode *dip; | struct inode *dip; | ||||
Show All 20 Lines | ufs_link(ap) | ||||
struct inode *ip; | struct inode *ip; | ||||
struct direct newdir; | struct direct newdir; | ||||
int error; | int error; | ||||
#ifdef INVARIANTS | #ifdef INVARIANTS | ||||
if ((cnp->cn_flags & HASBUF) == 0) | if ((cnp->cn_flags & HASBUF) == 0) | ||||
panic("ufs_link: no name"); | panic("ufs_link: no name"); | ||||
#endif | #endif | ||||
if (DOINGSOFTDEP(tdvp)) { | |||||
error = softdep_prelink(tdvp, vp, true); | |||||
if (error != 0) { | |||||
MPASS(error == ERELOOKUP); | |||||
return (error); | |||||
} | |||||
} | |||||
if (VTOI(tdvp)->i_effnlink < 2) { | if (VTOI(tdvp)->i_effnlink < 2) { | ||||
print_bad_link_count("ufs_link", tdvp); | print_bad_link_count("ufs_link", tdvp); | ||||
error = EINVAL; | error = EINVAL; | ||||
goto out; | goto out; | ||||
} | } | ||||
ip = VTOI(vp); | ip = VTOI(vp); | ||||
if (ip->i_nlink >= UFS_LINK_MAX) { | if (ip->i_nlink >= UFS_LINK_MAX) { | ||||
error = EMLINK; | error = EMLINK; | ||||
goto out; | goto out; | ||||
} | } | ||||
/* | /* | ||||
* The file may have been removed after namei droped the original | * The file may have been removed after namei droped the original | ||||
* lock. | * lock. | ||||
*/ | */ | ||||
if (ip->i_effnlink == 0) { | if (ip->i_effnlink == 0) { | ||||
error = ENOENT; | error = ENOENT; | ||||
goto out; | goto out; | ||||
} | } | ||||
if (ip->i_flags & (IMMUTABLE | APPEND)) { | if (ip->i_flags & (IMMUTABLE | APPEND)) { | ||||
error = EPERM; | error = EPERM; | ||||
goto out; | goto out; | ||||
} | } | ||||
ip->i_effnlink++; | ip->i_effnlink++; | ||||
ip->i_nlink++; | ip->i_nlink++; | ||||
DIP_SET(ip, i_nlink, ip->i_nlink); | DIP_SET(ip, i_nlink, ip->i_nlink); | ||||
UFS_INODE_SET_FLAG(ip, IN_CHANGE); | UFS_INODE_SET_FLAG(ip, IN_CHANGE); | ||||
if (DOINGSOFTDEP(vp)) | if (DOINGSOFTDEP(vp)) | ||||
softdep_setup_link(VTOI(tdvp), ip); | softdep_setup_link(VTOI(tdvp), ip); | ||||
error = UFS_UPDATE(vp, !DOINGSOFTDEP(vp) && !DOINGASYNC(vp)); | error = UFS_UPDATE(vp, !DOINGSOFTDEP(vp) && !DOINGASYNC(vp)); | ||||
if (!error) { | if (!error) { | ||||
Show All 24 Lines | struct vop_whiteout_args /* { | ||||
int a_flags; | int a_flags; | ||||
} */ *ap; | } */ *ap; | ||||
{ | { | ||||
struct vnode *dvp = ap->a_dvp; | struct vnode *dvp = ap->a_dvp; | ||||
struct componentname *cnp = ap->a_cnp; | struct componentname *cnp = ap->a_cnp; | ||||
struct direct newdir; | struct direct newdir; | ||||
int error = 0; | int error = 0; | ||||
if (DOINGSOFTDEP(dvp) && (ap->a_flags == CREATE || | |||||
mckusick: What is this? | |||||
Done Inline ActionsRight, this should call softdep_prelink() as well. This leaked from earlier prototype but probably was not tested enough because whiteouts are too obscure. kib: Right, this should call softdep_prelink() as well. This leaked from earlier prototype but… | |||||
ap->a_flags == DELETE)) { | |||||
error = softdep_prelink(dvp, NULL, true); | |||||
if (error != 0) { | |||||
MPASS(error == ERELOOKUP); | |||||
return (error); | |||||
} | |||||
} | |||||
switch (ap->a_flags) { | switch (ap->a_flags) { | ||||
case LOOKUP: | case LOOKUP: | ||||
/* 4.4 format directories support whiteout operations */ | /* 4.4 format directories support whiteout operations */ | ||||
if (dvp->v_mount->mnt_maxsymlinklen > 0) | if (dvp->v_mount->mnt_maxsymlinklen > 0) | ||||
return (0); | return (0); | ||||
return (EOPNOTSUPP); | return (EOPNOTSUPP); | ||||
case CREATE: | case CREATE: | ||||
▲ Show 20 Lines • Show All 193 Lines • ▼ Show 20 Lines | if (error) { | ||||
error = VFS_VGET(mp, ino, LK_EXCLUSIVE, &nvp); | error = VFS_VGET(mp, ino, LK_EXCLUSIVE, &nvp); | ||||
if (error != 0) | if (error != 0) | ||||
goto releout; | goto releout; | ||||
vput(nvp); | vput(nvp); | ||||
atomic_add_int(&rename_restarts, 1); | atomic_add_int(&rename_restarts, 1); | ||||
goto relock; | goto relock; | ||||
} | } | ||||
} | } | ||||
if (DOINGSOFTDEP(fdvp)) { | |||||
error = softdep_prerename(fdvp, fvp, tdvp, tvp); | |||||
if (error != 0) { | |||||
if (error == ERELOOKUP) { | |||||
atomic_add_int(&rename_restarts, 1); | |||||
goto relock; | |||||
} | |||||
goto releout; | |||||
} | |||||
} | |||||
fdp = VTOI(fdvp); | fdp = VTOI(fdvp); | ||||
fip = VTOI(fvp); | fip = VTOI(fvp); | ||||
tdp = VTOI(tdvp); | tdp = VTOI(tdvp); | ||||
tip = NULL; | tip = NULL; | ||||
if (tvp) | if (tvp) | ||||
tip = VTOI(tvp); | tip = VTOI(tvp); | ||||
if (tvp && ((VTOI(tvp)->i_flags & (NOUNLINK | IMMUTABLE | APPEND)) || | if (tvp && ((VTOI(tvp)->i_flags & (NOUNLINK | IMMUTABLE | APPEND)) || | ||||
(VTOI(tdvp)->i_flags & APPEND))) { | (VTOI(tdvp)->i_flags & APPEND))) { | ||||
▲ Show 20 Lines • Show All 127 Lines • ▼ Show 20 Lines | if (doingdirectory && newparent) { | ||||
goto bad; | goto bad; | ||||
} | } | ||||
} | } | ||||
ufs_makedirentry(fip, tcnp, &newdir); | ufs_makedirentry(fip, tcnp, &newdir); | ||||
error = ufs_direnter(tdvp, NULL, &newdir, tcnp, NULL, 1); | error = ufs_direnter(tdvp, NULL, &newdir, tcnp, NULL, 1); | ||||
if (error) | if (error) | ||||
goto bad; | goto bad; | ||||
/* Setup tdvp for directory compaction if needed. */ | /* Setup tdvp for directory compaction if needed. */ | ||||
if (tdp->i_count && tdp->i_endoff && | if (I_COUNT(tdp) != 0 && I_ENDOFF(tdp) != 0 && | ||||
tdp->i_endoff < tdp->i_size) | I_ENDOFF(tdp) < tdp->i_size) | ||||
endoff = tdp->i_endoff; | endoff = I_ENDOFF(tdp); | ||||
} else { | } else { | ||||
if (ITODEV(tip) != ITODEV(tdp) || ITODEV(tip) != ITODEV(fip)) | if (ITODEV(tip) != ITODEV(tdp) || ITODEV(tip) != ITODEV(fip)) | ||||
panic("ufs_rename: EXDEV"); | panic("ufs_rename: EXDEV"); | ||||
/* | /* | ||||
* Short circuit rename(foo, foo). | * Short circuit rename(foo, foo). | ||||
*/ | */ | ||||
if (tip->i_number == fip->i_number) | if (tip->i_number == fip->i_number) | ||||
panic("ufs_rename: same file"); | panic("ufs_rename: same file"); | ||||
▲ Show 20 Lines • Show All 111 Lines • ▼ Show 20 Lines | if (tip == NULL) { | ||||
error = UFS_UPDATE(tdvp, !DOINGSOFTDEP(tdvp) && | error = UFS_UPDATE(tdvp, !DOINGSOFTDEP(tdvp) && | ||||
!DOINGASYNC(tdvp)); | !DOINGASYNC(tdvp)); | ||||
/* Don't go to bad here as the new link exists. */ | /* Don't go to bad here as the new link exists. */ | ||||
if (error) | if (error) | ||||
goto unlockout; | goto unlockout; | ||||
} else if (DOINGSUJ(tdvp)) | } else if (DOINGSUJ(tdvp)) | ||||
/* Journal must account for each new link. */ | /* Journal must account for each new link. */ | ||||
softdep_setup_dotdot_link(tdp, fip); | softdep_setup_dotdot_link(tdp, fip); | ||||
fip->i_offset = mastertemplate.dot_reclen; | SET_I_OFFSET(fip, mastertemplate.dot_reclen); | ||||
ufs_dirrewrite(fip, fdp, newparent, DT_DIR, 0); | ufs_dirrewrite(fip, fdp, newparent, DT_DIR, 0); | ||||
cache_purge(fdvp); | cache_purge(fdvp); | ||||
} | } | ||||
error = ufs_dirremove(fdvp, fip, fcnp->cn_flags, 0); | error = ufs_dirremove(fdvp, fip, fcnp->cn_flags, 0); | ||||
/* | /* | ||||
* The kern_renameat() looks up the fvp using the DELETE flag, which | * The kern_renameat() looks up the fvp using the DELETE flag, which | ||||
* causes the removal of the name cache entry for fvp. | * causes the removal of the name cache entry for fvp. | ||||
* As the relookup of the fvp is done in two steps: | * As the relookup of the fvp is done in two steps: | ||||
Show All 21 Lines | unlockout: | ||||
vput(fvp); | vput(fvp); | ||||
if (tvp) | if (tvp) | ||||
vput(tvp); | vput(tvp); | ||||
/* | /* | ||||
* If compaction or fsync was requested do it now that other locks | * If compaction or fsync was requested do it now that other locks | ||||
* are no longer needed. | * are no longer needed. | ||||
*/ | */ | ||||
if (error == 0 && endoff != 0) { | if (error == 0 && endoff != 0) { | ||||
do { | |||||
error = UFS_TRUNCATE(tdvp, endoff, IO_NORMAL | | error = UFS_TRUNCATE(tdvp, endoff, IO_NORMAL | | ||||
(DOINGASYNC(tdvp) ? 0 : IO_SYNC), tcnp->cn_cred); | (DOINGASYNC(tdvp) ? 0 : IO_SYNC), tcnp->cn_cred); | ||||
} while (error == ERELOOKUP); | |||||
if (error != 0 && !ffs_fsfail_cleanup(VFSTOUFS(mp), error)) | if (error != 0 && !ffs_fsfail_cleanup(VFSTOUFS(mp), error)) | ||||
vn_printf(tdvp, | vn_printf(tdvp, | ||||
"ufs_rename: failed to truncate, error %d\n", | "ufs_rename: failed to truncate, error %d\n", | ||||
error); | error); | ||||
#ifdef UFS_DIRHASH | #ifdef UFS_DIRHASH | ||||
if (error != 0) | if (error != 0) | ||||
ufsdirhash_free(tdp); | ufsdirhash_free(tdp); | ||||
else if (tdp->i_dirhash != NULL) | else if (tdp->i_dirhash != NULL) | ||||
ufsdirhash_dirtrunc(tdp, endoff); | ufsdirhash_dirtrunc(tdp, endoff); | ||||
#endif | #endif | ||||
/* | /* | ||||
* Even if the directory compaction failed, rename was | * Even if the directory compaction failed, rename was | ||||
* succesful. Do not propagate a UFS_TRUNCATE() error | * succesful. Do not propagate a UFS_TRUNCATE() error | ||||
* to the caller. | * to the caller. | ||||
*/ | */ | ||||
error = 0; | error = 0; | ||||
} | } | ||||
if (error == 0 && tdp->i_flag & IN_NEEDSYNC) | if (error == 0 && tdp->i_flag & IN_NEEDSYNC) { | ||||
do { | |||||
error = VOP_FSYNC(tdvp, MNT_WAIT, td); | error = VOP_FSYNC(tdvp, MNT_WAIT, td); | ||||
} while (error == ERELOOKUP); | |||||
} | |||||
vput(tdvp); | vput(tdvp); | ||||
return (error); | return (error); | ||||
bad: | bad: | ||||
fip->i_effnlink--; | fip->i_effnlink--; | ||||
fip->i_nlink--; | fip->i_nlink--; | ||||
DIP_SET(fip, i_nlink, fip->i_nlink); | DIP_SET(fip, i_nlink, fip->i_nlink); | ||||
UFS_INODE_SET_FLAG(fip, IN_CHANGE); | UFS_INODE_SET_FLAG(fip, IN_CHANGE); | ||||
▲ Show 20 Lines • Show All 232 Lines • ▼ Show 20 Lines | |||||
#endif | #endif | ||||
dp = VTOI(dvp); | dp = VTOI(dvp); | ||||
if (dp->i_nlink >= UFS_LINK_MAX) { | if (dp->i_nlink >= UFS_LINK_MAX) { | ||||
error = EMLINK; | error = EMLINK; | ||||
goto out; | goto out; | ||||
} | } | ||||
dmode = vap->va_mode & 0777; | dmode = vap->va_mode & 0777; | ||||
dmode |= IFDIR; | dmode |= IFDIR; | ||||
/* | /* | ||||
* Must simulate part of ufs_makeinode here to acquire the inode, | * Must simulate part of ufs_makeinode here to acquire the inode, | ||||
* but not have it entered in the parent directory. The entry is | * but not have it entered in the parent directory. The entry is | ||||
* made later after writing "." and ".." entries. | * made later after writing "." and ".." entries. | ||||
*/ | */ | ||||
if (dp->i_effnlink < 2) { | if (dp->i_effnlink < 2) { | ||||
print_bad_link_count("ufs_mkdir", dvp); | print_bad_link_count("ufs_mkdir", dvp); | ||||
error = EINVAL; | error = EINVAL; | ||||
goto out; | goto out; | ||||
} | } | ||||
if (DOINGSOFTDEP(dvp)) { | |||||
error = softdep_prelink(dvp, NULL, true); | |||||
if (error != 0) { | |||||
MPASS(error == ERELOOKUP); | |||||
return (error); | |||||
} | |||||
} | |||||
error = UFS_VALLOC(dvp, dmode, cnp->cn_cred, &tvp); | error = UFS_VALLOC(dvp, dmode, cnp->cn_cred, &tvp); | ||||
if (error) | if (error) | ||||
goto out; | goto out; | ||||
vn_seqc_write_begin(tvp); | vn_seqc_write_begin(tvp); | ||||
ip = VTOI(tvp); | ip = VTOI(tvp); | ||||
ip->i_gid = dp->i_gid; | ip->i_gid = dp->i_gid; | ||||
DIP_SET(ip, i_gid, dp->i_gid); | DIP_SET(ip, i_gid, dp->i_gid); | ||||
#ifdef SUIDDIR | #ifdef SUIDDIR | ||||
▲ Show 20 Lines • Show All 240 Lines • ▼ Show 20 Lines | if ((dp->i_flags & APPEND) | ||||
|| (ip->i_flags & (NOUNLINK | IMMUTABLE | APPEND))) { | || (ip->i_flags & (NOUNLINK | IMMUTABLE | APPEND))) { | ||||
error = EPERM; | error = EPERM; | ||||
goto out; | goto out; | ||||
} | } | ||||
if (vp->v_mountedhere != 0) { | if (vp->v_mountedhere != 0) { | ||||
error = EINVAL; | error = EINVAL; | ||||
goto out; | goto out; | ||||
} | } | ||||
if (DOINGSOFTDEP(dvp)) { | |||||
error = softdep_prelink(dvp, vp, false); | |||||
if (error != 0) { | |||||
MPASS(error == ERELOOKUP); | |||||
return (error); | |||||
} | |||||
} | |||||
#ifdef UFS_GJOURNAL | #ifdef UFS_GJOURNAL | ||||
ufs_gjournal_orphan(vp); | ufs_gjournal_orphan(vp); | ||||
#endif | #endif | ||||
/* | /* | ||||
* Delete reference to directory before purging | * Delete reference to directory before purging | ||||
* inode. If we crash in between, the directory | * inode. If we crash in between, the directory | ||||
* will be reattached to lost+found, | * will be reattached to lost+found, | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 502 Lines • ▼ Show 20 Lines | |||||
#endif | #endif | ||||
*vpp = NULL; | *vpp = NULL; | ||||
if ((mode & IFMT) == 0) | if ((mode & IFMT) == 0) | ||||
mode |= IFREG; | mode |= IFREG; | ||||
if (pdir->i_effnlink < 2) { | if (pdir->i_effnlink < 2) { | ||||
print_bad_link_count(callfunc, dvp); | print_bad_link_count(callfunc, dvp); | ||||
return (EINVAL); | return (EINVAL); | ||||
} | |||||
if (DOINGSOFTDEP(dvp)) { | |||||
error = softdep_prelink(dvp, NULL, true); | |||||
if (error != 0) { | |||||
MPASS(error == ERELOOKUP); | |||||
return (error); | |||||
} | |||||
} | } | ||||
error = UFS_VALLOC(dvp, mode, cnp->cn_cred, &tvp); | error = UFS_VALLOC(dvp, mode, cnp->cn_cred, &tvp); | ||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
ip = VTOI(tvp); | ip = VTOI(tvp); | ||||
ip->i_gid = pdir->i_gid; | ip->i_gid = pdir->i_gid; | ||||
DIP_SET(ip, i_gid, pdir->i_gid); | DIP_SET(ip, i_gid, pdir->i_gid); | ||||
#ifdef SUIDDIR | #ifdef SUIDDIR | ||||
▲ Show 20 Lines • Show All 262 Lines • Show Last 20 Lines |
What is this?