@@ -6260,32 +6260,65 @@ pub const CopyFileRangeError = error{
6260
6260
6261
6261
var has_copy_file_range_syscall = std .atomic .Atomic (bool ).init (true );
6262
6262
6263
- /// Transfer data between file descriptors at specified offsets.
6264
- /// Returns the number of bytes written, which can less than requested.
6263
+ /// Transfer data between file descriptors at specified offsets. Returns the
6264
+ /// number of bytes written, which can be less than requested.
6265
+ /// The `copy_file_range` call copies `len` bytes from one file descriptor to
6266
+ /// another. When possible, this is done within the operating system kernel,
6267
+ /// which can provide better performance characteristics than transferring data
6268
+ /// from kernel to user space and back.
6265
6269
///
6266
- /// The `copy_file_range` call copies `len` bytes from one file descriptor to another. When possible,
6267
- /// this is done within the operating system kernel, which can provide better performance
6268
- /// characteristics than transferring data from kernel to user space and back, such as with
6269
- /// `pread` and `pwrite` calls.
6270
- ///
6271
- /// `fd_in` must be a file descriptor opened for reading, and `fd_out` must be a file descriptor
6272
- /// opened for writing. They may be any kind of file descriptor; however, if `fd_in` is not a regular
6273
- /// file system file, it may cause this function to fall back to calling `pread` and `pwrite`, in which case
6274
- /// atomicity guarantees no longer apply.
6270
+ /// `fd_in` must be a file descriptor opened for reading, and `fd_out` must be
6271
+ /// a file descriptor opened for writing. They may be any kind of file
6272
+ /// descriptor; however, if `fd_in` is not a regular file system file, it may
6273
+ /// cause this function to fall back to calling `pread` and `pwrite`, in which
6274
+ /// case atomicity guarantees no longer apply.
6275
6275
///
6276
- /// If `fd_in` and `fd_out` are the same, source and target ranges must not overlap.
6277
- /// The file descriptor seek positions are ignored and not updated.
6278
- /// When `off_in` is past the end of the input file, it successfully reads 0 bytes.
6276
+ /// If `fd_in` and `fd_out` are the same, source and target ranges must not
6277
+ /// overlap. The file descriptor seek positions are ignored and not updated.
6278
+ /// When `off_in` is past the end of the input file, it successfully reads 0
6279
+ /// bytes.
6279
6280
///
6280
- /// `flags` has different meanings per operating system; refer to the respective man pages.
6281
- ///
6282
- /// These systems support in-kernel data copying:
6283
- /// * Linux 4.5 (cross-filesystem 5.3)
6281
+ /// Depending on the system, a few mechanisms are tried:
6284
6282
///
6285
- /// Other systems fall back to calling `pread` / `pwrite`.
6283
+ /// * Linux 4.5+: ioctl.FICLONERANGE: atomic, O(1), the fastest method. Uses
6284
+ /// copy-on-write, therefore saves disk space and time. As of Linux 5.19,
6285
+ /// available on btrfs, cifs, nfs, ocfs2, overlayfs and xfs. The source and
6286
+ /// destination must be on the same file system.
6287
+ /// * Linux 4.5+: `copy_file_range(2)` via a libc wrapper (if libc is linked)
6288
+ /// or a syscall. This works at the block layer, so cross-filesystem
6289
+ /// in-kernel copying (Linux 5.3+) is possible.
6290
+ /// * Everything else: `pread`/`pwrite`.
6286
6291
///
6287
6292
/// Maximum offsets on Linux are `math.maxInt(i64)`.
6288
- pub fn copy_file_range (fd_in : fd_t , off_in : u64 , fd_out : fd_t , off_out : u64 , len : usize , flags : u32 ) CopyFileRangeError ! usize {
6293
+ ///
6294
+ pub fn copy_file_range (fd_in : fd_t , off_in : u64 , fd_out : fd_t , off_out : u64 , len : usize ) CopyFileRangeError ! usize {
6295
+ const ficlone_range = comptime builtin .os .isAtLeast (.linux , .{ .major = 4 , .minor = 5 }) orelse true ;
6296
+
6297
+ if (ficlone_range ) {
6298
+ const arg = linux.FICLONERANGE_arg {
6299
+ .src_fd = fd_in ,
6300
+ .src_offset = off_in ,
6301
+ .src_length = len ,
6302
+ .dest_offset = off_out ,
6303
+ };
6304
+ while (true ) {
6305
+ const rc = system .ioctl (fd_out , linux .T .FICLONERANGE , @ptrToInt (& arg ));
6306
+ switch (system .getErrno (rc )) {
6307
+ .SUCCESS = > return @intCast (usize , rc ),
6308
+ .INTR = > continue ,
6309
+ .BADF = > return error .FilesOpenedWithWrongFlags ,
6310
+ // may not be regular files, try fallback
6311
+ .INVAL = > break ,
6312
+ .ISDIR = > return error .IsDir ,
6313
+ // not regular files or FS does not support reflinking; fallback worthy
6314
+ .OPNOTSUPP = > break ,
6315
+ .PERM = > return error .PermissionDenied ,
6316
+ .TXTBSY = > return error .FileBusy ,
6317
+ else = > | err | return unexpectedErrno (err ),
6318
+ }
6319
+ }
6320
+ }
6321
+
6289
6322
const call_cfr = comptime if (builtin .os .tag == .wasi )
6290
6323
// WASI-libc doesn't have copy_file_range.
6291
6324
false
@@ -6298,7 +6331,7 @@ pub fn copy_file_range(fd_in: fd_t, off_in: u64, fd_out: fd_t, off_out: u64, len
6298
6331
var off_in_copy = @bitCast (i64 , off_in );
6299
6332
var off_out_copy = @bitCast (i64 , off_out );
6300
6333
6301
- const rc = system .copy_file_range (fd_in , & off_in_copy , fd_out , & off_out_copy , len , flags );
6334
+ const rc = system .copy_file_range (fd_in , & off_in_copy , fd_out , & off_out_copy , len , 0 );
6302
6335
switch (system .getErrno (rc )) {
6303
6336
.SUCCESS = > return @intCast (usize , rc ),
6304
6337
.BADF = > return error .FilesOpenedWithWrongFlags ,
0 commit comments