diff --git a/sys/fs/tmpfs/tmpfs_vnops.c b/sys/fs/tmpfs/tmpfs_vnops.c index 8436dc3bc48c..c492f003c97d 100644 --- a/sys/fs/tmpfs/tmpfs_vnops.c +++ b/sys/fs/tmpfs/tmpfs_vnops.c @@ -1078,7 +1078,9 @@ tmpfs_rename(struct vop_rename_args *v) } if (fnode->tn_type == VDIR && tnode->tn_type == VDIR) { - if (tnode->tn_size > tnode->tn_dir.tn_wht_size) { + if ((tnode->tn_size != 0 && + (tcnp->cn_flags & DROPWHITEOUT) == 0) || + tnode->tn_size > tnode->tn_dir.tn_wht_size) { error = ENOTEMPTY; goto out_locked; } @@ -1312,6 +1314,7 @@ tmpfs_rmdir(struct vop_rmdir_args *v) { struct vnode *dvp = v->a_dvp; struct vnode *vp = v->a_vp; + struct componentname *cnp = v->a_cnp; int error; struct tmpfs_dirent *de; @@ -1327,7 +1330,9 @@ tmpfs_rmdir(struct vop_rmdir_args *v) * Directories with more than two non-whiteout entries ('.' and '..') * cannot be removed. */ - if (node->tn_size > node->tn_dir.tn_wht_size) { + if ((node->tn_size != 0 && + (cnp->cn_flags & DROPWHITEOUT) == 0) || + node->tn_size > node->tn_dir.tn_wht_size) { error = ENOTEMPTY; goto out; } @@ -1344,10 +1349,10 @@ tmpfs_rmdir(struct vop_rmdir_args *v) /* Get the directory entry associated with node (vp). This was * filled by tmpfs_lookup while looking up the entry. */ - de = tmpfs_dir_lookup(dnode, node, v->a_cnp); + de = tmpfs_dir_lookup(dnode, node, cnp); MPASS(TMPFS_DIRENT_MATCHES(de, - v->a_cnp->cn_nameptr, - v->a_cnp->cn_namelen)); + cnp->cn_nameptr, + cnp->cn_namelen)); /* Check flags to see if we are allowed to remove the directory. */ if ((dnode->tn_flags & APPEND) != 0 || @@ -1362,8 +1367,8 @@ tmpfs_rmdir(struct vop_rmdir_args *v) if (node->tn_size > 0) tmpfs_dir_clear(vp); - if (v->a_cnp->cn_flags & DOWHITEOUT) - tmpfs_dir_whiteout_add(dvp, v->a_cnp); + if (cnp->cn_flags & DOWHITEOUT) + tmpfs_dir_whiteout_add(dvp, cnp); /* No vnode should be allocated for this entry from this point */ TMPFS_NODE_LOCK(node); diff --git a/sys/fs/unionfs/union_vnops.c b/sys/fs/unionfs/union_vnops.c index 3f39352ea5c0..60baa4aeb6b9 100644 --- a/sys/fs/unionfs/union_vnops.c +++ b/sys/fs/unionfs/union_vnops.c @@ -1732,7 +1732,7 @@ unionfs_rmdir(struct vop_rmdir_args *ap) } ump = MOUNTTOUNIONFSMOUNT(ap->a_vp->v_mount); if (ump->um_whitemode == UNIONFS_WHITE_ALWAYS || lvp != NULLVP) - cnp->cn_flags |= DOWHITEOUT; + cnp->cn_flags |= (DOWHITEOUT | DROPWHITEOUT); int udvp_lkflags, uvp_lkflags; unionfs_forward_vop_start_pair(udvp, &udvp_lkflags, uvp, &uvp_lkflags); diff --git a/sys/sys/namei.h b/sys/sys/namei.h index 2ea4f502e8fd..5745e13ebab5 100644 --- a/sys/sys/namei.h +++ b/sys/sys/namei.h @@ -159,7 +159,7 @@ int cache_fplookup(struct nameidata *ndp, enum cache_fpl_status *status, */ #define RDONLY 0x00000200 /* lookup with read-only semantics */ #define ISRESTARTED 0x00000400 /* restarted namei */ -/* UNUSED 0x00000800 */ +#define DROPWHITEOUT 0x00000800 /* discard whiteouts on removing otherwise-empty dir */ #define ISWHITEOUT 0x00001000 /* found whiteout */ #define DOWHITEOUT 0x00002000 /* do whiteouts */ #define WILLBEDIR 0x00004000 /* new files will be dirs; allow trailing / */ diff --git a/sys/ufs/ufs/ufs_extern.h b/sys/ufs/ufs/ufs_extern.h index b1d55ed1f180..ccd9046a5fa8 100644 --- a/sys/ufs/ufs/ufs_extern.h +++ b/sys/ufs/ufs/ufs_extern.h @@ -59,7 +59,7 @@ int ufs_bmap_seekdata(struct vnode *, off_t *); int ufs_checkpath(ino_t, ino_t, struct inode *, struct ucred *, ino_t *); void ufs_dirbad(struct inode *, doff_t, char *); int ufs_dirbadentry(struct vnode *, struct direct *, int); -int ufs_dirempty(struct inode *, ino_t, struct ucred *); +int ufs_dirempty(struct inode *, ino_t, struct ucred *, int); int ufs_extread(struct vop_read_args *); int ufs_extwrite(struct vop_write_args *); void ufs_makedirentry(struct inode *, struct componentname *, diff --git a/sys/ufs/ufs/ufs_lookup.c b/sys/ufs/ufs/ufs_lookup.c index 2d6c79970c96..eaf37c58756b 100644 --- a/sys/ufs/ufs/ufs_lookup.c +++ b/sys/ufs/ufs/ufs_lookup.c @@ -1298,7 +1298,8 @@ ufs_dirrewrite(struct inode *dp, struct inode *oip, ino_t newinum, int newtype, * NB: does not handle corrupted directories. */ int -ufs_dirempty(struct inode *ip, ino_t parentino, struct ucred *cred) +ufs_dirempty(struct inode *ip, ino_t parentino, struct ucred *cred, + int skipwhiteout) { doff_t off; struct dirtemplate dbuf; @@ -1321,7 +1322,8 @@ ufs_dirempty(struct inode *ip, ino_t parentino, struct ucred *cred) if (dp->d_reclen == 0) return (0); /* skip empty entries */ - if (dp->d_ino == 0 || dp->d_ino == UFS_WINO) + if (dp->d_ino == 0 || + (skipwhiteout != 0 && dp->d_ino == UFS_WINO)) continue; /* accept only "." and ".." */ # if (BYTE_ORDER == LITTLE_ENDIAN) diff --git a/sys/ufs/ufs/ufs_vnops.c b/sys/ufs/ufs/ufs_vnops.c index c62583afaab6..a09b893a4d8d 100644 --- a/sys/ufs/ufs/ufs_vnops.c +++ b/sys/ufs/ufs/ufs_vnops.c @@ -1625,7 +1625,8 @@ ufs_rename( */ if ((tip->i_mode & IFMT) == IFDIR) { if ((tip->i_effnlink > 2) || - !ufs_dirempty(tip, tdp->i_number, tcnp->cn_cred)) { + !ufs_dirempty(tip, tdp->i_number, tcnp->cn_cred, + (tcnp->cn_flags & DROPWHITEOUT) != 0)) { error = ENOTEMPTY; goto bad; } @@ -2281,7 +2282,8 @@ ufs_rmdir( error = EINVAL; goto out; } - if (!ufs_dirempty(ip, dp->i_number, cnp->cn_cred)) { + if (!ufs_dirempty(ip, dp->i_number, cnp->cn_cred, + (cnp->cn_flags & DROPWHITEOUT) != 0)) { error = ENOTEMPTY; goto out; }