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.
This commit is contained in:
parent
0f248e0988
commit
8b4f5f039d
@ -1823,7 +1823,7 @@ pub const Dir = struct {
|
|||||||
var atomic_file = try dest_dir.atomicFile(dest_path, .{ .mode = mode });
|
var atomic_file = try dest_dir.atomicFile(dest_path, .{ .mode = mode });
|
||||||
defer atomic_file.deinit();
|
defer atomic_file.deinit();
|
||||||
|
|
||||||
try os.copy_file(in_file.handle, atomic_file.file.handle, .{ .file_size = size });
|
try os.copy_file(in_file.handle, atomic_file.file.handle, .{});
|
||||||
return atomic_file.finish();
|
return atomic_file.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5024,12 +5024,10 @@ pub fn copy_file_range(fd_in: fd_t, off_in: u64, fd_out: fd_t, off_out: u64, len
|
|||||||
|
|
||||||
var has_copy_file_range_syscall = std.atomic.Int(u1).init(1);
|
var has_copy_file_range_syscall = std.atomic.Int(u1).init(1);
|
||||||
|
|
||||||
pub const CopyFileOptions = struct {
|
pub const CopyFileOptions = struct {};
|
||||||
/// Size in bytes of the source files, if available saves a call to stat().
|
|
||||||
file_size: ?u64 = null,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const CopyFileError = error{
|
pub const CopyFileError = error{
|
||||||
|
BadFileHandle,
|
||||||
SystemResources,
|
SystemResources,
|
||||||
FileTooBig,
|
FileTooBig,
|
||||||
InputOutput,
|
InputOutput,
|
||||||
@ -5057,20 +5055,18 @@ pub fn copy_file(fd_in: fd_t, fd_out: fd_t, options: CopyFileOptions) CopyFileEr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const src_file_size = options.file_size orelse
|
|
||||||
@bitCast(u64, (try fstat(fd_in)).size);
|
|
||||||
var remaining = src_file_size;
|
|
||||||
|
|
||||||
if (std.Target.current.os.tag == .linux) {
|
if (std.Target.current.os.tag == .linux) {
|
||||||
// Try copy_file_range first as that works at the FS level and is the
|
// Try copy_file_range first as that works at the FS level and is the
|
||||||
// most efficient method (if available).
|
// most efficient method (if available).
|
||||||
if (has_copy_file_range_syscall.get() != 0) {
|
if (has_copy_file_range_syscall.get() != 0) {
|
||||||
cfr_loop: while (remaining > 0) {
|
cfr_loop: while (true) {
|
||||||
const copy_amt = math.cast(usize, remaining) catch math.maxInt(usize);
|
// The kernel checks `file_pos+count` for overflow, use a 32 bit
|
||||||
const rc = linux.copy_file_range(fd_in, null, fd_out, null, copy_amt, 0);
|
// value so that the syscall won't return EINVAL except for
|
||||||
|
// impossibly large files.
|
||||||
|
const rc = linux.copy_file_range(fd_in, null, fd_out, null, math.maxInt(u32), 0);
|
||||||
switch (errno(rc)) {
|
switch (errno(rc)) {
|
||||||
0 => {},
|
0 => {},
|
||||||
EBADF => unreachable,
|
EBADF => return error.BadFileHandle,
|
||||||
EFBIG => return error.FileTooBig,
|
EFBIG => return error.FileTooBig,
|
||||||
EIO => return error.InputOutput,
|
EIO => return error.InputOutput,
|
||||||
EISDIR => return error.IsDir,
|
EISDIR => return error.IsDir,
|
||||||
@ -5079,27 +5075,34 @@ pub fn copy_file(fd_in: fd_t, fd_out: fd_t, options: CopyFileOptions) CopyFileEr
|
|||||||
EOVERFLOW => return error.Unseekable,
|
EOVERFLOW => return error.Unseekable,
|
||||||
EPERM => return error.PermissionDenied,
|
EPERM => return error.PermissionDenied,
|
||||||
ETXTBSY => return error.FileBusy,
|
ETXTBSY => return error.FileBusy,
|
||||||
// these may not be regular files, try fallback
|
// These may not be regular files, try fallback
|
||||||
EINVAL => break :cfr_loop,
|
EINVAL => break :cfr_loop,
|
||||||
// support for cross-filesystem copy added in Linux 5.3, use fallback
|
// Support for cross-filesystem copy added in Linux 5.3, use fallback
|
||||||
EXDEV => break :cfr_loop,
|
EXDEV => break :cfr_loop,
|
||||||
// syscall added in Linux 4.5, use fallback
|
// Syscall added in Linux 4.5, use fallback
|
||||||
ENOSYS => {
|
ENOSYS => {
|
||||||
has_copy_file_range_syscall.set(0);
|
has_copy_file_range_syscall.set(0);
|
||||||
break :cfr_loop;
|
break :cfr_loop;
|
||||||
},
|
},
|
||||||
else => |err| return unexpectedErrno(err),
|
else => |err| return unexpectedErrno(err),
|
||||||
}
|
}
|
||||||
remaining -= rc;
|
// Terminate when no data was copied
|
||||||
|
if (rc == 0) return;
|
||||||
}
|
}
|
||||||
return;
|
// This point is reached when an error occurred, hopefully no data
|
||||||
|
// was transferred yet
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sendfile is a zero-copy mechanism iff the OS supports it, otherwise the
|
// Sendfile is a zero-copy mechanism iff the OS supports it, otherwise the
|
||||||
// fallback code will copy the contents chunk by chunk.
|
// fallback code will copy the contents chunk by chunk.
|
||||||
const empty_iovec = [0]iovec_const{};
|
const empty_iovec = [0]iovec_const{};
|
||||||
_ = try sendfile(fd_out, fd_in, 0, remaining, &empty_iovec, &empty_iovec, 0);
|
var offset: u64 = 0;
|
||||||
|
sendfile_loop: while (true) {
|
||||||
|
const amt = try sendfile(fd_out, fd_in, offset, 0, &empty_iovec, &empty_iovec, 0);
|
||||||
|
if (amt == 0) break :sendfile_loop;
|
||||||
|
offset += amt;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const PollError = error{
|
pub const PollError = error{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user