| ... | ... | @@ -813,226 +813,6 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize { |
| 813 | 813 | } |
| 814 | 814 | } |
| 815 | 815 | |
| 816 | | /// Number of bytes read is returned. Upon reading end-of-file, zero is returned. |
| 817 | | /// |
| 818 | | /// For POSIX systems, if `fd` is opened in non blocking mode, the function will |
| 819 | | /// return error.WouldBlock when EAGAIN is received. |
| 820 | | /// On Windows, if the application has a global event loop enabled, I/O Completion Ports are |
| 821 | | /// used to perform the I/O. `error.WouldBlock` is not possible on Windows. |
| 822 | | /// |
| 823 | | /// This operation is non-atomic on the following systems: |
| 824 | | /// * Windows |
| 825 | | /// On these systems, the read races with concurrent writes to the same file descriptor. |
| 826 | | /// |
| 827 | | /// This function assumes that all vectors, including zero-length vectors, have |
| 828 | | /// a pointer within the address space of the application. |
| 829 | | pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize { |
| 830 | | if (native_os == .windows) { |
| 831 | | if (iov.len == 0) return 0; |
| 832 | | const first = iov[0]; |
| 833 | | return read(fd, first.base[0..first.len]); |
| 834 | | } |
| 835 | | if (native_os == .wasi and !builtin.link_libc) { |
| 836 | | var nread: usize = undefined; |
| 837 | | switch (wasi.fd_read(fd, iov.ptr, iov.len, &nread)) { |
| 838 | | .SUCCESS => return nread, |
| 839 | | .INTR => unreachable, |
| 840 | | .INVAL => unreachable, |
| 841 | | .FAULT => unreachable, |
| 842 | | .AGAIN => unreachable, // currently not support in WASI |
| 843 | | .BADF => return error.NotOpenForReading, // can be a race condition |
| 844 | | .IO => return error.InputOutput, |
| 845 | | .ISDIR => return error.IsDir, |
| 846 | | .NOBUFS => return error.SystemResources, |
| 847 | | .NOMEM => return error.SystemResources, |
| 848 | | .NOTCONN => return error.SocketUnconnected, |
| 849 | | .CONNRESET => return error.ConnectionResetByPeer, |
| 850 | | .TIMEDOUT => return error.Timeout, |
| 851 | | .NOTCAPABLE => return error.AccessDenied, |
| 852 | | else => |err| return unexpectedErrno(err), |
| 853 | | } |
| 854 | | } |
| 855 | | |
| 856 | | while (true) { |
| 857 | | const rc = system.readv(fd, iov.ptr, @min(iov.len, IOV_MAX)); |
| 858 | | switch (errno(rc)) { |
| 859 | | .SUCCESS => return @intCast(rc), |
| 860 | | .INTR => continue, |
| 861 | | .INVAL => unreachable, |
| 862 | | .FAULT => unreachable, |
| 863 | | .SRCH => return error.ProcessNotFound, |
| 864 | | .AGAIN => return error.WouldBlock, |
| 865 | | .BADF => return error.NotOpenForReading, // can be a race condition |
| 866 | | .IO => return error.InputOutput, |
| 867 | | .ISDIR => return error.IsDir, |
| 868 | | .NOBUFS => return error.SystemResources, |
| 869 | | .NOMEM => return error.SystemResources, |
| 870 | | .NOTCONN => return error.SocketUnconnected, |
| 871 | | .CONNRESET => return error.ConnectionResetByPeer, |
| 872 | | .TIMEDOUT => return error.Timeout, |
| 873 | | else => |err| return unexpectedErrno(err), |
| 874 | | } |
| 875 | | } |
| 876 | | } |
| 877 | | |
| 878 | | pub const PReadError = std.Io.File.ReadPositionalError; |
| 879 | | |
| 880 | | /// Number of bytes read is returned. Upon reading end-of-file, zero is returned. |
| 881 | | /// |
| 882 | | /// Retries when interrupted by a signal. |
| 883 | | /// |
| 884 | | /// For POSIX systems, if `fd` is opened in non blocking mode, the function will |
| 885 | | /// return error.WouldBlock when EAGAIN is received. |
| 886 | | /// On Windows, if the application has a global event loop enabled, I/O Completion Ports are |
| 887 | | /// used to perform the I/O. `error.WouldBlock` is not possible on Windows. |
| 888 | | /// |
| 889 | | /// Linux has a limit on how many bytes may be transferred in one `pread` call, which is `0x7ffff000` |
| 890 | | /// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as |
| 891 | | /// well as stuffing the errno codes into the last `4096` values. This is noted on the `read` man page. |
| 892 | | /// The limit on Darwin is `0x7fffffff`, trying to read more than that returns EINVAL. |
| 893 | | /// The corresponding POSIX limit is `maxInt(isize)`. |
| 894 | | pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize { |
| 895 | | if (buf.len == 0) return 0; |
| 896 | | if (native_os == .windows) { |
| 897 | | return windows.ReadFile(fd, buf, offset); |
| 898 | | } |
| 899 | | if (native_os == .wasi and !builtin.link_libc) { |
| 900 | | const iovs = [1]iovec{iovec{ |
| 901 | | .base = buf.ptr, |
| 902 | | .len = buf.len, |
| 903 | | }}; |
| 904 | | |
| 905 | | var nread: usize = undefined; |
| 906 | | switch (wasi.fd_pread(fd, &iovs, iovs.len, offset, &nread)) { |
| 907 | | .SUCCESS => return nread, |
| 908 | | .INTR => unreachable, |
| 909 | | .INVAL => unreachable, |
| 910 | | .FAULT => unreachable, |
| 911 | | .AGAIN => unreachable, |
| 912 | | .BADF => return error.NotOpenForReading, // Can be a race condition. |
| 913 | | .IO => return error.InputOutput, |
| 914 | | .ISDIR => return error.IsDir, |
| 915 | | .NOBUFS => return error.SystemResources, |
| 916 | | .NOMEM => return error.SystemResources, |
| 917 | | .NOTCONN => return error.SocketUnconnected, |
| 918 | | .CONNRESET => return error.ConnectionResetByPeer, |
| 919 | | .TIMEDOUT => return error.Timeout, |
| 920 | | .NXIO => return error.Unseekable, |
| 921 | | .SPIPE => return error.Unseekable, |
| 922 | | .OVERFLOW => return error.Unseekable, |
| 923 | | .NOTCAPABLE => return error.AccessDenied, |
| 924 | | else => |err| return unexpectedErrno(err), |
| 925 | | } |
| 926 | | } |
| 927 | | |
| 928 | | // Prevent EINVAL. |
| 929 | | const max_count = switch (native_os) { |
| 930 | | .linux => 0x7ffff000, |
| 931 | | .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => maxInt(i32), |
| 932 | | else => maxInt(isize), |
| 933 | | }; |
| 934 | | |
| 935 | | const pread_sym = if (lfs64_abi) system.pread64 else system.pread; |
| 936 | | while (true) { |
| 937 | | const rc = pread_sym(fd, buf.ptr, @min(buf.len, max_count), @bitCast(offset)); |
| 938 | | switch (errno(rc)) { |
| 939 | | .SUCCESS => return @intCast(rc), |
| 940 | | .INTR => continue, |
| 941 | | .INVAL => unreachable, |
| 942 | | .FAULT => unreachable, |
| 943 | | .SRCH => return error.ProcessNotFound, |
| 944 | | .AGAIN => return error.WouldBlock, |
| 945 | | .BADF => return error.NotOpenForReading, // Can be a race condition. |
| 946 | | .IO => return error.InputOutput, |
| 947 | | .ISDIR => return error.IsDir, |
| 948 | | .NOBUFS => return error.SystemResources, |
| 949 | | .NOMEM => return error.SystemResources, |
| 950 | | .NOTCONN => return error.SocketUnconnected, |
| 951 | | .CONNRESET => return error.ConnectionResetByPeer, |
| 952 | | .TIMEDOUT => return error.Timeout, |
| 953 | | .NXIO => return error.Unseekable, |
| 954 | | .SPIPE => return error.Unseekable, |
| 955 | | .OVERFLOW => return error.Unseekable, |
| 956 | | else => |err| return unexpectedErrno(err), |
| 957 | | } |
| 958 | | } |
| 959 | | } |
| 960 | | |
| 961 | | /// Number of bytes read is returned. Upon reading end-of-file, zero is returned. |
| 962 | | /// |
| 963 | | /// Retries when interrupted by a signal. |
| 964 | | /// |
| 965 | | /// For POSIX systems, if `fd` is opened in non blocking mode, the function will |
| 966 | | /// return error.WouldBlock when EAGAIN is received. |
| 967 | | /// On Windows, if the application has a global event loop enabled, I/O Completion Ports are |
| 968 | | /// used to perform the I/O. `error.WouldBlock` is not possible on Windows. |
| 969 | | /// |
| 970 | | /// This operation is non-atomic on the following systems: |
| 971 | | /// * Darwin |
| 972 | | /// * Windows |
| 973 | | /// On these systems, the read races with concurrent writes to the same file descriptor. |
| 974 | | pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) PReadError!usize { |
| 975 | | const have_pread_but_not_preadv = switch (native_os) { |
| 976 | | .windows, .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos, .haiku => true, |
| 977 | | else => false, |
| 978 | | }; |
| 979 | | if (have_pread_but_not_preadv) { |
| 980 | | // We could loop here; but proper usage of `preadv` must handle partial reads anyway. |
| 981 | | // So we simply read into the first vector only. |
| 982 | | if (iov.len == 0) return 0; |
| 983 | | const first = iov[0]; |
| 984 | | return pread(fd, first.base[0..first.len], offset); |
| 985 | | } |
| 986 | | if (native_os == .wasi and !builtin.link_libc) { |
| 987 | | var nread: usize = undefined; |
| 988 | | switch (wasi.fd_pread(fd, iov.ptr, iov.len, offset, &nread)) { |
| 989 | | .SUCCESS => return nread, |
| 990 | | .INTR => unreachable, |
| 991 | | .INVAL => unreachable, |
| 992 | | .FAULT => unreachable, |
| 993 | | .AGAIN => unreachable, |
| 994 | | .BADF => return error.NotOpenForReading, // can be a race condition |
| 995 | | .IO => return error.InputOutput, |
| 996 | | .ISDIR => return error.IsDir, |
| 997 | | .NOBUFS => return error.SystemResources, |
| 998 | | .NOMEM => return error.SystemResources, |
| 999 | | .NOTCONN => return error.SocketUnconnected, |
| 1000 | | .CONNRESET => return error.ConnectionResetByPeer, |
| 1001 | | .TIMEDOUT => return error.Timeout, |
| 1002 | | .NXIO => return error.Unseekable, |
| 1003 | | .SPIPE => return error.Unseekable, |
| 1004 | | .OVERFLOW => return error.Unseekable, |
| 1005 | | .NOTCAPABLE => return error.AccessDenied, |
| 1006 | | else => |err| return unexpectedErrno(err), |
| 1007 | | } |
| 1008 | | } |
| 1009 | | |
| 1010 | | const preadv_sym = if (lfs64_abi) system.preadv64 else system.preadv; |
| 1011 | | while (true) { |
| 1012 | | const rc = preadv_sym(fd, iov.ptr, @min(iov.len, IOV_MAX), @bitCast(offset)); |
| 1013 | | switch (errno(rc)) { |
| 1014 | | .SUCCESS => return @bitCast(rc), |
| 1015 | | .INTR => continue, |
| 1016 | | .INVAL => unreachable, |
| 1017 | | .FAULT => unreachable, |
| 1018 | | .SRCH => return error.ProcessNotFound, |
| 1019 | | .AGAIN => return error.WouldBlock, |
| 1020 | | .BADF => return error.NotOpenForReading, // can be a race condition |
| 1021 | | .IO => return error.InputOutput, |
| 1022 | | .ISDIR => return error.IsDir, |
| 1023 | | .NOBUFS => return error.SystemResources, |
| 1024 | | .NOMEM => return error.SystemResources, |
| 1025 | | .NOTCONN => return error.SocketUnconnected, |
| 1026 | | .CONNRESET => return error.ConnectionResetByPeer, |
| 1027 | | .TIMEDOUT => return error.Timeout, |
| 1028 | | .NXIO => return error.Unseekable, |
| 1029 | | .SPIPE => return error.Unseekable, |
| 1030 | | .OVERFLOW => return error.Unseekable, |
| 1031 | | else => |err| return unexpectedErrno(err), |
| 1032 | | } |
| 1033 | | } |
| 1034 | | } |
| 1035 | | |
| 1036 | 816 | pub const WriteError = error{ |
| 1037 | 817 | DiskQuota, |
| 1038 | 818 | FileTooBig, |
| ... | ... | @@ -1157,183 +937,6 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize { |
| 1157 | 937 | } |
| 1158 | 938 | } |
| 1159 | 939 | |
| 1160 | | pub const PWriteError = WriteError || error{Unseekable}; |
| 1161 | | |
| 1162 | | /// Write to a file descriptor, with a position offset. |
| 1163 | | /// Retries when interrupted by a signal. |
| 1164 | | /// Returns the number of bytes written. If nonzero bytes were supplied, this will be nonzero. |
| 1165 | | /// |
| 1166 | | /// Note that a successful write() may transfer fewer bytes than supplied. Such partial writes can |
| 1167 | | /// occur for various reasons; for example, because there was insufficient space on the disk |
| 1168 | | /// device to write all of the requested bytes, or because a blocked write() to a socket, pipe, or |
| 1169 | | /// similar was interrupted by a signal handler after it had transferred some, but before it had |
| 1170 | | /// transferred all of the requested bytes. In the event of a partial write, the caller can make |
| 1171 | | /// another write() call to transfer the remaining bytes. The subsequent call will either |
| 1172 | | /// transfer further bytes or may result in an error (e.g., if the disk is now full). |
| 1173 | | /// |
| 1174 | | /// For POSIX systems, if `fd` is opened in non blocking mode, the function will |
| 1175 | | /// return error.WouldBlock when EAGAIN is received. |
| 1176 | | /// On Windows, if the application has a global event loop enabled, I/O Completion Ports are |
| 1177 | | /// used to perform the I/O. `error.WouldBlock` is not possible on Windows. |
| 1178 | | /// |
| 1179 | | /// Linux has a limit on how many bytes may be transferred in one `pwrite` call, which is `0x7ffff000` |
| 1180 | | /// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as |
| 1181 | | /// well as stuffing the errno codes into the last `4096` values. This is noted on the `write` man page. |
| 1182 | | /// The limit on Darwin is `0x7fffffff`, trying to write more than that returns EINVAL. |
| 1183 | | /// The corresponding POSIX limit is `maxInt(isize)`. |
| 1184 | | pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize { |
| 1185 | | if (bytes.len == 0) return 0; |
| 1186 | | if (native_os == .windows) { |
| 1187 | | return windows.WriteFile(fd, bytes, offset); |
| 1188 | | } |
| 1189 | | if (native_os == .wasi and !builtin.link_libc) { |
| 1190 | | const ciovs = [1]iovec_const{iovec_const{ |
| 1191 | | .base = bytes.ptr, |
| 1192 | | .len = bytes.len, |
| 1193 | | }}; |
| 1194 | | |
| 1195 | | var nwritten: usize = undefined; |
| 1196 | | switch (wasi.fd_pwrite(fd, &ciovs, ciovs.len, offset, &nwritten)) { |
| 1197 | | .SUCCESS => return nwritten, |
| 1198 | | .INTR => unreachable, |
| 1199 | | .INVAL => unreachable, |
| 1200 | | .FAULT => unreachable, |
| 1201 | | .AGAIN => unreachable, |
| 1202 | | .BADF => return error.NotOpenForWriting, // can be a race condition. |
| 1203 | | .DESTADDRREQ => unreachable, // `connect` was never called. |
| 1204 | | .DQUOT => return error.DiskQuota, |
| 1205 | | .FBIG => return error.FileTooBig, |
| 1206 | | .IO => return error.InputOutput, |
| 1207 | | .NOSPC => return error.NoSpaceLeft, |
| 1208 | | .PERM => return error.PermissionDenied, |
| 1209 | | .PIPE => return error.BrokenPipe, |
| 1210 | | .NXIO => return error.Unseekable, |
| 1211 | | .SPIPE => return error.Unseekable, |
| 1212 | | .OVERFLOW => return error.Unseekable, |
| 1213 | | .NOTCAPABLE => return error.AccessDenied, |
| 1214 | | else => |err| return unexpectedErrno(err), |
| 1215 | | } |
| 1216 | | } |
| 1217 | | |
| 1218 | | // Prevent EINVAL. |
| 1219 | | const max_count = switch (native_os) { |
| 1220 | | .linux => 0x7ffff000, |
| 1221 | | .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => maxInt(i32), |
| 1222 | | else => maxInt(isize), |
| 1223 | | }; |
| 1224 | | |
| 1225 | | const pwrite_sym = if (lfs64_abi) system.pwrite64 else system.pwrite; |
| 1226 | | while (true) { |
| 1227 | | const rc = pwrite_sym(fd, bytes.ptr, @min(bytes.len, max_count), @bitCast(offset)); |
| 1228 | | switch (errno(rc)) { |
| 1229 | | .SUCCESS => return @intCast(rc), |
| 1230 | | .INTR => continue, |
| 1231 | | .INVAL => return error.InvalidArgument, |
| 1232 | | .FAULT => unreachable, |
| 1233 | | .SRCH => return error.ProcessNotFound, |
| 1234 | | .AGAIN => return error.WouldBlock, |
| 1235 | | .BADF => return error.NotOpenForWriting, // Can be a race condition. |
| 1236 | | .DESTADDRREQ => unreachable, // `connect` was never called. |
| 1237 | | .DQUOT => return error.DiskQuota, |
| 1238 | | .FBIG => return error.FileTooBig, |
| 1239 | | .IO => return error.InputOutput, |
| 1240 | | .NOSPC => return error.NoSpaceLeft, |
| 1241 | | .PERM => return error.PermissionDenied, |
| 1242 | | .PIPE => return error.BrokenPipe, |
| 1243 | | .NXIO => return error.Unseekable, |
| 1244 | | .SPIPE => return error.Unseekable, |
| 1245 | | .OVERFLOW => return error.Unseekable, |
| 1246 | | .BUSY => return error.DeviceBusy, |
| 1247 | | else => |err| return unexpectedErrno(err), |
| 1248 | | } |
| 1249 | | } |
| 1250 | | } |
| 1251 | | |
| 1252 | | /// Write multiple buffers to a file descriptor, with a position offset. |
| 1253 | | /// Retries when interrupted by a signal. |
| 1254 | | /// Returns the number of bytes written. If nonzero bytes were supplied, this will be nonzero. |
| 1255 | | /// |
| 1256 | | /// Note that a successful write() may transfer fewer than count bytes. Such partial writes can |
| 1257 | | /// occur for various reasons; for example, because there was insufficient space on the disk |
| 1258 | | /// device to write all of the requested bytes, or because a blocked write() to a socket, pipe, or |
| 1259 | | /// similar was interrupted by a signal handler after it had transferred some, but before it had |
| 1260 | | /// transferred all of the requested bytes. In the event of a partial write, the caller can make |
| 1261 | | /// another write() call to transfer the remaining bytes. The subsequent call will either |
| 1262 | | /// transfer further bytes or may result in an error (e.g., if the disk is now full). |
| 1263 | | /// |
| 1264 | | /// If `fd` is opened in non blocking mode, the function will |
| 1265 | | /// return error.WouldBlock when EAGAIN is received. |
| 1266 | | /// |
| 1267 | | /// The following systems do not have this syscall, and will return partial writes if more than one |
| 1268 | | /// vector is provided: |
| 1269 | | /// * Darwin |
| 1270 | | /// * Windows |
| 1271 | | /// |
| 1272 | | /// If `iov.len` is larger than `IOV_MAX`, a partial write will occur. |
| 1273 | | pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usize { |
| 1274 | | const have_pwrite_but_not_pwritev = switch (native_os) { |
| 1275 | | .windows, .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos, .haiku => true, |
| 1276 | | else => false, |
| 1277 | | }; |
| 1278 | | |
| 1279 | | if (have_pwrite_but_not_pwritev) { |
| 1280 | | // We could loop here; but proper usage of `pwritev` must handle partial writes anyway. |
| 1281 | | // So we simply write the first vector only. |
| 1282 | | if (iov.len == 0) return 0; |
| 1283 | | const first = iov[0]; |
| 1284 | | return pwrite(fd, first.base[0..first.len], offset); |
| 1285 | | } |
| 1286 | | if (native_os == .wasi and !builtin.link_libc) { |
| 1287 | | var nwritten: usize = undefined; |
| 1288 | | switch (wasi.fd_pwrite(fd, iov.ptr, iov.len, offset, &nwritten)) { |
| 1289 | | .SUCCESS => return nwritten, |
| 1290 | | .INTR => unreachable, |
| 1291 | | .INVAL => unreachable, |
| 1292 | | .FAULT => unreachable, |
| 1293 | | .AGAIN => unreachable, |
| 1294 | | .BADF => return error.NotOpenForWriting, // Can be a race condition. |
| 1295 | | .DESTADDRREQ => unreachable, // `connect` was never called. |
| 1296 | | .DQUOT => return error.DiskQuota, |
| 1297 | | .FBIG => return error.FileTooBig, |
| 1298 | | .IO => return error.InputOutput, |
| 1299 | | .NOSPC => return error.NoSpaceLeft, |
| 1300 | | .PERM => return error.PermissionDenied, |
| 1301 | | .PIPE => return error.BrokenPipe, |
| 1302 | | .NXIO => return error.Unseekable, |
| 1303 | | .SPIPE => return error.Unseekable, |
| 1304 | | .OVERFLOW => return error.Unseekable, |
| 1305 | | .NOTCAPABLE => return error.AccessDenied, |
| 1306 | | else => |err| return unexpectedErrno(err), |
| 1307 | | } |
| 1308 | | } |
| 1309 | | |
| 1310 | | const pwritev_sym = if (lfs64_abi) system.pwritev64 else system.pwritev; |
| 1311 | | while (true) { |
| 1312 | | const rc = pwritev_sym(fd, iov.ptr, @min(iov.len, IOV_MAX), @bitCast(offset)); |
| 1313 | | switch (errno(rc)) { |
| 1314 | | .SUCCESS => return @intCast(rc), |
| 1315 | | .INTR => continue, |
| 1316 | | .INVAL => return error.InvalidArgument, |
| 1317 | | .FAULT => unreachable, |
| 1318 | | .SRCH => return error.ProcessNotFound, |
| 1319 | | .AGAIN => return error.WouldBlock, |
| 1320 | | .BADF => return error.NotOpenForWriting, // Can be a race condition. |
| 1321 | | .DESTADDRREQ => unreachable, // `connect` was never called. |
| 1322 | | .DQUOT => return error.DiskQuota, |
| 1323 | | .FBIG => return error.FileTooBig, |
| 1324 | | .IO => return error.InputOutput, |
| 1325 | | .NOSPC => return error.NoSpaceLeft, |
| 1326 | | .PERM => return error.PermissionDenied, |
| 1327 | | .PIPE => return error.BrokenPipe, |
| 1328 | | .NXIO => return error.Unseekable, |
| 1329 | | .SPIPE => return error.Unseekable, |
| 1330 | | .OVERFLOW => return error.Unseekable, |
| 1331 | | .BUSY => return error.DeviceBusy, |
| 1332 | | else => |err| return unexpectedErrno(err), |
| 1333 | | } |
| 1334 | | } |
| 1335 | | } |
| 1336 | | |
| 1337 | 940 | pub const OpenError = std.Io.File.OpenError || error{WouldBlock}; |
| 1338 | 941 | |
| 1339 | 942 | /// Open and possibly create a file. Keeps trying if it gets interrupted. |