Commit 4a67dd04c9
Changed files (5)
lib
lib/std/fs/file.zig
@@ -271,6 +271,8 @@ pub const File = struct {
/// The `iovecs` parameter is mutable because this function needs to mutate the fields in
/// order to handle partial reads from the underlying OS layer.
pub fn readvAll(self: File, iovecs: []os.iovec) ReadError!void {
+ if (iovecs.len == 0) return;
+
var i: usize = 0;
while (true) {
var amt = try self.readv(iovecs[i..]);
@@ -295,6 +297,8 @@ pub const File = struct {
/// The `iovecs` parameter is mutable because this function needs to mutate the fields in
/// order to handle partial reads from the underlying OS layer.
pub fn preadvAll(self: File, iovecs: []const os.iovec, offset: u64) PReadError!void {
+ if (iovecs.len == 0) return;
+
var i: usize = 0;
var off: usize = 0;
while (true) {
@@ -354,6 +358,8 @@ pub const File = struct {
/// The `iovecs` parameter is mutable because this function needs to mutate the fields in
/// order to handle partial writes from the underlying OS layer.
pub fn writevAll(self: File, iovecs: []os.iovec_const) WriteError!void {
+ if (iovecs.len == 0) return;
+
var i: usize = 0;
while (true) {
var amt = try self.writev(iovecs[i..]);
@@ -378,6 +384,8 @@ pub const File = struct {
/// The `iovecs` parameter is mutable because this function needs to mutate the fields in
/// order to handle partial writes from the underlying OS layer.
pub fn pwritevAll(self: File, iovecs: []os.iovec_const, offset: usize) PWriteError!void {
+ if (iovecs.len == 0) return;
+
var i: usize = 0;
var off: usize = 0;
while (true) {
@@ -393,6 +401,89 @@ pub const File = struct {
}
}
+ pub const WriteFileOptions = struct {
+ in_offset: u64 = 0,
+
+ /// `null` means the entire file. `0` means no bytes from the file.
+ /// When this is `null`, trailers must be sent in a separate writev() call
+ /// due to a flaw in the BSD sendfile API. Other operating systems, such as
+ /// Linux, already do this anyway due to API limitations.
+ /// If the size of the source file is known, passing the size here will save one syscall.
+ in_len: ?u64 = null,
+
+ headers_and_trailers: []os.iovec_const = &[0]os.iovec_const{},
+
+ /// The trailer count is inferred from `headers_and_trailers.len - header_count`
+ header_count: usize = 0,
+ };
+
+ pub const WriteFileError = os.SendFileError;
+
+ /// TODO integrate with async I/O
+ pub fn writeFileAll(self: File, in_file: File, args: WriteFileOptions) WriteFileError!void {
+ const count = blk: {
+ if (args.in_len) |l| {
+ if (l == 0) {
+ return self.writevAll(args.headers_and_trailers);
+ } else {
+ break :blk l;
+ }
+ } else {
+ break :blk 0;
+ }
+ };
+ const headers = args.headers_and_trailers[0..args.header_count];
+ const trailers = args.headers_and_trailers[args.header_count..];
+ const zero_iovec = &[0]os.iovec_const{};
+ // When reading the whole file, we cannot put the trailers in the sendfile() syscall,
+ // because we have no way to determine whether a partial write is past the end of the file or not.
+ const trls = if (count == 0) zero_iovec else trailers;
+ const offset = args.in_offset;
+ const out_fd = self.handle;
+ const in_fd = in_file.handle;
+ const flags = 0;
+ var amt: usize = 0;
+ hdrs: {
+ var i: usize = 0;
+ while (i < headers.len) {
+ amt = try os.sendfile(out_fd, in_fd, offset, count, headers[i..], trls, flags);
+ while (amt >= headers[i].iov_len) {
+ amt -= headers[i].iov_len;
+ i += 1;
+ if (i >= headers.len) break :hdrs;
+ }
+ headers[i].iov_base += amt;
+ headers[i].iov_len -= amt;
+ }
+ }
+ if (count == 0) {
+ var off: u64 = amt;
+ while (true) {
+ amt = try os.sendfile(out_fd, in_fd, offset + off, 0, zero_iovec, zero_iovec, flags);
+ if (amt == 0) break;
+ off += amt;
+ }
+ } else {
+ var off: u64 = amt;
+ while (off < count) {
+ amt = try os.sendfile(out_fd, in_fd, offset + off, count - off, zero_iovec, trailers, flags);
+ off += amt;
+ }
+ amt = @intCast(usize, off - count);
+ }
+ var i: usize = 0;
+ while (i < trailers.len) {
+ while (amt >= headers[i].iov_len) {
+ amt -= trailers[i].iov_len;
+ i += 1;
+ if (i >= trailers.len) return;
+ }
+ trailers[i].iov_base += amt;
+ trailers[i].iov_len -= amt;
+ amt = try os.writev(self.handle, trailers[i..]);
+ }
+ }
+
pub fn inStream(file: File) InStream {
return InStream{
.file = file,
lib/std/os/test.zig
@@ -45,7 +45,7 @@ fn testThreadIdFn(thread_id: *Thread.Id) void {
}
test "sendfile" {
- try fs.makePath(a, "os_test_tmp");
+ try fs.cwd().makePath("os_test_tmp");
defer fs.deleteTree("os_test_tmp") catch {};
var dir = try fs.cwd().openDirList("os_test_tmp");
@@ -74,7 +74,9 @@ test "sendfile" {
const header1 = "header1\n";
const header2 = "second header\n";
- var headers = [_]os.iovec_const{
+ const trailer1 = "trailer1\n";
+ const trailer2 = "second trailer\n";
+ var hdtr = [_]os.iovec_const{
.{
.iov_base = header1,
.iov_len = header1.len,
@@ -83,11 +85,6 @@ test "sendfile" {
.iov_base = header2,
.iov_len = header2.len,
},
- };
-
- const trailer1 = "trailer1\n";
- const trailer2 = "second trailer\n";
- var trailers = [_]os.iovec_const{
.{
.iov_base = trailer1,
.iov_len = trailer1.len,
@@ -99,59 +96,16 @@ test "sendfile" {
};
var written_buf: [header1.len + header2.len + 10 + trailer1.len + trailer2.len]u8 = undefined;
- try sendfileAll(dest_file.handle, src_file.handle, 1, 10, &headers, &trailers, 0);
-
+ try dest_file.writeFileAll(src_file, .{
+ .in_offset = 1,
+ .in_len = 10,
+ .headers_and_trailers = &hdtr,
+ .header_count = 2,
+ });
try dest_file.preadAll(&written_buf, 0);
expect(mem.eql(u8, &written_buf, "header1\nsecond header\nine1\nsecontrailer1\nsecond trailer\n"));
}
-fn sendfileAll(
- out_fd: os.fd_t,
- in_fd: os.fd_t,
- offset: u64,
- count: usize,
- headers: []os.iovec_const,
- trailers: []os.iovec_const,
- flags: u32,
-) os.SendFileError!void {
- var amt: usize = undefined;
- hdrs: {
- var i: usize = 0;
- while (i < headers.len) {
- amt = try os.sendfile(out_fd, in_fd, offset, count, headers[i..], trailers, flags);
- while (amt >= headers[i].iov_len) {
- amt -= headers[i].iov_len;
- i += 1;
- if (i >= headers.len) break :hdrs;
- }
- headers[i].iov_base += amt;
- headers[i].iov_len -= amt;
- }
- }
- var off = amt;
- while (off < count) {
- amt = try os.sendfile(out_fd, in_fd, offset + off, count - off, &[0]os.iovec_const{}, trailers, flags);
- off += amt;
- }
- amt = off - count;
- var i: usize = 0;
- while (i < trailers.len) {
- while (amt >= headers[i].iov_len) {
- amt -= trailers[i].iov_len;
- i += 1;
- if (i >= trailers.len) return;
- }
- trailers[i].iov_base += amt;
- trailers[i].iov_len -= amt;
- if (std.Target.current.os.tag == .windows) {
- amt = try os.writev(out_fd, trailers[i..]);
- } else {
- // Here we must use send because it's the only way to give the flags.
- amt = try os.send(out_fd, trailers[i].iov_base[0..trailers[i].iov_len], flags);
- }
- }
-}
-
test "std.Thread.getCurrentId" {
if (builtin.single_threaded) return error.SkipZigTest;
lib/std/os/windows.zig
@@ -337,7 +337,7 @@ pub fn GetQueuedCompletionStatus(
}
pub fn CloseHandle(hObject: HANDLE) void {
- assert(kernel32.CloseHandle(hObject) != 0);
+ assert(ntdll.NtClose(hObject) == .SUCCESS);
}
pub fn FindClose(hFindFile: HANDLE) void {
@@ -586,23 +586,74 @@ pub fn MoveFileExW(old_path: [*:0]const u16, new_path: [*:0]const u16, flags: DW
}
pub const CreateDirectoryError = error{
+ NameTooLong,
PathAlreadyExists,
FileNotFound,
+ NoDevice,
+ AccessDenied,
Unexpected,
};
-pub fn CreateDirectory(pathname: []const u8, attrs: ?*SECURITY_ATTRIBUTES) CreateDirectoryError!void {
+/// Returns an open directory handle which the caller is responsible for closing with `CloseHandle`.
+pub fn CreateDirectory(dir: ?HANDLE, pathname: []const u8, sa: ?*SECURITY_ATTRIBUTES) CreateDirectoryError!HANDLE {
const pathname_w = try sliceToPrefixedFileW(pathname);
- return CreateDirectoryW(&pathname_w, attrs);
+ return CreateDirectoryW(dir, &pathname_w, sa);
}
-pub fn CreateDirectoryW(pathname: [*:0]const u16, attrs: ?*SECURITY_ATTRIBUTES) CreateDirectoryError!void {
- if (kernel32.CreateDirectoryW(pathname, attrs) == 0) {
- switch (kernel32.GetLastError()) {
- .ALREADY_EXISTS => return error.PathAlreadyExists,
- .PATH_NOT_FOUND => return error.FileNotFound,
- else => |err| return unexpectedError(err),
- }
+/// Same as `CreateDirectory` except takes a WTF-16 encoded path.
+pub fn CreateDirectoryW(
+ dir: ?HANDLE,
+ sub_path_w: [*:0]const u16,
+ sa: ?*SECURITY_ATTRIBUTES,
+) CreateDirectoryError!HANDLE {
+ const path_len_bytes = math.cast(u16, mem.toSliceConst(u16, sub_path_w).len * 2) catch |err| switch (err) {
+ error.Overflow => return error.NameTooLong,
+ };
+ var nt_name = UNICODE_STRING{
+ .Length = path_len_bytes,
+ .MaximumLength = path_len_bytes,
+ .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)),
+ };
+
+ if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
+ // Windows does not recognize this, but it does work with empty string.
+ nt_name.Length = 0;
+ }
+
+ var attr = OBJECT_ATTRIBUTES{
+ .Length = @sizeOf(OBJECT_ATTRIBUTES),
+ .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sub_path_w)) null else dir,
+ .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
+ .ObjectName = &nt_name,
+ .SecurityDescriptor = if (sa) |ptr| ptr.lpSecurityDescriptor else null,
+ .SecurityQualityOfService = null,
+ };
+ var io: IO_STATUS_BLOCK = undefined;
+ var result_handle: HANDLE = undefined;
+ const rc = ntdll.NtCreateFile(
+ &result_handle,
+ GENERIC_READ | SYNCHRONIZE,
+ &attr,
+ &io,
+ null,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ,
+ FILE_CREATE,
+ FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
+ null,
+ 0,
+ );
+ switch (rc) {
+ .SUCCESS => return result_handle,
+ .OBJECT_NAME_INVALID => unreachable,
+ .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
+ .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
+ .NO_MEDIA_IN_DEVICE => return error.NoDevice,
+ .INVALID_PARAMETER => unreachable,
+ .ACCESS_DENIED => return error.AccessDenied,
+ .OBJECT_PATH_SYNTAX_BAD => unreachable,
+ .OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
+ else => return unexpectedStatus(rc),
}
}
lib/std/fs.zig
@@ -123,47 +123,21 @@ pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?Fil
}
const actual_mode = mode orelse src_stat.mode;
- // TODO this logic could be made more efficient by calling makePath, once
- // that API does not require an allocator
- var atomic_file = make_atomic_file: while (true) {
- const af = AtomicFile.init(dest_path, actual_mode) catch |err| switch (err) {
- error.FileNotFound => {
- var p = dest_path;
- while (path.dirname(p)) |dirname| {
- makeDir(dirname) catch |e| switch (e) {
- error.FileNotFound => {
- p = dirname;
- continue;
- },
- else => return e,
- };
- continue :make_atomic_file;
- } else {
- return err;
- }
- },
- else => |e| return e,
- };
- break af;
- } else unreachable;
- defer atomic_file.deinit();
+ if (path.dirname(dest_path)) |dirname| {
+ try cwd().makePath(dirname);
+ }
- const in_stream = &src_file.inStream().stream;
+ var atomic_file = try AtomicFile.init(dest_path, actual_mode);
+ defer atomic_file.deinit();
- var buf: [mem.page_size * 6]u8 = undefined;
- while (true) {
- const amt = try in_stream.readFull(buf[0..]);
- try atomic_file.file.writeAll(buf[0..amt]);
- if (amt != buf.len) {
- try atomic_file.file.updateTimes(src_stat.atime, src_stat.mtime);
- try atomic_file.finish();
- return PrevStatus.stale;
- }
- }
+ try atomic_file.file.writeFileAll(src_file, .{ .in_len = src_stat.size });
+ try atomic_file.file.updateTimes(src_stat.atime, src_stat.mtime);
+ try atomic_file.finish();
+ return PrevStatus.stale;
}
-/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is
-/// merged and readily available,
+/// Guaranteed to be atomic.
+/// On Linux, until https://patchwork.kernel.org/patch/9636735/ is merged and readily available,
/// there is a possibility of power loss or application termination leaving temporary files present
/// in the same directory as dest_path.
/// Destination file will have the same mode as the source file.
@@ -207,6 +181,9 @@ pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.M
}
}
+/// TODO update this API to avoid a getrandom syscall for every operation. It
+/// should accept a random interface.
+/// TODO rework this to integrate with Dir
pub const AtomicFile = struct {
file: File,
tmp_path_buf: [MAX_PATH_BYTES]u8,
@@ -268,33 +245,42 @@ pub const AtomicFile = struct {
pub fn finish(self: *AtomicFile) !void {
assert(!self.finished);
- self.file.close();
- self.finished = true;
- if (builtin.os.tag == .windows) {
+ if (std.Target.current.os.tag == .windows) {
const dest_path_w = try os.windows.sliceToPrefixedFileW(self.dest_path);
const tmp_path_w = try os.windows.cStrToPrefixedFileW(@ptrCast([*:0]u8, &self.tmp_path_buf));
+ self.file.close();
+ self.finished = true;
return os.renameW(&tmp_path_w, &dest_path_w);
+ } else {
+ const dest_path_c = try os.toPosixPath(self.dest_path);
+ self.file.close();
+ self.finished = true;
+ return os.renameC(@ptrCast([*:0]u8, &self.tmp_path_buf), &dest_path_c);
}
- const dest_path_c = try os.toPosixPath(self.dest_path);
- return os.renameC(@ptrCast([*:0]u8, &self.tmp_path_buf), &dest_path_c);
}
};
const default_new_dir_mode = 0o755;
-/// Create a new directory.
-pub fn makeDir(dir_path: []const u8) !void {
- return os.mkdir(dir_path, default_new_dir_mode);
+/// Create a new directory, based on an absolute path.
+/// Asserts that the path is absolute. See `Dir.makeDir` for a function that operates
+/// on both absolute and relative paths.
+pub fn makeDirAbsolute(absolute_path: []const u8) !void {
+ assert(path.isAbsoluteC(absolute_path));
+ return os.mkdir(absolute_path, default_new_dir_mode);
}
-/// Same as `makeDir` except the parameter is a null-terminated UTF8-encoded string.
-pub fn makeDirC(dir_path: [*:0]const u8) !void {
- return os.mkdirC(dir_path, default_new_dir_mode);
+/// Same as `makeDirAbsolute` except the parameter is a null-terminated UTF8-encoded string.
+pub fn makeDirAbsoluteZ(absolute_path_z: [*:0]const u8) !void {
+ assert(path.isAbsoluteC(absolute_path_z));
+ return os.mkdirZ(absolute_path_z, default_new_dir_mode);
}
-/// Same as `makeDir` except the parameter is a null-terminated UTF16LE-encoded string.
-pub fn makeDirW(dir_path: [*:0]const u16) !void {
- return os.mkdirW(dir_path, default_new_dir_mode);
+/// Same as `makeDirAbsolute` except the parameter is a null-terminated WTF-16 encoded string.
+pub fn makeDirAbsoluteW(absolute_path_w: [*:0]const u16) !void {
+ assert(path.isAbsoluteWindowsW(absolute_path_w));
+ const handle = try os.windows.CreateDirectoryW(null, absolute_path_w, null);
+ os.windows.CloseHandle(handle);
}
/// Returns `error.DirNotEmpty` if the directory is not empty.
@@ -847,10 +833,15 @@ pub const Dir = struct {
try os.mkdirat(self.fd, sub_path, default_new_dir_mode);
}
- pub fn makeDirC(self: Dir, sub_path: [*:0]const u8) !void {
+ pub fn makeDirZ(self: Dir, sub_path: [*:0]const u8) !void {
try os.mkdiratC(self.fd, sub_path, default_new_dir_mode);
}
+ pub fn makeDirW(self: Dir, sub_path: [*:0]const u16) !void {
+ const handle = try os.windows.CreateDirectoryW(self.fd, sub_path, null);
+ os.windows.CloseHandle(handle);
+ }
+
/// Calls makeDir recursively to make an entire path. Returns success if the path
/// already exists and is a directory.
/// This function is not atomic, and if it returns an error, the file system may
@@ -885,7 +876,14 @@ pub const Dir = struct {
}
}
- pub fn changeTo(self: Dir) !void {
+ /// Changes the current working directory to the open directory handle.
+ /// This modifies global state and can have surprising effects in multi-
+ /// threaded applications. Most applications and especially libraries should
+ /// not call this function as a general rule, however it can have use cases
+ /// in, for example, implementing a shell, or child process execution.
+ /// Not all targets support this. For example, WASI does not have the concept
+ /// of a current working directory.
+ pub fn setAsCwd(self: Dir) !void {
try os.fchdir(self.fd);
}
lib/std/os.zig
@@ -1539,12 +1539,13 @@ pub const MakeDirError = error{
ReadOnlyFileSystem,
InvalidUtf8,
BadPathName,
+ NoDevice,
} || UnexpectedError;
pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!void {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const sub_dir_path_w = try windows.sliceToPrefixedFileW(sub_dir_path);
- @compileError("TODO implement mkdirat for Windows");
+ return mkdiratW(dir_fd, &sub_dir_path_w, mode);
} else {
const sub_dir_path_c = try toPosixPath(sub_dir_path);
return mkdiratC(dir_fd, &sub_dir_path_c, mode);
@@ -1552,9 +1553,9 @@ pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!v
}
pub fn mkdiratC(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: u32) MakeDirError!void {
- if (builtin.os == .windows) {
+ if (builtin.os.tag == .windows) {
const sub_dir_path_w = try windows.cStrToPrefixedFileW(sub_dir_path);
- @compileError("TODO implement mkdiratC for Windows");
+ return mkdiratW(dir_fd, &sub_dir_path_w, mode);
}
switch (errno(system.mkdirat(dir_fd, sub_dir_path, mode))) {
0 => return,
@@ -1576,23 +1577,31 @@ pub fn mkdiratC(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: u32) MakeDirErr
}
}
+pub fn mkdiratW(dir_fd: fd_t, sub_path_w: [*:0]const u16, mode: u32) MakeDirError!void {
+ const sub_dir_handle = try windows.CreateDirectoryW(dir_fd, sub_path_w, null);
+ windows.CloseHandle(sub_dir_handle);
+}
+
/// Create a directory.
/// `mode` is ignored on Windows.
pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {
if (builtin.os.tag == .windows) {
- const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);
- return windows.CreateDirectoryW(&dir_path_w, null);
+ const sub_dir_handle = try windows.CreateDirectory(null, dir_path, null);
+ windows.CloseHandle(sub_dir_handle);
+ return;
} else {
const dir_path_c = try toPosixPath(dir_path);
- return mkdirC(&dir_path_c, mode);
+ return mkdirZ(&dir_path_c, mode);
}
}
/// Same as `mkdir` but the parameter is a null-terminated UTF8-encoded string.
-pub fn mkdirC(dir_path: [*:0]const u8, mode: u32) MakeDirError!void {
+pub fn mkdirZ(dir_path: [*:0]const u8, mode: u32) MakeDirError!void {
if (builtin.os.tag == .windows) {
const dir_path_w = try windows.cStrToPrefixedFileW(dir_path);
- return windows.CreateDirectoryW(&dir_path_w, null);
+ const sub_dir_handle = try windows.CreateDirectoryW(null, &dir_path_w, null);
+ windows.CloseHandle(sub_dir_handle);
+ return;
}
switch (errno(system.mkdir(dir_path, mode))) {
0 => return,
@@ -1705,7 +1714,13 @@ pub fn chdirC(dir_path: [*:0]const u8) ChangeCurDirError!void {
}
}
-pub fn fchdir(dirfd: fd_t) ChangeCurDirError!void {
+pub const FchdirError = error{
+ AccessDenied,
+ NotDir,
+ FileSystem,
+} || UnexpectedError;
+
+pub fn fchdir(dirfd: fd_t) FchdirError!void {
while (true) {
switch (errno(system.fchdir(dirfd))) {
0 => return,
@@ -3564,12 +3579,12 @@ fn count_iovec_bytes(iovs: []const iovec_const) usize {
}
/// Transfer data between file descriptors, with optional headers and trailers.
-/// Returns the number of bytes written. This will be zero if `in_offset` falls beyond the end of the file.
+/// Returns the number of bytes written, which can be zero.
///
-/// The `sendfile` call copies `count` bytes from one file descriptor to another. When possible,
+/// The `sendfile` call copies `in_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
-/// `read` and `write` calls. When `count` is `0`, it means to copy until the end of the input file has been
+/// `read` and `write` calls. When `in_len` is `0`, it means to copy until the end of the input file has been
/// reached. Note, however, that partial writes are still possible in this case.
///
/// `in_fd` must be a file descriptor opened for reading, and `out_fd` must be a file descriptor
@@ -3578,7 +3593,8 @@ fn count_iovec_bytes(iovs: []const iovec_const) usize {
/// atomicity guarantees no longer apply.
///
/// Copying begins reading at `in_offset`. The input file descriptor seek position is ignored and not updated.
-/// If the output file descriptor has a seek position, it is updated as bytes are written.
+/// If the output file descriptor has a seek position, it is updated as bytes are written. When
+/// `in_offset` 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.
///
@@ -3599,7 +3615,7 @@ pub fn sendfile(
out_fd: fd_t,
in_fd: fd_t,
in_offset: u64,
- count: usize,
+ in_len: u64,
headers: []const iovec_const,
trailers: []const iovec_const,
flags: u32,
@@ -3608,9 +3624,15 @@ pub fn sendfile(
var total_written: usize = 0;
// Prevents EOVERFLOW.
+ const size_t = @Type(std.builtin.TypeInfo{
+ .Int = .{
+ .is_signed = false,
+ .bits = @typeInfo(usize).Int.bits - 1,
+ },
+ });
const max_count = switch (std.Target.current.os.tag) {
.linux => 0x7ffff000,
- else => math.maxInt(isize),
+ else => math.maxInt(size_t),
};
switch (std.Target.current.os.tag) {
@@ -3630,7 +3652,7 @@ pub fn sendfile(
}
// Here we match BSD behavior, making a zero count value send as many bytes as possible.
- const adjusted_count = if (count == 0) max_count else math.min(count, max_count);
+ const adjusted_count = if (in_len == 0) max_count else math.min(in_len, @as(size_t, max_count));
while (true) {
var offset: off_t = @bitCast(off_t, in_offset);
@@ -3639,10 +3661,10 @@ pub fn sendfile(
0 => {
const amt = @bitCast(usize, rc);
total_written += amt;
- if (count == 0 and amt == 0) {
+ if (in_len == 0 and amt == 0) {
// We have detected EOF from `in_fd`.
break;
- } else if (amt < count) {
+ } else if (amt < in_len) {
return total_written;
} else {
break;
@@ -3708,7 +3730,7 @@ pub fn sendfile(
hdtr = &hdtr_data;
}
- const adjusted_count = math.min(count, max_count);
+ const adjusted_count = math.min(in_len, max_count);
while (true) {
var sbytes: off_t = undefined;
@@ -3786,7 +3808,7 @@ pub fn sendfile(
hdtr = &hdtr_data;
}
- const adjusted_count = math.min(count, @as(u63, max_count));
+ const adjusted_count = math.min(in_len, @as(u63, max_count));
while (true) {
var sbytes: off_t = adjusted_count;
@@ -3840,10 +3862,10 @@ pub fn sendfile(
rw: {
var buf: [8 * 4096]u8 = undefined;
// Here we match BSD behavior, making a zero count value send as many bytes as possible.
- const adjusted_count = if (count == 0) buf.len else math.min(buf.len, count);
+ const adjusted_count = if (in_len == 0) buf.len else math.min(buf.len, in_len);
const amt_read = try pread(in_fd, buf[0..adjusted_count], in_offset);
if (amt_read == 0) {
- if (count == 0) {
+ if (in_len == 0) {
// We have detected EOF from `in_fd`.
break :rw;
} else {
@@ -3852,7 +3874,7 @@ pub fn sendfile(
}
const amt_written = try write(out_fd, buf[0..amt_read]);
total_written += amt_written;
- if (amt_written < count or count == 0) return total_written;
+ if (amt_written < in_len or in_len == 0) return total_written;
}
if (trailers.len != 0) {