| ... | ... | @@ -22,6 +22,12 @@ const windows = std.os.windows; |
| 22 | 22 | const ws2_32 = std.os.windows.ws2_32; |
| 23 | 23 | |
| 24 | 24 | /// Thread-safe. |
| 25 | /// |
| 26 | /// Used for: |
| 27 | /// * allocating `Io.Future` and `Io.Group` closures. |
| 28 | /// * formatting spawning child processes |
| 29 | /// * scanning environment variables on some targets |
| 30 | /// * memory-mapping when mmap or equivalent is not available |
| 25 | 31 | allocator: Allocator, |
| 26 | 32 | mutex: std.Thread.Mutex = .{}, |
| 27 | 33 | cond: std.Thread.Condition = .{}, |
| ... | ... | @@ -1490,6 +1496,12 @@ pub fn io(t: *Threaded) Io { |
| 1490 | 1496 | .fileRealPath = fileRealPath, |
| 1491 | 1497 | .fileHardLink = fileHardLink, |
| 1492 | 1498 | |
| 1499 | .fileMemoryMapCreate = fileMemoryMapCreate, |
| 1500 | .fileMemoryMapDestroy = fileMemoryMapDestroy, |
| 1501 | .fileMemoryMapSetLength = fileMemoryMapSetLength, |
| 1502 | .fileMemoryMapRead = fileMemoryMapRead, |
| 1503 | .fileMemoryMapWrite = fileMemoryMapWrite, |
| 1504 | |
| 1493 | 1505 | .processExecutableOpen = processExecutableOpen, |
| 1494 | 1506 | .processExecutablePath = processExecutablePath, |
| 1495 | 1507 | .lockStderr = lockStderr, |
| ... | ... | @@ -1642,6 +1654,12 @@ pub fn ioBasic(t: *Threaded) Io { |
| 1642 | 1654 | .fileRealPath = fileRealPath, |
| 1643 | 1655 | .fileHardLink = fileHardLink, |
| 1644 | 1656 | |
| 1657 | .fileMemoryMapCreate = fileMemoryMapCreate, |
| 1658 | .fileMemoryMapDestroy = fileMemoryMapDestroy, |
| 1659 | .fileMemoryMapSetLength = fileMemoryMapSetLength, |
| 1660 | .fileMemoryMapRead = fileMemoryMapRead, |
| 1661 | .fileMemoryMapWrite = fileMemoryMapWrite, |
| 1662 | |
| 1645 | 1663 | .processExecutableOpen = processExecutableOpen, |
| 1646 | 1664 | .processExecutablePath = processExecutablePath, |
| 1647 | 1665 | .lockStderr = lockStderr, |
| ... | ... | @@ -1733,15 +1751,24 @@ const have_wait4 = switch (native_os) { |
| 1733 | 1751 | else => false, |
| 1734 | 1752 | }; |
| 1735 | 1753 | |
| 1754 | const have_mmap = switch (native_os) { |
| 1755 | .wasi, .windows => false, |
| 1756 | else => true, |
| 1757 | }; |
| 1758 | |
| 1736 | 1759 | const open_sym = if (posix.lfs64_abi) posix.system.open64 else posix.system.open; |
| 1737 | 1760 | const openat_sym = if (posix.lfs64_abi) posix.system.openat64 else posix.system.openat; |
| 1738 | 1761 | const fstat_sym = if (posix.lfs64_abi) posix.system.fstat64 else posix.system.fstat; |
| 1739 | 1762 | const fstatat_sym = if (posix.lfs64_abi) posix.system.fstatat64 else posix.system.fstatat; |
| 1740 | 1763 | const lseek_sym = if (posix.lfs64_abi) posix.system.lseek64 else posix.system.lseek; |
| 1741 | 1764 | const preadv_sym = if (posix.lfs64_abi) posix.system.preadv64 else posix.system.preadv; |
| 1765 | const pread_sym = if (posix.lfs64_abi) posix.system.pread64 else posix.system.pread; |
| 1742 | 1766 | const ftruncate_sym = if (posix.lfs64_abi) posix.system.ftruncate64 else posix.system.ftruncate; |
| 1743 | 1767 | const pwritev_sym = if (posix.lfs64_abi) posix.system.pwritev64 else posix.system.pwritev; |
| 1768 | const pwrite_sym = if (posix.lfs64_abi) posix.system.pwrite64 else posix.system.pwrite; |
| 1744 | 1769 | const sendfile_sym = if (posix.lfs64_abi) posix.system.sendfile64 else posix.system.sendfile; |
| 1770 | const mmap_sym = if (posix.lfs64_abi) posix.system.mmap64 else posix.system.mmap; |
| 1771 | |
| 1745 | 1772 | const linux_copy_file_range_use_c = std.c.versionCheck(if (builtin.abi.isAndroid()) .{ |
| 1746 | 1773 | .major = 34, |
| 1747 | 1774 | .minor = 0, |
| ... | ... | @@ -8028,27 +8055,22 @@ fn fileReadPositionalPosix(userdata: ?*anyopaque, file: File, data: []const []u8 |
| 8028 | 8055 | try syscall.checkCancel(); |
| 8029 | 8056 | continue; |
| 8030 | 8057 | }, |
| 8031 | | else => |e| { |
| 8032 | | syscall.finish(); |
| 8033 | | switch (e) { |
| 8034 | | .INVAL => |err| return errnoBug(err), |
| 8035 | | .FAULT => |err| return errnoBug(err), |
| 8036 | | .AGAIN => |err| return errnoBug(err), |
| 8037 | | .BADF => return error.NotOpenForReading, // File operation on directory. |
| 8038 | | .IO => return error.InputOutput, |
| 8039 | | .ISDIR => return error.IsDir, |
| 8040 | | .NOBUFS => return error.SystemResources, |
| 8041 | | .NOMEM => return error.SystemResources, |
| 8042 | | .NOTCONN => return error.SocketUnconnected, |
| 8043 | | .CONNRESET => return error.ConnectionResetByPeer, |
| 8044 | | .TIMEDOUT => return error.Timeout, |
| 8045 | | .NXIO => return error.Unseekable, |
| 8046 | | .SPIPE => return error.Unseekable, |
| 8047 | | .OVERFLOW => return error.Unseekable, |
| 8048 | | .NOTCAPABLE => return error.AccessDenied, |
| 8049 | | else => |err| return posix.unexpectedErrno(err), |
| 8050 | | } |
| 8051 | | }, |
| 8058 | .NOTCONN => |err| return syscall.errnoBug(err), // not a socket |
| 8059 | .CONNRESET => |err| return syscall.errnoBug(err), // not a socket |
| 8060 | .BADF => |err| return syscall.errnoBug(err), // use after free |
| 8061 | .INVAL => |err| return syscall.errnoBug(err), |
| 8062 | .FAULT => |err| return syscall.errnoBug(err), // segmentation fault |
| 8063 | .AGAIN => |err| return syscall.errnoBug(err), |
| 8064 | .IO => return syscall.fail(error.InputOutput), |
| 8065 | .ISDIR => return syscall.fail(error.IsDir), |
| 8066 | .NOBUFS => return syscall.fail(error.SystemResources), |
| 8067 | .NOMEM => return syscall.fail(error.SystemResources), |
| 8068 | .TIMEDOUT => return syscall.fail(error.Timeout), |
| 8069 | .NXIO => return syscall.fail(error.Unseekable), |
| 8070 | .SPIPE => return syscall.fail(error.Unseekable), |
| 8071 | .OVERFLOW => return syscall.fail(error.Unseekable), |
| 8072 | .NOTCAPABLE => return syscall.fail(error.AccessDenied), |
| 8073 | else => |err| return syscall.unexpectedErrno(err), |
| 8052 | 8074 | } |
| 8053 | 8075 | } |
| 8054 | 8076 | } |
| ... | ... | @@ -8061,33 +8083,28 @@ fn fileReadPositionalPosix(userdata: ?*anyopaque, file: File, data: []const []u8 |
| 8061 | 8083 | syscall.finish(); |
| 8062 | 8084 | return @bitCast(rc); |
| 8063 | 8085 | }, |
| 8064 | | .INTR => { |
| 8086 | .INTR, .TIMEDOUT => { |
| 8065 | 8087 | try syscall.checkCancel(); |
| 8066 | 8088 | continue; |
| 8067 | 8089 | }, |
| 8068 | | else => |e| { |
| 8090 | .NXIO => return syscall.fail(error.Unseekable), |
| 8091 | .SPIPE => return syscall.fail(error.Unseekable), |
| 8092 | .OVERFLOW => return syscall.fail(error.Unseekable), |
| 8093 | .NOBUFS => return syscall.fail(error.SystemResources), |
| 8094 | .NOMEM => return syscall.fail(error.SystemResources), |
| 8095 | .AGAIN => return syscall.fail(error.WouldBlock), |
| 8096 | .IO => return syscall.fail(error.InputOutput), |
| 8097 | .ISDIR => return syscall.fail(error.IsDir), |
| 8098 | .NOTCONN => |err| return syscall.errnoBug(err), // not a socket |
| 8099 | .CONNRESET => |err| return syscall.errnoBug(err), // not a socket |
| 8100 | .INVAL => |err| return syscall.errnoBug(err), |
| 8101 | .FAULT => |err| return syscall.errnoBug(err), |
| 8102 | .BADF => |err| { |
| 8069 | 8103 | syscall.finish(); |
| 8070 | | switch (e) { |
| 8071 | | .INVAL => |err| return errnoBug(err), |
| 8072 | | .FAULT => |err| return errnoBug(err), |
| 8073 | | .AGAIN => return error.WouldBlock, |
| 8074 | | .BADF => |err| { |
| 8075 | | if (native_os == .wasi) return error.NotOpenForReading; // File operation on directory. |
| 8076 | | return errnoBug(err); // File descriptor used after closed. |
| 8077 | | }, |
| 8078 | | .IO => return error.InputOutput, |
| 8079 | | .ISDIR => return error.IsDir, |
| 8080 | | .NOBUFS => return error.SystemResources, |
| 8081 | | .NOMEM => return error.SystemResources, |
| 8082 | | .NOTCONN => return error.SocketUnconnected, |
| 8083 | | .CONNRESET => return error.ConnectionResetByPeer, |
| 8084 | | .TIMEDOUT => return error.Timeout, |
| 8085 | | .NXIO => return error.Unseekable, |
| 8086 | | .SPIPE => return error.Unseekable, |
| 8087 | | .OVERFLOW => return error.Unseekable, |
| 8088 | | else => |err| return posix.unexpectedErrno(err), |
| 8089 | | } |
| 8104 | if (native_os == .wasi) return error.IsDir; // File operation on directory. |
| 8105 | return errnoBug(err); // File descriptor used after closed. |
| 8090 | 8106 | }, |
| 8107 | else => |err| return syscall.unexpectedErrno(err), |
| 8091 | 8108 | } |
| 8092 | 8109 | } |
| 8093 | 8110 | } |
| ... | ... | @@ -8770,29 +8787,24 @@ fn fileWritePositional( |
| 8770 | 8787 | try syscall.checkCancel(); |
| 8771 | 8788 | continue; |
| 8772 | 8789 | }, |
| 8773 | | else => |e| { |
| 8774 | | syscall.finish(); |
| 8775 | | switch (e) { |
| 8776 | | .INVAL => |err| return errnoBug(err), |
| 8777 | | .FAULT => |err| return errnoBug(err), |
| 8778 | | .AGAIN => return error.WouldBlock, |
| 8779 | | .BADF => return error.NotOpenForWriting, // Usually a race condition. |
| 8780 | | .DESTADDRREQ => |err| return errnoBug(err), // `connect` was never called. |
| 8781 | | .DQUOT => return error.DiskQuota, |
| 8782 | | .FBIG => return error.FileTooBig, |
| 8783 | | .IO => return error.InputOutput, |
| 8784 | | .NOSPC => return error.NoSpaceLeft, |
| 8785 | | .PERM => return error.PermissionDenied, |
| 8786 | | .PIPE => return error.BrokenPipe, |
| 8787 | | .CONNRESET => |err| return errnoBug(err), // Not a socket handle. |
| 8788 | | .BUSY => return error.DeviceBusy, |
| 8789 | | .TXTBSY => return error.FileBusy, |
| 8790 | | .NXIO => return error.Unseekable, |
| 8791 | | .SPIPE => return error.Unseekable, |
| 8792 | | .OVERFLOW => return error.Unseekable, |
| 8793 | | else => |err| return posix.unexpectedErrno(err), |
| 8794 | | } |
| 8795 | | }, |
| 8790 | .INVAL => |err| return syscall.errnoBug(err), |
| 8791 | .FAULT => |err| return syscall.errnoBug(err), |
| 8792 | .DESTADDRREQ => |err| return syscall.errnoBug(err), // `connect` was never called. |
| 8793 | .CONNRESET => |err| return syscall.errnoBug(err), // Not a socket handle. |
| 8794 | .BADF => |err| return syscall.errnoBug(err), // use after free |
| 8795 | .AGAIN => return syscall.fail(error.WouldBlock), |
| 8796 | .DQUOT => return syscall.fail(error.DiskQuota), |
| 8797 | .FBIG => return syscall.fail(error.FileTooBig), |
| 8798 | .IO => return syscall.fail(error.InputOutput), |
| 8799 | .NOSPC => return syscall.fail(error.NoSpaceLeft), |
| 8800 | .PERM => return syscall.fail(error.PermissionDenied), |
| 8801 | .PIPE => return syscall.fail(error.BrokenPipe), |
| 8802 | .BUSY => return syscall.fail(error.DeviceBusy), |
| 8803 | .TXTBSY => return syscall.fail(error.FileBusy), |
| 8804 | .NXIO => return syscall.fail(error.Unseekable), |
| 8805 | .SPIPE => return syscall.fail(error.Unseekable), |
| 8806 | .OVERFLOW => return syscall.fail(error.Unseekable), |
| 8807 | else => |err| return syscall.unexpectedErrno(err), |
| 8796 | 8808 | } |
| 8797 | 8809 | } |
| 8798 | 8810 | } |
| ... | ... | @@ -16107,3 +16119,381 @@ pub fn chdir(dir_path: []const u8) ChdirError!void { |
| 16107 | 16119 | else => |err| return syscall.unexpectedErrno(err), |
| 16108 | 16120 | }; |
| 16109 | 16121 | } |
| 16122 | |
| 16123 | fn fileMemoryMapCreate( |
| 16124 | userdata: ?*anyopaque, |
| 16125 | file: File, |
| 16126 | options: File.MemoryMap.CreateOptions, |
| 16127 | ) File.MemoryMap.CreateError!File.MemoryMap { |
| 16128 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 16129 | const offset = options.offset; |
| 16130 | |
| 16131 | const page_size = std.heap.pageSize(); |
| 16132 | const aligned_len: usize = options.len.?; // TODO query if necessary |
| 16133 | |
| 16134 | if (createFileMap(file, options.protection, offset, options.populate, aligned_len)) |result| { |
| 16135 | return result; |
| 16136 | } else |err| switch (err) { |
| 16137 | error.Unseekable, error.Canceled => |e| return e, |
| 16138 | else => { |
| 16139 | if (builtin.mode == .Debug) |
| 16140 | std.log.warn("memory mapping failed with {t}, falling back to file operations", .{err}); |
| 16141 | }, |
| 16142 | } |
| 16143 | |
| 16144 | const gpa = t.allocator; |
| 16145 | const alignment: Alignment = .fromByteUnits(page_size); |
| 16146 | const memory = m: { |
| 16147 | const ptr = gpa.rawAlloc(aligned_len, alignment, @returnAddress()) orelse |
| 16148 | return error.OutOfMemory; |
| 16149 | break :m ptr[0..aligned_len]; |
| 16150 | }; |
| 16151 | errdefer gpa.rawFree(memory, alignment, @returnAddress()); |
| 16152 | |
| 16153 | // If the mapping does not have read permissions, no need to populate the contents. |
| 16154 | if (options.protection.read) try mmSyncRead(file, memory, offset); |
| 16155 | |
| 16156 | return .{ |
| 16157 | .file = file, |
| 16158 | .offset = offset, |
| 16159 | .memory = memory, |
| 16160 | .section = null, |
| 16161 | }; |
| 16162 | } |
| 16163 | |
| 16164 | const CreateFileMapError = error{ |
| 16165 | /// MaximumSize is greater than the system-defined maximum for sections, or |
| 16166 | /// greater than the specified file and the section is not writable. |
| 16167 | SectionOversize, |
| 16168 | /// A file descriptor refers to a non-regular file. Or a file mapping was requested, |
| 16169 | /// but the file descriptor is not open for reading. Or `MAP.SHARED` was requested |
| 16170 | /// and `PROT_WRITE` is set, but the file descriptor is not open in `RDWR` mode. |
| 16171 | /// Or `PROT_WRITE` is set, but the file is append-only. |
| 16172 | AccessDenied, |
| 16173 | /// The `prot` argument asks for `PROT_EXEC` but the mapped area belongs to a file on |
| 16174 | /// a filesystem that was mounted no-exec. |
| 16175 | PermissionDenied, |
| 16176 | FileBusy, |
| 16177 | LockedMemoryLimitExceeded, |
| 16178 | OperationUnsupported, |
| 16179 | ProcessFdQuotaExceeded, |
| 16180 | SystemFdQuotaExceeded, |
| 16181 | OutOfMemory, |
| 16182 | MappingAlreadyExists, |
| 16183 | Unseekable, |
| 16184 | } || Io.Cancelable || Io.UnexpectedError; |
| 16185 | |
| 16186 | fn createFileMap( |
| 16187 | file: File, |
| 16188 | protection: std.process.MemoryProtection, |
| 16189 | offset: usize, |
| 16190 | populate: bool, |
| 16191 | aligned_len: usize, |
| 16192 | ) CreateFileMapError!File.MemoryMap { |
| 16193 | if (is_windows) { |
| 16194 | try Thread.checkCancel(); |
| 16195 | |
| 16196 | var section = windows.INVALID_HANDLE_VALUE; |
| 16197 | switch (windows.ntdll.NtCreateSection( |
| 16198 | &section, |
| 16199 | .{ |
| 16200 | .SPECIFIC = .{ .SECTION = .{ |
| 16201 | .QUERY = true, |
| 16202 | .MAP_WRITE = protection.write, |
| 16203 | .MAP_READ = protection.read, |
| 16204 | .MAP_EXECUTE = protection.execute, |
| 16205 | .EXTEND_SIZE = true, |
| 16206 | } }, |
| 16207 | .STANDARD = .{ .RIGHTS = .REQUIRED }, |
| 16208 | }, |
| 16209 | null, |
| 16210 | @constCast(&@as(i64, @intCast(aligned_len))), |
| 16211 | .{ .READWRITE = true }, |
| 16212 | .{ .COMMIT = populate }, |
| 16213 | file.handle, |
| 16214 | )) { |
| 16215 | .SUCCESS => {}, |
| 16216 | .FILE_LOCK_CONFLICT => return error.FileLocked, |
| 16217 | .INVALID_FILE_FOR_SECTION => return error.OperationUnsupported, |
| 16218 | else => |status| return windows.unexpectedStatus(status), |
| 16219 | } |
| 16220 | const current_process: windows.HANDLE = @ptrFromInt(@as(usize, @bitCast(@as(isize, -1)))); |
| 16221 | var contents_ptr: ?[*]u8 = null; |
| 16222 | var contents_len = aligned_len; |
| 16223 | switch (windows.ntdll.NtMapViewOfSection( |
| 16224 | section, |
| 16225 | current_process, |
| 16226 | @ptrCast(&contents_ptr), |
| 16227 | null, |
| 16228 | 0, |
| 16229 | null, |
| 16230 | &contents_len, |
| 16231 | .Unmap, |
| 16232 | .{}, |
| 16233 | .{ .READWRITE = true }, |
| 16234 | )) { |
| 16235 | .SUCCESS => {}, |
| 16236 | .CONFLICTING_ADDRESSES => return error.MappingAlreadyExists, |
| 16237 | .SECTION_PROTECTION => return error.PermissionDenied, |
| 16238 | else => |status| return windows.unexpectedStatus(status), |
| 16239 | } |
| 16240 | return .{ |
| 16241 | .file = file, |
| 16242 | .offset = offset, |
| 16243 | .memory = contents_ptr.?[0..contents_len], |
| 16244 | .section = section, |
| 16245 | }; |
| 16246 | } else if (have_mmap) { |
| 16247 | const prot: posix.PROT = .{ |
| 16248 | .READ = protection.read, |
| 16249 | .WRITE = protection.write, |
| 16250 | .EXEC = protection.execute, |
| 16251 | }; |
| 16252 | const flags: posix.MAP = .{ |
| 16253 | .TYPE = if (native_os == .linux) .SHARED_VALIDATE else .SHARED, |
| 16254 | .POPULATE = populate, |
| 16255 | }; |
| 16256 | |
| 16257 | const contents = while (true) { |
| 16258 | const syscall: Syscall = try .start(); |
| 16259 | const casted_offset = std.math.cast(i64, offset) orelse return error.Unseekable; |
| 16260 | const rc = mmap_sym(null, aligned_len, prot, flags, file.handle, casted_offset); |
| 16261 | syscall.finish(); |
| 16262 | const err: posix.E = if (builtin.link_libc) e: { |
| 16263 | if (rc != std.c.MAP_FAILED) { |
| 16264 | break @as([*]u8, @ptrCast(@alignCast(rc)))[0..aligned_len]; |
| 16265 | } |
| 16266 | break :e @enumFromInt(posix.system._errno().*); |
| 16267 | } else e: { |
| 16268 | const err = posix.errno(rc); |
| 16269 | if (err == .SUCCESS) { |
| 16270 | break @as([*]u8, @ptrFromInt(rc))[0..aligned_len]; |
| 16271 | } |
| 16272 | break :e err; |
| 16273 | }; |
| 16274 | switch (err) { |
| 16275 | .SUCCESS => unreachable, |
| 16276 | .INTR => continue, |
| 16277 | .ACCES => return error.AccessDenied, |
| 16278 | .AGAIN => return error.LockedMemoryLimitExceeded, |
| 16279 | .EXIST => return error.MappingAlreadyExists, |
| 16280 | .MFILE => return error.ProcessFdQuotaExceeded, |
| 16281 | .NFILE => return error.SystemFdQuotaExceeded, |
| 16282 | .NODEV => return error.OperationUnsupported, |
| 16283 | .NOMEM => return error.OutOfMemory, |
| 16284 | .PERM => return error.PermissionDenied, |
| 16285 | .TXTBSY => return error.FileBusy, |
| 16286 | .OVERFLOW => return error.Unseekable, |
| 16287 | .BADF => return errnoBug(err), // Always a race condition. |
| 16288 | .INVAL => return errnoBug(err), // Invalid parameters to mmap() |
| 16289 | else => return posix.unexpectedErrno(err), |
| 16290 | } |
| 16291 | }; |
| 16292 | return .{ |
| 16293 | .file = file, |
| 16294 | .offset = offset, |
| 16295 | .memory = contents, |
| 16296 | .section = {}, |
| 16297 | }; |
| 16298 | } |
| 16299 | |
| 16300 | return error.OperationUnsupported; |
| 16301 | } |
| 16302 | |
| 16303 | fn fileMemoryMapDestroy(userdata: ?*anyopaque, mm: *File.MemoryMap) void { |
| 16304 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 16305 | const memory = mm.memory; |
| 16306 | if (mm.section) |section| { |
| 16307 | if (is_windows) { |
| 16308 | const current_process: windows.HANDLE = @ptrFromInt(@as(usize, @bitCast(@as(isize, -1)))); |
| 16309 | _ = windows.ntdll.NtUnmapViewOfSection(current_process, memory.ptr); |
| 16310 | windows.CloseHandle(section); |
| 16311 | } else { |
| 16312 | switch (posix.errno(posix.system.munmap(memory.ptr, memory.len))) { |
| 16313 | .SUCCESS => {}, |
| 16314 | else => |e| { |
| 16315 | if (builtin.mode == .Debug) |
| 16316 | std.log.err("failed to unmap {d} bytes at {*}: {t}", .{ memory.len, memory.ptr, e }); |
| 16317 | }, |
| 16318 | } |
| 16319 | } |
| 16320 | } else { |
| 16321 | const gpa = t.allocator; |
| 16322 | const page_size = std.heap.pageSize(); |
| 16323 | const alignment: Alignment = .fromByteUnits(page_size); |
| 16324 | gpa.rawFree(memory, alignment, @returnAddress()); |
| 16325 | } |
| 16326 | mm.* = undefined; |
| 16327 | } |
| 16328 | |
| 16329 | fn fileMemoryMapSetLength( |
| 16330 | userdata: ?*anyopaque, |
| 16331 | mm: *File.MemoryMap, |
| 16332 | new_len: usize, |
| 16333 | ) File.MemoryMap.SetLengthError!void { |
| 16334 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 16335 | if (mm.section) |section| switch (native_os) { |
| 16336 | .windows => { |
| 16337 | _ = section; |
| 16338 | @panic("TODO"); |
| 16339 | }, |
| 16340 | .wasi => unreachable, |
| 16341 | else => { |
| 16342 | const flags: posix.MREMAP = .{ .MAYMOVE = true }; |
| 16343 | const addr_hint: ?[*]const u8 = null; |
| 16344 | const new_memory = while (true) { |
| 16345 | const syscall: Syscall = try .start(); |
| 16346 | const rc = posix.system.mremap(mm.memory.ptr, mm.memory.len, new_len, flags, addr_hint); |
| 16347 | syscall.finish(); |
| 16348 | const err: posix.E = if (builtin.link_libc) e: { |
| 16349 | if (rc != std.c.MAP_FAILED) break @as([*]u8, @ptrCast(@alignCast(rc)))[0..new_len]; |
| 16350 | break :e @enumFromInt(posix.system._errno().*); |
| 16351 | } else e: { |
| 16352 | const err = posix.errno(rc); |
| 16353 | if (err == .SUCCESS) break @as([*]u8, @ptrFromInt(rc))[0..new_len]; |
| 16354 | break :e err; |
| 16355 | }; |
| 16356 | switch (err) { |
| 16357 | .SUCCESS => unreachable, |
| 16358 | .INTR => continue, |
| 16359 | .AGAIN => return error.LockedMemoryLimitExceeded, |
| 16360 | .NOMEM => return error.OutOfMemory, |
| 16361 | .INVAL => return errnoBug(err), |
| 16362 | .FAULT => return errnoBug(err), |
| 16363 | else => return posix.unexpectedErrno(err), |
| 16364 | } |
| 16365 | }; |
| 16366 | mm.memory = new_memory; |
| 16367 | }, |
| 16368 | } else { |
| 16369 | const gpa = t.allocator; |
| 16370 | const page_size = std.heap.pageSize(); |
| 16371 | const alignment: Alignment = .fromByteUnits(page_size); |
| 16372 | if (gpa.rawRemap(mm.memory, alignment, new_len, @returnAddress())) |new_ptr| { |
| 16373 | mm.memory = new_ptr[0..new_len]; |
| 16374 | } else { |
| 16375 | const new_ptr = gpa.rawAlloc(new_len, alignment, @returnAddress()) orelse |
| 16376 | return error.OutOfMemory; |
| 16377 | const copy_len = @min(new_len, mm.memory.len); |
| 16378 | @memcpy(new_ptr[0..copy_len], mm.memory[0..copy_len]); |
| 16379 | mm.memory = new_ptr[0..new_len]; |
| 16380 | } |
| 16381 | } |
| 16382 | } |
| 16383 | |
| 16384 | fn fileMemoryMapRead(userdata: ?*anyopaque, mm: *File.MemoryMap) File.ReadPositionalError!void { |
| 16385 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 16386 | _ = t; |
| 16387 | if (mm.section != null) return; |
| 16388 | return mmSyncRead(mm.file, mm.memory, mm.offset); |
| 16389 | } |
| 16390 | |
| 16391 | fn fileMemoryMapWrite(userdata: ?*anyopaque, mm: *File.MemoryMap) File.WritePositionalError!void { |
| 16392 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 16393 | _ = t; |
| 16394 | if (mm.section != null) return; |
| 16395 | return mmSyncWrite(mm.file, mm.memory, mm.offset); |
| 16396 | } |
| 16397 | |
| 16398 | fn mmSyncRead(file: File, memory: []u8, offset: u64) File.ReadPositionalError!void { |
| 16399 | switch (native_os) { |
| 16400 | .windows => @panic("TODO"), |
| 16401 | .wasi => @panic("TODO"), |
| 16402 | else => { |
| 16403 | var i: usize = 0; |
| 16404 | const syscall: Syscall = try .start(); |
| 16405 | while (true) { |
| 16406 | const buf = memory[i..]; |
| 16407 | if (buf.len == 0) { |
| 16408 | syscall.finish(); |
| 16409 | break; |
| 16410 | } |
| 16411 | const rc = pread_sym(file.handle, buf.ptr, buf.len, @intCast(offset + i)); |
| 16412 | switch (posix.errno(rc)) { |
| 16413 | .SUCCESS => { |
| 16414 | const n: usize = @intCast(rc); |
| 16415 | if (n == 0) { |
| 16416 | syscall.finish(); |
| 16417 | @memset(memory[i..], 0); |
| 16418 | break; |
| 16419 | } |
| 16420 | i += n; |
| 16421 | try syscall.checkCancel(); |
| 16422 | continue; |
| 16423 | }, |
| 16424 | .INTR, .TIMEDOUT => { |
| 16425 | try syscall.checkCancel(); |
| 16426 | continue; |
| 16427 | }, |
| 16428 | .NXIO => return syscall.fail(error.Unseekable), |
| 16429 | .SPIPE => return syscall.fail(error.Unseekable), |
| 16430 | .OVERFLOW => return syscall.fail(error.Unseekable), |
| 16431 | .NOBUFS => return syscall.fail(error.SystemResources), |
| 16432 | .NOMEM => return syscall.fail(error.SystemResources), |
| 16433 | .AGAIN => return syscall.fail(error.WouldBlock), |
| 16434 | .IO => return syscall.fail(error.InputOutput), |
| 16435 | .ISDIR => return syscall.fail(error.IsDir), |
| 16436 | .NOTCONN => |err| return syscall.errnoBug(err), // not a socket |
| 16437 | .CONNRESET => |err| return syscall.errnoBug(err), // not a socket |
| 16438 | .INVAL => |err| return syscall.errnoBug(err), |
| 16439 | .FAULT => |err| return syscall.errnoBug(err), |
| 16440 | .BADF => |err| { |
| 16441 | syscall.finish(); |
| 16442 | if (native_os == .wasi) return error.IsDir; // File operation on directory. |
| 16443 | return errnoBug(err); // File descriptor used after closed. |
| 16444 | }, |
| 16445 | else => |err| return syscall.unexpectedErrno(err), |
| 16446 | } |
| 16447 | } |
| 16448 | }, |
| 16449 | } |
| 16450 | } |
| 16451 | |
| 16452 | fn mmSyncWrite(file: File, memory: []u8, offset: u64) File.WritePositionalError!void { |
| 16453 | switch (native_os) { |
| 16454 | .windows => @panic("TODO"), |
| 16455 | .wasi => @panic("TODO"), |
| 16456 | else => { |
| 16457 | var i: usize = 0; |
| 16458 | const syscall: Syscall = try .start(); |
| 16459 | while (true) { |
| 16460 | const buf = memory[i..]; |
| 16461 | if (buf.len == 0) { |
| 16462 | syscall.finish(); |
| 16463 | break; |
| 16464 | } |
| 16465 | const rc = pwrite_sym(file.handle, buf.ptr, buf.len, @intCast(offset)); |
| 16466 | switch (posix.errno(rc)) { |
| 16467 | .SUCCESS => { |
| 16468 | const n: usize = @bitCast(rc); |
| 16469 | i += n; |
| 16470 | try syscall.checkCancel(); |
| 16471 | continue; |
| 16472 | }, |
| 16473 | .INTR => { |
| 16474 | try syscall.checkCancel(); |
| 16475 | continue; |
| 16476 | }, |
| 16477 | .INVAL => |err| return syscall.errnoBug(err), |
| 16478 | .FAULT => |err| return syscall.errnoBug(err), |
| 16479 | .DESTADDRREQ => |err| return syscall.errnoBug(err), // not a socket |
| 16480 | .CONNRESET => |err| return syscall.errnoBug(err), // not a socket |
| 16481 | .BADF => |err| return syscall.errnoBug(err), // use after free |
| 16482 | .AGAIN => return syscall.fail(error.WouldBlock), |
| 16483 | .DQUOT => return syscall.fail(error.DiskQuota), |
| 16484 | .FBIG => return syscall.fail(error.FileTooBig), |
| 16485 | .IO => return syscall.fail(error.InputOutput), |
| 16486 | .NOSPC => return syscall.fail(error.NoSpaceLeft), |
| 16487 | .PERM => return syscall.fail(error.PermissionDenied), |
| 16488 | .PIPE => return syscall.fail(error.BrokenPipe), |
| 16489 | .BUSY => return syscall.fail(error.DeviceBusy), |
| 16490 | .TXTBSY => return syscall.fail(error.FileBusy), |
| 16491 | .NXIO => return syscall.fail(error.Unseekable), |
| 16492 | .SPIPE => return syscall.fail(error.Unseekable), |
| 16493 | .OVERFLOW => return syscall.fail(error.Unseekable), |
| 16494 | else => |err| return syscall.unexpectedErrno(err), |
| 16495 | } |
| 16496 | } |
| 16497 | }, |
| 16498 | } |
| 16499 | } |