git: 8e6e77ec25bc - main - renameat(2): add AT_RENAME_EXCHANGE flag
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Sat, 11 Jul 2026 22:05:22 UTC
The branch main has been updated by kib:
URL: https://cgit.FreeBSD.org/src/commit/?id=8e6e77ec25bc5809500d29bf318a843b40205657
commit 8e6e77ec25bc5809500d29bf318a843b40205657
Author: Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2026-06-18 18:12:01 +0000
Commit: Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2026-07-11 22:04:43 +0000
renameat(2): add AT_RENAME_EXCHANGE flag
Currently no filesystems support it.
Reviewed by: mckusick
Discussed with: markj
Tested by: pho
Sponsored by: The FreeBSD Foundation
MFC after: 1 week
Differential revision: https://reviews.freebsd.org/D57658
---
sys/kern/vfs_syscalls.c | 70 ++++++++++++++++++++++++++++++++++++++++---------
sys/sys/fcntl.h | 2 ++
2 files changed, 59 insertions(+), 13 deletions(-)
diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c
index 59d0c9c40927..29d503bbb3fa 100644
--- a/sys/kern/vfs_syscalls.c
+++ b/sys/kern/vfs_syscalls.c
@@ -3752,12 +3752,13 @@ sys_renameat2(struct thread *td, struct renameat2_args *uap)
#ifdef MAC
static int
kern_renameat_mac(struct thread *td, int oldfd, const char *old, int newfd,
- const char *new, enum uio_seg pathseg, struct nameidata *fromnd)
+ const char *new, enum uio_seg pathseg, struct nameidata *fromnd, int op,
+ int ndflags)
{
int error;
- NDINIT_ATRIGHTS(fromnd, DELETE, LOCKPARENT | LOCKLEAF | AUDITVNODE1,
- pathseg, old, oldfd, &cap_renameat_source_rights);
+ NDINIT_ATRIGHTS(fromnd, op, LOCKPARENT | LOCKLEAF | AUDITVNODE1 |
+ ndflags, pathseg, old, oldfd, &cap_renameat_source_rights);
if ((error = namei(fromnd)) != 0)
return (error);
error = mac_vnode_check_rename_from(td->td_ucred, fromnd->ni_dvp,
@@ -3782,23 +3783,35 @@ kern_renameat(struct thread *td, int oldfd, const char *old, int newfd,
struct vnode *tvp, *fvp, *tdvp;
struct nameidata fromnd, tond;
uint64_t tondflags;
- int error;
+ int error, fndflags, op;
short irflag;
+ bool exchange;
- 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);
+ if ((flags & AT_RENAME_EXCHANGE) != 0) {
+ op = RENAME;
+ exchange = true;
+ } else {
+ op = DELETE;
+ exchange = false;
+ }
+ fndflags = 0;
again:
tmp = mp = NULL;
bwillwrite();
#ifdef MAC
if (mac_vnode_check_rename_from_enabled()) {
error = kern_renameat_mac(td, oldfd, old, newfd, new, pathseg,
- &fromnd);
+ &fromnd, op, fndflags);
if (error != 0)
return (error);
} else {
#endif
- NDINIT_ATRIGHTS(&fromnd, DELETE, WANTPARENT | AUDITVNODE1,
+ NDINIT_ATRIGHTS(&fromnd, op, WANTPARENT | AUDITVNODE1 | fndflags,
pathseg, old, oldfd, &cap_renameat_source_rights);
if ((error = namei(&fromnd)) != 0)
return (error);
@@ -3806,6 +3819,11 @@ again:
}
#endif
fvp = fromnd.ni_vp;
+ if (exchange && fvp == NULL) {
+ NDFREE_PNBUF(&fromnd);
+ vrele(fromnd.ni_dvp);
+ return (ENOENT);
+ }
tondflags = LOCKPARENT | LOCKLEAF | NOCACHE | AUDITVNODE2;
if (fromnd.ni_vp->v_type == VDIR)
tondflags |= WILLBEDIR;
@@ -3844,6 +3862,29 @@ again:
error = EEXIST;
goto out;
}
+ if (exchange) {
+ if (tvp == NULL) {
+ error = ENOENT;
+ goto out;
+ }
+ if (tvp->v_type == VDIR && fndflags == 0) {
+ fndflags = WILLBEDIR;
+again2:
+ NDFREE_PNBUF(&fromnd);
+ vrele(fromnd.ni_dvp);
+ vrele(fvp);
+ NDFREE_PNBUF(&tond);
+ VOP_VPUT_PAIR(tdvp, &tvp, true);
+ error = sig_intr();
+ if (error != 0)
+ goto out1;
+ goto again;
+ }
+ if (tvp->v_type != VDIR && fndflags != 0) {
+ fndflags = 0;
+ goto again2;
+ }
+ }
error = vn_start_write(fvp, &mp, V_NOWAIT);
if (error != 0) {
again1:
@@ -3886,12 +3927,15 @@ again1:
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 (!exchange) {
+ 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
index 80cbca4ea753..44f43f745762 100644
--- a/sys/sys/fcntl.h
+++ b/sys/sys/fcntl.h
@@ -260,7 +260,9 @@ typedef __pid_t pid_t;
#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 */
/*