Page MenuHomeFreeBSD

D45987.1775888958.diff
No OneTemporary

Size
9 KB
Referenced Files
None
Subscribers
None

D45987.1775888958.diff

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

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)

Event Timeline