| ... | ... | @@ -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 = .{}, |
| ... | ... | @@ -51,6 +57,7 @@ use_sendfile: UseSendfile = .default, |
| 51 | 57 | use_copy_file_range: UseCopyFileRange = .default, |
| 52 | 58 | use_fcopyfile: UseFcopyfile = .default, |
| 53 | 59 | use_fchmodat2: UseFchmodat2 = .default, |
| 60 | disable_memory_mapping: bool, |
| 54 | 61 | |
| 55 | 62 | stderr_writer: File.Writer = .{ |
| 56 | 63 | .io = undefined, |
| ... | ... | @@ -69,6 +76,13 @@ random_file: RandomFile = .{}, |
| 69 | 76 | |
| 70 | 77 | csprng: Csprng = .{}, |
| 71 | 78 | |
| 79 | system_basic_information: SystemBasicInformation = .{}, |
| 80 | |
| 81 | const SystemBasicInformation = if (!is_windows) struct {} else struct { |
| 82 | buffer: windows.SYSTEM_BASIC_INFORMATION = undefined, |
| 83 | initialized: std.atomic.Value(bool) = .{ .raw = false }, |
| 84 | }; |
| 85 | |
| 72 | 86 | pub const Csprng = struct { |
| 73 | 87 | rng: std.Random.DefaultCsprng = .{ |
| 74 | 88 | .state = undefined, |
| ... | ... | @@ -1214,6 +1228,8 @@ pub const InitOptions = struct { |
| 1214 | 1228 | /// * `processExecutablePath` on OpenBSD and Haiku (observes "PATH"). |
| 1215 | 1229 | /// * `processSpawn`, `processSpawnPath`, `processReplace`, `processReplacePath` |
| 1216 | 1230 | environ: process.Environ, |
| 1231 | /// If set to `true`, `File.MemoryMap` APIs will always take the fallback path. |
| 1232 | disable_memory_mapping: bool = false, |
| 1217 | 1233 | }; |
| 1218 | 1234 | |
| 1219 | 1235 | /// Related: |
| ... | ... | @@ -1241,6 +1257,7 @@ pub fn init( |
| 1241 | 1257 | .argv0 = options.argv0, |
| 1242 | 1258 | .environ = .{ .process_environ = options.environ }, |
| 1243 | 1259 | .worker_threads = init_single_threaded.worker_threads, |
| 1260 | .disable_memory_mapping = options.disable_memory_mapping, |
| 1244 | 1261 | }; |
| 1245 | 1262 | |
| 1246 | 1263 | const cpu_count = std.Thread.getCpuCount(); |
| ... | ... | @@ -1257,6 +1274,7 @@ pub fn init( |
| 1257 | 1274 | .argv0 = options.argv0, |
| 1258 | 1275 | .environ = .{ .process_environ = options.environ }, |
| 1259 | 1276 | .worker_threads = .init(null), |
| 1277 | .disable_memory_mapping = options.disable_memory_mapping, |
| 1260 | 1278 | }; |
| 1261 | 1279 | |
| 1262 | 1280 | if (posix.Sigaction != void) { |
| ... | ... | @@ -1293,6 +1311,7 @@ pub const init_single_threaded: Threaded = .{ |
| 1293 | 1311 | .argv0 = .empty, |
| 1294 | 1312 | .environ = .{}, |
| 1295 | 1313 | .worker_threads = .init(null), |
| 1314 | .disable_memory_mapping = false, |
| 1296 | 1315 | }; |
| 1297 | 1316 | |
| 1298 | 1317 | var global_single_threaded_instance: Threaded = .init_single_threaded; |
| ... | ... | @@ -1490,6 +1509,12 @@ pub fn io(t: *Threaded) Io { |
| 1490 | 1509 | .fileRealPath = fileRealPath, |
| 1491 | 1510 | .fileHardLink = fileHardLink, |
| 1492 | 1511 | |
| 1512 | .fileMemoryMapCreate = fileMemoryMapCreate, |
| 1513 | .fileMemoryMapDestroy = fileMemoryMapDestroy, |
| 1514 | .fileMemoryMapSetLength = fileMemoryMapSetLength, |
| 1515 | .fileMemoryMapRead = fileMemoryMapRead, |
| 1516 | .fileMemoryMapWrite = fileMemoryMapWrite, |
| 1517 | |
| 1493 | 1518 | .processExecutableOpen = processExecutableOpen, |
| 1494 | 1519 | .processExecutablePath = processExecutablePath, |
| 1495 | 1520 | .lockStderr = lockStderr, |
| ... | ... | @@ -1642,6 +1667,12 @@ pub fn ioBasic(t: *Threaded) Io { |
| 1642 | 1667 | .fileRealPath = fileRealPath, |
| 1643 | 1668 | .fileHardLink = fileHardLink, |
| 1644 | 1669 | |
| 1670 | .fileMemoryMapCreate = fileMemoryMapCreate, |
| 1671 | .fileMemoryMapDestroy = fileMemoryMapDestroy, |
| 1672 | .fileMemoryMapSetLength = fileMemoryMapSetLength, |
| 1673 | .fileMemoryMapRead = fileMemoryMapRead, |
| 1674 | .fileMemoryMapWrite = fileMemoryMapWrite, |
| 1675 | |
| 1645 | 1676 | .processExecutableOpen = processExecutableOpen, |
| 1646 | 1677 | .processExecutablePath = processExecutablePath, |
| 1647 | 1678 | .lockStderr = lockStderr, |
| ... | ... | @@ -1733,15 +1764,24 @@ const have_wait4 = switch (native_os) { |
| 1733 | 1764 | else => false, |
| 1734 | 1765 | }; |
| 1735 | 1766 | |
| 1767 | const have_mmap = switch (native_os) { |
| 1768 | .wasi, .windows => false, |
| 1769 | else => true, |
| 1770 | }; |
| 1771 | |
| 1736 | 1772 | const open_sym = if (posix.lfs64_abi) posix.system.open64 else posix.system.open; |
| 1737 | 1773 | const openat_sym = if (posix.lfs64_abi) posix.system.openat64 else posix.system.openat; |
| 1738 | 1774 | const fstat_sym = if (posix.lfs64_abi) posix.system.fstat64 else posix.system.fstat; |
| 1739 | 1775 | const fstatat_sym = if (posix.lfs64_abi) posix.system.fstatat64 else posix.system.fstatat; |
| 1740 | 1776 | const lseek_sym = if (posix.lfs64_abi) posix.system.lseek64 else posix.system.lseek; |
| 1741 | 1777 | const preadv_sym = if (posix.lfs64_abi) posix.system.preadv64 else posix.system.preadv; |
| 1778 | const pread_sym = if (posix.lfs64_abi) posix.system.pread64 else posix.system.pread; |
| 1742 | 1779 | const ftruncate_sym = if (posix.lfs64_abi) posix.system.ftruncate64 else posix.system.ftruncate; |
| 1743 | 1780 | const pwritev_sym = if (posix.lfs64_abi) posix.system.pwritev64 else posix.system.pwritev; |
| 1781 | const pwrite_sym = if (posix.lfs64_abi) posix.system.pwrite64 else posix.system.pwrite; |
| 1744 | 1782 | const sendfile_sym = if (posix.lfs64_abi) posix.system.sendfile64 else posix.system.sendfile; |
| 1783 | const mmap_sym = if (posix.lfs64_abi) posix.system.mmap64 else posix.system.mmap; |
| 1784 | |
| 1745 | 1785 | const linux_copy_file_range_use_c = std.c.versionCheck(if (builtin.abi.isAndroid()) .{ |
| 1746 | 1786 | .major = 34, |
| 1747 | 1787 | .minor = 0, |
| ... | ... | @@ -2908,7 +2948,11 @@ fn fileStatLinux(userdata: ?*anyopaque, file: File) File.StatError!File.Stat { |
| 2908 | 2948 | |
| 2909 | 2949 | fn fileStatWindows(userdata: ?*anyopaque, file: File) File.StatError!File.Stat { |
| 2910 | 2950 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 2911 | | _ = t; |
| 2951 | |
| 2952 | const block_size: u32 = if (t.systemBasicInformation()) |sbi| |
| 2953 | @intCast(@max(sbi.PageSize, sbi.AllocationGranularity)) |
| 2954 | else |
| 2955 | std.heap.page_size_max; |
| 2912 | 2956 | |
| 2913 | 2957 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; |
| 2914 | 2958 | var info: windows.FILE.ALL_INFORMATION = undefined; |
| ... | ... | @@ -2970,10 +3014,31 @@ fn fileStatWindows(userdata: ?*anyopaque, file: File) File.StatError!File.Stat { |
| 2970 | 3014 | .atime = windows.fromSysTime(info.BasicInformation.LastAccessTime), |
| 2971 | 3015 | .mtime = windows.fromSysTime(info.BasicInformation.LastWriteTime), |
| 2972 | 3016 | .ctime = windows.fromSysTime(info.BasicInformation.ChangeTime), |
| 2973 | | .nlink = 0, |
| 3017 | .nlink = info.StandardInformation.NumberOfLinks, |
| 3018 | .block_size = block_size, |
| 2974 | 3019 | }; |
| 2975 | 3020 | } |
| 2976 | 3021 | |
| 3022 | fn systemBasicInformation(t: *Threaded) ?*const windows.SYSTEM_BASIC_INFORMATION { |
| 3023 | if (!t.system_basic_information.initialized.load(.acquire)) { |
| 3024 | t.mutex.lock(); |
| 3025 | defer t.mutex.unlock(); |
| 3026 | |
| 3027 | switch (windows.ntdll.NtQuerySystemInformation( |
| 3028 | .SystemBasicInformation, |
| 3029 | &t.system_basic_information.buffer, |
| 3030 | @sizeOf(windows.SYSTEM_BASIC_INFORMATION), |
| 3031 | null, |
| 3032 | )) { |
| 3033 | .SUCCESS => {}, |
| 3034 | else => return null, |
| 3035 | } |
| 3036 | |
| 3037 | t.system_basic_information.initialized.store(true, .release); |
| 3038 | } |
| 3039 | return &t.system_basic_information.buffer; |
| 3040 | } |
| 3041 | |
| 2977 | 3042 | fn fileStatWasi(userdata: ?*anyopaque, file: File) File.StatError!File.Stat { |
| 2978 | 3043 | if (builtin.link_libc) return fileStatPosix(userdata, file); |
| 2979 | 3044 | |
| ... | ... | @@ -7889,7 +7954,7 @@ fn fileReadStreamingPosix(userdata: ?*anyopaque, file: File, data: []const []u8) |
| 7889 | 7954 | syscall.finish(); |
| 7890 | 7955 | return nread; |
| 7891 | 7956 | }, |
| 7892 | | .INTR => { |
| 7957 | .INTR, .TIMEDOUT => { |
| 7893 | 7958 | try syscall.checkCancel(); |
| 7894 | 7959 | continue; |
| 7895 | 7960 | }, |
| ... | ... | @@ -7898,14 +7963,13 @@ fn fileReadStreamingPosix(userdata: ?*anyopaque, file: File, data: []const []u8) |
| 7898 | 7963 | switch (e) { |
| 7899 | 7964 | .INVAL => |err| return errnoBug(err), |
| 7900 | 7965 | .FAULT => |err| return errnoBug(err), |
| 7901 | | .BADF => return error.NotOpenForReading, // File operation on directory. |
| 7966 | .BADF => return error.IsDir, // File operation on directory. |
| 7902 | 7967 | .IO => return error.InputOutput, |
| 7903 | 7968 | .ISDIR => return error.IsDir, |
| 7904 | 7969 | .NOBUFS => return error.SystemResources, |
| 7905 | 7970 | .NOMEM => return error.SystemResources, |
| 7906 | 7971 | .NOTCONN => return error.SocketUnconnected, |
| 7907 | 7972 | .CONNRESET => return error.ConnectionResetByPeer, |
| 7908 | | .TIMEDOUT => return error.Timeout, |
| 7909 | 7973 | .NOTCAPABLE => return error.AccessDenied, |
| 7910 | 7974 | else => |err| return posix.unexpectedErrno(err), |
| 7911 | 7975 | } |
| ... | ... | @@ -7922,7 +7986,7 @@ fn fileReadStreamingPosix(userdata: ?*anyopaque, file: File, data: []const []u8) |
| 7922 | 7986 | syscall.finish(); |
| 7923 | 7987 | return @intCast(rc); |
| 7924 | 7988 | }, |
| 7925 | | .INTR => { |
| 7989 | .INTR, .TIMEDOUT => { |
| 7926 | 7990 | try syscall.checkCancel(); |
| 7927 | 7991 | continue; |
| 7928 | 7992 | }, |
| ... | ... | @@ -7932,9 +7996,9 @@ fn fileReadStreamingPosix(userdata: ?*anyopaque, file: File, data: []const []u8) |
| 7932 | 7996 | .INVAL => |err| return errnoBug(err), |
| 7933 | 7997 | .FAULT => |err| return errnoBug(err), |
| 7934 | 7998 | .AGAIN => return error.WouldBlock, |
| 7935 | | .BADF => |err| { |
| 7936 | | if (native_os == .wasi) return error.NotOpenForReading; // File operation on directory. |
| 7937 | | return errnoBug(err); // File descriptor used after closed. |
| 7999 | .BADF => { |
| 8000 | if (native_os == .wasi) return error.IsDir; // File operation on directory. |
| 8001 | return error.NotOpenForReading; |
| 7938 | 8002 | }, |
| 7939 | 8003 | .IO => return error.InputOutput, |
| 7940 | 8004 | .ISDIR => return error.IsDir, |
| ... | ... | @@ -7942,7 +8006,6 @@ fn fileReadStreamingPosix(userdata: ?*anyopaque, file: File, data: []const []u8) |
| 7942 | 8006 | .NOMEM => return error.SystemResources, |
| 7943 | 8007 | .NOTCONN => return error.SocketUnconnected, |
| 7944 | 8008 | .CONNRESET => return error.ConnectionResetByPeer, |
| 7945 | | .TIMEDOUT => return error.Timeout, |
| 7946 | 8009 | else => |err| return posix.unexpectedErrno(err), |
| 7947 | 8010 | } |
| 7948 | 8011 | }, |
| ... | ... | @@ -7981,10 +8044,10 @@ fn fileReadStreamingWindows(userdata: ?*anyopaque, file: File, data: []const []u |
| 7981 | 8044 | syscall.finish(); |
| 7982 | 8045 | return 0; |
| 7983 | 8046 | }, |
| 7984 | | .NETNAME_DELETED => return syscall.fail(error.ConnectionResetByPeer), |
| 8047 | .NETNAME_DELETED => if (is_debug) unreachable else return error.Unexpected, |
| 7985 | 8048 | .LOCK_VIOLATION => return syscall.fail(error.LockViolation), |
| 7986 | 8049 | .ACCESS_DENIED => return syscall.fail(error.AccessDenied), |
| 7987 | | .INVALID_HANDLE => return syscall.fail(error.NotOpenForReading), |
| 8050 | .INVALID_HANDLE => if (is_debug) unreachable else return error.Unexpected, |
| 7988 | 8051 | // TODO: Determine if INVALID_FUNCTION is possible in more scenarios than just passing |
| 7989 | 8052 | // a handle to a directory. |
| 7990 | 8053 | .INVALID_FUNCTION => return syscall.fail(error.IsDir), |
| ... | ... | @@ -8024,31 +8087,25 @@ fn fileReadPositionalPosix(userdata: ?*anyopaque, file: File, data: []const []u8 |
| 8024 | 8087 | syscall.finish(); |
| 8025 | 8088 | return nread; |
| 8026 | 8089 | }, |
| 8027 | | .INTR => { |
| 8090 | .INTR, .TIMEDOUT => { |
| 8028 | 8091 | try syscall.checkCancel(); |
| 8029 | 8092 | continue; |
| 8030 | 8093 | }, |
| 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 | | }, |
| 8094 | .NOTCONN => |err| return syscall.errnoBug(err), // not a socket |
| 8095 | .CONNRESET => |err| return syscall.errnoBug(err), // not a socket |
| 8096 | .INVAL => |err| return syscall.errnoBug(err), |
| 8097 | .FAULT => |err| return syscall.errnoBug(err), // segmentation fault |
| 8098 | .AGAIN => |err| return syscall.errnoBug(err), |
| 8099 | .IO => return syscall.fail(error.InputOutput), |
| 8100 | .ISDIR => return syscall.fail(error.IsDir), |
| 8101 | .BADF => return syscall.fail(error.IsDir), |
| 8102 | .NOBUFS => return syscall.fail(error.SystemResources), |
| 8103 | .NOMEM => return syscall.fail(error.SystemResources), |
| 8104 | .NXIO => return syscall.fail(error.Unseekable), |
| 8105 | .SPIPE => return syscall.fail(error.Unseekable), |
| 8106 | .OVERFLOW => return syscall.fail(error.Unseekable), |
| 8107 | .NOTCAPABLE => return syscall.fail(error.AccessDenied), |
| 8108 | else => |err| return syscall.unexpectedErrno(err), |
| 8052 | 8109 | } |
| 8053 | 8110 | } |
| 8054 | 8111 | } |
| ... | ... | @@ -8061,33 +8118,28 @@ fn fileReadPositionalPosix(userdata: ?*anyopaque, file: File, data: []const []u8 |
| 8061 | 8118 | syscall.finish(); |
| 8062 | 8119 | return @bitCast(rc); |
| 8063 | 8120 | }, |
| 8064 | | .INTR => { |
| 8121 | .INTR, .TIMEDOUT => { |
| 8065 | 8122 | try syscall.checkCancel(); |
| 8066 | 8123 | continue; |
| 8067 | 8124 | }, |
| 8068 | | else => |e| { |
| 8125 | .NXIO => return syscall.fail(error.Unseekable), |
| 8126 | .SPIPE => return syscall.fail(error.Unseekable), |
| 8127 | .OVERFLOW => return syscall.fail(error.Unseekable), |
| 8128 | .NOBUFS => return syscall.fail(error.SystemResources), |
| 8129 | .NOMEM => return syscall.fail(error.SystemResources), |
| 8130 | .AGAIN => return syscall.fail(error.WouldBlock), |
| 8131 | .IO => return syscall.fail(error.InputOutput), |
| 8132 | .ISDIR => return syscall.fail(error.IsDir), |
| 8133 | .NOTCONN => |err| return syscall.errnoBug(err), // not a socket |
| 8134 | .CONNRESET => |err| return syscall.errnoBug(err), // not a socket |
| 8135 | .INVAL => |err| return syscall.errnoBug(err), |
| 8136 | .FAULT => |err| return syscall.errnoBug(err), |
| 8137 | .BADF => { |
| 8069 | 8138 | 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 | | } |
| 8139 | if (native_os == .wasi) return error.IsDir; // File operation on directory. |
| 8140 | return error.NotOpenForReading; |
| 8090 | 8141 | }, |
| 8142 | else => |err| return syscall.unexpectedErrno(err), |
| 8091 | 8143 | } |
| 8092 | 8144 | } |
| 8093 | 8145 | } |
| ... | ... | @@ -8101,14 +8153,17 @@ fn fileReadPositionalWindows(userdata: ?*anyopaque, file: File, data: []const [] |
| 8101 | 8153 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 8102 | 8154 | _ = t; |
| 8103 | 8155 | |
| 8104 | | const DWORD = windows.DWORD; |
| 8105 | | |
| 8106 | 8156 | var index: usize = 0; |
| 8107 | 8157 | while (index < data.len and data[index].len == 0) index += 1; |
| 8108 | 8158 | if (index == data.len) return 0; |
| 8109 | 8159 | const buffer = data[index]; |
| 8110 | | const want_read_count: DWORD = @min(std.math.maxInt(DWORD), buffer.len); |
| 8111 | 8160 | |
| 8161 | return readFilePositionalWindows(file, buffer, offset); |
| 8162 | } |
| 8163 | |
| 8164 | fn readFilePositionalWindows(file: File, buffer: []u8, offset: u64) File.ReadPositionalError!usize { |
| 8165 | const DWORD = windows.DWORD; |
| 8166 | const want_read_count: DWORD = @min(std.math.maxInt(DWORD), buffer.len); |
| 8112 | 8167 | var overlapped: windows.OVERLAPPED = .{ |
| 8113 | 8168 | .Internal = 0, |
| 8114 | 8169 | .InternalHigh = 0, |
| ... | ... | @@ -8141,10 +8196,10 @@ fn fileReadPositionalWindows(userdata: ?*anyopaque, file: File, data: []const [] |
| 8141 | 8196 | syscall.finish(); |
| 8142 | 8197 | return 0; |
| 8143 | 8198 | }, |
| 8144 | | .NETNAME_DELETED => return syscall.fail(error.ConnectionResetByPeer), |
| 8199 | .NETNAME_DELETED => if (is_debug) unreachable else return error.Unexpected, |
| 8145 | 8200 | .LOCK_VIOLATION => return syscall.fail(error.LockViolation), |
| 8146 | 8201 | .ACCESS_DENIED => return syscall.fail(error.AccessDenied), |
| 8147 | | .INVALID_HANDLE => return syscall.fail(error.NotOpenForReading), |
| 8202 | .INVALID_HANDLE => if (is_debug) unreachable else return error.Unexpected, |
| 8148 | 8203 | // TODO: Determine if INVALID_FUNCTION is possible in more scenarios than just passing |
| 8149 | 8204 | // a handle to a directory. |
| 8150 | 8205 | .INVALID_FUNCTION => return syscall.fail(error.IsDir), |
| ... | ... | @@ -8739,7 +8794,7 @@ fn fileWritePositional( |
| 8739 | 8794 | .INVAL => |err| return errnoBug(err), |
| 8740 | 8795 | .FAULT => |err| return errnoBug(err), |
| 8741 | 8796 | .AGAIN => |err| return errnoBug(err), |
| 8742 | | .BADF => return error.NotOpenForWriting, // can be a race condition. |
| 8797 | .BADF => return error.NotOpenForWriting, |
| 8743 | 8798 | .DESTADDRREQ => |err| return errnoBug(err), // `connect` was never called. |
| 8744 | 8799 | .DQUOT => return error.DiskQuota, |
| 8745 | 8800 | .FBIG => return error.FileTooBig, |
| ... | ... | @@ -8770,29 +8825,24 @@ fn fileWritePositional( |
| 8770 | 8825 | try syscall.checkCancel(); |
| 8771 | 8826 | continue; |
| 8772 | 8827 | }, |
| 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 | | }, |
| 8828 | .INVAL => |err| return syscall.errnoBug(err), |
| 8829 | .FAULT => |err| return syscall.errnoBug(err), |
| 8830 | .DESTADDRREQ => |err| return syscall.errnoBug(err), // `connect` was never called. |
| 8831 | .CONNRESET => |err| return syscall.errnoBug(err), // Not a socket handle. |
| 8832 | .BADF => return syscall.fail(error.NotOpenForWriting), |
| 8833 | .AGAIN => return syscall.fail(error.WouldBlock), |
| 8834 | .DQUOT => return syscall.fail(error.DiskQuota), |
| 8835 | .FBIG => return syscall.fail(error.FileTooBig), |
| 8836 | .IO => return syscall.fail(error.InputOutput), |
| 8837 | .NOSPC => return syscall.fail(error.NoSpaceLeft), |
| 8838 | .PERM => return syscall.fail(error.PermissionDenied), |
| 8839 | .PIPE => return syscall.fail(error.BrokenPipe), |
| 8840 | .BUSY => return syscall.fail(error.DeviceBusy), |
| 8841 | .TXTBSY => return syscall.fail(error.FileBusy), |
| 8842 | .NXIO => return syscall.fail(error.Unseekable), |
| 8843 | .SPIPE => return syscall.fail(error.Unseekable), |
| 8844 | .OVERFLOW => return syscall.fail(error.Unseekable), |
| 8845 | else => |err| return syscall.unexpectedErrno(err), |
| 8796 | 8846 | } |
| 8797 | 8847 | } |
| 8798 | 8848 | } |
| ... | ... | @@ -8830,7 +8880,7 @@ fn writeFilePositionalWindows( |
| 8830 | 8880 | .NOT_ENOUGH_MEMORY => return syscall.fail(error.SystemResources), |
| 8831 | 8881 | .NOT_ENOUGH_QUOTA => return syscall.fail(error.SystemResources), |
| 8832 | 8882 | .NO_DATA => return syscall.fail(error.BrokenPipe), |
| 8833 | | .INVALID_HANDLE => return syscall.fail(error.NotOpenForWriting), |
| 8883 | .INVALID_HANDLE => if (is_debug) unreachable else return error.Unexpected, // use after free |
| 8834 | 8884 | .LOCK_VIOLATION => return syscall.fail(error.LockViolation), |
| 8835 | 8885 | .ACCESS_DENIED => return syscall.fail(error.AccessDenied), |
| 8836 | 8886 | .WORKING_SET_QUOTA => return syscall.fail(error.SystemResources), |
| ... | ... | @@ -12458,6 +12508,7 @@ const linux_statx_request: std.os.linux.STATX = .{ |
| 12458 | 12508 | .INO = true, |
| 12459 | 12509 | .SIZE = true, |
| 12460 | 12510 | .NLINK = true, |
| 12511 | .BLOCKS = true, |
| 12461 | 12512 | }; |
| 12462 | 12513 | |
| 12463 | 12514 | const linux_statx_check: std.os.linux.STATX = .{ |
| ... | ... | @@ -12469,6 +12520,7 @@ const linux_statx_check: std.os.linux.STATX = .{ |
| 12469 | 12520 | .INO = true, |
| 12470 | 12521 | .SIZE = true, |
| 12471 | 12522 | .NLINK = true, |
| 12523 | .BLOCKS = false, |
| 12472 | 12524 | }; |
| 12473 | 12525 | |
| 12474 | 12526 | fn statFromLinux(stx: *const std.os.linux.Statx) Io.UnexpectedError!File.Stat { |
| ... | ... | @@ -12487,6 +12539,7 @@ fn statFromLinux(stx: *const std.os.linux.Statx) Io.UnexpectedError!File.Stat { |
| 12487 | 12539 | }, |
| 12488 | 12540 | .mtime = .{ .nanoseconds = @intCast(@as(i128, stx.mtime.sec) * std.time.ns_per_s + stx.mtime.nsec) }, |
| 12489 | 12541 | .ctime = .{ .nanoseconds = @intCast(@as(i128, stx.ctime.sec) * std.time.ns_per_s + stx.ctime.nsec) }, |
| 12542 | .block_size = if (stx.mask.BLOCKS) stx.blksize else 1, |
| 12490 | 12543 | }; |
| 12491 | 12544 | } |
| 12492 | 12545 | |
| ... | ... | @@ -12535,6 +12588,7 @@ fn statFromPosix(st: *const posix.Stat) File.Stat { |
| 12535 | 12588 | .atime = timestampFromPosix(&atime), |
| 12536 | 12589 | .mtime = timestampFromPosix(&mtime), |
| 12537 | 12590 | .ctime = timestampFromPosix(&ctime), |
| 12591 | .block_size = @intCast(st.blksize), |
| 12538 | 12592 | }; |
| 12539 | 12593 | } |
| 12540 | 12594 | |
| ... | ... | @@ -12556,6 +12610,7 @@ fn statFromWasi(st: *const std.os.wasi.filestat_t) File.Stat { |
| 12556 | 12610 | .atime = .fromNanoseconds(st.atim), |
| 12557 | 12611 | .mtime = .fromNanoseconds(st.mtim), |
| 12558 | 12612 | .ctime = .fromNanoseconds(st.ctim), |
| 12613 | .block_size = 1, |
| 12559 | 12614 | }; |
| 12560 | 12615 | } |
| 12561 | 12616 | |
| ... | ... | @@ -16107,3 +16162,529 @@ pub fn chdir(dir_path: []const u8) ChdirError!void { |
| 16107 | 16162 | else => |err| return syscall.unexpectedErrno(err), |
| 16108 | 16163 | }; |
| 16109 | 16164 | } |
| 16165 | |
| 16166 | fn fileMemoryMapCreate( |
| 16167 | userdata: ?*anyopaque, |
| 16168 | file: File, |
| 16169 | options: File.MemoryMap.CreateOptions, |
| 16170 | ) File.MemoryMap.CreateError!File.MemoryMap { |
| 16171 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 16172 | const offset = options.offset; |
| 16173 | const len = options.len; |
| 16174 | |
| 16175 | if (!t.disable_memory_mapping) { |
| 16176 | if (createFileMap(file, options.protection, offset, options.populate, len)) |result| { |
| 16177 | return result; |
| 16178 | } else |err| switch (err) { |
| 16179 | error.Unseekable, error.Canceled, error.AccessDenied => |e| return e, |
| 16180 | error.OperationUnsupported => {}, |
| 16181 | else => { |
| 16182 | if (builtin.mode == .Debug) |
| 16183 | std.log.warn("memory mapping failed with {t}, falling back to file operations", .{err}); |
| 16184 | }, |
| 16185 | } |
| 16186 | } |
| 16187 | |
| 16188 | const gpa = t.allocator; |
| 16189 | const page_size = std.heap.pageSize(); |
| 16190 | const alignment: Alignment = .fromByteUnits(page_size); |
| 16191 | const memory = m: { |
| 16192 | const ptr = gpa.rawAlloc(len, alignment, @returnAddress()) orelse return error.OutOfMemory; |
| 16193 | break :m ptr[0..len]; |
| 16194 | }; |
| 16195 | errdefer gpa.rawFree(memory, alignment, @returnAddress()); |
| 16196 | |
| 16197 | if (!options.undefined_contents) try mmSyncRead(file, memory, offset); |
| 16198 | |
| 16199 | return .{ |
| 16200 | .file = file, |
| 16201 | .offset = offset, |
| 16202 | .memory = @alignCast(memory), |
| 16203 | .section = null, |
| 16204 | }; |
| 16205 | } |
| 16206 | |
| 16207 | const CreateFileMapError = error{ |
| 16208 | /// MaximumSize is greater than the system-defined maximum for sections, or |
| 16209 | /// greater than the specified file and the section is not writable. |
| 16210 | SectionOversize, |
| 16211 | /// A file descriptor refers to a non-regular file. Or a file mapping was requested, |
| 16212 | /// but the file descriptor is not open for reading. Or `MAP.SHARED` was requested |
| 16213 | /// and `PROT_WRITE` is set, but the file descriptor is not open in `RDWR` mode. |
| 16214 | /// Or `PROT_WRITE` is set, but the file is append-only. |
| 16215 | AccessDenied, |
| 16216 | /// The `prot` argument asks for `PROT_EXEC` but the mapped area belongs to a file on |
| 16217 | /// a filesystem that was mounted no-exec. |
| 16218 | PermissionDenied, |
| 16219 | FileBusy, |
| 16220 | LockedMemoryLimitExceeded, |
| 16221 | OperationUnsupported, |
| 16222 | ProcessFdQuotaExceeded, |
| 16223 | SystemFdQuotaExceeded, |
| 16224 | OutOfMemory, |
| 16225 | MappingAlreadyExists, |
| 16226 | Unseekable, |
| 16227 | FileLockConflict, |
| 16228 | } || Io.Cancelable || Io.UnexpectedError; |
| 16229 | |
| 16230 | fn createFileMap( |
| 16231 | file: File, |
| 16232 | protection: std.process.MemoryProtection, |
| 16233 | offset: u64, |
| 16234 | populate: bool, |
| 16235 | len: usize, |
| 16236 | ) CreateFileMapError!File.MemoryMap { |
| 16237 | if (is_windows) { |
| 16238 | try Thread.checkCancel(); |
| 16239 | |
| 16240 | var section = windows.INVALID_HANDLE_VALUE; |
| 16241 | const section_size: windows.LARGE_INTEGER = @intCast(len); |
| 16242 | const page = windows.PAGE.fromProtection(protection) orelse return error.AccessDenied; |
| 16243 | switch (windows.ntdll.NtCreateSection( |
| 16244 | &section, |
| 16245 | .{ |
| 16246 | .SPECIFIC = .{ .SECTION = .{ |
| 16247 | .QUERY = true, |
| 16248 | .MAP_WRITE = protection.write, |
| 16249 | .MAP_READ = protection.read, |
| 16250 | .MAP_EXECUTE = protection.execute, |
| 16251 | .EXTEND_SIZE = true, |
| 16252 | } }, |
| 16253 | .STANDARD = .{ .RIGHTS = .REQUIRED }, |
| 16254 | }, |
| 16255 | null, |
| 16256 | &section_size, |
| 16257 | page, |
| 16258 | .{ .COMMIT = populate }, |
| 16259 | file.handle, |
| 16260 | )) { |
| 16261 | .SUCCESS => {}, |
| 16262 | .FILE_LOCK_CONFLICT => return error.FileLockConflict, |
| 16263 | .INVALID_FILE_FOR_SECTION => return error.OperationUnsupported, |
| 16264 | .ACCESS_DENIED => return error.AccessDenied, |
| 16265 | .SECTION_TOO_BIG => return error.SectionOversize, |
| 16266 | else => |status| return windows.unexpectedStatus(status), |
| 16267 | } |
| 16268 | var contents_ptr: ?[*]align(std.heap.page_size_min) u8 = null; |
| 16269 | var contents_len = len; |
| 16270 | switch (windows.ntdll.NtMapViewOfSection( |
| 16271 | section, |
| 16272 | windows.current_process, |
| 16273 | @ptrCast(&contents_ptr), |
| 16274 | null, |
| 16275 | 0, |
| 16276 | null, |
| 16277 | &contents_len, |
| 16278 | .Unmap, |
| 16279 | .{}, |
| 16280 | page, |
| 16281 | )) { |
| 16282 | .SUCCESS => {}, |
| 16283 | .CONFLICTING_ADDRESSES => return error.MappingAlreadyExists, |
| 16284 | .SECTION_PROTECTION => return error.PermissionDenied, |
| 16285 | .ACCESS_DENIED => return error.AccessDenied, |
| 16286 | .INVALID_VIEW_SIZE => |status| return windows.statusBug(status), |
| 16287 | else => |status| return windows.unexpectedStatus(status), |
| 16288 | } |
| 16289 | if (builtin.mode == .Debug) { |
| 16290 | const page_size = std.heap.pageSize(); |
| 16291 | const alignment: Alignment = .fromByteUnits(page_size); |
| 16292 | assert(contents_len == alignment.forward(len)); |
| 16293 | } |
| 16294 | return .{ |
| 16295 | .file = file, |
| 16296 | .offset = offset, |
| 16297 | .memory = contents_ptr.?[0..len], |
| 16298 | .section = section, |
| 16299 | }; |
| 16300 | } else if (have_mmap) { |
| 16301 | const prot: posix.PROT = .{ |
| 16302 | .READ = protection.read, |
| 16303 | .WRITE = protection.write, |
| 16304 | .EXEC = protection.execute, |
| 16305 | }; |
| 16306 | const flags: posix.MAP = switch (native_os) { |
| 16307 | .linux => .{ |
| 16308 | .TYPE = .SHARED_VALIDATE, |
| 16309 | .POPULATE = populate, |
| 16310 | }, |
| 16311 | else => .{ |
| 16312 | .TYPE = .SHARED, |
| 16313 | }, |
| 16314 | }; |
| 16315 | |
| 16316 | const page_align = std.heap.page_size_min; |
| 16317 | |
| 16318 | const contents = while (true) { |
| 16319 | const syscall: Syscall = try .start(); |
| 16320 | const casted_offset = std.math.cast(i64, offset) orelse return error.Unseekable; |
| 16321 | const rc = mmap_sym(null, len, prot, flags, file.handle, casted_offset); |
| 16322 | syscall.finish(); |
| 16323 | const err: posix.E = if (builtin.link_libc) e: { |
| 16324 | if (rc != std.c.MAP_FAILED) { |
| 16325 | break @as([*]align(page_align) u8, @ptrCast(@alignCast(rc)))[0..len]; |
| 16326 | } |
| 16327 | break :e @enumFromInt(posix.system._errno().*); |
| 16328 | } else e: { |
| 16329 | const err = posix.errno(rc); |
| 16330 | if (err == .SUCCESS) { |
| 16331 | break @as([*]align(page_align) u8, @ptrFromInt(rc))[0..len]; |
| 16332 | } |
| 16333 | break :e err; |
| 16334 | }; |
| 16335 | switch (err) { |
| 16336 | .SUCCESS => unreachable, |
| 16337 | .INTR => continue, |
| 16338 | .ACCES => return error.AccessDenied, |
| 16339 | .AGAIN => return error.LockedMemoryLimitExceeded, |
| 16340 | .EXIST => return error.MappingAlreadyExists, |
| 16341 | .MFILE => return error.ProcessFdQuotaExceeded, |
| 16342 | .NFILE => return error.SystemFdQuotaExceeded, |
| 16343 | .NODEV => return error.OperationUnsupported, |
| 16344 | .NOMEM => return error.OutOfMemory, |
| 16345 | .PERM => return error.PermissionDenied, |
| 16346 | .TXTBSY => return error.FileBusy, |
| 16347 | .OVERFLOW => return error.Unseekable, |
| 16348 | .BADF => return errnoBug(err), // Always a race condition. |
| 16349 | .INVAL => return errnoBug(err), // Invalid parameters to mmap() |
| 16350 | else => return posix.unexpectedErrno(err), |
| 16351 | } |
| 16352 | }; |
| 16353 | return .{ |
| 16354 | .file = file, |
| 16355 | .offset = offset, |
| 16356 | .memory = contents, |
| 16357 | .section = {}, |
| 16358 | }; |
| 16359 | } |
| 16360 | |
| 16361 | return error.OperationUnsupported; |
| 16362 | } |
| 16363 | |
| 16364 | fn fileMemoryMapDestroy(userdata: ?*anyopaque, mm: *File.MemoryMap) void { |
| 16365 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 16366 | const memory = mm.memory; |
| 16367 | if (mm.section) |section| switch (native_os) { |
| 16368 | .windows => { |
| 16369 | if (section == windows.INVALID_HANDLE_VALUE) return; |
| 16370 | _ = windows.ntdll.NtUnmapViewOfSection(windows.current_process, memory.ptr); |
| 16371 | windows.CloseHandle(section); |
| 16372 | }, |
| 16373 | .wasi => unreachable, |
| 16374 | else => { |
| 16375 | if (memory.len == 0) return; |
| 16376 | switch (posix.errno(posix.system.munmap(memory.ptr, memory.len))) { |
| 16377 | .SUCCESS => {}, |
| 16378 | else => |e| { |
| 16379 | if (builtin.mode == .Debug) |
| 16380 | std.log.err("failed to unmap {d} bytes at {*}: {t}", .{ memory.len, memory.ptr, e }); |
| 16381 | }, |
| 16382 | } |
| 16383 | }, |
| 16384 | } else { |
| 16385 | const gpa = t.allocator; |
| 16386 | gpa.rawFree(memory, .fromByteUnits(std.heap.pageSize()), @returnAddress()); |
| 16387 | } |
| 16388 | mm.* = undefined; |
| 16389 | } |
| 16390 | |
| 16391 | fn fileMemoryMapSetLength( |
| 16392 | userdata: ?*anyopaque, |
| 16393 | mm: *File.MemoryMap, |
| 16394 | options: File.MemoryMap.CreateOptions, |
| 16395 | ) File.MemoryMap.SetLengthError!void { |
| 16396 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 16397 | const page_size = std.heap.pageSize(); |
| 16398 | const alignment: Alignment = .fromByteUnits(page_size); |
| 16399 | const page_align = std.heap.page_size_min; |
| 16400 | const old_memory = mm.memory; |
| 16401 | const new_len = options.len; |
| 16402 | |
| 16403 | if (mm.section) |section| { |
| 16404 | if (alignment.forward(new_len) == alignment.forward(old_memory.len)) { |
| 16405 | mm.memory.len = new_len; |
| 16406 | return; |
| 16407 | } |
| 16408 | switch (native_os) { |
| 16409 | .windows => { |
| 16410 | _ = windows.ntdll.NtUnmapViewOfSection(windows.current_process, old_memory.ptr); |
| 16411 | windows.CloseHandle(section); |
| 16412 | mm.section = windows.INVALID_HANDLE_VALUE; |
| 16413 | mm.memory = &.{}; |
| 16414 | }, |
| 16415 | .wasi => unreachable, |
| 16416 | .linux => { |
| 16417 | const flags: posix.MREMAP = .{ .MAYMOVE = true }; |
| 16418 | const addr_hint: ?[*]const u8 = null; |
| 16419 | const new_memory = while (true) { |
| 16420 | const syscall: Syscall = try .start(); |
| 16421 | const rc = posix.system.mremap(old_memory.ptr, old_memory.len, new_len, flags, addr_hint); |
| 16422 | syscall.finish(); |
| 16423 | const err: posix.E = if (builtin.link_libc) e: { |
| 16424 | if (rc != std.c.MAP_FAILED) break @as([*]align(page_align) u8, @ptrCast(@alignCast(rc)))[0..new_len]; |
| 16425 | break :e @enumFromInt(posix.system._errno().*); |
| 16426 | } else e: { |
| 16427 | const err = posix.errno(rc); |
| 16428 | if (err == .SUCCESS) break @as([*]align(page_align) u8, @ptrFromInt(rc))[0..new_len]; |
| 16429 | break :e err; |
| 16430 | }; |
| 16431 | switch (err) { |
| 16432 | .SUCCESS => unreachable, |
| 16433 | .INTR => continue, |
| 16434 | .AGAIN => return error.LockedMemoryLimitExceeded, |
| 16435 | .NOMEM => return error.OutOfMemory, |
| 16436 | .INVAL => return errnoBug(err), |
| 16437 | .FAULT => return errnoBug(err), |
| 16438 | else => return posix.unexpectedErrno(err), |
| 16439 | } |
| 16440 | }; |
| 16441 | mm.memory = new_memory; |
| 16442 | return; |
| 16443 | }, |
| 16444 | else => { |
| 16445 | switch (posix.errno(posix.system.munmap(old_memory.ptr, old_memory.len))) { |
| 16446 | .SUCCESS => {}, |
| 16447 | else => |e| { |
| 16448 | if (builtin.mode == .Debug) std.log.err("failed to unmap {d} bytes at {*}: {t}", .{ |
| 16449 | old_memory.len, old_memory.ptr, e, |
| 16450 | }); |
| 16451 | // munmap must be infallible, or we cannot design reliable software. |
| 16452 | return error.Unexpected; |
| 16453 | }, |
| 16454 | } |
| 16455 | mm.memory = &.{}; |
| 16456 | }, |
| 16457 | } |
| 16458 | if (createFileMap(mm.file, options.protection, mm.offset, options.populate, new_len)) |result| { |
| 16459 | mm.* = result; |
| 16460 | return; |
| 16461 | } else |err| switch (err) { |
| 16462 | error.OperationUnsupported, |
| 16463 | error.Unseekable, |
| 16464 | error.SectionOversize, |
| 16465 | error.MappingAlreadyExists, |
| 16466 | error.FileLockConflict, |
| 16467 | => return error.Unexpected, // It worked before on the same open file. |
| 16468 | else => |e| return e, |
| 16469 | } |
| 16470 | } else { |
| 16471 | const gpa = t.allocator; |
| 16472 | if (gpa.rawRemap(old_memory, alignment, new_len, @returnAddress())) |new_ptr| { |
| 16473 | mm.memory = @alignCast(new_ptr[0..new_len]); |
| 16474 | } else { |
| 16475 | const new_ptr: [*]align(page_align) u8 = @alignCast( |
| 16476 | gpa.rawAlloc(new_len, alignment, @returnAddress()) orelse return error.OutOfMemory, |
| 16477 | ); |
| 16478 | const copy_len = @min(new_len, old_memory.len); |
| 16479 | @memcpy(new_ptr[0..copy_len], old_memory[0..copy_len]); |
| 16480 | mm.memory = new_ptr[0..new_len]; |
| 16481 | gpa.rawFree(old_memory, alignment, @returnAddress()); |
| 16482 | } |
| 16483 | } |
| 16484 | } |
| 16485 | |
| 16486 | fn fileMemoryMapRead(userdata: ?*anyopaque, mm: *File.MemoryMap) File.ReadPositionalError!void { |
| 16487 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 16488 | _ = t; |
| 16489 | const section = mm.section orelse return mmSyncRead(mm.file, mm.memory, mm.offset); |
| 16490 | _ = section; |
| 16491 | } |
| 16492 | |
| 16493 | fn fileMemoryMapWrite(userdata: ?*anyopaque, mm: *File.MemoryMap) File.WritePositionalError!void { |
| 16494 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 16495 | _ = t; |
| 16496 | const section = mm.section orelse return mmSyncWrite(mm.file, mm.memory, mm.offset); |
| 16497 | _ = section; |
| 16498 | } |
| 16499 | |
| 16500 | fn mmSyncRead(file: File, memory: []u8, offset: u64) File.ReadPositionalError!void { |
| 16501 | if (is_windows) { |
| 16502 | var i: usize = 0; |
| 16503 | while (true) { |
| 16504 | const buf = memory[i..]; |
| 16505 | if (buf.len == 0) break; |
| 16506 | const n = try readFilePositionalWindows(file, buf, offset + i); |
| 16507 | if (n == 0) { |
| 16508 | @memset(memory[i..], 0); |
| 16509 | break; |
| 16510 | } |
| 16511 | i += n; |
| 16512 | } |
| 16513 | } else if (native_os == .wasi and !builtin.link_libc) { |
| 16514 | var i: usize = 0; |
| 16515 | const syscall: Syscall = try .start(); |
| 16516 | while (true) { |
| 16517 | const buf = memory[i..]; |
| 16518 | if (buf.len == 0) { |
| 16519 | syscall.finish(); |
| 16520 | break; |
| 16521 | } |
| 16522 | var n: usize = undefined; |
| 16523 | const vec: std.os.wasi.iovec_t = .{ .base = buf.ptr, .len = buf.len }; |
| 16524 | switch (std.os.wasi.fd_pread(file.handle, (&vec)[0..1], 1, offset + i, &n)) { |
| 16525 | .SUCCESS => { |
| 16526 | if (n == 0) { |
| 16527 | syscall.finish(); |
| 16528 | @memset(memory[i..], 0); |
| 16529 | break; |
| 16530 | } |
| 16531 | i += n; |
| 16532 | try syscall.checkCancel(); |
| 16533 | continue; |
| 16534 | }, |
| 16535 | .INTR, .TIMEDOUT => { |
| 16536 | try syscall.checkCancel(); |
| 16537 | continue; |
| 16538 | }, |
| 16539 | .NOTCONN => |err| return syscall.errnoBug(err), // not a socket |
| 16540 | .CONNRESET => |err| return syscall.errnoBug(err), // not a socket |
| 16541 | .BADF => |err| return syscall.errnoBug(err), // use after free |
| 16542 | .INVAL => |err| return syscall.errnoBug(err), |
| 16543 | .FAULT => |err| return syscall.errnoBug(err), // segmentation fault |
| 16544 | .AGAIN => |err| return syscall.errnoBug(err), |
| 16545 | .IO => return syscall.fail(error.InputOutput), |
| 16546 | .ISDIR => return syscall.fail(error.IsDir), |
| 16547 | .NOBUFS => return syscall.fail(error.SystemResources), |
| 16548 | .NOMEM => return syscall.fail(error.SystemResources), |
| 16549 | .NXIO => return syscall.fail(error.Unseekable), |
| 16550 | .SPIPE => return syscall.fail(error.Unseekable), |
| 16551 | .OVERFLOW => return syscall.fail(error.Unseekable), |
| 16552 | .NOTCAPABLE => return syscall.fail(error.AccessDenied), |
| 16553 | else => |err| return syscall.unexpectedErrno(err), |
| 16554 | } |
| 16555 | } |
| 16556 | } else { |
| 16557 | var i: usize = 0; |
| 16558 | const syscall: Syscall = try .start(); |
| 16559 | while (true) { |
| 16560 | const buf = memory[i..]; |
| 16561 | if (buf.len == 0) { |
| 16562 | syscall.finish(); |
| 16563 | break; |
| 16564 | } |
| 16565 | const rc = pread_sym(file.handle, buf.ptr, buf.len, @intCast(offset + i)); |
| 16566 | switch (posix.errno(rc)) { |
| 16567 | .SUCCESS => { |
| 16568 | const n: usize = @intCast(rc); |
| 16569 | if (n == 0) { |
| 16570 | syscall.finish(); |
| 16571 | @memset(memory[i..], 0); |
| 16572 | break; |
| 16573 | } |
| 16574 | i += n; |
| 16575 | try syscall.checkCancel(); |
| 16576 | continue; |
| 16577 | }, |
| 16578 | .INTR, .TIMEDOUT => { |
| 16579 | try syscall.checkCancel(); |
| 16580 | continue; |
| 16581 | }, |
| 16582 | .NXIO => return syscall.fail(error.Unseekable), |
| 16583 | .SPIPE => return syscall.fail(error.Unseekable), |
| 16584 | .OVERFLOW => return syscall.fail(error.Unseekable), |
| 16585 | .NOBUFS => return syscall.fail(error.SystemResources), |
| 16586 | .NOMEM => return syscall.fail(error.SystemResources), |
| 16587 | .AGAIN => return syscall.fail(error.WouldBlock), |
| 16588 | .IO => return syscall.fail(error.InputOutput), |
| 16589 | .ISDIR => return syscall.fail(error.IsDir), |
| 16590 | .NOTCONN => |err| return syscall.errnoBug(err), // not a socket |
| 16591 | .CONNRESET => |err| return syscall.errnoBug(err), // not a socket |
| 16592 | .INVAL => |err| return syscall.errnoBug(err), |
| 16593 | .FAULT => |err| return syscall.errnoBug(err), |
| 16594 | .BADF => |err| return syscall.errnoBug(err), // use after free |
| 16595 | else => |err| return syscall.unexpectedErrno(err), |
| 16596 | } |
| 16597 | } |
| 16598 | } |
| 16599 | } |
| 16600 | |
| 16601 | fn mmSyncWrite(file: File, memory: []u8, offset: u64) File.WritePositionalError!void { |
| 16602 | if (is_windows) { |
| 16603 | var i: usize = 0; |
| 16604 | while (true) { |
| 16605 | const buf = memory[i..]; |
| 16606 | if (buf.len == 0) break; |
| 16607 | i += try writeFilePositionalWindows(file.handle, memory[i..], offset + i); |
| 16608 | } |
| 16609 | } else if (native_os == .wasi and !builtin.link_libc) { |
| 16610 | var i: usize = 0; |
| 16611 | var n: usize = undefined; |
| 16612 | const syscall: Syscall = try .start(); |
| 16613 | while (true) { |
| 16614 | const buf = memory[i..]; |
| 16615 | if (buf.len == 0) { |
| 16616 | syscall.finish(); |
| 16617 | break; |
| 16618 | } |
| 16619 | const iovec: std.os.wasi.ciovec_t = .{ .base = buf.ptr, .len = buf.len }; |
| 16620 | switch (std.os.wasi.fd_pwrite(file.handle, (&iovec)[0..1], 1, offset + i, &n)) { |
| 16621 | .SUCCESS => { |
| 16622 | i += n; |
| 16623 | try syscall.checkCancel(); |
| 16624 | continue; |
| 16625 | }, |
| 16626 | .INTR => { |
| 16627 | try syscall.checkCancel(); |
| 16628 | continue; |
| 16629 | }, |
| 16630 | .DQUOT => return syscall.fail(error.DiskQuota), |
| 16631 | .FBIG => return syscall.fail(error.FileTooBig), |
| 16632 | .IO => return syscall.fail(error.InputOutput), |
| 16633 | .NOSPC => return syscall.fail(error.NoSpaceLeft), |
| 16634 | .PERM => return syscall.fail(error.PermissionDenied), |
| 16635 | .PIPE => return syscall.fail(error.BrokenPipe), |
| 16636 | .NOTCAPABLE => return syscall.fail(error.AccessDenied), |
| 16637 | .NXIO => return syscall.fail(error.Unseekable), |
| 16638 | .SPIPE => return syscall.fail(error.Unseekable), |
| 16639 | .OVERFLOW => return syscall.fail(error.Unseekable), |
| 16640 | .INVAL => |err| return syscall.errnoBug(err), |
| 16641 | .FAULT => |err| return syscall.errnoBug(err), |
| 16642 | .AGAIN => |err| return syscall.errnoBug(err), |
| 16643 | .BADF => |err| return syscall.errnoBug(err), // use after free |
| 16644 | .DESTADDRREQ => |err| return syscall.errnoBug(err), // not a socket |
| 16645 | else => |err| return syscall.unexpectedErrno(err), |
| 16646 | } |
| 16647 | } |
| 16648 | } else { |
| 16649 | var i: usize = 0; |
| 16650 | const syscall: Syscall = try .start(); |
| 16651 | while (true) { |
| 16652 | const buf = memory[i..]; |
| 16653 | if (buf.len == 0) { |
| 16654 | syscall.finish(); |
| 16655 | break; |
| 16656 | } |
| 16657 | const rc = pwrite_sym(file.handle, buf.ptr, buf.len, @intCast(offset + i)); |
| 16658 | switch (posix.errno(rc)) { |
| 16659 | .SUCCESS => { |
| 16660 | const n: usize = @bitCast(rc); |
| 16661 | i += n; |
| 16662 | try syscall.checkCancel(); |
| 16663 | continue; |
| 16664 | }, |
| 16665 | .INTR => { |
| 16666 | try syscall.checkCancel(); |
| 16667 | continue; |
| 16668 | }, |
| 16669 | .INVAL => |err| return syscall.errnoBug(err), |
| 16670 | .FAULT => |err| return syscall.errnoBug(err), |
| 16671 | .DESTADDRREQ => |err| return syscall.errnoBug(err), // not a socket |
| 16672 | .CONNRESET => |err| return syscall.errnoBug(err), // not a socket |
| 16673 | .BADF => return syscall.fail(error.NotOpenForWriting), |
| 16674 | .AGAIN => return syscall.fail(error.WouldBlock), |
| 16675 | .DQUOT => return syscall.fail(error.DiskQuota), |
| 16676 | .FBIG => return syscall.fail(error.FileTooBig), |
| 16677 | .IO => return syscall.fail(error.InputOutput), |
| 16678 | .NOSPC => return syscall.fail(error.NoSpaceLeft), |
| 16679 | .PERM => return syscall.fail(error.PermissionDenied), |
| 16680 | .PIPE => return syscall.fail(error.BrokenPipe), |
| 16681 | .BUSY => return syscall.fail(error.DeviceBusy), |
| 16682 | .TXTBSY => return syscall.fail(error.FileBusy), |
| 16683 | .NXIO => return syscall.fail(error.Unseekable), |
| 16684 | .SPIPE => return syscall.fail(error.Unseekable), |
| 16685 | .OVERFLOW => return syscall.fail(error.Unseekable), |
| 16686 | else => |err| return syscall.unexpectedErrno(err), |
| 16687 | } |
| 16688 | } |
| 16689 | } |
| 16690 | } |