| ... | @@ -22,6 +22,12 @@ const windows = std.os.windows; | ... | @@ -22,6 +22,12 @@ const windows = std.os.windows; |
| 22 | const ws2_32 = std.os.windows.ws2_32; | 22 | const ws2_32 = std.os.windows.ws2_32; |
| 23 | | 23 | |
| 24 | /// Thread-safe. | 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 | allocator: Allocator, | 31 | allocator: Allocator, |
| 26 | mutex: std.Thread.Mutex = .{}, | 32 | mutex: std.Thread.Mutex = .{}, |
| 27 | cond: std.Thread.Condition = .{}, | 33 | cond: std.Thread.Condition = .{}, |
| ... | @@ -1490,6 +1496,12 @@ pub fn io(t: *Threaded) Io { | ... | @@ -1490,6 +1496,12 @@ pub fn io(t: *Threaded) Io { |
| 1490 | .fileRealPath = fileRealPath, | 1496 | .fileRealPath = fileRealPath, |
| 1491 | .fileHardLink = fileHardLink, | 1497 | .fileHardLink = fileHardLink, |
| 1492 | | 1498 | |
| | 1499 | .fileMemoryMapCreate = fileMemoryMapCreate, |
| | 1500 | .fileMemoryMapDestroy = fileMemoryMapDestroy, |
| | 1501 | .fileMemoryMapSetLength = fileMemoryMapSetLength, |
| | 1502 | .fileMemoryMapRead = fileMemoryMapRead, |
| | 1503 | .fileMemoryMapWrite = fileMemoryMapWrite, |
| | 1504 | |
| 1493 | .processExecutableOpen = processExecutableOpen, | 1505 | .processExecutableOpen = processExecutableOpen, |
| 1494 | .processExecutablePath = processExecutablePath, | 1506 | .processExecutablePath = processExecutablePath, |
| 1495 | .lockStderr = lockStderr, | 1507 | .lockStderr = lockStderr, |
| ... | @@ -1642,6 +1654,12 @@ pub fn ioBasic(t: *Threaded) Io { | ... | @@ -1642,6 +1654,12 @@ pub fn ioBasic(t: *Threaded) Io { |
| 1642 | .fileRealPath = fileRealPath, | 1654 | .fileRealPath = fileRealPath, |
| 1643 | .fileHardLink = fileHardLink, | 1655 | .fileHardLink = fileHardLink, |
| 1644 | | 1656 | |
| | 1657 | .fileMemoryMapCreate = fileMemoryMapCreate, |
| | 1658 | .fileMemoryMapDestroy = fileMemoryMapDestroy, |
| | 1659 | .fileMemoryMapSetLength = fileMemoryMapSetLength, |
| | 1660 | .fileMemoryMapRead = fileMemoryMapRead, |
| | 1661 | .fileMemoryMapWrite = fileMemoryMapWrite, |
| | 1662 | |
| 1645 | .processExecutableOpen = processExecutableOpen, | 1663 | .processExecutableOpen = processExecutableOpen, |
| 1646 | .processExecutablePath = processExecutablePath, | 1664 | .processExecutablePath = processExecutablePath, |
| 1647 | .lockStderr = lockStderr, | 1665 | .lockStderr = lockStderr, |
| ... | @@ -1733,15 +1751,24 @@ const have_wait4 = switch (native_os) { | ... | @@ -1733,15 +1751,24 @@ const have_wait4 = switch (native_os) { |
| 1733 | else => false, | 1751 | else => false, |
| 1734 | }; | 1752 | }; |
| 1735 | | 1753 | |
| | 1754 | const have_mmap = switch (native_os) { |
| | 1755 | .wasi, .windows => false, |
| | 1756 | else => true, |
| | 1757 | }; |
| | 1758 | |
| 1736 | const open_sym = if (posix.lfs64_abi) posix.system.open64 else posix.system.open; | 1759 | const open_sym = if (posix.lfs64_abi) posix.system.open64 else posix.system.open; |
| 1737 | const openat_sym = if (posix.lfs64_abi) posix.system.openat64 else posix.system.openat; | 1760 | const openat_sym = if (posix.lfs64_abi) posix.system.openat64 else posix.system.openat; |
| 1738 | const fstat_sym = if (posix.lfs64_abi) posix.system.fstat64 else posix.system.fstat; | 1761 | const fstat_sym = if (posix.lfs64_abi) posix.system.fstat64 else posix.system.fstat; |
| 1739 | const fstatat_sym = if (posix.lfs64_abi) posix.system.fstatat64 else posix.system.fstatat; | 1762 | const fstatat_sym = if (posix.lfs64_abi) posix.system.fstatat64 else posix.system.fstatat; |
| 1740 | const lseek_sym = if (posix.lfs64_abi) posix.system.lseek64 else posix.system.lseek; | 1763 | const lseek_sym = if (posix.lfs64_abi) posix.system.lseek64 else posix.system.lseek; |
| 1741 | const preadv_sym = if (posix.lfs64_abi) posix.system.preadv64 else posix.system.preadv; | 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 | const ftruncate_sym = if (posix.lfs64_abi) posix.system.ftruncate64 else posix.system.ftruncate; | 1766 | const ftruncate_sym = if (posix.lfs64_abi) posix.system.ftruncate64 else posix.system.ftruncate; |
| 1743 | const pwritev_sym = if (posix.lfs64_abi) posix.system.pwritev64 else posix.system.pwritev; | 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 | const sendfile_sym = if (posix.lfs64_abi) posix.system.sendfile64 else posix.system.sendfile; | 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 | const linux_copy_file_range_use_c = std.c.versionCheck(if (builtin.abi.isAndroid()) .{ | 1772 | const linux_copy_file_range_use_c = std.c.versionCheck(if (builtin.abi.isAndroid()) .{ |
| 1746 | .major = 34, | 1773 | .major = 34, |
| 1747 | .minor = 0, | 1774 | .minor = 0, |
| ... | @@ -8028,27 +8055,22 @@ fn fileReadPositionalPosix(userdata: ?*anyopaque, file: File, data: []const []u8 | ... | @@ -8028,27 +8055,22 @@ fn fileReadPositionalPosix(userdata: ?*anyopaque, file: File, data: []const []u8 |
| 8028 | try syscall.checkCancel(); | 8055 | try syscall.checkCancel(); |
| 8029 | continue; | 8056 | continue; |
| 8030 | }, | 8057 | }, |
| 8031 | else => |e| { | 8058 | .NOTCONN => |err| return syscall.errnoBug(err), // not a socket |
| 8032 | syscall.finish(); | 8059 | .CONNRESET => |err| return syscall.errnoBug(err), // not a socket |
| 8033 | switch (e) { | 8060 | .BADF => |err| return syscall.errnoBug(err), // use after free |
| 8034 | .INVAL => |err| return errnoBug(err), | 8061 | .INVAL => |err| return syscall.errnoBug(err), |
| 8035 | .FAULT => |err| return errnoBug(err), | 8062 | .FAULT => |err| return syscall.errnoBug(err), // segmentation fault |
| 8036 | .AGAIN => |err| return errnoBug(err), | 8063 | .AGAIN => |err| return syscall.errnoBug(err), |
| 8037 | .BADF => return error.NotOpenForReading, // File operation on directory. | 8064 | .IO => return syscall.fail(error.InputOutput), |
| 8038 | .IO => return error.InputOutput, | 8065 | .ISDIR => return syscall.fail(error.IsDir), |
| 8039 | .ISDIR => return error.IsDir, | 8066 | .NOBUFS => return syscall.fail(error.SystemResources), |
| 8040 | .NOBUFS => return error.SystemResources, | 8067 | .NOMEM => return syscall.fail(error.SystemResources), |
| 8041 | .NOMEM => return error.SystemResources, | 8068 | .TIMEDOUT => return syscall.fail(error.Timeout), |
| 8042 | .NOTCONN => return error.SocketUnconnected, | 8069 | .NXIO => return syscall.fail(error.Unseekable), |
| 8043 | .CONNRESET => return error.ConnectionResetByPeer, | 8070 | .SPIPE => return syscall.fail(error.Unseekable), |
| 8044 | .TIMEDOUT => return error.Timeout, | 8071 | .OVERFLOW => return syscall.fail(error.Unseekable), |
| 8045 | .NXIO => return error.Unseekable, | 8072 | .NOTCAPABLE => return syscall.fail(error.AccessDenied), |
| 8046 | .SPIPE => return error.Unseekable, | 8073 | else => |err| return syscall.unexpectedErrno(err), |
| 8047 | .OVERFLOW => return error.Unseekable, | | |
| 8048 | .NOTCAPABLE => return error.AccessDenied, | | |
| 8049 | else => |err| return posix.unexpectedErrno(err), | | |
| 8050 | } | | |
| 8051 | }, | | |
| 8052 | } | 8074 | } |
| 8053 | } | 8075 | } |
| 8054 | } | 8076 | } |
| ... | @@ -8061,33 +8083,28 @@ fn fileReadPositionalPosix(userdata: ?*anyopaque, file: File, data: []const []u8 | ... | @@ -8061,33 +8083,28 @@ fn fileReadPositionalPosix(userdata: ?*anyopaque, file: File, data: []const []u8 |
| 8061 | syscall.finish(); | 8083 | syscall.finish(); |
| 8062 | return @bitCast(rc); | 8084 | return @bitCast(rc); |
| 8063 | }, | 8085 | }, |
| 8064 | .INTR => { | 8086 | .INTR, .TIMEDOUT => { |
| 8065 | try syscall.checkCancel(); | 8087 | try syscall.checkCancel(); |
| 8066 | continue; | 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 | syscall.finish(); | 8103 | syscall.finish(); |
| 8070 | switch (e) { | 8104 | if (native_os == .wasi) return error.IsDir; // File operation on directory. |
| 8071 | .INVAL => |err| return errnoBug(err), | 8105 | return errnoBug(err); // File descriptor used after closed. |
| 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 | } | | |
| 8090 | }, | 8106 | }, |
| | 8107 | else => |err| return syscall.unexpectedErrno(err), |
| 8091 | } | 8108 | } |
| 8092 | } | 8109 | } |
| 8093 | } | 8110 | } |
| ... | @@ -8770,29 +8787,24 @@ fn fileWritePositional( | ... | @@ -8770,29 +8787,24 @@ fn fileWritePositional( |
| 8770 | try syscall.checkCancel(); | 8787 | try syscall.checkCancel(); |
| 8771 | continue; | 8788 | continue; |
| 8772 | }, | 8789 | }, |
| 8773 | else => |e| { | 8790 | .INVAL => |err| return syscall.errnoBug(err), |
| 8774 | syscall.finish(); | 8791 | .FAULT => |err| return syscall.errnoBug(err), |
| 8775 | switch (e) { | 8792 | .DESTADDRREQ => |err| return syscall.errnoBug(err), // `connect` was never called. |
| 8776 | .INVAL => |err| return errnoBug(err), | 8793 | .CONNRESET => |err| return syscall.errnoBug(err), // Not a socket handle. |
| 8777 | .FAULT => |err| return errnoBug(err), | 8794 | .BADF => |err| return syscall.errnoBug(err), // use after free |
| 8778 | .AGAIN => return error.WouldBlock, | 8795 | .AGAIN => return syscall.fail(error.WouldBlock), |
| 8779 | .BADF => return error.NotOpenForWriting, // Usually a race condition. | 8796 | .DQUOT => return syscall.fail(error.DiskQuota), |
| 8780 | .DESTADDRREQ => |err| return errnoBug(err), // `connect` was never called. | 8797 | .FBIG => return syscall.fail(error.FileTooBig), |
| 8781 | .DQUOT => return error.DiskQuota, | 8798 | .IO => return syscall.fail(error.InputOutput), |
| 8782 | .FBIG => return error.FileTooBig, | 8799 | .NOSPC => return syscall.fail(error.NoSpaceLeft), |
| 8783 | .IO => return error.InputOutput, | 8800 | .PERM => return syscall.fail(error.PermissionDenied), |
| 8784 | .NOSPC => return error.NoSpaceLeft, | 8801 | .PIPE => return syscall.fail(error.BrokenPipe), |
| 8785 | .PERM => return error.PermissionDenied, | 8802 | .BUSY => return syscall.fail(error.DeviceBusy), |
| 8786 | .PIPE => return error.BrokenPipe, | 8803 | .TXTBSY => return syscall.fail(error.FileBusy), |
| 8787 | .CONNRESET => |err| return errnoBug(err), // Not a socket handle. | 8804 | .NXIO => return syscall.fail(error.Unseekable), |
| 8788 | .BUSY => return error.DeviceBusy, | 8805 | .SPIPE => return syscall.fail(error.Unseekable), |
| 8789 | .TXTBSY => return error.FileBusy, | 8806 | .OVERFLOW => return syscall.fail(error.Unseekable), |
| 8790 | .NXIO => return error.Unseekable, | 8807 | else => |err| return syscall.unexpectedErrno(err), |
| 8791 | .SPIPE => return error.Unseekable, | | |
| 8792 | .OVERFLOW => return error.Unseekable, | | |
| 8793 | else => |err| return posix.unexpectedErrno(err), | | |
| 8794 | } | | |
| 8795 | }, | | |
| 8796 | } | 8808 | } |
| 8797 | } | 8809 | } |
| 8798 | } | 8810 | } |
| ... | @@ -16107,3 +16119,381 @@ pub fn chdir(dir_path: []const u8) ChdirError!void { | ... | @@ -16107,3 +16119,381 @@ pub fn chdir(dir_path: []const u8) ChdirError!void { |
| 16107 | else => |err| return syscall.unexpectedErrno(err), | 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 | } |