Skip to content

Commit 85292b9

Browse files
committed
Linux: Check renameat2() is available in the header files and kernel.
- Add a dummy _CF_renameat2() if SYS_renameat2 is not defined. - Add a check that the system call is defined and the kernel version is high enough (>= 3.15) to call SYS_renameat2().
1 parent 52334ad commit 85292b9

File tree

3 files changed

+36
-8
lines changed

3 files changed

+36
-8
lines changed

CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h

+14-2
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
#include <sys/stat.h>
6060
#include <sys/syscall.h>
6161
#elif TARGET_OS_LINUX
62+
#include <errno.h>
6263
#include <features.h>
6364

6465
#if __GLIBC_PREREQ(2, 28) == 0
@@ -594,10 +595,21 @@ _stat_with_btime(const char *filename, struct stat *buffer, struct timespec *bti
594595
#endif // __NR_statx
595596

596597
static unsigned int const _CF_renameat2_RENAME_EXCHANGE = 1 << 1;
597-
static int _CF_renameat2(int olddirfd, const char *_Nonnull oldpath,
598-
int newdirfd, const char *_Nonnull newpath, unsigned int flags) {
598+
#ifdef SYS_renameat2
599+
static _Bool const _CFHasRenameat2 = 1;
600+
static inline int _CF_renameat2(int olddirfd, const char *_Nonnull oldpath,
601+
int newdirfd, const char *_Nonnull newpath, unsigned int flags) {
599602
return syscall(SYS_renameat2, olddirfd, oldpath, newdirfd, newpath, flags);
600603
}
604+
#else
605+
static _Bool const _CFHasRenameat2 = 0;
606+
static inline int _CF_renameat2(int olddirfd, const char *_Nonnull oldpath,
607+
int newdirfd, const char *_Nonnull newpath, unsigned int flags) {
608+
return ENOSYS;
609+
}
610+
#endif // __SYS_renameat2
611+
612+
601613
#endif // TARGET_OS_LINUX
602614

603615
#if __HAS_STATX

Foundation/FileManager+POSIX.swift

+16-6
Original file line numberDiff line numberDiff line change
@@ -1185,7 +1185,7 @@ internal func _contentsEqual(atPath path1: String, andPath path2: String) -> Boo
11851185
throw _NSErrorWithErrno(error, reading: false, path: path)
11861186
}
11871187
}
1188-
1188+
11891189
internal func _replaceItem(at originalItemURL: URL, withItemAt newItemURL: URL, backupItemName: String?, options: ItemReplacementOptions = [], allowPlatformSpecificSyscalls: Bool = true) throws -> URL? {
11901190

11911191
// 1. Make a backup, if asked to.
@@ -1245,11 +1245,21 @@ internal func _contentsEqual(atPath path1: String, andPath path2: String) -> Boo
12451245
return newItemURL.withUnsafeFileSystemRepresentation { (newItemFS) -> Int32? in
12461246
if let originalFS = originalFS,
12471247
let newItemFS = newItemFS {
1248-
if _CF_renameat2(AT_FDCWD, originalFS, AT_FDCWD, newItemFS, _CF_renameat2_RENAME_EXCHANGE) == 0 {
1249-
return nil
1250-
} else {
1251-
return errno
1252-
}
1248+
1249+
#if os(Linux)
1250+
if _CFHasRenameat2 && kernelSupportsRenameat2 {
1251+
if _CF_renameat2(AT_FDCWD, originalFS, AT_FDCWD, newItemFS, _CF_renameat2_RENAME_EXCHANGE) == 0 {
1252+
return nil
1253+
} else {
1254+
return errno
1255+
}
1256+
}
1257+
#endif
1258+
if renameat(AT_FDCWD, originalFS, AT_FDCWD, newItemFS) == 0 {
1259+
return nil
1260+
} else {
1261+
return errno
1262+
}
12531263
} else {
12541264
return Int32(EINVAL)
12551265
}

Foundation/FileManager.swift

+6
Original file line numberDiff line numberDiff line change
@@ -841,6 +841,12 @@ open class FileManager : NSObject {
841841
let requiredVersion = OperatingSystemVersion(majorVersion: 4, minorVersion: 11, patchVersion: 0)
842842
return ProcessInfo.processInfo.isOperatingSystemAtLeast(requiredVersion)
843843
}()
844+
845+
// renameat2() is only supported by Linux kernels >= 3.15
846+
internal lazy var kernelSupportsRenameat2: Bool = {
847+
let requiredVersion = OperatingSystemVersion(majorVersion: 3, minorVersion: 15, patchVersion: 0)
848+
return ProcessInfo.processInfo.isOperatingSystemAtLeast(requiredVersion)
849+
}()
844850
#endif
845851

846852
/* -contentsEqualAtPath:andPath: does not take into account data stored in the resource fork or filesystem extended attributes.

0 commit comments

Comments
 (0)