Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F160653829
D57658.id180251.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
11 KB
Referenced Files
None
Subscribers
None
D57658.id180251.diff
View Options
diff --git a/lib/libsys/rename.2 b/lib/libsys/rename.2
--- a/lib/libsys/rename.2
+++ b/lib/libsys/rename.2
@@ -143,6 +143,12 @@
.Fa to
exists, the request fails with the error
.Er EEXIST .
+.It Dv AT_RENAME_EXCHANGE
+Atomically exchange the files pointed to by the
+.Fa from
+and
+.Fa to
+names.
.El
.Sh RETURN VALUES
.Rv -std rename
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
@@ -962,6 +962,89 @@
return (error);
}
+static int
+tmpfs_rename_check_parent(struct tmpfs_mount *tmp, struct tmpfs_node *fdnode,
+ struct vnode *fvp, struct tmpfs_node *fnode, struct tmpfs_dirent *de,
+ struct tmpfs_node *tdnode, struct ucred *tcred)
+{
+ struct tmpfs_node *n;
+ int error;
+
+ TMPFS_NODE_LOCK(fnode);
+ error = tmpfs_access_locked(fvp, fnode, VWRITE, tcred);
+ TMPFS_NODE_UNLOCK(fnode);
+ if (error != 0)
+ return (error);
+
+ /*
+ * Ensure the target directory is not a child of the
+ * directory being moved. Otherwise, we'd end up
+ * with stale nodes.
+ *
+ * TMPFS_LOCK guaranties that no nodes are freed while
+ * traversing the list. Nodes can only be marked as
+ * removed: tn_parent == NULL.
+ */
+ n = tdnode;
+ TMPFS_LOCK(tmp);
+ TMPFS_NODE_LOCK(n);
+ while (n != n->tn_dir.tn_parent) {
+ struct tmpfs_node *parent;
+
+ if (n == fnode) {
+ TMPFS_NODE_UNLOCK(n);
+ TMPFS_UNLOCK(tmp);
+ return (EINVAL);
+ }
+ parent = n->tn_dir.tn_parent;
+ TMPFS_NODE_UNLOCK(n);
+ if (parent == NULL) {
+ n = NULL;
+ break;
+ }
+ TMPFS_NODE_LOCK(parent);
+ if (parent->tn_dir.tn_parent == NULL) {
+ TMPFS_NODE_UNLOCK(parent);
+ n = NULL;
+ break;
+ }
+ n = parent;
+ }
+ TMPFS_UNLOCK(tmp);
+ if (n == NULL)
+ return (EINVAL);
+
+ TMPFS_NODE_UNLOCK(n);
+
+ return (0);
+}
+
+static void
+tmpfs_rename_set_parent(struct tmpfs_node *fdnode, struct tmpfs_node *fnode,
+ struct tmpfs_dirent *de, struct tmpfs_node *tdnode)
+{
+ /* Adjust the parent pointer. */
+ TMPFS_VALIDATE_DIR(fnode);
+ TMPFS_NODE_LOCK(de->td_node);
+ de->td_node->tn_dir.tn_parent = tdnode;
+ TMPFS_NODE_UNLOCK(de->td_node);
+
+ /*
+ * As a result of changing the target of the '..'
+ * entry, the link count of the source and target
+ * directories has to be adjusted.
+ */
+ TMPFS_NODE_LOCK(tdnode);
+ TMPFS_ASSERT_LOCKED(tdnode);
+ tdnode->tn_links++;
+ TMPFS_NODE_UNLOCK(tdnode);
+
+ TMPFS_NODE_LOCK(fdnode);
+ TMPFS_ASSERT_LOCKED(fdnode);
+ fdnode->tn_links--;
+ TMPFS_NODE_UNLOCK(fdnode);
+}
+
static int
tmpfs_rename(struct vop_rename_args *v)
{
@@ -972,7 +1055,7 @@
struct vnode *tvp = v->a_tvp;
struct componentname *tcnp = v->a_tcnp;
char *newname;
- struct tmpfs_dirent *de;
+ struct tmpfs_dirent *de, *tde;
struct tmpfs_mount *tmp;
struct tmpfs_node *fdnode;
struct tmpfs_node *fnode;
@@ -980,8 +1063,11 @@
struct tmpfs_node *tdnode;
int error;
bool want_seqc_end;
+ bool exchange;
want_seqc_end = false;
+ newname = NULL;
+ error = 0;
/*
* Disallow cross-device renames.
@@ -993,16 +1079,15 @@
goto out;
}
- if ((v->a_flags & ~(AT_RENAME_NOREPLACE)) != 0) {
+ if ((v->a_flags & ~(AT_RENAME_NOREPLACE | AT_RENAME_EXCHANGE)) != 0) {
error = EOPNOTSUPP;
goto out;
}
/* If source and target are the same file, there is nothing to do. */
- if (fvp == tvp) {
- error = 0;
+ if (fvp == tvp)
goto out;
- }
+ exchange = (v->a_flags & AT_RENAME_EXCHANGE) != 0;
/*
* If we need to move the directory between entries, lock the
@@ -1055,6 +1140,8 @@
fdnode = VP_TO_TMPFS_DIR(fdvp);
fnode = VP_TO_TMPFS_NODE(fvp);
de = tmpfs_dir_lookup(fdnode, fnode, fcnp);
+ if (exchange)
+ tde = tmpfs_dir_lookup(tdnode, tnode, tcnp);
/*
* Entry can disappear before we lock fdvp.
@@ -1068,6 +1155,13 @@
goto out_locked;
}
MPASS(de->td_node == fnode);
+ if (exchange) {
+ if (tde == NULL) {
+ error = ENOENT;
+ goto out_locked;
+ }
+ MPASS(tde->td_node == tnode);
+ }
/*
* If re-naming a directory to another preexisting directory
@@ -1085,27 +1179,32 @@
goto out_locked;
}
- if (fnode->tn_type == VDIR && tnode->tn_type == VDIR) {
- if (tnode->tn_size != 0 &&
- ((tcnp->cn_flags & IGNOREWHITEOUT) == 0 ||
- tnode->tn_size > tnode->tn_dir.tn_wht_size)) {
- error = ENOTEMPTY;
+ if (!exchange) {
+ if (fnode->tn_type == VDIR && tnode->tn_type == VDIR) {
+ if (tnode->tn_size != 0 &&
+ ((tcnp->cn_flags & IGNOREWHITEOUT) == 0 ||
+ tnode->tn_size >
+ tnode->tn_dir.tn_wht_size)) {
+ error = ENOTEMPTY;
+ goto out_locked;
+ }
+ } else if (fnode->tn_type == VDIR &&
+ tnode->tn_type != VDIR) {
+ error = ENOTDIR;
goto out_locked;
+ } else if (fnode->tn_type != VDIR &&
+ tnode->tn_type == VDIR) {
+ error = EISDIR;
+ goto out_locked;
+ } else {
+ MPASS(fnode->tn_type != VDIR &&
+ tnode->tn_type != VDIR);
}
- } else if (fnode->tn_type == VDIR && tnode->tn_type != VDIR) {
- error = ENOTDIR;
- goto out_locked;
- } else if (fnode->tn_type != VDIR && tnode->tn_type == VDIR) {
- error = EISDIR;
- goto out_locked;
- } else {
- MPASS(fnode->tn_type != VDIR &&
- tnode->tn_type != VDIR);
}
}
- if ((fnode->tn_flags & (NOUNLINK | IMMUTABLE | APPEND))
- || (fdnode->tn_flags & (APPEND | IMMUTABLE))) {
+ if ((fnode->tn_flags & (NOUNLINK | IMMUTABLE | APPEND)) != 0 ||
+ (fdnode->tn_flags & (APPEND | IMMUTABLE)) != 0) {
error = EPERM;
goto out_locked;
}
@@ -1114,11 +1213,9 @@
* Ensure that we have enough memory to hold the new name, if it
* has to be changed.
*/
- if (fcnp->cn_namelen != tcnp->cn_namelen ||
- bcmp(fcnp->cn_nameptr, tcnp->cn_nameptr, fcnp->cn_namelen) != 0) {
+ if (!exchange && (fcnp->cn_namelen != tcnp->cn_namelen ||
+ bcmp(fcnp->cn_nameptr, tcnp->cn_nameptr, fcnp->cn_namelen) != 0))
newname = malloc(tcnp->cn_namelen, M_TMPFSNAME, M_WAITOK);
- } else
- newname = NULL;
/*
* If the node is being moved to another directory, we have to do
@@ -1129,87 +1226,45 @@
* In case we are moving a directory, we have to adjust its
* parent to point to the new parent.
*/
- if (de->td_node->tn_type == VDIR) {
- struct tmpfs_node *n;
-
- TMPFS_NODE_LOCK(fnode);
- error = tmpfs_access_locked(fvp, fnode, VWRITE,
- tcnp->cn_cred);
- TMPFS_NODE_UNLOCK(fnode);
- if (error) {
- if (newname != NULL)
- free(newname, M_TMPFSNAME);
+ if (fnode->tn_type == VDIR) {
+ error = tmpfs_rename_check_parent(tmp, fdnode, fvp,
+ fnode, de, tdnode, tcnp->cn_cred);
+ if (error != 0) {
+ free(newname, M_TMPFSNAME);
goto out_locked;
}
-
- /*
- * Ensure the target directory is not a child of the
- * directory being moved. Otherwise, we'd end up
- * with stale nodes.
- */
- n = tdnode;
- /*
- * TMPFS_LOCK guaranties that no nodes are freed while
- * traversing the list. Nodes can only be marked as
- * removed: tn_parent == NULL.
- */
- TMPFS_LOCK(tmp);
- TMPFS_NODE_LOCK(n);
- while (n != n->tn_dir.tn_parent) {
- struct tmpfs_node *parent;
-
- if (n == fnode) {
- TMPFS_NODE_UNLOCK(n);
- TMPFS_UNLOCK(tmp);
- error = EINVAL;
- if (newname != NULL)
- free(newname, M_TMPFSNAME);
- goto out_locked;
- }
- parent = n->tn_dir.tn_parent;
- TMPFS_NODE_UNLOCK(n);
- if (parent == NULL) {
- n = NULL;
- break;
- }
- TMPFS_NODE_LOCK(parent);
- if (parent->tn_dir.tn_parent == NULL) {
- TMPFS_NODE_UNLOCK(parent);
- n = NULL;
- break;
- }
- n = parent;
- }
- TMPFS_UNLOCK(tmp);
- if (n == NULL) {
- error = EINVAL;
- if (newname != NULL)
- free(newname, M_TMPFSNAME);
- goto out_locked;
- }
- TMPFS_NODE_UNLOCK(n);
-
- /* Adjust the parent pointer. */
- TMPFS_VALIDATE_DIR(fnode);
- TMPFS_NODE_LOCK(de->td_node);
- de->td_node->tn_dir.tn_parent = tdnode;
- TMPFS_NODE_UNLOCK(de->td_node);
-
- /*
- * As a result of changing the target of the '..'
- * entry, the link count of the source and target
- * directories has to be adjusted.
- */
- TMPFS_NODE_LOCK(tdnode);
- TMPFS_ASSERT_LOCKED(tdnode);
- tdnode->tn_links++;
- TMPFS_NODE_UNLOCK(tdnode);
-
- TMPFS_NODE_LOCK(fdnode);
- TMPFS_ASSERT_LOCKED(fdnode);
- fdnode->tn_links--;
- TMPFS_NODE_UNLOCK(fdnode);
}
+ if (exchange && tnode->tn_type == VDIR) {
+ MPASS(newname == NULL);
+ error = tmpfs_rename_check_parent(tmp, tdnode, tvp,
+ tnode, tde, fdnode, fcnp->cn_cred);
+ if (error != 0)
+ goto out_locked;
+ }
+ if (fnode->tn_type == VDIR)
+ tmpfs_rename_set_parent(fdnode, fnode, de, tdnode);
+ if (exchange && tnode->tn_type == VDIR)
+ tmpfs_rename_set_parent(tdnode, tnode, tde, fdnode);
+ }
+ if (exchange) {
+ de->td_node = tnode;
+ tde->td_node = fnode;
+ if (tmpfs_use_nc(fvp)) {
+ cache_purge(fvp);
+ cache_purge(tvp);
+ cache_enter(tdvp, fvp, tcnp);
+ cache_enter(fdvp, tvp, fcnp);
+ }
+ fdnode->tn_status |= TMPFS_NODE_MODIFIED | TMPFS_NODE_CHANGED;
+ fdnode->tn_accessed = true;
+ tmpfs_update(fdvp);
+ if (fdvp != tdvp) {
+ tdnode->tn_status |= TMPFS_NODE_MODIFIED |
+ TMPFS_NODE_CHANGED;
+ tdnode->tn_accessed = true;
+ tmpfs_update(tdvp);
+ }
+ goto out_locked;
}
/*
diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c
--- a/sys/kern/vfs_syscalls.c
+++ b/sys/kern/vfs_syscalls.c
@@ -3786,7 +3786,10 @@
int error;
short irflag;
- if ((flags & ~(AT_RENAME_NOREPLACE)) != 0)
+ if ((flags & ~(AT_RENAME_NOREPLACE | AT_RENAME_EXCHANGE)) != 0)
+ return (EINVAL);
+ if ((flags & (AT_RENAME_NOREPLACE | AT_RENAME_EXCHANGE)) ==
+ (AT_RENAME_NOREPLACE | AT_RENAME_EXCHANGE))
return (EINVAL);
again:
tmp = mp = NULL;
@@ -3799,7 +3802,8 @@
return (error);
} else {
#endif
- NDINIT_ATRIGHTS(&fromnd, DELETE, WANTPARENT | AUDITVNODE1,
+ NDINIT_ATRIGHTS(&fromnd, (flags & AT_RENAME_EXCHANGE) != 0 ? RENAME :
+ DELETE, WANTPARENT | AUDITVNODE1,
pathseg, old, oldfd, &cap_renameat_source_rights);
if ((error = namei(&fromnd)) != 0)
return (error);
@@ -3845,6 +3849,10 @@
error = EEXIST;
goto out;
}
+ if (tvp == NULL && (flags & AT_RENAME_EXCHANGE) != 0) {
+ error = ENOENT;
+ goto out;
+ }
error = vn_start_write(fvp, &mp, V_NOWAIT);
if (error != 0) {
again1:
@@ -3887,12 +3895,15 @@
goto out;
}
if (tvp != NULL) {
- if (fvp->v_type == VDIR && tvp->v_type != VDIR) {
- error = ENOTDIR;
- goto out;
- } else if (fvp->v_type != VDIR && tvp->v_type == VDIR) {
- error = EISDIR;
- goto out;
+ if ((flags & AT_RENAME_EXCHANGE) == 0) {
+ if (fvp->v_type == VDIR && tvp->v_type != VDIR) {
+ error = ENOTDIR;
+ goto out;
+ } else if (fvp->v_type != VDIR &&
+ tvp->v_type == VDIR) {
+ error = EISDIR;
+ goto out;
+ }
}
#ifdef CAPABILITIES
if (newfd != AT_FDCWD && (tond.ni_resflags & NIRES_ABS) == 0) {
diff --git a/sys/sys/fcntl.h b/sys/sys/fcntl.h
--- a/sys/sys/fcntl.h
+++ b/sys/sys/fcntl.h
@@ -260,7 +260,9 @@
#define AT_EMPTY_PATH 0x4000 /* Operate on dirfd if path is empty */
#define AT_RENAME_NOREPLACE 0x0001 /* Fail rename if target exists */
+#define AT_RENAME_EXCHANGE 0x0002 /* Atomically exchange 'from' and 'to' */
#define RENAME_NOREPLACE AT_RENAME_NOREPLACE
+#define RENAME_EXCHANGE AT_RENAME_EXCHANGE
#endif /* __BSD_VISIBLE */
/*
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Jun 27, 12:38 PM (11 h, 25 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
34400462
Default Alt Text
D57658.id180251.diff (11 KB)
Attached To
Mode
D57658: Implement AT_RENAME_EXCHANGE flag for VFS and tmpfs
Attached
Detach File
Event Timeline
Log In to Comment