aboutsummaryrefslogtreecommitdiff
path: root/lib/std/os.zig
diff options
context:
space:
mode:
authorMaciej Walczak <14938807+xackus@users.noreply.github.com>2020-08-11 21:49:43 +0200
committerGitHub <noreply@github.com>2020-08-11 15:49:43 -0400
commit6febe7e977072fea1bf7b6003be2d6c1f3654905 (patch)
tree77c7d9bae1ec5ea2e7254eefba240f50f6904390 /lib/std/os.zig
parent2b28cebf644b29543fcb52504b11931a7c797ffb (diff)
downloadzig-6febe7e977072fea1bf7b6003be2d6c1f3654905.tar.gz
zig-6febe7e977072fea1bf7b6003be2d6c1f3654905.zip
copy_file_range linux syscall (#6010)
Diffstat (limited to 'lib/std/os.zig')
-rw-r--r--lib/std/os.zig79
1 files changed, 79 insertions, 0 deletions
diff --git a/lib/std/os.zig b/lib/std/os.zig
index 04c2340cad..ae2b232ef7 100644
--- a/lib/std/os.zig
+++ b/lib/std/os.zig
@@ -4926,6 +4926,85 @@ pub fn sendfile(
return total_written;
}
+pub const CopyFileRangeError = error{
+ FileTooBig,
+ InputOutput,
+ IsDir,
+ OutOfMemory,
+ NoSpaceLeft,
+ Unseekable,
+ PermissionDenied,
+ FileBusy,
+} || PReadError || PWriteError || UnexpectedError;
+
+/// Transfer data between file descriptors at specified offsets.
+/// Returns the number of bytes written, which can less than requested.
+///
+/// The `copy_file_range` call copies `len` bytes from one file descriptor to another. When possible,
+/// this is done within the operating system kernel, which can provide better performance
+/// characteristics than transferring data from kernel to user space and back, such as with
+/// `pread` and `pwrite` calls.
+///
+/// `fd_in` must be a file descriptor opened for reading, and `fd_out` must be a file descriptor
+/// opened for writing. They may be any kind of file descriptor; however, if `fd_in` is not a regular
+/// file system file, it may cause this function to fall back to calling `pread` and `pwrite`, in which case
+/// atomicity guarantees no longer apply.
+///
+/// If `fd_in` and `fd_out` are the same, source and target ranges must not overlap.
+/// The file descriptor seek positions are ignored and not updated.
+/// When `off_in` is past the end of the input file, it successfully reads 0 bytes.
+///
+/// `flags` has different meanings per operating system; refer to the respective man pages.
+///
+/// These systems support in-kernel data copying:
+/// * Linux 4.5 (cross-filesystem 5.3)
+///
+/// Other systems fall back to calling `pread` / `pwrite`.
+///
+/// Maximum offsets on Linux are `math.maxInt(i64)`.
+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 {
+ const use_c = std.c.versionCheck(.{ .major = 2, .minor = 27, .patch = 0 }).ok;
+
+ // TODO support for other systems than linux
+ const try_syscall = comptime std.Target.current.os.isAtLeast(.linux, .{ .major = 4, .minor = 5 }) != false;
+
+ if (use_c or try_syscall) {
+ const sys = if (use_c) std.c else linux;
+
+ var off_in_copy = @bitCast(i64, off_in);
+ var off_out_copy = @bitCast(i64, off_out);
+
+ const rc = sys.copy_file_range(fd_in, &off_in_copy, fd_out, &off_out_copy, len, flags);
+
+ // TODO avoid wasting a syscall every time if kernel is too old and returns ENOSYS https://github.com/ziglang/zig/issues/1018
+
+ switch (sys.getErrno(rc)) {
+ 0 => return @intCast(usize, rc),
+ EBADF => unreachable,
+ EFBIG => return error.FileTooBig,
+ EIO => return error.InputOutput,
+ EISDIR => return error.IsDir,
+ ENOMEM => return error.OutOfMemory,
+ ENOSPC => return error.NoSpaceLeft,
+ EOVERFLOW => return error.Unseekable,
+ EPERM => return error.PermissionDenied,
+ ETXTBSY => return error.FileBusy,
+ EINVAL => {}, // these may not be regular files, try fallback
+ EXDEV => {}, // support for cross-filesystem copy added in Linux 5.3, use fallback
+ ENOSYS => {}, // syscall added in Linux 4.5, use fallback
+ else => |err| return unexpectedErrno(err),
+ }
+ }
+
+ var buf: [8 * 4096]u8 = undefined;
+ const adjusted_count = math.min(buf.len, len);
+ const amt_read = try pread(fd_in, buf[0..adjusted_count], off_in);
+ // TODO without @as the line below fails to compile for wasm32-wasi:
+ // error: integer value 0 cannot be coerced to type 'os.PWriteError!usize'
+ if (amt_read == 0) return @as(usize, 0);
+ return pwrite(fd_out, buf[0..amt_read], off_out);
+}
+
pub const PollError = error{
/// The kernel had no space to allocate file descriptor tables.
SystemResources,