authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-08-21 20:28:37-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-08-21 20:31:50-04:00
logea1b21dbdb3d5e680b133be68d174dcc0067fa1e
tree88114bd3c7235e8b0478a1e98ddc48b25d013a95
parent51852d2587b931767a12d42ce39d5c191eea10ea

fix linux

* error.BadFd is not a valid error code. it would always be a bug to get this error code. * merge error.Io with existing error.InputOutput * merge error.PathNotFound with existing error.FileNotFound. Not all OS's support both. * add os.File.openReadC * add error.BadPathName for windows file operations with invalid characters * add os.toPosixPath to help stack allocate a null terminating byte * add some TODOs for other functions to investigate removing the allocator requirement * optimize some implementations to use the alternate functions when a null byte is already available * add a missing error.SkipZigTest * os.selfExePath uses a non-allocating API * os.selfExeDirPath uses a non-allocating API * os.path.real uses a non-allocating API * add os.path.realAlloc and os.path.realC * convert many windows syscalls to use the W versions (See #534)

17 files changed, 319 insertions(+), 288 deletions(-)

doc/docgen.zig+3-3
......@@ -34,10 +34,10 @@ pub fn main() !void {
3434 const out_file_name = try (args_it.next(allocator) orelse @panic("expected output arg"));
3535 defer allocator.free(out_file_name);
3636
37 var in_file = try os.File.openRead(allocator, in_file_name);
37 var in_file = try os.File.openRead(in_file_name);
3838 defer in_file.close();
3939
40 var out_file = try os.File.openWrite(allocator, out_file_name);
40 var out_file = try os.File.openWrite(out_file_name);
4141 defer out_file.close();
4242
4343 var file_in_stream = io.FileInStream.init(&in_file);
......@@ -738,7 +738,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
738738 try out.print("<pre><code class=\"zig\">{}</code></pre>", escaped_source);
739739 const name_plus_ext = try std.fmt.allocPrint(allocator, "{}.zig", code.name);
740740 const tmp_source_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_ext);
741 try io.writeFile(allocator, tmp_source_file_name, trimmed_raw_source);
741 try io.writeFile(tmp_source_file_name, trimmed_raw_source);
742742
743743 switch (code.id) {
744744 Code.Id.Exe => |expected_outcome| {
example/cat/main.zig+1-1
......@@ -20,7 +20,7 @@ pub fn main() !void {
2020 } else if (arg[0] == '-') {
2121 return usage(exe);
2222 } else {
23 var file = os.File.openRead(allocator, arg) catch |err| {
23 var file = os.File.openRead(arg) catch |err| {
2424 warn("Unable to open file: {}\n", @errorName(err));
2525 return err;
2626 };
src-self-hosted/compilation.zig+1-4
......@@ -257,8 +257,6 @@ pub const Compilation = struct {
257257 pub const BuildError = error{
258258 OutOfMemory,
259259 EndOfStream,
260 BadFd,
261 Io,
262260 IsDir,
263261 Unexpected,
264262 SystemResources,
......@@ -273,7 +271,6 @@ pub const Compilation = struct {
273271 NameTooLong,
274272 SystemFdQuotaExceeded,
275273 NoDevice,
276 PathNotFound,
277274 NoSpaceLeft,
278275 NotDir,
279276 FileSystem,
......@@ -962,7 +959,7 @@ pub const Compilation = struct {
962959 if (self.root_src_path) |root_src_path| {
963960 const root_scope = blk: {
964961 // TODO async/await os.path.real
965 const root_src_real_path = os.path.real(self.gpa(), root_src_path) catch |err| {
962 const root_src_real_path = os.path.realAlloc(self.gpa(), root_src_path) catch |err| {
966963 try self.addCompileErrorCli(root_src_path, "unable to open: {}", @errorName(err));
967964 return;
968965 };
src-self-hosted/introspect.zig+1-1
......@@ -22,7 +22,7 @@ pub fn testZigInstallPrefix(allocator: *mem.Allocator, test_path: []const u8) ![
2222
2323/// Caller must free result
2424pub fn findZigLibDir(allocator: *mem.Allocator) ![]u8 {
25 const self_exe_path = try os.selfExeDirPath(allocator);
25 const self_exe_path = try os.selfExeDirPathAlloc(allocator);
2626 defer allocator.free(self_exe_path);
2727
2828 var cur_path: []const u8 = self_exe_path;
src-self-hosted/libc_installation.zig+1-1
......@@ -453,7 +453,7 @@ fn fileExists(path: []const u8) !bool {
453453 if (std.os.File.access(path)) |_| {
454454 return true;
455455 } else |err| switch (err) {
456 error.FileNotFound, error.PathNotFound, error.PermissionDenied => return false,
456 error.FileNotFound, error.PermissionDenied => return false,
457457 else => return error.FileSystem,
458458 }
459459}
std/build.zig+12-6
......@@ -1491,11 +1491,14 @@ pub const LibExeObjStep = struct {
14911491 }
14921492
14931493 if (!is_darwin) {
1494 const rpath_arg = builder.fmt("-Wl,-rpath,{}", os.path.real(builder.allocator, builder.pathFromRoot(builder.cache_root)) catch unreachable);
1494 const rpath_arg = builder.fmt("-Wl,-rpath,{}", try os.path.realAlloc(
1495 builder.allocator,
1496 builder.pathFromRoot(builder.cache_root),
1497 ));
14951498 defer builder.allocator.free(rpath_arg);
1496 cc_args.append(rpath_arg) catch unreachable;
1499 try cc_args.append(rpath_arg);
14971500
1498 cc_args.append("-rdynamic") catch unreachable;
1501 try cc_args.append("-rdynamic");
14991502 }
15001503
15011504 for (self.full_path_libs.toSliceConst()) |full_path_lib| {
......@@ -1566,11 +1569,14 @@ pub const LibExeObjStep = struct {
15661569 cc_args.append("-o") catch unreachable;
15671570 cc_args.append(output_path) catch unreachable;
15681571
1569 const rpath_arg = builder.fmt("-Wl,-rpath,{}", os.path.real(builder.allocator, builder.pathFromRoot(builder.cache_root)) catch unreachable);
1572 const rpath_arg = builder.fmt("-Wl,-rpath,{}", try os.path.realAlloc(
1573 builder.allocator,
1574 builder.pathFromRoot(builder.cache_root),
1575 ));
15701576 defer builder.allocator.free(rpath_arg);
1571 cc_args.append(rpath_arg) catch unreachable;
1577 try cc_args.append(rpath_arg);
15721578
1573 cc_args.append("-rdynamic") catch unreachable;
1579 try cc_args.append("-rdynamic");
15741580
15751581 {
15761582 var it = self.link_libs.iterator();
std/debug/index.zig+1-1
......@@ -255,7 +255,7 @@ pub fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address
255255 address,
256256 compile_unit_name,
257257 );
258 if (printLineFromFile(debug_info.allocator(), out_stream, line_info)) {
258 if (printLineFromFile(out_stream, line_info)) {
259259 if (line_info.column == 0) {
260260 try out_stream.write("\n");
261261 } else {
std/event/fs.zig+24-32
......@@ -78,8 +78,7 @@ pub async fn pwritev(loop: *Loop, fd: os.FileHandle, data: []const []const u8, o
7878 builtin.Os.macosx,
7979 builtin.Os.linux,
8080 => return await (async pwritevPosix(loop, fd, data, offset) catch unreachable),
81 builtin.Os.windows,
82 => return await (async pwritevWindows(loop, fd, data, offset) catch unreachable),
81 builtin.Os.windows => return await (async pwritevWindows(loop, fd, data, offset) catch unreachable),
8382 else => @compileError("Unsupported OS"),
8483 }
8584}
......@@ -147,7 +146,6 @@ pub async fn pwriteWindows(loop: *Loop, fd: os.FileHandle, data: []const u8, off
147146 }
148147}
149148
150
151149/// data - just the inner references - must live until pwritev promise completes.
152150pub async fn pwritevPosix(loop: *Loop, fd: os.FileHandle, data: []const []const u8, offset: usize) !void {
153151 // workaround for https://github.com/ziglang/zig/issues/1194
......@@ -203,8 +201,7 @@ pub async fn preadv(loop: *Loop, fd: os.FileHandle, data: []const []u8, offset:
203201 builtin.Os.macosx,
204202 builtin.Os.linux,
205203 => return await (async preadvPosix(loop, fd, data, offset) catch unreachable),
206 builtin.Os.windows,
207 => return await (async preadvWindows(loop, fd, data, offset) catch unreachable),
204 builtin.Os.windows => return await (async preadvWindows(loop, fd, data, offset) catch unreachable),
208205 else => @compileError("Unsupported OS"),
209206 }
210207}
......@@ -222,7 +219,7 @@ pub async fn preadvWindows(loop: *Loop, fd: os.FileHandle, data: []const []u8, o
222219 var inner_off: usize = 0;
223220 while (true) {
224221 const v = data_copy[iov_i];
225 const amt_read = try await (async preadWindows(loop, fd, v[inner_off .. v.len-inner_off], offset + off) catch unreachable);
222 const amt_read = try await (async preadWindows(loop, fd, v[inner_off .. v.len - inner_off], offset + off) catch unreachable);
226223 off += amt_read;
227224 inner_off += amt_read;
228225 if (inner_off == v.len) {
......@@ -340,8 +337,7 @@ pub async fn openPosix(
340337 resume @handle();
341338 }
342339
343 const path_with_null = try std.cstr.addNullByte(loop.allocator, path);
344 defer loop.allocator.free(path_with_null);
340 const path_c = try std.os.toPosixPath(path);
345341
346342 var req_node = RequestNode{
347343 .prev = null,
......@@ -349,7 +345,7 @@ pub async fn openPosix(
349345 .data = Request{
350346 .msg = Request.Msg{
351347 .Open = Request.Msg.Open{
352 .path = path_with_null[0..path.len],
348 .path = path_c[0..path.len],
353349 .flags = flags,
354350 .mode = mode,
355351 .result = undefined,
......@@ -408,8 +404,7 @@ pub async fn openWriteMode(loop: *Loop, path: []const u8, mode: os.File.Mode) os
408404 const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_TRUNC;
409405 return await (async openPosix(loop, path, flags, os.File.default_mode) catch unreachable);
410406 },
411 builtin.Os.windows,
412 => return os.windowsOpen(
407 builtin.Os.windows => return os.windowsOpen(
413408 path,
414409 windows.GENERIC_WRITE,
415410 windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
......@@ -434,7 +429,7 @@ pub async fn openReadWrite(
434429
435430 builtin.Os.windows => return os.windowsOpen(
436431 path,
437 windows.GENERIC_WRITE|windows.GENERIC_READ,
432 windows.GENERIC_WRITE | windows.GENERIC_READ,
438433 windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
439434 windows.OPEN_ALWAYS,
440435 windows.FILE_ATTRIBUTE_NORMAL | windows.FILE_FLAG_OVERLAPPED,
......@@ -510,8 +505,7 @@ pub const CloseOperation = struct {
510505 self.loop.allocator.destroy(self);
511506 }
512507 },
513 builtin.Os.windows,
514 => {
508 builtin.Os.windows => {
515509 if (self.os_data.handle) |handle| {
516510 os.close(handle);
517511 }
......@@ -529,8 +523,7 @@ pub const CloseOperation = struct {
529523 self.os_data.close_req_node.data.msg.Close.fd = handle;
530524 self.os_data.have_fd = true;
531525 },
532 builtin.Os.windows,
533 => {
526 builtin.Os.windows => {
534527 self.os_data.handle = handle;
535528 },
536529 else => @compileError("Unsupported OS"),
......@@ -545,8 +538,7 @@ pub const CloseOperation = struct {
545538 => {
546539 self.os_data.have_fd = false;
547540 },
548 builtin.Os.windows,
549 => {
541 builtin.Os.windows => {
550542 self.os_data.handle = null;
551543 },
552544 else => @compileError("Unsupported OS"),
......@@ -561,8 +553,7 @@ pub const CloseOperation = struct {
561553 assert(self.os_data.have_fd);
562554 return self.os_data.close_req_node.data.msg.Close.fd;
563555 },
564 builtin.Os.windows,
565 => {
556 builtin.Os.windows => {
566557 return self.os_data.handle.?;
567558 },
568559 else => @compileError("Unsupported OS"),
......@@ -582,8 +573,7 @@ pub async fn writeFileMode(loop: *Loop, path: []const u8, contents: []const u8,
582573 builtin.Os.linux,
583574 builtin.Os.macosx,
584575 => return await (async writeFileModeThread(loop, path, contents, mode) catch unreachable),
585 builtin.Os.windows,
586 => return await (async writeFileWindows(loop, path, contents) catch unreachable),
576 builtin.Os.windows => return await (async writeFileWindows(loop, path, contents) catch unreachable),
587577 else => @compileError("Unsupported OS"),
588578 }
589579}
......@@ -1000,7 +990,7 @@ pub fn Watch(comptime V: type) type {
1000990 const basename_utf16le_null = try std.unicode.utf8ToUtf16LeWithNull(self.channel.loop.allocator, basename);
1001991 var basename_utf16le_null_consumed = false;
1002992 defer if (!basename_utf16le_null_consumed) self.channel.loop.allocator.free(basename_utf16le_null);
1003 const basename_utf16le_no_null = basename_utf16le_null[0..basename_utf16le_null.len-1];
993 const basename_utf16le_no_null = basename_utf16le_null[0 .. basename_utf16le_null.len - 1];
1004994
1005995 const dir_handle = windows.CreateFileW(
1006996 dirname_utf16le.ptr,
......@@ -1014,9 +1004,8 @@ pub fn Watch(comptime V: type) type {
10141004 if (dir_handle == windows.INVALID_HANDLE_VALUE) {
10151005 const err = windows.GetLastError();
10161006 switch (err) {
1017 windows.ERROR.FILE_NOT_FOUND,
1018 windows.ERROR.PATH_NOT_FOUND,
1019 => return error.PathNotFound,
1007 windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
1008 windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
10201009 else => return os.unexpectedErrorWindows(err),
10211010 }
10221011 }
......@@ -1102,7 +1091,10 @@ pub fn Watch(comptime V: type) type {
11021091
11031092 // TODO handle this error not in the channel but in the setup
11041093 _ = os.windowsCreateIoCompletionPort(
1105 dir_handle, self.channel.loop.os_data.io_port, completion_key, undefined,
1094 dir_handle,
1095 self.channel.loop.os_data.io_port,
1096 completion_key,
1097 undefined,
11061098 ) catch |err| {
11071099 await (async self.channel.put(err) catch unreachable);
11081100 return;
......@@ -1122,10 +1114,10 @@ pub fn Watch(comptime V: type) type {
11221114 &event_buf,
11231115 @intCast(windows.DWORD, event_buf.len),
11241116 windows.FALSE, // watch subtree
1125 windows.FILE_NOTIFY_CHANGE_FILE_NAME | windows.FILE_NOTIFY_CHANGE_DIR_NAME |
1126 windows.FILE_NOTIFY_CHANGE_ATTRIBUTES | windows.FILE_NOTIFY_CHANGE_SIZE |
1127 windows.FILE_NOTIFY_CHANGE_LAST_WRITE | windows.FILE_NOTIFY_CHANGE_LAST_ACCESS |
1128 windows.FILE_NOTIFY_CHANGE_CREATION | windows.FILE_NOTIFY_CHANGE_SECURITY,
1117 windows.FILE_NOTIFY_CHANGE_FILE_NAME | windows.FILE_NOTIFY_CHANGE_DIR_NAME |
1118 windows.FILE_NOTIFY_CHANGE_ATTRIBUTES | windows.FILE_NOTIFY_CHANGE_SIZE |
1119 windows.FILE_NOTIFY_CHANGE_LAST_WRITE | windows.FILE_NOTIFY_CHANGE_LAST_ACCESS |
1120 windows.FILE_NOTIFY_CHANGE_CREATION | windows.FILE_NOTIFY_CHANGE_SECURITY,
11291121 null, // number of bytes transferred (unused for async)
11301122 &overlapped,
11311123 null, // completion routine - unused because we use IOCP
......@@ -1152,7 +1144,7 @@ pub fn Watch(comptime V: type) type {
11521144 else => null,
11531145 };
11541146 if (emit) |id| {
1155 const basename_utf16le = ([*]u16)(&ev.FileName)[0..ev.FileNameLength/2];
1147 const basename_utf16le = ([*]u16)(&ev.FileName)[0 .. ev.FileNameLength / 2];
11561148 const user_value = blk: {
11571149 const held = await (async dir.table_lock.acquire() catch unreachable);
11581150 defer held.release();
std/os/child_process.zig+1-8
......@@ -349,14 +349,7 @@ pub const ChildProcess = struct {
349349 };
350350
351351 const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore);
352 const dev_null_fd = if (any_ignore) blk: {
353 const dev_null_path = "/dev/null";
354 var fixed_buffer_mem: [dev_null_path.len + 1]u8 = undefined;
355 var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
356 break :blk try os.posixOpen(&fixed_allocator.allocator, "/dev/null", posix.O_RDWR, 0);
357 } else blk: {
358 break :blk undefined;
359 };
352 const dev_null_fd = if (any_ignore) try os.posixOpenC(c"/dev/null", posix.O_RDWR, 0) else undefined;
360353 defer {
361354 if (any_ignore) os.close(dev_null_fd);
362355 }
std/os/file.zig+40-23
......@@ -28,13 +28,26 @@ pub const File = struct {
2828
2929 pub const OpenError = os.WindowsOpenError || os.PosixOpenError;
3030
31 /// Call close to clean up.
32 pub fn openRead(path: []const u8) OpenError!File {
31 /// `openRead` except with a null terminated path
32 pub fn openReadC(path: [*]const u8) OpenError!File {
3333 if (is_posix) {
3434 const flags = posix.O_LARGEFILE | posix.O_RDONLY;
35 const fd = try os.posixOpen(path, flags, 0);
35 const fd = try os.posixOpenC(path, flags, 0);
3636 return openHandle(fd);
37 } else if (is_windows) {
37 }
38 if (is_windows) {
39 return openRead(mem.toSliceConst(u8, path));
40 }
41 @compileError("Unsupported OS");
42 }
43
44 /// Call close to clean up.
45 pub fn openRead(path: []const u8) OpenError!File {
46 if (is_posix) {
47 const path_c = try os.toPosixPath(path);
48 return openReadC(&path_c);
49 }
50 if (is_windows) {
3851 const handle = try os.windowsOpen(
3952 path,
4053 windows.GENERIC_READ,
......@@ -43,9 +56,8 @@ pub const File = struct {
4356 windows.FILE_ATTRIBUTE_NORMAL,
4457 );
4558 return openHandle(handle);
46 } else {
47 @compileError("TODO implement openRead for this OS");
4859 }
60 @compileError("Unsupported OS");
4961 }
5062
5163 /// Calls `openWriteMode` with os.File.default_mode for the mode.
......@@ -103,13 +115,11 @@ pub const File = struct {
103115
104116 pub const AccessError = error{
105117 PermissionDenied,
106 PathNotFound,
107118 FileNotFound,
108119 NameTooLong,
109 BadMode,
110 BadPathName,
111 Io,
120 InputOutput,
112121 SystemResources,
122 BadPathName,
113123
114124 /// On Windows, file paths must be valid Unicode.
115125 InvalidUtf8,
......@@ -127,7 +137,7 @@ pub const File = struct {
127137 const err = windows.GetLastError();
128138 switch (err) {
129139 windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
130 windows.ERROR.PATH_NOT_FOUND => return error.PathNotFound,
140 windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
131141 windows.ERROR.ACCESS_DENIED => return error.PermissionDenied,
132142 else => return os.unexpectedErrorWindows(err),
133143 }
......@@ -149,13 +159,13 @@ pub const File = struct {
149159 posix.EROFS => return error.PermissionDenied,
150160 posix.ELOOP => return error.PermissionDenied,
151161 posix.ETXTBSY => return error.PermissionDenied,
152 posix.ENOTDIR => return error.NotFound,
153 posix.ENOENT => return error.NotFound,
162 posix.ENOTDIR => return error.FileNotFound,
163 posix.ENOENT => return error.FileNotFound,
154164
155165 posix.ENAMETOOLONG => return error.NameTooLong,
156166 posix.EINVAL => unreachable,
157 posix.EFAULT => return error.BadPathName,
158 posix.EIO => return error.Io,
167 posix.EFAULT => unreachable,
168 posix.EIO => return error.InputOutput,
159169 posix.ENOMEM => return error.SystemResources,
160170 else => return os.unexpectedErrorPosix(err),
161171 }
......@@ -197,7 +207,9 @@ pub const File = struct {
197207 const err = posix.getErrno(result);
198208 if (err > 0) {
199209 return switch (err) {
200 posix.EBADF => error.BadFd,
210 // We do not make this an error code because if you get EBADF it's always a bug,
211 // since the fd could have been reused.
212 posix.EBADF => unreachable,
201213 posix.EINVAL => error.Unseekable,
202214 posix.EOVERFLOW => error.Unseekable,
203215 posix.ESPIPE => error.Unseekable,
......@@ -210,7 +222,7 @@ pub const File = struct {
210222 if (windows.SetFilePointerEx(self.handle, amount, null, windows.FILE_CURRENT) == 0) {
211223 const err = windows.GetLastError();
212224 return switch (err) {
213 windows.ERROR.INVALID_PARAMETER => error.BadFd,
225 windows.ERROR.INVALID_PARAMETER => unreachable,
214226 else => os.unexpectedErrorWindows(err),
215227 };
216228 }
......@@ -227,7 +239,9 @@ pub const File = struct {
227239 const err = posix.getErrno(result);
228240 if (err > 0) {
229241 return switch (err) {
230 posix.EBADF => error.BadFd,
242 // We do not make this an error code because if you get EBADF it's always a bug,
243 // since the fd could have been reused.
244 posix.EBADF => unreachable,
231245 posix.EINVAL => error.Unseekable,
232246 posix.EOVERFLOW => error.Unseekable,
233247 posix.ESPIPE => error.Unseekable,
......@@ -241,7 +255,7 @@ pub const File = struct {
241255 if (windows.SetFilePointerEx(self.handle, ipos, null, windows.FILE_BEGIN) == 0) {
242256 const err = windows.GetLastError();
243257 return switch (err) {
244 windows.ERROR.INVALID_PARAMETER => error.BadFd,
258 windows.ERROR.INVALID_PARAMETER => unreachable,
245259 else => os.unexpectedErrorWindows(err),
246260 };
247261 }
......@@ -257,7 +271,9 @@ pub const File = struct {
257271 const err = posix.getErrno(result);
258272 if (err > 0) {
259273 return switch (err) {
260 posix.EBADF => error.BadFd,
274 // We do not make this an error code because if you get EBADF it's always a bug,
275 // since the fd could have been reused.
276 posix.EBADF => unreachable,
261277 posix.EINVAL => error.Unseekable,
262278 posix.EOVERFLOW => error.Unseekable,
263279 posix.ESPIPE => error.Unseekable,
......@@ -272,7 +288,7 @@ pub const File = struct {
272288 if (windows.SetFilePointerEx(self.handle, 0, &pos, windows.FILE_CURRENT) == 0) {
273289 const err = windows.GetLastError();
274290 return switch (err) {
275 windows.ERROR.INVALID_PARAMETER => error.BadFd,
291 windows.ERROR.INVALID_PARAMETER => unreachable,
276292 else => os.unexpectedErrorWindows(err),
277293 };
278294 }
......@@ -305,7 +321,6 @@ pub const File = struct {
305321 }
306322
307323 pub const ModeError = error{
308 BadFd,
309324 SystemResources,
310325 Unexpected,
311326 };
......@@ -316,7 +331,9 @@ pub const File = struct {
316331 const err = posix.getErrno(posix.fstat(self.handle, &stat));
317332 if (err > 0) {
318333 return switch (err) {
319 posix.EBADF => error.BadFd,
334 // We do not make this an error code because if you get EBADF it's always a bug,
335 // since the fd could have been reused.
336 posix.EBADF => unreachable,
320337 posix.ENOMEM => error.SystemResources,
321338 else => os.unexpectedErrorPosix(err),
322339 };
std/os/index.zig+79-97
......@@ -436,7 +436,7 @@ pub const PosixOpenError = error{
436436 NameTooLong,
437437 SystemFdQuotaExceeded,
438438 NoDevice,
439 PathNotFound,
439 FileNotFound,
440440 SystemResources,
441441 NoSpaceLeft,
442442 NotDir,
......@@ -450,11 +450,8 @@ pub const PosixOpenError = error{
450450/// Calls POSIX open, keeps trying if it gets interrupted, and translates
451451/// the return value into zig errors.
452452pub fn posixOpen(file_path: []const u8, flags: u32, perm: usize) PosixOpenError!i32 {
453 var path_with_null: [posix.PATH_MAX]u8 = undefined;
454 if (file_path.len >= posix.PATH_MAX) return error.NameTooLong;
455 mem.copy(u8, path_with_null[0..], file_path);
456 path_with_null[file_path.len] = 0;
457 return posixOpenC(&path_with_null, flags, perm);
453 const file_path_c = try toPosixPath(file_path);
454 return posixOpenC(&file_path_c, flags, perm);
458455}
459456
460457// TODO https://github.com/ziglang/zig/issues/265
......@@ -476,7 +473,7 @@ pub fn posixOpenC(file_path: [*]const u8, flags: u32, perm: usize) !i32 {
476473 posix.ENAMETOOLONG => return PosixOpenError.NameTooLong,
477474 posix.ENFILE => return PosixOpenError.SystemFdQuotaExceeded,
478475 posix.ENODEV => return PosixOpenError.NoDevice,
479 posix.ENOENT => return PosixOpenError.PathNotFound,
476 posix.ENOENT => return PosixOpenError.FileNotFound,
480477 posix.ENOMEM => return PosixOpenError.SystemResources,
481478 posix.ENOSPC => return PosixOpenError.NoSpaceLeft,
482479 posix.ENOTDIR => return PosixOpenError.NotDir,
......@@ -489,6 +486,16 @@ pub fn posixOpenC(file_path: [*]const u8, flags: u32, perm: usize) !i32 {
489486 }
490487}
491488
489/// Used to convert a slice to a null terminated slice on the stack.
490/// TODO well defined copy elision
491pub fn toPosixPath(file_path: []const u8) ![posix.PATH_MAX]u8 {
492 var path_with_null: [posix.PATH_MAX]u8 = undefined;
493 if (file_path.len >= posix.PATH_MAX) return error.NameTooLong;
494 mem.copy(u8, path_with_null[0..], file_path);
495 path_with_null[file_path.len] = 0;
496 return path_with_null;
497}
498
492499pub fn posixDup2(old_fd: i32, new_fd: i32) !void {
493500 while (true) {
494501 const err = posix.getErrno(posix.dup2(old_fd, new_fd));
......@@ -742,7 +749,6 @@ pub fn getCwdAlloc(allocator: *Allocator) ![]u8 {
742749pub const GetCwdError = error{Unexpected};
743750
744751/// The result is a slice of out_buffer.
745/// TODO with well defined copy elision we could make the API of this function better.
746752pub fn getCwd(out_buffer: *[MAX_PATH_BYTES]u8) GetCwdError![]u8 {
747753 switch (builtin.os) {
748754 Os.windows => {
......@@ -960,11 +966,8 @@ pub fn deleteFilePosixC(file_path: [*]const u8) !void {
960966}
961967
962968pub fn deleteFilePosix(file_path: []const u8) !void {
963 var path_with_null: [posix.PATH_MAX]u8 = undefined;
964 if (file_path.len >= posix.PATH_MAX) return error.NameTooLong;
965 mem.copy(u8, path_with_null[0..], file_path);
966 path_with_null[file_path.len] = 0;
967 return deleteFilePosixC(&path_with_null);
969 const file_path_c = try toPosixPath(file_path);
970 return deleteFilePosixC(&file_path_c);
968971}
969972
970973/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is
......@@ -1120,17 +1123,9 @@ pub fn rename(old_path: []const u8, new_path: []const u8) !void {
11201123 }
11211124 }
11221125 } else {
1123 var old_path_with_null: [posix.PATH_MAX]u8 = undefined;
1124 if (old_path.len >= posix.PATH_MAX) return error.NameTooLong;
1125 mem.copy(u8, old_path_with_null[0..], old_path);
1126 old_path_with_null[old_path.len] = 0;
1127
1128 var new_path_with_null: [posix.PATH_MAX]u8 = undefined;
1129 if (new_path.len >= posix.PATH_MAX) return error.NameTooLong;
1130 mem.copy(u8, new_path_with_null[0..], new_path);
1131 new_path_with_null[new_path.len] = 0;
1132
1133 return renameC(&old_path_with_null, &new_path_with_null);
1126 const old_path_c = try toPosixPath(old_path);
1127 const new_path_c = try toPosixPath(new_path);
1128 return renameC(&old_path_c, &new_path_c);
11341129 }
11351130}
11361131
......@@ -1156,7 +1151,7 @@ pub fn makeDirWindows(dir_path: []const u8) !void {
11561151}
11571152
11581153pub fn makeDirPosixC(dir_path: [*]const u8) !void {
1159 const err = posix.getErrno(posix.mkdir(path_buf.ptr, 0o755));
1154 const err = posix.getErrno(posix.mkdir(dir_path, 0o755));
11601155 switch (err) {
11611156 0 => return,
11621157 posix.EACCES => return error.AccessDenied,
......@@ -1177,11 +1172,8 @@ pub fn makeDirPosixC(dir_path: [*]const u8) !void {
11771172}
11781173
11791174pub fn makeDirPosix(dir_path: []const u8) !void {
1180 var path_with_null: [posix.PATH_MAX]u8 = undefined;
1181 if (dir_path.len >= posix.PATH_MAX) return error.NameTooLong;
1182 mem.copy(u8, path_with_null[0..], dir_path);
1183 path_with_null[dir_path.len] = 0;
1184 return makeDirPosixC(&path_with_null);
1175 const dir_path_c = try toPosixPath(dir_path);
1176 return makeDirPosixC(&dir_path_c);
11851177}
11861178
11871179/// Calls makeDir recursively to make an entire path. Returns success if the path
......@@ -1290,7 +1282,6 @@ const DeleteTreeError = error{
12901282 NameTooLong,
12911283 SystemFdQuotaExceeded,
12921284 NoDevice,
1293 PathNotFound,
12941285 SystemResources,
12951286 NoSpaceLeft,
12961287 PathAlreadyExists,
......@@ -1354,7 +1345,7 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
13541345 error.NameTooLong,
13551346 error.SystemFdQuotaExceeded,
13561347 error.NoDevice,
1357 error.PathNotFound,
1348 error.FileNotFound,
13581349 error.SystemResources,
13591350 error.NoSpaceLeft,
13601351 error.PathAlreadyExists,
......@@ -1424,7 +1415,7 @@ pub const Dir = struct {
14241415 };
14251416
14261417 pub const OpenError = error{
1427 PathNotFound,
1418 FileNotFound,
14281419 NotDir,
14291420 AccessDenied,
14301421 FileTooBig,
......@@ -1443,6 +1434,7 @@ pub const Dir = struct {
14431434 Unexpected,
14441435 };
14451436
1437 /// TODO remove the allocator requirement from this API
14461438 pub fn open(allocator: *Allocator, dir_path: []const u8) OpenError!Dir {
14471439 return Dir{
14481440 .allocator = allocator,
......@@ -1458,7 +1450,6 @@ pub const Dir = struct {
14581450 },
14591451 Os.macosx, Os.ios => Handle{
14601452 .fd = try posixOpen(
1461 allocator,
14621453 dir_path,
14631454 posix.O_RDONLY | posix.O_NONBLOCK | posix.O_DIRECTORY | posix.O_CLOEXEC,
14641455 0,
......@@ -1470,7 +1461,6 @@ pub const Dir = struct {
14701461 },
14711462 Os.linux => Handle{
14721463 .fd = try posixOpen(
1473 allocator,
14741464 dir_path,
14751465 posix.O_RDONLY | posix.O_DIRECTORY | posix.O_CLOEXEC,
14761466 0,
......@@ -1668,12 +1658,12 @@ pub fn changeCurDir(allocator: *Allocator, dir_path: []const u8) !void {
16681658
16691659/// Read value of a symbolic link.
16701660/// The return value is a slice of out_buffer.
1671pub fn readLinkC(pathname: [*]const u8, out_buffer: *[posix.PATH_MAX]u8) ![]u8 {
1661pub fn readLinkC(out_buffer: *[posix.PATH_MAX]u8, pathname: [*]const u8) ![]u8 {
16721662 const rc = posix.readlink(pathname, out_buffer, out_buffer.len);
16731663 const err = posix.getErrno(rc);
16741664 switch (err) {
16751665 0 => return out_buffer[0..rc],
1676 posix.EACCES => error.AccessDenied,
1666 posix.EACCES => return error.AccessDenied,
16771667 posix.EFAULT => unreachable,
16781668 posix.EINVAL => unreachable,
16791669 posix.EIO => return error.FileSystem,
......@@ -1688,12 +1678,9 @@ pub fn readLinkC(pathname: [*]const u8, out_buffer: *[posix.PATH_MAX]u8) ![]u8 {
16881678
16891679/// Read value of a symbolic link.
16901680/// The return value is a slice of out_buffer.
1691pub fn readLink(file_path: []const u8, out_buffer: *[posix.PATH_MAX]u8) ![]u8 {
1692 var path_with_null: [posix.PATH_MAX]u8 = undefined;
1693 if (file_path.len >= posix.PATH_MAX) return error.NameTooLong;
1694 mem.copy(u8, path_with_null[0..], file_path);
1695 path_with_null[file_path.len] = 0;
1696 return readLinkC(&path_with_null, out_buffer);
1681pub fn readLink(out_buffer: *[posix.PATH_MAX]u8, file_path: []const u8) ![]u8 {
1682 const file_path_c = try toPosixPath(file_path);
1683 return readLinkC(out_buffer, &file_path_c);
16971684}
16981685
16991686pub fn posix_setuid(uid: u32) !void {
......@@ -2080,17 +2067,12 @@ pub fn unexpectedErrorWindows(err: windows.DWORD) UnexpectedError {
20802067
20812068pub fn openSelfExe() !os.File {
20822069 switch (builtin.os) {
2083 Os.linux => {
2084 const proc_file_path = "/proc/self/exe";
2085 var fixed_buffer_mem: [proc_file_path.len + 1]u8 = undefined;
2086 var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
2087 return os.File.openRead(&fixed_allocator.allocator, proc_file_path);
2088 },
2070 Os.linux => return os.File.openReadC(c"/proc/self/exe"),
20892071 Os.macosx, Os.ios => {
2090 var fixed_buffer_mem: [darwin.PATH_MAX * 2]u8 = undefined;
2091 var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
2092 const self_exe_path = try selfExePath(&fixed_allocator.allocator);
2093 return os.File.openRead(&fixed_allocator.allocator, self_exe_path);
2072 var buf: [MAX_PATH_BYTES]u8 = undefined;
2073 const self_exe_path = try selfExePath(&buf);
2074 buf[self_exe_path.len] = 0;
2075 return os.File.openReadC(self_exe_path.ptr);
20942076 },
20952077 else => @compileError("Unsupported OS"),
20962078 }
......@@ -2099,7 +2081,7 @@ pub fn openSelfExe() !os.File {
20992081test "openSelfExe" {
21002082 switch (builtin.os) {
21012083 Os.linux, Os.macosx, Os.ios => (try openSelfExe()).close(),
2102 else => return, // Unsupported OS.
2084 else => return error.SkipZigTest, // Unsupported OS
21032085 }
21042086}
21052087
......@@ -2108,69 +2090,67 @@ test "openSelfExe" {
21082090/// If you only want an open file handle, use openSelfExe.
21092091/// This function may return an error if the current executable
21102092/// was deleted after spawning.
2111/// Caller owns returned memory.
2112pub fn selfExePath(allocator: *mem.Allocator) ![]u8 {
2093/// Returned value is a slice of out_buffer.
2094///
2095/// On Linux, depends on procfs being mounted. If the currently executing binary has
2096/// been deleted, the file path looks something like `/a/b/c/exe (deleted)`.
2097pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
21132098 switch (builtin.os) {
2114 Os.linux => {
2115 // If the currently executing binary has been deleted,
2116 // the file path looks something like `/a/b/c/exe (deleted)`
2117 return readLink(allocator, "/proc/self/exe");
2118 },
2099 Os.linux => return readLink(out_buffer, "/proc/self/exe"),
21192100 Os.windows => {
2120 var out_path = try Buffer.initSize(allocator, 0xff);
2121 errdefer out_path.deinit();
2122 while (true) {
2123 const dword_len = try math.cast(windows.DWORD, out_path.len());
2124 const copied_amt = windows.GetModuleFileNameA(null, out_path.ptr(), dword_len);
2125 if (copied_amt <= 0) {
2126 const err = windows.GetLastError();
2127 return switch (err) {
2128 else => unexpectedErrorWindows(err),
2129 };
2130 }
2131 if (copied_amt < out_path.len()) {
2132 out_path.shrink(copied_amt);
2133 return out_path.toOwnedSlice();
2101 var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined;
2102 const casted_len = @intCast(windows.DWORD, utf16le_buf.len); // TODO shouldn't need this cast
2103 const rc = windows.GetModuleFileNameW(null, &utf16le_buf, casted_len);
2104 assert(rc <= utf16le_buf.len);
2105 if (rc == 0) {
2106 const err = windows.GetLastError();
2107 switch (err) {
2108 else => return unexpectedErrorWindows(err),
21342109 }
2135 const new_len = (out_path.len() << 1) | 0b1;
2136 try out_path.resize(new_len);
21372110 }
2111 const utf16le_slice = utf16le_buf[0..rc];
2112 // Trust that Windows gives us valid UTF-16LE.
2113 const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice) catch unreachable;
2114 return out_buffer[0..end_index];
21382115 },
21392116 Os.macosx, Os.ios => {
2140 var u32_len: u32 = 0;
2141 const ret1 = c._NSGetExecutablePath(undefined, &u32_len);
2142 assert(ret1 != 0);
2143 const bytes = try allocator.alloc(u8, u32_len);
2144 errdefer allocator.free(bytes);
2145 const ret2 = c._NSGetExecutablePath(bytes.ptr, &u32_len);
2146 assert(ret2 == 0);
2147 return bytes;
2117 var u32_len: u32 = @intCast(u32, out_buffer.len); // TODO shouldn't need this cast
2118 const rc = c._NSGetExecutablePath(out_buffer, &u32_len);
2119 if (rc != 0) return error.NameTooLong;
2120 return out_buffer[0..u32_len];
21482121 },
21492122 else => @compileError("Unsupported OS"),
21502123 }
21512124}
21522125
2153/// Get the directory path that contains the current executable.
2126/// `selfExeDirPath` except allocates the result on the heap.
21542127/// Caller owns returned memory.
2155pub fn selfExeDirPath(allocator: *mem.Allocator) ![]u8 {
2128pub fn selfExeDirPathAlloc(allocator: *Allocator) ![]u8 {
2129 var buf: [MAX_PATH_BYTES]u8 = undefined;
2130 return mem.dupe(allocator, u8, try selfExeDirPath(&buf));
2131}
2132
2133/// Get the directory path that contains the current executable.
2134/// Returned value is a slice of out_buffer.
2135pub fn selfExeDirPath(out_buffer: *[MAX_PATH_BYTES]u8) ![]const u8 {
21562136 switch (builtin.os) {
21572137 Os.linux => {
21582138 // If the currently executing binary has been deleted,
21592139 // the file path looks something like `/a/b/c/exe (deleted)`
21602140 // This path cannot be opened, but it's valid for determining the directory
21612141 // the executable was in when it was run.
2162 const full_exe_path = try readLink(allocator, "/proc/self/exe");
2163 errdefer allocator.free(full_exe_path);
2164 const dir = path.dirname(full_exe_path) orelse ".";
2165 return allocator.shrink(u8, full_exe_path, dir.len);
2142 const full_exe_path = try readLinkC(out_buffer, c"/proc/self/exe");
2143 // Assume that /proc/self/exe has an absolute path, and therefore dirname
2144 // will not return null.
2145 return path.dirname(full_exe_path).?;
21662146 },
21672147 Os.windows, Os.macosx, Os.ios => {
2168 const self_exe_path = try selfExePath(allocator);
2169 errdefer allocator.free(self_exe_path);
2170 const dirname = os.path.dirname(self_exe_path) orelse ".";
2171 return allocator.shrink(u8, self_exe_path, dirname.len);
2148 const self_exe_path = try selfExePath(out_buffer);
2149 // Assume that the OS APIs return absolute paths, and therefore dirname
2150 // will not return null.
2151 return path.dirname(self_exe_path).?;
21722152 },
2173 else => @compileError("unimplemented: std.os.selfExeDirPath for " ++ @tagName(builtin.os)),
2153 else => @compileError("Unsupported OS"),
21742154 }
21752155}
21762156
......@@ -2991,7 +2971,9 @@ pub fn posixFStat(fd: i32) !posix.Stat {
29912971 const err = posix.getErrno(posix.fstat(fd, &stat));
29922972 if (err > 0) {
29932973 return switch (err) {
2994 posix.EBADF => error.BadFd,
2974 // We do not make this an error code because if you get EBADF it's always a bug,
2975 // since the fd could have been reused.
2976 posix.EBADF => unreachable,
29952977 posix.ENOMEM => error.SystemResources,
29962978 else => os.unexpectedErrorPosix(err),
29972979 };
std/os/path.zig+127-91
......@@ -11,6 +11,7 @@ const math = std.math;
1111const posix = os.posix;
1212const windows = os.windows;
1313const cstr = std.cstr;
14const windows_util = @import("windows/util.zig");
1415
1516pub const sep_windows = '\\';
1617pub const sep_posix = '/';
......@@ -1075,113 +1076,148 @@ fn testRelativeWindows(from: []const u8, to: []const u8, expected_output: []cons
10751076 assert(mem.eql(u8, result, expected_output));
10761077}
10771078
1078/// Return the canonicalized absolute pathname.
1079/// Expands all symbolic links and resolves references to `.`, `..`, and
1080/// extra `/` characters in ::pathname.
1081/// Caller must deallocate result.
1082/// TODO rename this to realAlloc and provide real with no allocator. See #1392
1083pub fn real(allocator: *Allocator, pathname: []const u8) ![]u8 {
1084 switch (builtin.os) {
1085 Os.windows => {
1086 const pathname_buf = try allocator.alloc(u8, pathname.len + 1);
1087 defer allocator.free(pathname_buf);
1088
1089 mem.copy(u8, pathname_buf, pathname);
1090 pathname_buf[pathname.len] = 0;
1091
1092 const h_file = windows.CreateFileA(pathname_buf.ptr, windows.GENERIC_READ, windows.FILE_SHARE_READ, null, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL, null);
1093 if (h_file == windows.INVALID_HANDLE_VALUE) {
1094 const err = windows.GetLastError();
1095 return switch (err) {
1096 windows.ERROR.FILE_NOT_FOUND => error.FileNotFound,
1097 windows.ERROR.ACCESS_DENIED => error.AccessDenied,
1098 windows.ERROR.FILENAME_EXCED_RANGE => error.NameTooLong,
1099 else => os.unexpectedErrorWindows(err),
1100 };
1101 }
1102 defer os.close(h_file);
1103 var buf = try allocator.alloc(u8, 256);
1104 errdefer allocator.free(buf);
1105 while (true) {
1106 const buf_len = math.cast(windows.DWORD, buf.len) catch return error.NameTooLong;
1107 const result = windows.GetFinalPathNameByHandleA(h_file, buf.ptr, buf_len, windows.VOLUME_NAME_DOS);
1108
1109 if (result == 0) {
1110 const err = windows.GetLastError();
1111 return switch (err) {
1112 windows.ERROR.PATH_NOT_FOUND => error.FileNotFound,
1113 windows.ERROR.NOT_ENOUGH_MEMORY => error.OutOfMemory,
1114 windows.ERROR.INVALID_PARAMETER => unreachable,
1115 else => os.unexpectedErrorWindows(err),
1116 };
1117 }
1079pub const RealError = error{
1080 FileNotFound,
1081 AccessDenied,
1082 NameTooLong,
1083 NotSupported,
1084 NotDir,
1085 SymLinkLoop,
1086 InputOutput,
1087 FileTooBig,
1088 IsDir,
1089 ProcessFdQuotaExceeded,
1090 SystemFdQuotaExceeded,
1091 NoDevice,
1092 SystemResources,
1093 NoSpaceLeft,
1094 FileSystem,
1095 BadPathName,
1096
1097 /// On Windows, file paths must be valid Unicode.
1098 InvalidUtf8,
1099
1100 /// TODO remove this possibility
1101 PathAlreadyExists,
1102
1103 /// TODO remove this possibility
1104 Unexpected,
1105};
11181106
1119 if (result > buf.len) {
1120 buf = try allocator.realloc(u8, buf, result);
1121 continue;
1122 }
1107/// Call from Windows-specific code if you already have a UTF-16LE encoded, null terminated string.
1108/// Otherwise use `real` or `realC`.
1109pub fn realW(out_buffer: *[os.MAX_PATH_BYTES]u8, pathname: [*]const u16) RealError![]u8 {
1110 const h_file = windows.CreateFileW(
1111 pathname,
1112 windows.GENERIC_READ,
1113 windows.FILE_SHARE_READ,
1114 null,
1115 windows.OPEN_EXISTING,
1116 windows.FILE_ATTRIBUTE_NORMAL,
1117 null,
1118 );
1119 if (h_file == windows.INVALID_HANDLE_VALUE) {
1120 const err = windows.GetLastError();
1121 switch (err) {
1122 windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
1123 windows.ERROR.ACCESS_DENIED => return error.AccessDenied,
1124 windows.ERROR.FILENAME_EXCED_RANGE => return error.NameTooLong,
1125 else => return os.unexpectedErrorWindows(err),
1126 }
1127 }
1128 defer os.close(h_file);
1129 var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined;
1130 const casted_len = @intCast(windows.DWORD, utf16le_buf.len); // TODO shouldn't need this cast
1131 const result = windows.GetFinalPathNameByHandleW(h_file, &utf16le_buf, casted_len, windows.VOLUME_NAME_DOS);
1132 assert(result <= utf16le_buf.len);
1133 if (result == 0) {
1134 const err = windows.GetLastError();
1135 switch (err) {
1136 windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
1137 windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
1138 windows.ERROR.NOT_ENOUGH_MEMORY => return error.SystemResources,
1139 windows.ERROR.FILENAME_EXCED_RANGE => return error.NameTooLong,
1140 windows.ERROR.INVALID_PARAMETER => unreachable,
1141 else => return os.unexpectedErrorWindows(err),
1142 }
1143 }
1144 const utf16le_slice = utf16le_buf[0..result];
11231145
1124 // windows returns \\?\ prepended to the path
1125 // we strip it because nobody wants \\?\ prepended to their path
1126 const final_len = x: {
1127 if (result > 4 and mem.startsWith(u8, buf, "\\\\?\\")) {
1128 var i: usize = 4;
1129 while (i < result) : (i += 1) {
1130 buf[i - 4] = buf[i];
1131 }
1132 break :x result - 4;
1133 } else {
1134 break :x result;
1135 }
1136 };
1137
1138 return allocator.shrink(u8, buf, final_len);
1139 }
1146 // windows returns \\?\ prepended to the path
1147 // we strip it because nobody wants \\?\ prepended to their path
1148 const prefix = []u16{ '\\', '\\', '?', '\\' };
1149 const start_index = if (mem.startsWith(u16, utf16le_slice, prefix)) prefix.len else 0;
1150
1151 // Trust that Windows gives us valid UTF-16LE.
1152 const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice[start_index..]) catch unreachable;
1153 return out_buffer[0..end_index];
1154}
1155
1156/// See `real`
1157/// Use this when you have a null terminated pointer path.
1158pub fn realC(out_buffer: *[os.MAX_PATH_BYTES]u8, pathname: [*]const u8) RealError![]u8 {
1159 switch (builtin.os) {
1160 Os.windows => {
1161 const pathname_w = try windows_util.cStrToPrefixedFileW(pathname);
1162 return realW(out_buffer, pathname_w);
11401163 },
11411164 Os.macosx, Os.ios => {
1142 // TODO instead of calling the libc function here, port the implementation
1143 // to Zig, and then remove the NameTooLong error possibility.
1144 const pathname_buf = try allocator.alloc(u8, pathname.len + 1);
1145 defer allocator.free(pathname_buf);
1146
1147 const result_buf = try allocator.alloc(u8, posix.PATH_MAX);
1148 errdefer allocator.free(result_buf);
1149
1150 mem.copy(u8, pathname_buf, pathname);
1151 pathname_buf[pathname.len] = 0;
1152
1153 const err = posix.getErrno(posix.realpath(pathname_buf.ptr, result_buf.ptr));
1154 if (err > 0) {
1155 return switch (err) {
1156 posix.EINVAL => unreachable,
1157 posix.EBADF => unreachable,
1158 posix.EFAULT => unreachable,
1159 posix.EACCES => error.AccessDenied,
1160 posix.ENOENT => error.FileNotFound,
1161 posix.ENOTSUP => error.NotSupported,
1162 posix.ENOTDIR => error.NotDir,
1163 posix.ENAMETOOLONG => error.NameTooLong,
1164 posix.ELOOP => error.SymLinkLoop,
1165 posix.EIO => error.InputOutput,
1166 else => os.unexpectedErrorPosix(err),
1167 };
1165 // TODO instead of calling the libc function here, port the implementation to Zig
1166 const err = posix.getErrno(posix.realpath(pathname, out_buffer));
1167 switch (err) {
1168 0 => return mem.toSlice(u8, out_buffer),
1169 posix.EINVAL => unreachable,
1170 posix.EBADF => unreachable,
1171 posix.EFAULT => unreachable,
1172 posix.EACCES => return error.AccessDenied,
1173 posix.ENOENT => return error.FileNotFound,
1174 posix.ENOTSUP => return error.NotSupported,
1175 posix.ENOTDIR => return error.NotDir,
1176 posix.ENAMETOOLONG => return error.NameTooLong,
1177 posix.ELOOP => return error.SymLinkLoop,
1178 posix.EIO => return error.InputOutput,
1179 else => return os.unexpectedErrorPosix(err),
11681180 }
1169 return allocator.shrink(u8, result_buf, cstr.len(result_buf.ptr));
11701181 },
11711182 Os.linux => {
1172 const fd = try os.posixOpen(allocator, pathname, posix.O_PATH | posix.O_NONBLOCK | posix.O_CLOEXEC, 0);
1183 const fd = try os.posixOpenC(pathname, posix.O_PATH | posix.O_NONBLOCK | posix.O_CLOEXEC, 0);
11731184 defer os.close(fd);
11741185
11751186 var buf: ["/proc/self/fd/-2147483648".len]u8 = undefined;
1176 const proc_path = fmt.bufPrint(buf[0..], "/proc/self/fd/{}", fd) catch unreachable;
1187 const proc_path = fmt.bufPrint(buf[0..], "/proc/self/fd/{}\x00", fd) catch unreachable;
11771188
1178 return os.readLink(allocator, proc_path);
1189 return os.readLinkC(out_buffer, proc_path.ptr);
11791190 },
11801191 else => @compileError("TODO implement os.path.real for " ++ @tagName(builtin.os)),
11811192 }
11821193}
11831194
1195/// Return the canonicalized absolute pathname.
1196/// Expands all symbolic links and resolves references to `.`, `..`, and
1197/// extra `/` characters in ::pathname.
1198/// The return value is a slice of out_buffer, and not necessarily from the beginning.
1199pub fn real(out_buffer: *[os.MAX_PATH_BYTES]u8, pathname: []const u8) RealError![]u8 {
1200 switch (builtin.os) {
1201 Os.windows => {
1202 const pathname_w = try windows_util.sliceToPrefixedFileW(pathname);
1203 return realW(out_buffer, &pathname_w);
1204 },
1205 Os.macosx, Os.ios, Os.linux => {
1206 const pathname_c = try os.toPosixPath(pathname);
1207 return realC(out_buffer, &pathname_c);
1208 },
1209 else => @compileError("Unsupported OS"),
1210 }
1211}
1212
1213/// `real`, except caller must free the returned memory.
1214pub fn realAlloc(allocator: *Allocator, pathname: []const u8) ![]u8 {
1215 var buf: [os.MAX_PATH_BYTES]u8 = undefined;
1216 return mem.dupe(allocator, u8, try real(&buf, pathname));
1217}
1218
11841219test "os.path.real" {
11851220 // at least call it so it gets compiled
1186 _ = real(debug.global_allocator, "some_path");
1221 var buf: [os.MAX_PATH_BYTES]u8 = undefined;
1222 std.debug.assertError(real(&buf, "definitely_bogus_does_not_exist1234"), error.FileNotFound);
11871223}
std/os/test.zig+1-1
......@@ -17,7 +17,7 @@ test "makePath, put some files in it, deleteTree" {
1717 if (os.Dir.open(a, "os_test_tmp")) |dir| {
1818 @panic("expected error");
1919 } else |err| {
20 assert(err == error.PathNotFound);
20 assert(err == error.FileNotFound);
2121 }
2222}
2323
std/os/windows/kernel32.zig+11-3
......@@ -4,8 +4,8 @@ pub extern "kernel32" stdcallcc fn CancelIoEx(hFile: HANDLE, lpOverlapped: LPOVE
44
55pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL;
66
7pub extern "kernel32" stdcallcc fn CreateDirectoryA( lpPathName: [*]const u8, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) BOOL;
8pub extern "kernel32" stdcallcc fn CreateDirectoryW( lpPathName: [*]const u16, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) BOOL;
7pub extern "kernel32" stdcallcc fn CreateDirectoryA(lpPathName: [*]const u8, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) BOOL;
8pub extern "kernel32" stdcallcc fn CreateDirectoryW(lpPathName: [*]const u16, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) BOOL;
99
1010pub extern "kernel32" stdcallcc fn CreateFileA(
1111 lpFileName: [*]const u8, // TODO null terminated pointer type
......@@ -89,7 +89,8 @@ pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LAR
8989pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: [*]const CHAR) DWORD;
9090pub extern "kernel32" stdcallcc fn GetFileAttributesW(lpFileName: [*]const WCHAR) DWORD;
9191
92pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: LPSTR, nSize: DWORD) DWORD;
92pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: [*]u8, nSize: DWORD) DWORD;
93pub extern "kernel32" stdcallcc fn GetModuleFileNameW(hModule: ?HMODULE, lpFilename: [*]u16, nSize: DWORD) DWORD;
9394
9495pub extern "kernel32" stdcallcc fn GetLastError() DWORD;
9596
......@@ -107,6 +108,13 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(
107108 dwFlags: DWORD,
108109) DWORD;
109110
111pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleW(
112 hFile: HANDLE,
113 lpszFilePath: [*]u16,
114 cchFilePath: DWORD,
115 dwFlags: DWORD,
116) DWORD;
117
110118pub extern "kernel32" stdcallcc fn GetOverlappedResult(hFile: HANDLE, lpOverlapped: *OVERLAPPED, lpNumberOfBytesTransferred: *DWORD, bWait: BOOL) BOOL;
111119
112120pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;
std/os/windows/util.zig+13-13
......@@ -97,12 +97,12 @@ pub const OpenError = error{
9797 SharingViolation,
9898 PathAlreadyExists,
9999
100 /// When all the path components are found but the file component is not.
100 /// When any of the path components can not be found or the file component can not
101 /// be found. Some operating systems distinguish between path components not found and
102 /// file components not found, but they are collapsed into FileNotFound to gain
103 /// consistency across operating systems.
101104 FileNotFound,
102105
103 /// When one or more path components are not found.
104 PathNotFound,
105
106106 AccessDenied,
107107 PipeBusy,
108108 NameTooLong,
......@@ -136,7 +136,7 @@ pub fn windowsOpen(
136136 windows.ERROR.ALREADY_EXISTS => return OpenError.PathAlreadyExists,
137137 windows.ERROR.FILE_EXISTS => return OpenError.PathAlreadyExists,
138138 windows.ERROR.FILE_NOT_FOUND => return OpenError.FileNotFound,
139 windows.ERROR.PATH_NOT_FOUND => return OpenError.PathNotFound,
139 windows.ERROR.PATH_NOT_FOUND => return OpenError.FileNotFound,
140140 windows.ERROR.ACCESS_DENIED => return OpenError.AccessDenied,
141141 windows.ERROR.PIPE_BUSY => return OpenError.PipeBusy,
142142 else => return os.unexpectedErrorWindows(err),
......@@ -216,9 +216,8 @@ pub fn windowsFindFirstFile(
216216 if (handle == windows.INVALID_HANDLE_VALUE) {
217217 const err = windows.GetLastError();
218218 switch (err) {
219 windows.ERROR.FILE_NOT_FOUND,
220 windows.ERROR.PATH_NOT_FOUND,
221 => return error.PathNotFound,
219 windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
220 windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
222221 else => return os.unexpectedErrorWindows(err),
223222 }
224223 }
......@@ -284,13 +283,13 @@ pub fn windowsGetQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_t
284283 return WindowsWaitResult.Normal;
285284}
286285
287pub fn cStrToPrefixedFileW(s: [*]const u8) ![PATH_MAX_WIDE+1]u16 {
286pub fn cStrToPrefixedFileW(s: [*]const u8) ![PATH_MAX_WIDE + 1]u16 {
288287 return sliceToPrefixedFileW(mem.toSliceConst(u8, s));
289288}
290289
291pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE+1]u16 {
290pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 {
292291 // TODO well defined copy elision
293 var result: [PATH_MAX_WIDE+1]u16 = undefined;
292 var result: [PATH_MAX_WIDE + 1]u16 = undefined;
294293
295294 // > File I/O functions in the Windows API convert "/" to "\" as part of
296295 // > converting the name to an NT-style name, except when using the "\\?\"
......@@ -298,12 +297,13 @@ pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE+1]u16 {
298297 // from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation
299298 // Because we want the larger maximum path length for absolute paths, we
300299 // disallow forward slashes in zig std lib file functions on Windows.
301 for (s) |byte| switch (byte) {
300 for (s) |byte|
301 switch (byte) {
302302 '/', '*', '?', '"', '<', '>', '|' => return error.BadPathName,
303303 else => {},
304304 };
305305 const start_index = if (mem.startsWith(u8, s, "\\\\") or !os.path.isAbsolute(s)) 0 else blk: {
306 const prefix = []u16{'\\', '\\', '?', '\\'};
306 const prefix = []u16{ '\\', '\\', '?', '\\' };
307307 mem.copy(u16, result[0..], prefix);
308308 break :blk prefix.len;
309309 };
std/unicode.zig+1-1
......@@ -495,7 +495,7 @@ pub fn utf16leToUtf8Alloc(allocator: *mem.Allocator, utf16le: []const u16) ![]u8
495495}
496496
497497/// Asserts that the output buffer is big enough.
498/// Returns end index.
498/// Returns end byte index into utf8.
499499pub fn utf16leToUtf8(utf8: []u8, utf16le: []const u16) !usize {
500500 var end_index: usize = 0;
501501 var it = Utf16LeIterator.init(utf16le);
test/cases/merge_error_sets.zig+2-2
......@@ -1,5 +1,5 @@
11const A = error{
2 PathNotFound,
2 FileNotFound,
33 NotDir,
44};
55const B = error{OutOfMemory};
......@@ -15,7 +15,7 @@ test "merge error sets" {
1515 @panic("unexpected");
1616 } else |err| switch (err) {
1717 error.OutOfMemory => @panic("unexpected"),
18 error.PathNotFound => @panic("unexpected"),
18 error.FileNotFound => @panic("unexpected"),
1919 error.NotDir => {},
2020 }
2121}