diff --git a/lib/std/Io.zig b/lib/std/Io.zig index 1da10a027fa172a208228600d7558378509d2094..bf12ee6c6cd6098e3d4a9e218a3b18326724747f 100644 --- a/lib/std/Io.zig +++ b/lib/std/Io.zig @@ -656,7 +656,7 @@ pub const VTable = struct { fileMemoryMapCreate: *const fn (?*anyopaque, File, File.MemoryMap.CreateOptions) File.MemoryMap.CreateError!File.MemoryMap, fileMemoryMapDestroy: *const fn (?*anyopaque, *File.MemoryMap) void, - fileMemoryMapSetLength: *const fn (?*anyopaque, *File.MemoryMap, n: usize) File.MemoryMap.SetLengthError!void, + fileMemoryMapSetLength: *const fn (?*anyopaque, *File.MemoryMap, File.MemoryMap.CreateOptions) File.MemoryMap.SetLengthError!void, fileMemoryMapRead: *const fn (?*anyopaque, *File.MemoryMap) File.ReadPositionalError!void, fileMemoryMapWrite: *const fn (?*anyopaque, *File.MemoryMap) File.WritePositionalError!void, diff --git a/lib/std/Io/File/MemoryMap.zig b/lib/std/Io/File/MemoryMap.zig index 0b7ff69bbaab0f7857719a5084a304a78089c9ed..2b917840a14409ce0008c63f47698aff4dee32ab 100644 --- a/lib/std/Io/File/MemoryMap.zig +++ b/lib/std/Io/File/MemoryMap.zig @@ -52,7 +52,9 @@ pub const CreateOptions = struct { /// undefined, and bytes unwritten before calling `write` to write /// undefined memory to the file. undefined_contents: bool = false, - /// Prefault the pages. + /// Prefault the pages. If this option is unsupported, it is silently + /// ignored. Aside from custom Io implementations, this option is only + /// supported on Linux. populate: bool = true, /// Asserted to be a multiple of page size which can be obtained via /// `std.heap.pageSize`. @@ -72,21 +74,29 @@ pub fn destroy(mm: *MemoryMap, io: Io) void { } pub const SetLengthError = error{ + /// One of the following: + /// * The `File.Kind` is not `file`. + /// * The file is not open for reading and read access protections enabled. + /// * The file is not open for writing and write access protections enabled. + AccessDenied, + /// The `prot` argument asks for `PROT_EXEC` but the mapped area belongs to a file on + /// a filesystem that was mounted no-exec. + PermissionDenied, LockedMemoryLimitExceeded, + ProcessFdQuotaExceeded, + SystemFdQuotaExceeded, } || Allocator.Error || File.SetLengthError; /// Change the size of the mapping. This does not sync the contents. The size /// of the file after calling this is unspecified until `write` is called. /// /// May change the pointer address of `memory`. -pub fn setLength( - mm: *MemoryMap, - io: Io, - /// New size of the mapping, in bytes. If this is longer than the file - /// size, it will be filled with zeroes. No alignment requirement. - new_length: usize, -) SetLengthError!void { - return io.vtable.fileMemoryMapSetLength(io.userdata, mm, new_length); +/// +/// `options` is needed because the mapping may need to be destroyed and +/// re-created. All the same options must be provided except for `len` which is +/// the new length. +pub fn setLength(mm: *MemoryMap, io: Io, options: CreateOptions) SetLengthError!void { + return io.vtable.fileMemoryMapSetLength(io.userdata, mm, options); } /// Synchronizes the contents of `memory` from `file`. diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index 5c4776b9b88e52efdc00e3988cf7eeb297ab3f8a..f39bdd2eaafab94a19d159d3063a764b90e8b5b0 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -7998,7 +7998,7 @@ fn fileReadStreamingPosix(userdata: ?*anyopaque, file: File, data: []const []u8) .FAULT => |err| return errnoBug(err), .AGAIN => return error.WouldBlock, .BADF => |err| { - if (native_os == .wasi) return error.NotOpenForReading; // File operation on directory. + if (native_os == .wasi) return error.IsDir; // File operation on directory. return errnoBug(err); // File descriptor used after closed. }, .IO => return error.InputOutput, @@ -8089,7 +8089,7 @@ fn fileReadPositionalPosix(userdata: ?*anyopaque, file: File, data: []const []u8 syscall.finish(); return nread; }, - .INTR => { + .INTR, .TIMEDOUT => { try syscall.checkCancel(); continue; }, @@ -8103,7 +8103,6 @@ fn fileReadPositionalPosix(userdata: ?*anyopaque, file: File, data: []const []u8 .ISDIR => return syscall.fail(error.IsDir), .NOBUFS => return syscall.fail(error.SystemResources), .NOMEM => return syscall.fail(error.SystemResources), - .TIMEDOUT => return syscall.fail(error.Timeout), .NXIO => return syscall.fail(error.Unseekable), .SPIPE => return syscall.fail(error.Unseekable), .OVERFLOW => return syscall.fail(error.Unseekable), @@ -12588,7 +12587,7 @@ fn statFromPosix(st: *const posix.Stat) File.Stat { .atime = timestampFromPosix(&atime), .mtime = timestampFromPosix(&mtime), .ctime = timestampFromPosix(&ctime), - .block_size = st.blksize, + .block_size = @intCast(st.blksize), }; } @@ -16292,9 +16291,14 @@ fn createFileMap( .WRITE = protection.write, .EXEC = protection.execute, }; - const flags: posix.MAP = .{ - .TYPE = if (native_os == .linux) .SHARED_VALIDATE else .SHARED, - .POPULATE = populate, + const flags: posix.MAP = switch (native_os) { + .linux => .{ + .TYPE = .SHARED_VALIDATE, + .POPULATE = populate, + }, + else => .{ + .TYPE = .SHARED, + }, }; const page_align = std.heap.page_size_min; @@ -16348,20 +16352,20 @@ fn createFileMap( fn fileMemoryMapDestroy(userdata: ?*anyopaque, mm: *File.MemoryMap) void { const t: *Threaded = @ptrCast(@alignCast(userdata)); const memory = mm.memory; - if (mm.section) |section| { - if (is_windows) { + if (mm.section) |section| switch (native_os) { + .windows => { const current_process: windows.HANDLE = @ptrFromInt(@as(usize, @bitCast(@as(isize, -1)))); _ = windows.ntdll.NtUnmapViewOfSection(current_process, memory.ptr); windows.CloseHandle(section); - } else { - switch (posix.errno(posix.system.munmap(memory.ptr, memory.len))) { - .SUCCESS => {}, - else => |e| { - if (builtin.mode == .Debug) - std.log.err("failed to unmap {d} bytes at {*}: {t}", .{ memory.len, memory.ptr, e }); - }, - } - } + }, + .wasi => unreachable, + else => switch (posix.errno(posix.system.munmap(memory.ptr, memory.len))) { + .SUCCESS => {}, + else => |e| { + if (builtin.mode == .Debug) + std.log.err("failed to unmap {d} bytes at {*}: {t}", .{ memory.len, memory.ptr, e }); + }, + }, } else { const gpa = t.allocator; gpa.rawFree(memory, .fromByteUnits(std.heap.pageSize()), @returnAddress()); @@ -16372,15 +16376,17 @@ fn fileMemoryMapDestroy(userdata: ?*anyopaque, mm: *File.MemoryMap) void { fn fileMemoryMapSetLength( userdata: ?*anyopaque, mm: *File.MemoryMap, - new_len: usize, + options: File.MemoryMap.CreateOptions, ) File.MemoryMap.SetLengthError!void { const t: *Threaded = @ptrCast(@alignCast(userdata)); const page_size = std.heap.pageSize(); const alignment: Alignment = .fromByteUnits(page_size); const page_align = std.heap.page_size_min; + const old_memory = mm.memory; + const new_len = options.len; if (mm.section) |section| { - if (alignment.forward(new_len) == alignment.forward(mm.memory.len)) { + if (alignment.forward(new_len) == alignment.forward(old_memory.len)) { mm.memory.len = new_len; return; } @@ -16390,12 +16396,12 @@ fn fileMemoryMapSetLength( @panic("TODO"); }, .wasi => unreachable, - else => { + .linux => { const flags: posix.MREMAP = .{ .MAYMOVE = true }; const addr_hint: ?[*]const u8 = null; const new_memory = while (true) { const syscall: Syscall = try .start(); - const rc = posix.system.mremap(mm.memory.ptr, mm.memory.len, new_len, flags, addr_hint); + const rc = posix.system.mremap(old_memory.ptr, old_memory.len, new_len, flags, addr_hint); syscall.finish(); const err: posix.E = if (builtin.link_libc) e: { if (rc != std.c.MAP_FAILED) break @as([*]align(page_align) u8, @ptrCast(@alignCast(rc)))[0..new_len]; @@ -16417,6 +16423,30 @@ fn fileMemoryMapSetLength( }; mm.memory = new_memory; }, + else => { + switch (posix.errno(posix.system.munmap(old_memory.ptr, old_memory.len))) { + .SUCCESS => {}, + else => |e| { + if (builtin.mode == .Debug) std.log.err("failed to unmap {d} bytes at {*}: {t}", .{ + old_memory.len, old_memory.ptr, e, + }); + // munmap must be infallible, or we cannot design reliable software. + return error.Unexpected; + }, + } + if (createFileMap(mm.file, options.protection, mm.offset, options.populate, new_len)) |result| { + mm.* = result; + return; + } else |err| switch (err) { + error.OperationUnsupported, + error.Unseekable, + error.SectionOversize, + error.MappingAlreadyExists, + error.FileLockConflict, + => return error.Unexpected, // It worked before on the same open file. + else => |e| return e, + } + }, } } else { const gpa = t.allocator; diff --git a/lib/std/Io/Threaded/test.zig b/lib/std/Io/Threaded/test.zig index f90ab6e745b5431aa438d03bad223e9cddeb76ec..0e5d90e6d4bdbf8363d8f48ce0abbf03cc3f662d 100644 --- a/lib/std/Io/Threaded/test.zig +++ b/lib/std/Io/Threaded/test.zig @@ -255,7 +255,7 @@ test "memory mapping fallback" { try testing.expectEqualStrings("this9is9my", mm.memory); - try mm.setLength(io, "this9is9my data123".len); + try mm.setLength(io, .{ .len = "this9is9my data123".len }); try mm.read(io); try testing.expectEqualStrings("this9is9my data123", mm.memory); diff --git a/lib/std/Io/test.zig b/lib/std/Io/test.zig index 1cfcd7a22193eb842f7f06233cffddc6ae8baaa0..e1c5bfea83934ae74e4754a28220ff95f12f873c 100644 --- a/lib/std/Io/test.zig +++ b/lib/std/Io/test.zig @@ -594,6 +594,8 @@ test "randomSecure" { } test "memory mapping" { + if (builtin.cpu.arch == .hexagon) return error.SkipZigTest; // mmap returned EINVAL + const io = testing.io; var tmp = tmpDir(.{}); @@ -634,7 +636,7 @@ test "memory mapping" { try expectEqualStrings("this9is9my", mm.memory); - try mm.setLength(io, "this9is9my data123".len); + try mm.setLength(io, .{ .len = "this9is9my data123".len }); try mm.read(io); try expectEqualStrings("this9is9my data123", mm.memory); diff --git a/lib/std/c.zig b/lib/std/c.zig index f10833cf0da4d7b033958f71708c5d85c4d12eff..535f0f79090bd88e70bbef0f112f64cd7ff76529 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -10334,7 +10334,7 @@ pub extern "c" fn getgrgid(gid: gid_t) ?*group; pub extern "c" fn getgrgid_r(gid: gid_t, grp: *group, buf: [*]u8, buflen: usize, result: *?*group) c_int; pub extern "c" fn getrlimit64(resource: rlimit_resource, rlim: *rlimit) c_int; pub extern "c" fn lseek64(fd: fd_t, offset: i64, whence: c_int) i64; -pub extern "c" fn mmap64(addr: ?*align(page_size) anyopaque, len: usize, prot: PROT, flags: c_uint, fd: fd_t, offset: i64) *anyopaque; +pub extern "c" fn mmap64(addr: ?*align(page_size) anyopaque, len: usize, prot: PROT, flags: MAP, fd: fd_t, offset: i64) *anyopaque; pub extern "c" fn open64(path: [*:0]const u8, oflag: O, ...) c_int; pub extern "c" fn openat64(fd: c_int, path: [*:0]const u8, oflag: O, ...) c_int; pub extern "c" fn pread64(fd: fd_t, buf: [*]u8, nbyte: usize, offset: i64) isize;