| ... | @@ -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 = .{}, |
| ... | @@ -51,6 +57,7 @@ use_sendfile: UseSendfile = .default, | ... | @@ -51,6 +57,7 @@ use_sendfile: UseSendfile = .default, |
| 51 | use_copy_file_range: UseCopyFileRange = .default, | 57 | use_copy_file_range: UseCopyFileRange = .default, |
| 52 | use_fcopyfile: UseFcopyfile = .default, | 58 | use_fcopyfile: UseFcopyfile = .default, |
| 53 | use_fchmodat2: UseFchmodat2 = .default, | 59 | use_fchmodat2: UseFchmodat2 = .default, |
| | 60 | disable_memory_mapping: bool, |
| 54 | | 61 | |
| 55 | stderr_writer: File.Writer = .{ | 62 | stderr_writer: File.Writer = .{ |
| 56 | .io = undefined, | 63 | .io = undefined, |
| ... | @@ -69,6 +76,13 @@ random_file: RandomFile = .{}, | ... | @@ -69,6 +76,13 @@ random_file: RandomFile = .{}, |
| 69 | | 76 | |
| 70 | csprng: Csprng = .{}, | 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 | pub const Csprng = struct { | 86 | pub const Csprng = struct { |
| 73 | rng: std.Random.DefaultCsprng = .{ | 87 | rng: std.Random.DefaultCsprng = .{ |
| 74 | .state = undefined, | 88 | .state = undefined, |
| ... | @@ -1214,6 +1228,8 @@ pub const InitOptions = struct { | ... | @@ -1214,6 +1228,8 @@ pub const InitOptions = struct { |
| 1214 | /// * `processExecutablePath` on OpenBSD and Haiku (observes "PATH"). | 1228 | /// * `processExecutablePath` on OpenBSD and Haiku (observes "PATH"). |
| 1215 | /// * `processSpawn`, `processSpawnPath`, `processReplace`, `processReplacePath` | 1229 | /// * `processSpawn`, `processSpawnPath`, `processReplace`, `processReplacePath` |
| 1216 | environ: process.Environ, | 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 | /// Related: | 1235 | /// Related: |
| ... | @@ -1241,6 +1257,7 @@ pub fn init( | ... | @@ -1241,6 +1257,7 @@ pub fn init( |
| 1241 | .argv0 = options.argv0, | 1257 | .argv0 = options.argv0, |
| 1242 | .environ = .{ .process_environ = options.environ }, | 1258 | .environ = .{ .process_environ = options.environ }, |
| 1243 | .worker_threads = init_single_threaded.worker_threads, | 1259 | .worker_threads = init_single_threaded.worker_threads, |
| | 1260 | .disable_memory_mapping = options.disable_memory_mapping, |
| 1244 | }; | 1261 | }; |
| 1245 | | 1262 | |
| 1246 | const cpu_count = std.Thread.getCpuCount(); | 1263 | const cpu_count = std.Thread.getCpuCount(); |
| ... | @@ -1257,6 +1274,7 @@ pub fn init( | ... | @@ -1257,6 +1274,7 @@ pub fn init( |
| 1257 | .argv0 = options.argv0, | 1274 | .argv0 = options.argv0, |
| 1258 | .environ = .{ .process_environ = options.environ }, | 1275 | .environ = .{ .process_environ = options.environ }, |
| 1259 | .worker_threads = .init(null), | 1276 | .worker_threads = .init(null), |
| | 1277 | .disable_memory_mapping = options.disable_memory_mapping, |
| 1260 | }; | 1278 | }; |
| 1261 | | 1279 | |
| 1262 | if (posix.Sigaction != void) { | 1280 | if (posix.Sigaction != void) { |
| ... | @@ -1293,6 +1311,7 @@ pub const init_single_threaded: Threaded = .{ | ... | @@ -1293,6 +1311,7 @@ pub const init_single_threaded: Threaded = .{ |
| 1293 | .argv0 = .empty, | 1311 | .argv0 = .empty, |
| 1294 | .environ = .{}, | 1312 | .environ = .{}, |
| 1295 | .worker_threads = .init(null), | 1313 | .worker_threads = .init(null), |
| | 1314 | .disable_memory_mapping = false, |
| 1296 | }; | 1315 | }; |
| 1297 | | 1316 | |
| 1298 | var global_single_threaded_instance: Threaded = .init_single_threaded; | 1317 | var global_single_threaded_instance: Threaded = .init_single_threaded; |
| ... | @@ -1490,6 +1509,12 @@ pub fn io(t: *Threaded) Io { | ... | @@ -1490,6 +1509,12 @@ pub fn io(t: *Threaded) Io { |
| 1490 | .fileRealPath = fileRealPath, | 1509 | .fileRealPath = fileRealPath, |
| 1491 | .fileHardLink = fileHardLink, | 1510 | .fileHardLink = fileHardLink, |
| 1492 | | 1511 | |
| | 1512 | .fileMemoryMapCreate = fileMemoryMapCreate, |
| | 1513 | .fileMemoryMapDestroy = fileMemoryMapDestroy, |
| | 1514 | .fileMemoryMapSetLength = fileMemoryMapSetLength, |
| | 1515 | .fileMemoryMapRead = fileMemoryMapRead, |
| | 1516 | .fileMemoryMapWrite = fileMemoryMapWrite, |
| | 1517 | |
| 1493 | .processExecutableOpen = processExecutableOpen, | 1518 | .processExecutableOpen = processExecutableOpen, |
| 1494 | .processExecutablePath = processExecutablePath, | 1519 | .processExecutablePath = processExecutablePath, |
| 1495 | .lockStderr = lockStderr, | 1520 | .lockStderr = lockStderr, |
| ... | @@ -1642,6 +1667,12 @@ pub fn ioBasic(t: *Threaded) Io { | ... | @@ -1642,6 +1667,12 @@ pub fn ioBasic(t: *Threaded) Io { |
| 1642 | .fileRealPath = fileRealPath, | 1667 | .fileRealPath = fileRealPath, |
| 1643 | .fileHardLink = fileHardLink, | 1668 | .fileHardLink = fileHardLink, |
| 1644 | | 1669 | |
| | 1670 | .fileMemoryMapCreate = fileMemoryMapCreate, |
| | 1671 | .fileMemoryMapDestroy = fileMemoryMapDestroy, |
| | 1672 | .fileMemoryMapSetLength = fileMemoryMapSetLength, |
| | 1673 | .fileMemoryMapRead = fileMemoryMapRead, |
| | 1674 | .fileMemoryMapWrite = fileMemoryMapWrite, |
| | 1675 | |
| 1645 | .processExecutableOpen = processExecutableOpen, | 1676 | .processExecutableOpen = processExecutableOpen, |
| 1646 | .processExecutablePath = processExecutablePath, | 1677 | .processExecutablePath = processExecutablePath, |
| 1647 | .lockStderr = lockStderr, | 1678 | .lockStderr = lockStderr, |
| ... | @@ -1733,15 +1764,24 @@ const have_wait4 = switch (native_os) { | ... | @@ -1733,15 +1764,24 @@ const have_wait4 = switch (native_os) { |
| 1733 | else => false, | 1764 | else => false, |
| 1734 | }; | 1765 | }; |
| 1735 | | 1766 | |
| | 1767 | const have_mmap = switch (native_os) { |
| | 1768 | .wasi, .windows => false, |
| | 1769 | else => true, |
| | 1770 | }; |
| | 1771 | |
| 1736 | const open_sym = if (posix.lfs64_abi) posix.system.open64 else posix.system.open; | 1772 | 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; | 1773 | 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; | 1774 | 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; | 1775 | 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; | 1776 | 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; | 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 | const ftruncate_sym = if (posix.lfs64_abi) posix.system.ftruncate64 else posix.system.ftruncate; | 1779 | 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; | 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 | const sendfile_sym = if (posix.lfs64_abi) posix.system.sendfile64 else posix.system.sendfile; | 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 | const linux_copy_file_range_use_c = std.c.versionCheck(if (builtin.abi.isAndroid()) .{ | 1785 | const linux_copy_file_range_use_c = std.c.versionCheck(if (builtin.abi.isAndroid()) .{ |
| 1746 | .major = 34, | 1786 | .major = 34, |
| 1747 | .minor = 0, | 1787 | .minor = 0, |
| ... | @@ -2908,7 +2948,11 @@ fn fileStatLinux(userdata: ?*anyopaque, file: File) File.StatError!File.Stat { | ... | @@ -2908,7 +2948,11 @@ fn fileStatLinux(userdata: ?*anyopaque, file: File) File.StatError!File.Stat { |
| 2908 | | 2948 | |
| 2909 | fn fileStatWindows(userdata: ?*anyopaque, file: File) File.StatError!File.Stat { | 2949 | fn fileStatWindows(userdata: ?*anyopaque, file: File) File.StatError!File.Stat { |
| 2910 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | 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 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; | 2957 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; |
| 2914 | var info: windows.FILE.ALL_INFORMATION = undefined; | 2958 | var info: windows.FILE.ALL_INFORMATION = undefined; |
| ... | @@ -2970,10 +3014,31 @@ fn fileStatWindows(userdata: ?*anyopaque, file: File) File.StatError!File.Stat { | ... | @@ -2970,10 +3014,31 @@ fn fileStatWindows(userdata: ?*anyopaque, file: File) File.StatError!File.Stat { |
| 2970 | .atime = windows.fromSysTime(info.BasicInformation.LastAccessTime), | 3014 | .atime = windows.fromSysTime(info.BasicInformation.LastAccessTime), |
| 2971 | .mtime = windows.fromSysTime(info.BasicInformation.LastWriteTime), | 3015 | .mtime = windows.fromSysTime(info.BasicInformation.LastWriteTime), |
| 2972 | .ctime = windows.fromSysTime(info.BasicInformation.ChangeTime), | 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 | fn fileStatWasi(userdata: ?*anyopaque, file: File) File.StatError!File.Stat { | 3042 | fn fileStatWasi(userdata: ?*anyopaque, file: File) File.StatError!File.Stat { |
| 2978 | if (builtin.link_libc) return fileStatPosix(userdata, file); | 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,7 +7954,7 @@ fn fileReadStreamingPosix(userdata: ?*anyopaque, file: File, data: []const []u8) |
| 7889 | syscall.finish(); | 7954 | syscall.finish(); |
| 7890 | return nread; | 7955 | return nread; |
| 7891 | }, | 7956 | }, |
| 7892 | .INTR => { | 7957 | .INTR, .TIMEDOUT => { |
| 7893 | try syscall.checkCancel(); | 7958 | try syscall.checkCancel(); |
| 7894 | continue; | 7959 | continue; |
| 7895 | }, | 7960 | }, |
| ... | @@ -7898,14 +7963,13 @@ fn fileReadStreamingPosix(userdata: ?*anyopaque, file: File, data: []const []u8) | ... | @@ -7898,14 +7963,13 @@ fn fileReadStreamingPosix(userdata: ?*anyopaque, file: File, data: []const []u8) |
| 7898 | switch (e) { | 7963 | switch (e) { |
| 7899 | .INVAL => |err| return errnoBug(err), | 7964 | .INVAL => |err| return errnoBug(err), |
| 7900 | .FAULT => |err| return errnoBug(err), | 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 | .IO => return error.InputOutput, | 7967 | .IO => return error.InputOutput, |
| 7903 | .ISDIR => return error.IsDir, | 7968 | .ISDIR => return error.IsDir, |
| 7904 | .NOBUFS => return error.SystemResources, | 7969 | .NOBUFS => return error.SystemResources, |
| 7905 | .NOMEM => return error.SystemResources, | 7970 | .NOMEM => return error.SystemResources, |
| 7906 | .NOTCONN => return error.SocketUnconnected, | 7971 | .NOTCONN => return error.SocketUnconnected, |
| 7907 | .CONNRESET => return error.ConnectionResetByPeer, | 7972 | .CONNRESET => return error.ConnectionResetByPeer, |
| 7908 | .TIMEDOUT => return error.Timeout, | | |
| 7909 | .NOTCAPABLE => return error.AccessDenied, | 7973 | .NOTCAPABLE => return error.AccessDenied, |
| 7910 | else => |err| return posix.unexpectedErrno(err), | 7974 | else => |err| return posix.unexpectedErrno(err), |
| 7911 | } | 7975 | } |
| ... | @@ -7922,7 +7986,7 @@ fn fileReadStreamingPosix(userdata: ?*anyopaque, file: File, data: []const []u8) | ... | @@ -7922,7 +7986,7 @@ fn fileReadStreamingPosix(userdata: ?*anyopaque, file: File, data: []const []u8) |
| 7922 | syscall.finish(); | 7986 | syscall.finish(); |
| 7923 | return @intCast(rc); | 7987 | return @intCast(rc); |
| 7924 | }, | 7988 | }, |
| 7925 | .INTR => { | 7989 | .INTR, .TIMEDOUT => { |
| 7926 | try syscall.checkCancel(); | 7990 | try syscall.checkCancel(); |
| 7927 | continue; | 7991 | continue; |
| 7928 | }, | 7992 | }, |
| ... | @@ -7932,9 +7996,9 @@ fn fileReadStreamingPosix(userdata: ?*anyopaque, file: File, data: []const []u8) | ... | @@ -7932,9 +7996,9 @@ fn fileReadStreamingPosix(userdata: ?*anyopaque, file: File, data: []const []u8) |
| 7932 | .INVAL => |err| return errnoBug(err), | 7996 | .INVAL => |err| return errnoBug(err), |
| 7933 | .FAULT => |err| return errnoBug(err), | 7997 | .FAULT => |err| return errnoBug(err), |
| 7934 | .AGAIN => return error.WouldBlock, | 7998 | .AGAIN => return error.WouldBlock, |
| 7935 | .BADF => |err| { | 7999 | .BADF => { |
| 7936 | if (native_os == .wasi) return error.NotOpenForReading; // File operation on directory. | 8000 | if (native_os == .wasi) return error.IsDir; // File operation on directory. |
| 7937 | return errnoBug(err); // File descriptor used after closed. | 8001 | return error.NotOpenForReading; |
| 7938 | }, | 8002 | }, |
| 7939 | .IO => return error.InputOutput, | 8003 | .IO => return error.InputOutput, |
| 7940 | .ISDIR => return error.IsDir, | 8004 | .ISDIR => return error.IsDir, |
| ... | @@ -7942,7 +8006,6 @@ fn fileReadStreamingPosix(userdata: ?*anyopaque, file: File, data: []const []u8) | ... | @@ -7942,7 +8006,6 @@ fn fileReadStreamingPosix(userdata: ?*anyopaque, file: File, data: []const []u8) |
| 7942 | .NOMEM => return error.SystemResources, | 8006 | .NOMEM => return error.SystemResources, |
| 7943 | .NOTCONN => return error.SocketUnconnected, | 8007 | .NOTCONN => return error.SocketUnconnected, |
| 7944 | .CONNRESET => return error.ConnectionResetByPeer, | 8008 | .CONNRESET => return error.ConnectionResetByPeer, |
| 7945 | .TIMEDOUT => return error.Timeout, | | |
| 7946 | else => |err| return posix.unexpectedErrno(err), | 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,10 +8044,10 @@ fn fileReadStreamingWindows(userdata: ?*anyopaque, file: File, data: []const []u |
| 7981 | syscall.finish(); | 8044 | syscall.finish(); |
| 7982 | return 0; | 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 | .LOCK_VIOLATION => return syscall.fail(error.LockViolation), | 8048 | .LOCK_VIOLATION => return syscall.fail(error.LockViolation), |
| 7986 | .ACCESS_DENIED => return syscall.fail(error.AccessDenied), | 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 | // TODO: Determine if INVALID_FUNCTION is possible in more scenarios than just passing | 8051 | // TODO: Determine if INVALID_FUNCTION is possible in more scenarios than just passing |
| 7989 | // a handle to a directory. | 8052 | // a handle to a directory. |
| 7990 | .INVALID_FUNCTION => return syscall.fail(error.IsDir), | 8053 | .INVALID_FUNCTION => return syscall.fail(error.IsDir), |
| ... | @@ -8024,31 +8087,25 @@ fn fileReadPositionalPosix(userdata: ?*anyopaque, file: File, data: []const []u8 | ... | @@ -8024,31 +8087,25 @@ fn fileReadPositionalPosix(userdata: ?*anyopaque, file: File, data: []const []u8 |
| 8024 | syscall.finish(); | 8087 | syscall.finish(); |
| 8025 | return nread; | 8088 | return nread; |
| 8026 | }, | 8089 | }, |
| 8027 | .INTR => { | 8090 | .INTR, .TIMEDOUT => { |
| 8028 | try syscall.checkCancel(); | 8091 | try syscall.checkCancel(); |
| 8029 | continue; | 8092 | continue; |
| 8030 | }, | 8093 | }, |
| 8031 | else => |e| { | 8094 | .NOTCONN => |err| return syscall.errnoBug(err), // not a socket |
| 8032 | syscall.finish(); | 8095 | .CONNRESET => |err| return syscall.errnoBug(err), // not a socket |
| 8033 | switch (e) { | 8096 | .INVAL => |err| return syscall.errnoBug(err), |
| 8034 | .INVAL => |err| return errnoBug(err), | 8097 | .FAULT => |err| return syscall.errnoBug(err), // segmentation fault |
| 8035 | .FAULT => |err| return errnoBug(err), | 8098 | .AGAIN => |err| return syscall.errnoBug(err), |
| 8036 | .AGAIN => |err| return errnoBug(err), | 8099 | .IO => return syscall.fail(error.InputOutput), |
| 8037 | .BADF => return error.NotOpenForReading, // File operation on directory. | 8100 | .ISDIR => return syscall.fail(error.IsDir), |
| 8038 | .IO => return error.InputOutput, | 8101 | .BADF => return syscall.fail(error.IsDir), |
| 8039 | .ISDIR => return error.IsDir, | 8102 | .NOBUFS => return syscall.fail(error.SystemResources), |
| 8040 | .NOBUFS => return error.SystemResources, | 8103 | .NOMEM => return syscall.fail(error.SystemResources), |
| 8041 | .NOMEM => return error.SystemResources, | 8104 | .NXIO => return syscall.fail(error.Unseekable), |
| 8042 | .NOTCONN => return error.SocketUnconnected, | 8105 | .SPIPE => return syscall.fail(error.Unseekable), |
| 8043 | .CONNRESET => return error.ConnectionResetByPeer, | 8106 | .OVERFLOW => return syscall.fail(error.Unseekable), |
| 8044 | .TIMEDOUT => return error.Timeout, | 8107 | .NOTCAPABLE => return syscall.fail(error.AccessDenied), |
| 8045 | .NXIO => return error.Unseekable, | 8108 | else => |err| return syscall.unexpectedErrno(err), |
| 8046 | .SPIPE => return error.Unseekable, | | |
| 8047 | .OVERFLOW => return error.Unseekable, | | |
| 8048 | .NOTCAPABLE => return error.AccessDenied, | | |
| 8049 | else => |err| return posix.unexpectedErrno(err), | | |
| 8050 | } | | |
| 8051 | }, | | |
| 8052 | } | 8109 | } |
| 8053 | } | 8110 | } |
| 8054 | } | 8111 | } |
| ... | @@ -8061,33 +8118,28 @@ fn fileReadPositionalPosix(userdata: ?*anyopaque, file: File, data: []const []u8 | ... | @@ -8061,33 +8118,28 @@ fn fileReadPositionalPosix(userdata: ?*anyopaque, file: File, data: []const []u8 |
| 8061 | syscall.finish(); | 8118 | syscall.finish(); |
| 8062 | return @bitCast(rc); | 8119 | return @bitCast(rc); |
| 8063 | }, | 8120 | }, |
| 8064 | .INTR => { | 8121 | .INTR, .TIMEDOUT => { |
| 8065 | try syscall.checkCancel(); | 8122 | try syscall.checkCancel(); |
| 8066 | continue; | 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 | syscall.finish(); | 8138 | syscall.finish(); |
| 8070 | switch (e) { | 8139 | if (native_os == .wasi) return error.IsDir; // File operation on directory. |
| 8071 | .INVAL => |err| return errnoBug(err), | 8140 | return error.NotOpenForReading; |
| 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 | }, | 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,14 +8153,17 @@ fn fileReadPositionalWindows(userdata: ?*anyopaque, file: File, data: []const [] |
| 8101 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | 8153 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 8102 | _ = t; | 8154 | _ = t; |
| 8103 | | 8155 | |
| 8104 | const DWORD = windows.DWORD; | | |
| 8105 | | | |
| 8106 | var index: usize = 0; | 8156 | var index: usize = 0; |
| 8107 | while (index < data.len and data[index].len == 0) index += 1; | 8157 | while (index < data.len and data[index].len == 0) index += 1; |
| 8108 | if (index == data.len) return 0; | 8158 | if (index == data.len) return 0; |
| 8109 | const buffer = data[index]; | 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 | var overlapped: windows.OVERLAPPED = .{ | 8167 | var overlapped: windows.OVERLAPPED = .{ |
| 8113 | .Internal = 0, | 8168 | .Internal = 0, |
| 8114 | .InternalHigh = 0, | 8169 | .InternalHigh = 0, |
| ... | @@ -8141,10 +8196,10 @@ fn fileReadPositionalWindows(userdata: ?*anyopaque, file: File, data: []const [] | ... | @@ -8141,10 +8196,10 @@ fn fileReadPositionalWindows(userdata: ?*anyopaque, file: File, data: []const [] |
| 8141 | syscall.finish(); | 8196 | syscall.finish(); |
| 8142 | return 0; | 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 | .LOCK_VIOLATION => return syscall.fail(error.LockViolation), | 8200 | .LOCK_VIOLATION => return syscall.fail(error.LockViolation), |
| 8146 | .ACCESS_DENIED => return syscall.fail(error.AccessDenied), | 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 | // TODO: Determine if INVALID_FUNCTION is possible in more scenarios than just passing | 8203 | // TODO: Determine if INVALID_FUNCTION is possible in more scenarios than just passing |
| 8149 | // a handle to a directory. | 8204 | // a handle to a directory. |
| 8150 | .INVALID_FUNCTION => return syscall.fail(error.IsDir), | 8205 | .INVALID_FUNCTION => return syscall.fail(error.IsDir), |
| ... | @@ -8739,7 +8794,7 @@ fn fileWritePositional( | ... | @@ -8739,7 +8794,7 @@ fn fileWritePositional( |
| 8739 | .INVAL => |err| return errnoBug(err), | 8794 | .INVAL => |err| return errnoBug(err), |
| 8740 | .FAULT => |err| return errnoBug(err), | 8795 | .FAULT => |err| return errnoBug(err), |
| 8741 | .AGAIN => |err| return errnoBug(err), | 8796 | .AGAIN => |err| return errnoBug(err), |
| 8742 | .BADF => return error.NotOpenForWriting, // can be a race condition. | 8797 | .BADF => return error.NotOpenForWriting, |
| 8743 | .DESTADDRREQ => |err| return errnoBug(err), // `connect` was never called. | 8798 | .DESTADDRREQ => |err| return errnoBug(err), // `connect` was never called. |
| 8744 | .DQUOT => return error.DiskQuota, | 8799 | .DQUOT => return error.DiskQuota, |
| 8745 | .FBIG => return error.FileTooBig, | 8800 | .FBIG => return error.FileTooBig, |
| ... | @@ -8770,29 +8825,24 @@ fn fileWritePositional( | ... | @@ -8770,29 +8825,24 @@ fn fileWritePositional( |
| 8770 | try syscall.checkCancel(); | 8825 | try syscall.checkCancel(); |
| 8771 | continue; | 8826 | continue; |
| 8772 | }, | 8827 | }, |
| 8773 | else => |e| { | 8828 | .INVAL => |err| return syscall.errnoBug(err), |
| 8774 | syscall.finish(); | 8829 | .FAULT => |err| return syscall.errnoBug(err), |
| 8775 | switch (e) { | 8830 | .DESTADDRREQ => |err| return syscall.errnoBug(err), // `connect` was never called. |
| 8776 | .INVAL => |err| return errnoBug(err), | 8831 | .CONNRESET => |err| return syscall.errnoBug(err), // Not a socket handle. |
| 8777 | .FAULT => |err| return errnoBug(err), | 8832 | .BADF => return syscall.fail(error.NotOpenForWriting), |
| 8778 | .AGAIN => return error.WouldBlock, | 8833 | .AGAIN => return syscall.fail(error.WouldBlock), |
| 8779 | .BADF => return error.NotOpenForWriting, // Usually a race condition. | 8834 | .DQUOT => return syscall.fail(error.DiskQuota), |
| 8780 | .DESTADDRREQ => |err| return errnoBug(err), // `connect` was never called. | 8835 | .FBIG => return syscall.fail(error.FileTooBig), |
| 8781 | .DQUOT => return error.DiskQuota, | 8836 | .IO => return syscall.fail(error.InputOutput), |
| 8782 | .FBIG => return error.FileTooBig, | 8837 | .NOSPC => return syscall.fail(error.NoSpaceLeft), |
| 8783 | .IO => return error.InputOutput, | 8838 | .PERM => return syscall.fail(error.PermissionDenied), |
| 8784 | .NOSPC => return error.NoSpaceLeft, | 8839 | .PIPE => return syscall.fail(error.BrokenPipe), |
| 8785 | .PERM => return error.PermissionDenied, | 8840 | .BUSY => return syscall.fail(error.DeviceBusy), |
| 8786 | .PIPE => return error.BrokenPipe, | 8841 | .TXTBSY => return syscall.fail(error.FileBusy), |
| 8787 | .CONNRESET => |err| return errnoBug(err), // Not a socket handle. | 8842 | .NXIO => return syscall.fail(error.Unseekable), |
| 8788 | .BUSY => return error.DeviceBusy, | 8843 | .SPIPE => return syscall.fail(error.Unseekable), |
| 8789 | .TXTBSY => return error.FileBusy, | 8844 | .OVERFLOW => return syscall.fail(error.Unseekable), |
| 8790 | .NXIO => return error.Unseekable, | 8845 | 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 | } | 8846 | } |
| 8797 | } | 8847 | } |
| 8798 | } | 8848 | } |
| ... | @@ -8830,7 +8880,7 @@ fn writeFilePositionalWindows( | ... | @@ -8830,7 +8880,7 @@ fn writeFilePositionalWindows( |
| 8830 | .NOT_ENOUGH_MEMORY => return syscall.fail(error.SystemResources), | 8880 | .NOT_ENOUGH_MEMORY => return syscall.fail(error.SystemResources), |
| 8831 | .NOT_ENOUGH_QUOTA => return syscall.fail(error.SystemResources), | 8881 | .NOT_ENOUGH_QUOTA => return syscall.fail(error.SystemResources), |
| 8832 | .NO_DATA => return syscall.fail(error.BrokenPipe), | 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 | .LOCK_VIOLATION => return syscall.fail(error.LockViolation), | 8884 | .LOCK_VIOLATION => return syscall.fail(error.LockViolation), |
| 8835 | .ACCESS_DENIED => return syscall.fail(error.AccessDenied), | 8885 | .ACCESS_DENIED => return syscall.fail(error.AccessDenied), |
| 8836 | .WORKING_SET_QUOTA => return syscall.fail(error.SystemResources), | 8886 | .WORKING_SET_QUOTA => return syscall.fail(error.SystemResources), |
| ... | @@ -12458,6 +12508,7 @@ const linux_statx_request: std.os.linux.STATX = .{ | ... | @@ -12458,6 +12508,7 @@ const linux_statx_request: std.os.linux.STATX = .{ |
| 12458 | .INO = true, | 12508 | .INO = true, |
| 12459 | .SIZE = true, | 12509 | .SIZE = true, |
| 12460 | .NLINK = true, | 12510 | .NLINK = true, |
| | 12511 | .BLOCKS = true, |
| 12461 | }; | 12512 | }; |
| 12462 | | 12513 | |
| 12463 | const linux_statx_check: std.os.linux.STATX = .{ | 12514 | const linux_statx_check: std.os.linux.STATX = .{ |
| ... | @@ -12469,6 +12520,7 @@ const linux_statx_check: std.os.linux.STATX = .{ | ... | @@ -12469,6 +12520,7 @@ const linux_statx_check: std.os.linux.STATX = .{ |
| 12469 | .INO = true, | 12520 | .INO = true, |
| 12470 | .SIZE = true, | 12521 | .SIZE = true, |
| 12471 | .NLINK = true, | 12522 | .NLINK = true, |
| | 12523 | .BLOCKS = false, |
| 12472 | }; | 12524 | }; |
| 12473 | | 12525 | |
| 12474 | fn statFromLinux(stx: *const std.os.linux.Statx) Io.UnexpectedError!File.Stat { | 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,6 +12539,7 @@ fn statFromLinux(stx: *const std.os.linux.Statx) Io.UnexpectedError!File.Stat { |
| 12487 | }, | 12539 | }, |
| 12488 | .mtime = .{ .nanoseconds = @intCast(@as(i128, stx.mtime.sec) * std.time.ns_per_s + stx.mtime.nsec) }, | 12540 | .mtime = .{ .nanoseconds = @intCast(@as(i128, stx.mtime.sec) * std.time.ns_per_s + stx.mtime.nsec) }, |
| 12489 | .ctime = .{ .nanoseconds = @intCast(@as(i128, stx.ctime.sec) * std.time.ns_per_s + stx.ctime.nsec) }, | 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,6 +12588,7 @@ fn statFromPosix(st: *const posix.Stat) File.Stat { |
| 12535 | .atime = timestampFromPosix(&atime), | 12588 | .atime = timestampFromPosix(&atime), |
| 12536 | .mtime = timestampFromPosix(&mtime), | 12589 | .mtime = timestampFromPosix(&mtime), |
| 12537 | .ctime = timestampFromPosix(&ctime), | 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,6 +12610,7 @@ fn statFromWasi(st: *const std.os.wasi.filestat_t) File.Stat { |
| 12556 | .atime = .fromNanoseconds(st.atim), | 12610 | .atime = .fromNanoseconds(st.atim), |
| 12557 | .mtime = .fromNanoseconds(st.mtim), | 12611 | .mtime = .fromNanoseconds(st.mtim), |
| 12558 | .ctime = .fromNanoseconds(st.ctim), | 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,3 +16162,529 @@ pub fn chdir(dir_path: []const u8) ChdirError!void { |
| 16107 | else => |err| return syscall.unexpectedErrno(err), | 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 | } |