authorgravatar for thatlemon@gmail.comLemonBoy <thatlemon@gmail.com> 2020-10-03 19:51:22+02:00
committergravatar for thatlemon@gmail.comLemonBoy <thatlemon@gmail.com> 2020-10-03 19:51:22+02:00
log8b4f5f039df65980d0a0ec6add1caf4c9bf2468c
tree100a497fee7a76a750100da7f23b190043530799
parent0f248e0988b24bf4fcdaadd0e47127462206711e

Alternative strategy to avoid calling stat()

This is an optimization as it avoids an extra syscall, but it's also a workaround for fstat being not available on Windows.

2 files changed, 22 insertions(+), 19 deletions(-)

lib/std/fs.zig+1-1
......@@ -1823,7 +1823,7 @@ pub const Dir = struct {
18231823 var atomic_file = try dest_dir.atomicFile(dest_path, .{ .mode = mode });
18241824 defer atomic_file.deinit();
18251825
1826 try os.copy_file(in_file.handle, atomic_file.file.handle, .{ .file_size = size });
1826 try os.copy_file(in_file.handle, atomic_file.file.handle, .{});
18271827 return atomic_file.finish();
18281828 }
18291829
lib/std/os.zig+21-18
......@@ -5024,12 +5024,10 @@ pub fn copy_file_range(fd_in: fd_t, off_in: u64, fd_out: fd_t, off_out: u64, len
50245024
50255025var has_copy_file_range_syscall = std.atomic.Int(u1).init(1);
50265026
5027pub const CopyFileOptions = struct {
5028 /// Size in bytes of the source files, if available saves a call to stat().
5029 file_size: ?u64 = null,
5030};
5027pub const CopyFileOptions = struct {};
50315028
50325029pub const CopyFileError = error{
5030 BadFileHandle,
50335031 SystemResources,
50345032 FileTooBig,
50355033 InputOutput,
......@@ -5057,20 +5055,18 @@ pub fn copy_file(fd_in: fd_t, fd_out: fd_t, options: CopyFileOptions) CopyFileEr
50575055 }
50585056 }
50595057
5060 const src_file_size = options.file_size orelse
5061 @bitCast(u64, (try fstat(fd_in)).size);
5062 var remaining = src_file_size;
5063
50645058 if (std.Target.current.os.tag == .linux) {
50655059 // Try copy_file_range first as that works at the FS level and is the
50665060 // most efficient method (if available).
50675061 if (has_copy_file_range_syscall.get() != 0) {
5068 cfr_loop: while (remaining > 0) {
5069 const copy_amt = math.cast(usize, remaining) catch math.maxInt(usize);
5070 const rc = linux.copy_file_range(fd_in, null, fd_out, null, copy_amt, 0);
5062 cfr_loop: while (true) {
5063 // The kernel checks `file_pos+count` for overflow, use a 32 bit
5064 // value so that the syscall won't return EINVAL except for
5065 // impossibly large files.
5066 const rc = linux.copy_file_range(fd_in, null, fd_out, null, math.maxInt(u32), 0);
50715067 switch (errno(rc)) {
50725068 0 => {},
5073 EBADF => unreachable,
5069 EBADF => return error.BadFileHandle,
50745070 EFBIG => return error.FileTooBig,
50755071 EIO => return error.InputOutput,
50765072 EISDIR => return error.IsDir,
......@@ -5079,27 +5075,34 @@ pub fn copy_file(fd_in: fd_t, fd_out: fd_t, options: CopyFileOptions) CopyFileEr
50795075 EOVERFLOW => return error.Unseekable,
50805076 EPERM => return error.PermissionDenied,
50815077 ETXTBSY => return error.FileBusy,
5082 // these may not be regular files, try fallback
5078 // These may not be regular files, try fallback
50835079 EINVAL => break :cfr_loop,
5084 // support for cross-filesystem copy added in Linux 5.3, use fallback
5080 // Support for cross-filesystem copy added in Linux 5.3, use fallback
50855081 EXDEV => break :cfr_loop,
5086 // syscall added in Linux 4.5, use fallback
5082 // Syscall added in Linux 4.5, use fallback
50875083 ENOSYS => {
50885084 has_copy_file_range_syscall.set(0);
50895085 break :cfr_loop;
50905086 },
50915087 else => |err| return unexpectedErrno(err),
50925088 }
5093 remaining -= rc;
5089 // Terminate when no data was copied
5090 if (rc == 0) return;
50945091 }
5095 return;
5092 // This point is reached when an error occurred, hopefully no data
5093 // was transferred yet
50965094 }
50975095 }
50985096
50995097 // Sendfile is a zero-copy mechanism iff the OS supports it, otherwise the
51005098 // fallback code will copy the contents chunk by chunk.
51015099 const empty_iovec = [0]iovec_const{};
5102 _ = try sendfile(fd_out, fd_in, 0, remaining, &empty_iovec, &empty_iovec, 0);
5100 var offset: u64 = 0;
5101 sendfile_loop: while (true) {
5102 const amt = try sendfile(fd_out, fd_in, offset, 0, &empty_iovec, &empty_iovec, 0);
5103 if (amt == 0) break :sendfile_loop;
5104 offset += amt;
5105 }
51035106}
51045107
51055108pub const PollError = error{