Skip to content

Commit cdab606

Browse files
authored
When possible, clone files on Linux and FreeBSD. (swiftlang#1736)
* When possible, clone files on Linux and FreeBSD. This change adds support for file cloning on Linux and FreeBSD where supported by the underlying OS and filesystem. I don't have a great story for unit testing here as if either call fails, we'll fall back to block-based copying which, in the general case, will appear to the caller like a successful copy. Furthermore, our CI systems aren't configured with file systems that support file cloning anyway. Resolves swiftlang#1727. * That's not what those are called... * FICLONE is a complex macro * Include fs.h in the right place * Move shims * Remove duplicate include
1 parent 4526802 commit cdab606

File tree

2 files changed

+40
-1
lines changed

2 files changed

+40
-1
lines changed

Sources/FoundationEssentials/FileManager/FileOperations.swift

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -872,6 +872,10 @@ enum _FileOperations {
872872
}
873873
#else
874874
#if !canImport(Darwin)
875+
#if os(FreeBSD)
876+
private static let _freeBSDRelease = getosreldate()
877+
#endif
878+
875879
private static func _copyRegularFile(_ srcPtr: UnsafePointer<CChar>, _ dstPtr: UnsafePointer<CChar>, delegate: some LinkOrCopyDelegate) throws {
876880
let srcfd = open(srcPtr, O_RDONLY)
877881
guard srcfd >= 0 else {
@@ -906,8 +910,25 @@ enum _FileOperations {
906910
// no copying required
907911
return
908912
}
909-
913+
910914
let total: Int = Int(fileInfo.st_size)
915+
916+
// Attempt to clone the file using platform-specific API. If this operation fails, don't throw
917+
// an error and just fall back to chunked writes.
918+
#if os(Linux)
919+
if ioctl(dstfd, _filemanager_shims_FICLONE(), srcfd) != -1 {
920+
return
921+
}
922+
#elseif os(FreeBSD)
923+
if _freeBSDRelease >= 1500000 {
924+
// `COPY_FILE_RANGE_CLONE` was introduced in FreeBSD 15.0.
925+
let flags = _filemanager_shims_COPY_FILE_RANGE_CLONE()
926+
if copy_file_range(srcfd, nil, dstfd, nil, total, flags) != -1 {
927+
return
928+
}
929+
}
930+
#endif
931+
911932
// Respect the optimal block size for the file system if available
912933
// Some platforms including WASI don't provide this information, so we
913934
// fall back to the default chunk size 4KB, which is a common page size.

Sources/_FoundationCShims/include/filemanager_shims.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,24 @@
4747
extern int _mkpath_np(const char *path, mode_t omode, const char **firstdir);
4848
#endif
4949

50+
#if TARGET_OS_LINUX
51+
#include <linux/fs.h>
52+
53+
static inline unsigned long _filemanager_shims_FICLONE(void) { return FICLONE; }
54+
#endif
55+
56+
#if TARGET_OS_BSD
57+
static inline unsigned int _filemanager_shims_COPY_FILE_RANGE_CLONE(void) {
58+
#if defined(COPY_FILE_RANGE_CLONE)
59+
return COPY_FILE_RANGE_CLONE;
60+
#else
61+
// Compiled against an older unistd.h, but presumably running on FreeBSD 15.0
62+
// or newer. SEE: https://github.com/freebsd/freebsd-src/blob/main/sys/sys/unistd.h
63+
return 0x00800000;
64+
#endif
65+
}
66+
#endif
67+
5068
#if TARGET_OS_ANDROID && __ANDROID_API__ <= 23
5169
#include <grp.h>
5270
#include <sys/types.h>

0 commit comments

Comments
 (0)