Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F144630779
D45987.1775888958.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
9 KB
Referenced Files
None
Subscribers
None
D45987.1775888958.diff
View Options
diff --git a/sys/fs/tmpfs/tmpfs.h b/sys/fs/tmpfs/tmpfs.h
--- a/sys/fs/tmpfs/tmpfs.h
+++ b/sys/fs/tmpfs/tmpfs.h
@@ -292,6 +292,15 @@
*/
off_t tn_readdir_lastn;
struct tmpfs_dirent * tn_readdir_lastp;
+
+ /*
+ * Total size of whiteout directory entries. This
+ * must be a multiple of sizeof(struct tmpfs_dirent)
+ * and is used to determine whether a directory is
+ * empty (excluding whiteout entries) during rename/
+ * rmdir operations.
+ */
+ off_t tn_wht_size; /* (v) */
} tn_dir;
/* Valid when tn_type == VLNK. */
@@ -484,6 +493,7 @@
struct uio *, int, uint64_t *, int *);
int tmpfs_dir_whiteout_add(struct vnode *, struct componentname *);
void tmpfs_dir_whiteout_remove(struct vnode *, struct componentname *);
+void tmpfs_dir_clear_whiteouts(struct vnode *);
int tmpfs_reg_resize(struct vnode *, off_t, boolean_t);
int tmpfs_reg_punch_hole(struct vnode *vp, off_t *, off_t *);
int tmpfs_chflags(struct vnode *, u_long, struct ucred *, struct thread *);
@@ -533,6 +543,8 @@
#define TMPFS_VALIDATE_DIR(node) do { \
MPASS((node)->tn_type == VDIR); \
MPASS((node)->tn_size % sizeof(struct tmpfs_dirent) == 0); \
+ MPASS((node)->tn_dir.tn_wht_size % sizeof(struct tmpfs_dirent) == 0); \
+ MPASS((node)->tn_dir.tn_wht_size <= (node)->tn_size); \
} while (0)
/*
diff --git a/sys/fs/tmpfs/tmpfs_subr.c b/sys/fs/tmpfs/tmpfs_subr.c
--- a/sys/fs/tmpfs/tmpfs_subr.c
+++ b/sys/fs/tmpfs/tmpfs_subr.c
@@ -646,6 +646,7 @@
nnode->tn_dir.tn_parent = (parent == NULL) ? nnode : parent;
nnode->tn_dir.tn_readdir_lastn = 0;
nnode->tn_dir.tn_readdir_lastp = NULL;
+ nnode->tn_dir.tn_wht_size = 0;
nnode->tn_links++;
TMPFS_NODE_LOCK(nnode->tn_dir.tn_parent);
nnode->tn_dir.tn_parent->tn_links++;
@@ -1831,13 +1832,16 @@
tmpfs_dir_whiteout_add(struct vnode *dvp, struct componentname *cnp)
{
struct tmpfs_dirent *de;
+ struct tmpfs_node *dnode;
int error;
error = tmpfs_alloc_dirent(VFS_TO_TMPFS(dvp->v_mount), NULL,
cnp->cn_nameptr, cnp->cn_namelen, &de);
if (error != 0)
return (error);
+ dnode = VP_TO_TMPFS_DIR(dvp);
tmpfs_dir_attach(dvp, de);
+ dnode->tn_dir.tn_wht_size += sizeof(*de);
return (0);
}
@@ -1845,13 +1849,43 @@
tmpfs_dir_whiteout_remove(struct vnode *dvp, struct componentname *cnp)
{
struct tmpfs_dirent *de;
+ struct tmpfs_node *dnode;
- de = tmpfs_dir_lookup(VP_TO_TMPFS_DIR(dvp), NULL, cnp);
+ dnode = VP_TO_TMPFS_DIR(dvp);
+ de = tmpfs_dir_lookup(dnode, NULL, cnp);
MPASS(de != NULL && de->td_node == NULL);
+ MPASS(dnode->tn_dir.tn_wht_size >= sizeof(*de));
+ dnode->tn_dir.tn_wht_size -= sizeof(*de);
tmpfs_dir_detach(dvp, de);
tmpfs_free_dirent(VFS_TO_TMPFS(dvp->v_mount), de);
}
+/*
+ * Frees any dirents still associated with the directory represented
+ * by dvp in preparation for the removal of the directory. This is
+ * required when removing a directory which contains only whiteout
+ * entries.
+ */
+void
+tmpfs_dir_clear_whiteouts(struct vnode *dvp)
+{
+ struct tmpfs_dir_cursor dc;
+ struct tmpfs_dirent *de;
+ struct tmpfs_node *dnode;
+
+ dnode = VP_TO_TMPFS_DIR(dvp);
+
+ while ((de = tmpfs_dir_first(dnode, &dc)) != NULL) {
+ KASSERT(de->td_node == NULL, ("%s: non-whiteout dirent %p",
+ __func__, de));
+ dnode->tn_dir.tn_wht_size -= sizeof(*de);
+ tmpfs_dir_detach(dvp, de);
+ tmpfs_free_dirent(VFS_TO_TMPFS(dvp->v_mount), de);
+ }
+ MPASS(dnode->tn_size == 0);
+ MPASS(dnode->tn_dir.tn_wht_size == 0);
+}
+
/*
* Resizes the aobj associated with the regular file pointed to by 'vp' to the
* size 'newsize'. 'vp' must point to a vnode that represents a regular file.
diff --git a/sys/fs/tmpfs/tmpfs_vnops.c b/sys/fs/tmpfs/tmpfs_vnops.c
--- a/sys/fs/tmpfs/tmpfs_vnops.c
+++ b/sys/fs/tmpfs/tmpfs_vnops.c
@@ -1078,7 +1078,9 @@
}
if (fnode->tn_type == VDIR && tnode->tn_type == VDIR) {
- if (tnode->tn_size > 0) {
+ if (tnode->tn_size != 0 &&
+ ((tcnp->cn_flags & IGNOREWHITEOUT) == 0 ||
+ tnode->tn_size > tnode->tn_dir.tn_wht_size)) {
error = ENOTEMPTY;
goto out_locked;
}
@@ -1239,6 +1241,16 @@
tde = tmpfs_dir_lookup(tdnode, tnode, tcnp);
tmpfs_dir_detach(tdvp, tde);
+ /*
+ * If we are overwriting a directory, per the ENOTEMPTY check
+ * above it must either be empty or contain only whiteout
+ * entries. In the latter case (which can only happen if
+ * IGNOREWHITEOUT was passed in tcnp->cn_flags), clear the
+ * whiteout entries to avoid leaking memory.
+ */
+ if (tnode->tn_type == VDIR && tnode->tn_size > 0)
+ tmpfs_dir_clear_whiteouts(tvp);
+
/* Update node's ctime because of possible hardlinks. */
tnode->tn_status |= TMPFS_NODE_CHANGED;
tmpfs_update(tvp);
@@ -1309,6 +1321,7 @@
{
struct vnode *dvp = v->a_dvp;
struct vnode *vp = v->a_vp;
+ struct componentname *cnp = v->a_cnp;
int error;
struct tmpfs_dirent *de;
@@ -1320,12 +1333,16 @@
dnode = VP_TO_TMPFS_DIR(dvp);
node = VP_TO_TMPFS_DIR(vp);
- /* Directories with more than two entries ('.' and '..') cannot be
- * removed. */
- if (node->tn_size > 0) {
- error = ENOTEMPTY;
- goto out;
- }
+ /*
+ * Directories with more than two non-whiteout entries ('.' and '..')
+ * cannot be removed.
+ */
+ if (node->tn_size != 0 &&
+ ((cnp->cn_flags & IGNOREWHITEOUT) == 0 ||
+ node->tn_size > node->tn_dir.tn_wht_size)) {
+ error = ENOTEMPTY;
+ goto out;
+ }
if ((dnode->tn_flags & APPEND)
|| (node->tn_flags & (NOUNLINK | IMMUTABLE | APPEND))) {
@@ -1334,15 +1351,15 @@
}
/* This invariant holds only if we are not trying to remove "..".
- * We checked for that above so this is safe now. */
+ * We checked for that above so this is safe now. */
MPASS(node->tn_dir.tn_parent == dnode);
/* 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 ||
@@ -1353,8 +1370,19 @@
/* Detach the directory entry from the directory (dnode). */
tmpfs_dir_detach(dvp, de);
- if (v->a_cnp->cn_flags & DOWHITEOUT)
- tmpfs_dir_whiteout_add(dvp, v->a_cnp);
+
+ /*
+ * If we are removing a directory, per the ENOTEMPTY check above it
+ * must either be empty or contain only whiteout entries. In the
+ * latter case (which can only happen if IGNOREWHITEOUT was passed
+ * in cnp->cn_flags), clear the whiteout entries to avoid leaking
+ * memory.
+ */
+ if (node->tn_size > 0)
+ tmpfs_dir_clear_whiteouts(vp);
+
+ 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
--- a/sys/fs/unionfs/union_vnops.c
+++ b/sys/fs/unionfs/union_vnops.c
@@ -1732,7 +1732,7 @@
}
ump = MOUNTTOUNIONFSMOUNT(ap->a_vp->v_mount);
if (ump->um_whitemode == UNIONFS_WHITE_ALWAYS || lvp != NULLVP)
- cnp->cn_flags |= DOWHITEOUT;
+ cnp->cn_flags |= (DOWHITEOUT | IGNOREWHITEOUT);
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
--- a/sys/sys/namei.h
+++ b/sys/sys/namei.h
@@ -159,7 +159,7 @@
*/
#define RDONLY 0x00000200 /* lookup with read-only semantics */
#define ISRESTARTED 0x00000400 /* restarted namei */
-/* UNUSED 0x00000800 */
+#define IGNOREWHITEOUT 0x00000800 /* ignore whiteouts, e.g. when checking if a dir is empty */
#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
--- a/sys/ufs/ufs/ufs_extern.h
+++ b/sys/ufs/ufs/ufs_extern.h
@@ -59,7 +59,7 @@
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
--- a/sys/ufs/ufs/ufs_lookup.c
+++ b/sys/ufs/ufs/ufs_lookup.c
@@ -1298,7 +1298,8 @@
* 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 @@
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
--- a/sys/ufs/ufs/ufs_vnops.c
+++ b/sys/ufs/ufs/ufs_vnops.c
@@ -1625,7 +1625,8 @@
*/
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 & IGNOREWHITEOUT) != 0)) {
error = ENOTEMPTY;
goto bad;
}
@@ -2281,7 +2282,8 @@
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 & IGNOREWHITEOUT) != 0)) {
error = ENOTEMPTY;
goto out;
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Apr 11, 6:29 AM (17 h, 9 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28323015
Default Alt Text
D45987.1775888958.diff (9 KB)
Attached To
Mode
D45987: tmpfs: Account for whiteouts during rename/rmdir
Attached
Detach File
Event Timeline
Log In to Comment