authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-03-18 16:42:58-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-03-18 16:42:58-04:00
log7a361751e563c131399050f339dc47edf3c32325
tree5141e62de6134816b62c7ff1399dcdc651f63b11
parentb1537b525fa0cd8d51ff89519254db0f066fc04b
parent46ffc798b6d9bb7be8b016ef7529092d647151ee
signaturelock-open Commit is signed but in an unrecognized format.

Merge branch 'LemonBoy-travbug'

closes #4747

20 files changed, 157 insertions(+), 138 deletions(-)

doc/docgen.zig+1-1
...@@ -48,7 +48,7 @@ pub fn main() !void {...@@ -48,7 +48,7 @@ pub fn main() !void {
48 var toc = try genToc(allocator, &tokenizer);48 var toc = try genToc(allocator, &tokenizer);
4949
50 try fs.cwd().makePath(tmp_dir_name);50 try fs.cwd().makePath(tmp_dir_name);
51 defer fs.deleteTree(tmp_dir_name) catch {};51 defer fs.cwd().deleteTree(tmp_dir_name) catch {};
5252
53 try genHtml(allocator, &tokenizer, &toc, buffered_out_stream.outStream(), zig_exe);53 try genHtml(allocator, &tokenizer, &toc, buffered_out_stream.outStream(), zig_exe);
54 try buffered_out_stream.flush();54 try buffered_out_stream.flush();
lib/std/build.zig+4-4
...@@ -377,7 +377,7 @@ pub const Builder = struct {...@@ -377,7 +377,7 @@ pub const Builder = struct {
377 if (self.verbose) {377 if (self.verbose) {
378 warn("rm {}\n", .{full_path});378 warn("rm {}\n", .{full_path});
379 }379 }
380 fs.deleteTree(full_path) catch {};380 fs.cwd().deleteTree(full_path) catch {};
381 }381 }
382382
383 // TODO remove empty directories383 // TODO remove empty directories
...@@ -2156,10 +2156,10 @@ pub const LibExeObjStep = struct {...@@ -2156,10 +2156,10 @@ pub const LibExeObjStep = struct {
2156 const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n");2156 const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n");
21572157
2158 if (self.output_dir) |output_dir| {2158 if (self.output_dir) |output_dir| {
2159 var src_dir = try std.fs.cwd().openDirTraverse(build_output_dir);2159 var src_dir = try std.fs.cwd().openDir(build_output_dir, .{ .iterate = true });
2160 defer src_dir.close();2160 defer src_dir.close();
21612161
2162 var dest_dir = try std.fs.cwd().openDirList(output_dir);2162 var dest_dir = try std.fs.cwd().openDir(output_dir, .{});
2163 defer dest_dir.close();2163 defer dest_dir.close();
21642164
2165 var it = src_dir.iterate();2165 var it = src_dir.iterate();
...@@ -2365,7 +2365,7 @@ pub const RemoveDirStep = struct {...@@ -2365,7 +2365,7 @@ pub const RemoveDirStep = struct {
2365 const self = @fieldParentPtr(RemoveDirStep, "step", step);2365 const self = @fieldParentPtr(RemoveDirStep, "step", step);
23662366
2367 const full_path = self.builder.pathFromRoot(self.dir_path);2367 const full_path = self.builder.pathFromRoot(self.dir_path);
2368 fs.deleteTree(full_path) catch |err| {2368 fs.cwd().deleteTree(full_path) catch |err| {
2369 warn("Unable to remove {}: {}\n", .{ full_path, @errorName(err) });2369 warn("Unable to remove {}: {}\n", .{ full_path, @errorName(err) });
2370 return err;2370 return err;
2371 };2371 };
lib/std/build/write_file.zig+1-1
...@@ -78,7 +78,7 @@ pub const WriteFileStep = struct {...@@ -78,7 +78,7 @@ pub const WriteFileStep = struct {
78 warn("unable to make path {}: {}\n", .{ self.output_dir, @errorName(err) });78 warn("unable to make path {}: {}\n", .{ self.output_dir, @errorName(err) });
79 return err;79 return err;
80 };80 };
81 var dir = try fs.cwd().openDirTraverse(self.output_dir);81 var dir = try fs.cwd().openDir(self.output_dir, .{});
82 defer dir.close();82 defer dir.close();
83 for (self.files.toSliceConst()) |file| {83 for (self.files.toSliceConst()) |file| {
84 dir.writeFile(file.basename, file.bytes) catch |err| {84 dir.writeFile(file.basename, file.bytes) catch |err| {
lib/std/fs.zig+64-111
...@@ -261,28 +261,6 @@ pub fn deleteDirW(dir_path: [*:0]const u16) !void {...@@ -261,28 +261,6 @@ pub fn deleteDirW(dir_path: [*:0]const u16) !void {
261 return os.rmdirW(dir_path);261 return os.rmdirW(dir_path);
262}262}
263263
264/// Removes a symlink, file, or directory.
265/// If `full_path` is relative, this is equivalent to `Dir.deleteTree` with the
266/// current working directory as the open directory handle.
267/// If `full_path` is absolute, this is equivalent to `Dir.deleteTree` with the
268/// base directory.
269pub fn deleteTree(full_path: []const u8) !void {
270 if (path.isAbsolute(full_path)) {
271 const dirname = path.dirname(full_path) orelse return error{
272 /// Attempt to remove the root file system path.
273 /// This error is unreachable if `full_path` is relative.
274 CannotDeleteRootDirectory,
275 }.CannotDeleteRootDirectory;
276
277 var dir = try cwd().openDirList(dirname);
278 defer dir.close();
279
280 return dir.deleteTree(path.basename(full_path));
281 } else {
282 return cwd().deleteTree(full_path);
283 }
284}
285
286pub const Dir = struct {264pub const Dir = struct {
287 fd: os.fd_t,265 fd: os.fd_t,
288266
...@@ -339,7 +317,7 @@ pub const Dir = struct {...@@ -339,7 +317,7 @@ pub const Dir = struct {
339 if (rc == 0) return null;317 if (rc == 0) return null;
340 if (rc < 0) {318 if (rc < 0) {
341 switch (os.errno(rc)) {319 switch (os.errno(rc)) {
342 os.EBADF => unreachable,320 os.EBADF => unreachable, // Dir is invalid or was opened without iteration ability
343 os.EFAULT => unreachable,321 os.EFAULT => unreachable,
344 os.ENOTDIR => unreachable,322 os.ENOTDIR => unreachable,
345 os.EINVAL => unreachable,323 os.EINVAL => unreachable,
...@@ -388,7 +366,7 @@ pub const Dir = struct {...@@ -388,7 +366,7 @@ pub const Dir = struct {
388 );366 );
389 switch (os.errno(rc)) {367 switch (os.errno(rc)) {
390 0 => {},368 0 => {},
391 os.EBADF => unreachable,369 os.EBADF => unreachable, // Dir is invalid or was opened without iteration ability
392 os.EFAULT => unreachable,370 os.EFAULT => unreachable,
393 os.ENOTDIR => unreachable,371 os.ENOTDIR => unreachable,
394 os.EINVAL => unreachable,372 os.EINVAL => unreachable,
...@@ -444,7 +422,7 @@ pub const Dir = struct {...@@ -444,7 +422,7 @@ pub const Dir = struct {
444 const rc = os.linux.getdents64(self.dir.fd, &self.buf, self.buf.len);422 const rc = os.linux.getdents64(self.dir.fd, &self.buf, self.buf.len);
445 switch (os.linux.getErrno(rc)) {423 switch (os.linux.getErrno(rc)) {
446 0 => {},424 0 => {},
447 os.EBADF => unreachable,425 os.EBADF => unreachable, // Dir is invalid or was opened without iteration ability
448 os.EFAULT => unreachable,426 os.EFAULT => unreachable,
449 os.ENOTDIR => unreachable,427 os.ENOTDIR => unreachable,
450 os.EINVAL => unreachable,428 os.EINVAL => unreachable,
...@@ -518,7 +496,8 @@ pub const Dir = struct {...@@ -518,7 +496,8 @@ pub const Dir = struct {
518 self.end_index = io.Information;496 self.end_index = io.Information;
519 switch (rc) {497 switch (rc) {
520 .SUCCESS => {},498 .SUCCESS => {},
521 .ACCESS_DENIED => return error.AccessDenied,499 .ACCESS_DENIED => return error.AccessDenied, // Double-check that the Dir was opened with iteration ability
500
522 else => return w.unexpectedStatus(rc),501 else => return w.unexpectedStatus(rc),
523 }502 }
524 }503 }
...@@ -596,16 +575,6 @@ pub const Dir = struct {...@@ -596,16 +575,6 @@ pub const Dir = struct {
596 DeviceBusy,575 DeviceBusy,
597 } || os.UnexpectedError;576 } || os.UnexpectedError;
598577
599 /// Deprecated; call `cwd().openDirList` directly.
600 pub fn open(dir_path: []const u8) OpenError!Dir {
601 return cwd().openDirList(dir_path);
602 }
603
604 /// Deprecated; call `cwd().openDirListC` directly.
605 pub fn openC(dir_path_c: [*:0]const u8) OpenError!Dir {
606 return cwd().openDirListC(dir_path_c);
607 }
608
609 pub fn close(self: *Dir) void {578 pub fn close(self: *Dir) void {
610 if (need_async_thread) {579 if (need_async_thread) {
611 std.event.Loop.instance.?.close(self.fd);580 std.event.Loop.instance.?.close(self.fd);
...@@ -792,79 +761,61 @@ pub const Dir = struct {...@@ -792,79 +761,61 @@ pub const Dir = struct {
792 try os.fchdir(self.fd);761 try os.fchdir(self.fd);
793 }762 }
794763
795 /// Deprecated; call `openDirList` directly.764 pub const OpenDirOptions = struct {
796 pub fn openDir(self: Dir, sub_path: []const u8) OpenError!Dir {765 /// `true` means the opened directory can be used as the `Dir` parameter
797 return self.openDirList(sub_path);766 /// for functions which operate based on an open directory handle. When `false`,
798 }767 /// such operations are Illegal Behavior.
799768 access_sub_paths: bool = true,
800 /// Deprecated; call `openDirListC` directly.
801 pub fn openDirC(self: Dir, sub_path_c: [*:0]const u8) OpenError!Dir {
802 return self.openDirListC(sub_path_c);
803 }
804
805 /// Opens a directory at the given path with the ability to access subpaths
806 /// of the result. Calling `iterate` on the result is illegal behavior; to
807 /// list the contents of a directory, open it with `openDirList`.
808 ///
809 /// Call `close` on the result when done.
810 ///
811 /// Asserts that the path parameter has no null bytes.
812 /// TODO collapse this and `openDirList` into one function with an options parameter
813 pub fn openDirTraverse(self: Dir, sub_path: []const u8) OpenError!Dir {
814 if (builtin.os.tag == .windows) {
815 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
816 return self.openDirTraverseW(&sub_path_w);
817 }
818769
819 const sub_path_c = try os.toPosixPath(sub_path);770 /// `true` means the opened directory can be scanned for the files and sub-directories
820 return self.openDirTraverseC(&sub_path_c);771 /// of the result. It means the `iterate` function can be called.
821 }772 iterate: bool = false,
773 };
822774
823 /// Opens a directory at the given path with the ability to access subpaths and list contents775 /// Opens a directory at the given path. The directory is a system resource that remains
824 /// of the result. If the ability to list contents is unneeded, `openDirTraverse` acts the776 /// open until `close` is called on the result.
825 /// same and may be more efficient.
826 ///
827 /// Call `close` on the result when done.
828 ///777 ///
829 /// Asserts that the path parameter has no null bytes.778 /// Asserts that the path parameter has no null bytes.
830 /// TODO collapse this and `openDirTraverse` into one function with an options parameter779 pub fn openDir(self: Dir, sub_path: []const u8, args: OpenDirOptions) OpenError!Dir {
831 pub fn openDirList(self: Dir, sub_path: []const u8) OpenError!Dir {
832 if (builtin.os.tag == .windows) {780 if (builtin.os.tag == .windows) {
833 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);781 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
834 return self.openDirListW(&sub_path_w);782 return self.openDirW(&sub_path_w, args);
783 } else {
784 const sub_path_c = try os.toPosixPath(sub_path);
785 return self.openDirC(&sub_path_c, args);
835 }786 }
836
837 const sub_path_c = try os.toPosixPath(sub_path);
838 return self.openDirListC(&sub_path_c);
839 }787 }
840788
841 /// Same as `openDirTraverse` except the parameter is null-terminated.789 /// Same as `openDir` except the parameter is null-terminated.
842 pub fn openDirTraverseC(self: Dir, sub_path_c: [*:0]const u8) OpenError!Dir {790 pub fn openDirC(self: Dir, sub_path_c: [*:0]const u8, args: OpenDirOptions) OpenError!Dir {
843 if (builtin.os.tag == .windows) {791 if (builtin.os.tag == .windows) {
844 const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c);792 const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c);
845 return self.openDirTraverseW(&sub_path_w);793 return self.openDirW(&sub_path_w, args);
846 } else {794 } else if (!args.iterate) {
847 const O_PATH = if (@hasDecl(os, "O_PATH")) os.O_PATH else 0;795 const O_PATH = if (@hasDecl(os, "O_PATH")) os.O_PATH else 0;
848 return self.openDirFlagsC(sub_path_c, os.O_RDONLY | os.O_CLOEXEC | O_PATH);796 return self.openDirFlagsC(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | os.O_CLOEXEC | O_PATH);
797 } else {
798 return self.openDirFlagsC(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | os.O_CLOEXEC);
849 }799 }
850 }800 }
851801
852 /// Same as `openDirList` except the parameter is null-terminated.802 /// Same as `openDir` except the path parameter is WTF-16 encoded, NT-prefixed.
853 pub fn openDirListC(self: Dir, sub_path_c: [*:0]const u8) OpenError!Dir {803 /// This function asserts the target OS is Windows.
854 if (builtin.os.tag == .windows) {804 pub fn openDirW(self: Dir, sub_path_w: [*:0]const u16, args: OpenDirOptions) OpenError!Dir {
855 const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c);805 const w = os.windows;
856 return self.openDirListW(&sub_path_w);806 // TODO remove some of these flags if args.access_sub_paths is false
857 } else {807 const base_flags = w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA |
858 return self.openDirFlagsC(sub_path_c, os.O_RDONLY | os.O_CLOEXEC);808 w.SYNCHRONIZE | w.FILE_TRAVERSE;
859 }809 const flags: u32 = if (args.iterate) base_flags | w.FILE_LIST_DIRECTORY else base_flags;
810 return self.openDirAccessMaskW(sub_path_w, flags);
860 }811 }
861812
813 /// `flags` must contain `os.O_DIRECTORY`.
862 fn openDirFlagsC(self: Dir, sub_path_c: [*:0]const u8, flags: u32) OpenError!Dir {814 fn openDirFlagsC(self: Dir, sub_path_c: [*:0]const u8, flags: u32) OpenError!Dir {
863 const os_flags = flags | os.O_DIRECTORY;
864 const result = if (need_async_thread)815 const result = if (need_async_thread)
865 std.event.Loop.instance.?.openatZ(self.fd, sub_path_c, os_flags, 0)816 std.event.Loop.instance.?.openatZ(self.fd, sub_path_c, flags, 0)
866 else817 else
867 os.openatC(self.fd, sub_path_c, os_flags, 0);818 os.openatC(self.fd, sub_path_c, flags, 0);
868 const fd = result catch |err| switch (err) {819 const fd = result catch |err| switch (err) {
869 error.FileTooBig => unreachable, // can't happen for directories820 error.FileTooBig => unreachable, // can't happen for directories
870 error.IsDir => unreachable, // we're providing O_DIRECTORY821 error.IsDir => unreachable, // we're providing O_DIRECTORY
...@@ -875,22 +826,6 @@ pub const Dir = struct {...@@ -875,22 +826,6 @@ pub const Dir = struct {
875 return Dir{ .fd = fd };826 return Dir{ .fd = fd };
876 }827 }
877828
878 /// Same as `openDirTraverse` except the path parameter is UTF16LE, NT-prefixed.
879 /// This function is Windows-only.
880 pub fn openDirTraverseW(self: Dir, sub_path_w: [*:0]const u16) OpenError!Dir {
881 const w = os.windows;
882
883 return self.openDirAccessMaskW(sub_path_w, w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA | w.SYNCHRONIZE | w.FILE_TRAVERSE);
884 }
885
886 /// Same as `openDirList` except the path parameter is UTF16LE, NT-prefixed.
887 /// This function is Windows-only.
888 pub fn openDirListW(self: Dir, sub_path_w: [*:0]const u16) OpenError!Dir {
889 const w = os.windows;
890
891 return self.openDirAccessMaskW(sub_path_w, w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA | w.SYNCHRONIZE | w.FILE_TRAVERSE | w.FILE_LIST_DIRECTORY);
892 }
893
894 fn openDirAccessMaskW(self: Dir, sub_path_w: [*:0]const u16, access_mask: u32) OpenError!Dir {829 fn openDirAccessMaskW(self: Dir, sub_path_w: [*:0]const u16, access_mask: u32) OpenError!Dir {
895 const w = os.windows;830 const w = os.windows;
896831
...@@ -1111,7 +1046,7 @@ pub const Dir = struct {...@@ -1111,7 +1046,7 @@ pub const Dir = struct {
1111 error.Unexpected,1046 error.Unexpected,
1112 => |e| return e,1047 => |e| return e,
1113 }1048 }
1114 var dir = self.openDirList(sub_path) catch |err| switch (err) {1049 var dir = self.openDir(sub_path, .{ .iterate = true }) catch |err| switch (err) {
1115 error.NotDir => {1050 error.NotDir => {
1116 if (got_access_denied) {1051 if (got_access_denied) {
1117 return error.AccessDenied;1052 return error.AccessDenied;
...@@ -1144,7 +1079,6 @@ pub const Dir = struct {...@@ -1144,7 +1079,6 @@ pub const Dir = struct {
11441079
1145 var dir_name_buf: [MAX_PATH_BYTES]u8 = undefined;1080 var dir_name_buf: [MAX_PATH_BYTES]u8 = undefined;
1146 var dir_name: []const u8 = sub_path;1081 var dir_name: []const u8 = sub_path;
1147 var parent_dir = self;
11481082
1149 // Here we must avoid recursion, in order to provide O(1) memory guarantee of this function.1083 // Here we must avoid recursion, in order to provide O(1) memory guarantee of this function.
1150 // Go through each entry and if it is not a directory, delete it. If it is a directory,1084 // Go through each entry and if it is not a directory, delete it. If it is a directory,
...@@ -1176,7 +1110,7 @@ pub const Dir = struct {...@@ -1176,7 +1110,7 @@ pub const Dir = struct {
1176 => |e| return e,1110 => |e| return e,
1177 }1111 }
11781112
1179 const new_dir = dir.openDirList(entry.name) catch |err| switch (err) {1113 const new_dir = dir.openDir(entry.name, .{ .iterate = true }) catch |err| switch (err) {
1180 error.NotDir => {1114 error.NotDir => {
1181 if (got_access_denied) {1115 if (got_access_denied) {
1182 return error.AccessDenied;1116 return error.AccessDenied;
...@@ -1349,7 +1283,7 @@ pub const Dir = struct {...@@ -1349,7 +1283,7 @@ pub const Dir = struct {
1349 }1283 }
1350};1284};
13511285
1352/// Returns an handle to the current working directory that is open for traversal.1286/// Returns an handle to the current working directory. It is not opened with iteration capability.
1353/// Closing the returned `Dir` is checked illegal behavior. Iterating over the result is illegal behavior.1287/// Closing the returned `Dir` is checked illegal behavior. Iterating over the result is illegal behavior.
1354/// On POSIX targets, this function is comptime-callable.1288/// On POSIX targets, this function is comptime-callable.
1355pub fn cwd() Dir {1289pub fn cwd() Dir {
...@@ -1427,6 +1361,25 @@ pub fn deleteFileAbsoluteW(absolute_path_w: [*:0]const u16) DeleteFileError!void...@@ -1427,6 +1361,25 @@ pub fn deleteFileAbsoluteW(absolute_path_w: [*:0]const u16) DeleteFileError!void
1427 return cwd().deleteFileW(absolute_path_w);1361 return cwd().deleteFileW(absolute_path_w);
1428}1362}
14291363
1364/// Removes a symlink, file, or directory.
1365/// This is equivalent to `Dir.deleteTree` with the base directory.
1366/// Asserts that the path is absolute. See `Dir.deleteTree` for a function that
1367/// operates on both absolute and relative paths.
1368/// Asserts that the path parameter has no null bytes.
1369pub fn deleteTreeAbsolute(absolute_path: []const u8) !void {
1370 assert(path.isAbsolute(absolute_path));
1371 const dirname = path.dirname(absolute_path) orelse return error{
1372 /// Attempt to remove the root file system path.
1373 /// This error is unreachable if `absolute_path` is relative.
1374 CannotDeleteRootDirectory,
1375 }.CannotDeleteRootDirectory;
1376
1377 var dir = try cwd().openDir(dirname, .{});
1378 defer dir.close();
1379
1380 return dir.deleteTree(path.basename(absolute_path));
1381}
1382
1430pub const Walker = struct {1383pub const Walker = struct {
1431 stack: std.ArrayList(StackItem),1384 stack: std.ArrayList(StackItem),
1432 name_buffer: std.Buffer,1385 name_buffer: std.Buffer,
...@@ -1461,7 +1414,7 @@ pub const Walker = struct {...@@ -1461,7 +1414,7 @@ pub const Walker = struct {
1461 try self.name_buffer.appendByte(path.sep);1414 try self.name_buffer.appendByte(path.sep);
1462 try self.name_buffer.append(base.name);1415 try self.name_buffer.append(base.name);
1463 if (base.kind == .Directory) {1416 if (base.kind == .Directory) {
1464 var new_dir = top.dir_it.dir.openDirList(base.name) catch |err| switch (err) {1417 var new_dir = top.dir_it.dir.openDir(base.name, .{ .iterate = true }) catch |err| switch (err) {
1465 error.NameTooLong => unreachable, // no path sep in base.name1418 error.NameTooLong => unreachable, // no path sep in base.name
1466 else => |e| return e,1419 else => |e| return e,
1467 };1420 };
...@@ -1499,7 +1452,7 @@ pub const Walker = struct {...@@ -1499,7 +1452,7 @@ pub const Walker = struct {
1499pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {1452pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {
1500 assert(!mem.endsWith(u8, dir_path, path.sep_str));1453 assert(!mem.endsWith(u8, dir_path, path.sep_str));
15011454
1502 var dir = try cwd().openDirList(dir_path);1455 var dir = try cwd().openDir(dir_path, .{ .iterate = true });
1503 errdefer dir.close();1456 errdefer dir.close();
15041457
1505 var name_buffer = try std.Buffer.init(allocator, dir_path);1458 var name_buffer = try std.Buffer.init(allocator, dir_path);
lib/std/fs/watch.zig+1-1
...@@ -619,7 +619,7 @@ test "write a file, watch it, write it again" {...@@ -619,7 +619,7 @@ test "write a file, watch it, write it again" {
619 if (true) return error.SkipZigTest;619 if (true) return error.SkipZigTest;
620620
621 try fs.cwd().makePath(test_tmp_dir);621 try fs.cwd().makePath(test_tmp_dir);
622 defer os.deleteTree(test_tmp_dir) catch {};622 defer fs.cwd().deleteTree(test_tmp_dir) catch {};
623623
624 const allocator = std.heap.page_allocator;624 const allocator = std.heap.page_allocator;
625 return testFsWatch(&allocator);625 return testFsWatch(&allocator);
lib/std/os.zig+25
...@@ -3163,6 +3163,31 @@ pub fn lseek_CUR_get(fd: fd_t) SeekError!u64 {...@@ -3163,6 +3163,31 @@ pub fn lseek_CUR_get(fd: fd_t) SeekError!u64 {
3163 }3163 }
3164}3164}
31653165
3166pub const FcntlError = error{
3167 PermissionDenied,
3168 FileBusy,
3169 ProcessFdQuotaExceeded,
3170 Locked,
3171} || UnexpectedError;
3172
3173pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) FcntlError!usize {
3174 while (true) {
3175 const rc = system.fcntl(fd, cmd, arg);
3176 switch (errno(rc)) {
3177 0 => return @intCast(usize, rc),
3178 EINTR => continue,
3179 EACCES => return error.Locked,
3180 EBADF => unreachable,
3181 EBUSY => return error.FileBusy,
3182 EINVAL => unreachable, // invalid parameters
3183 EPERM => return error.PermissionDenied,
3184 EMFILE => return error.ProcessFdQuotaExceeded,
3185 ENOTDIR => unreachable, // invalid parameter
3186 else => |err| return unexpectedErrno(err),
3187 }
3188 }
3189}
3190
3166pub const RealPathError = error{3191pub const RealPathError = error{
3167 FileNotFound,3192 FileNotFound,
3168 AccessDenied,3193 AccessDenied,
lib/std/os/bits/dragonfly.zig+2
...@@ -283,6 +283,8 @@ pub const F_LOCK = 1;...@@ -283,6 +283,8 @@ pub const F_LOCK = 1;
283pub const F_TLOCK = 2;283pub const F_TLOCK = 2;
284pub const F_TEST = 3;284pub const F_TEST = 3;
285285
286pub const FD_CLOEXEC = 1;
287
286pub const AT_FDCWD = -328243;288pub const AT_FDCWD = -328243;
287pub const AT_SYMLINK_NOFOLLOW = 1;289pub const AT_SYMLINK_NOFOLLOW = 1;
288pub const AT_REMOVEDIR = 2;290pub const AT_REMOVEDIR = 2;
lib/std/os/bits/freebsd.zig+2
...@@ -355,6 +355,8 @@ pub const F_GETOWN_EX = 16;...@@ -355,6 +355,8 @@ pub const F_GETOWN_EX = 16;
355355
356pub const F_GETOWNER_UIDS = 17;356pub const F_GETOWNER_UIDS = 17;
357357
358pub const FD_CLOEXEC = 1;
359
358pub const SEEK_SET = 0;360pub const SEEK_SET = 0;
359pub const SEEK_CUR = 1;361pub const SEEK_CUR = 1;
360pub const SEEK_END = 2;362pub const SEEK_END = 2;
lib/std/os/bits/linux.zig+2
...@@ -136,6 +136,8 @@ pub const MAP_FIXED_NOREPLACE = 0x100000;...@@ -136,6 +136,8 @@ pub const MAP_FIXED_NOREPLACE = 0x100000;
136/// For anonymous mmap, memory could be uninitialized136/// For anonymous mmap, memory could be uninitialized
137pub const MAP_UNINITIALIZED = 0x4000000;137pub const MAP_UNINITIALIZED = 0x4000000;
138138
139pub const FD_CLOEXEC = 1;
140
139pub const F_OK = 0;141pub const F_OK = 0;
140pub const X_OK = 1;142pub const X_OK = 1;
141pub const W_OK = 2;143pub const W_OK = 2;
lib/std/os/bits/netbsd.zig+2
...@@ -312,6 +312,8 @@ pub const F_GETLK = 7;...@@ -312,6 +312,8 @@ pub const F_GETLK = 7;
312pub const F_SETLK = 8;312pub const F_SETLK = 8;
313pub const F_SETLKW = 9;313pub const F_SETLKW = 9;
314314
315pub const FD_CLOEXEC = 1;
316
315pub const SEEK_SET = 0;317pub const SEEK_SET = 0;
316pub const SEEK_CUR = 1;318pub const SEEK_CUR = 1;
317pub const SEEK_END = 2;319pub const SEEK_END = 2;
lib/std/os/linux.zig+4
...@@ -588,6 +588,10 @@ pub fn waitpid(pid: pid_t, status: *u32, flags: u32) usize {...@@ -588,6 +588,10 @@ pub fn waitpid(pid: pid_t, status: *u32, flags: u32) usize {
588 return syscall4(SYS_wait4, @bitCast(usize, @as(isize, pid)), @ptrToInt(status), flags, 0);588 return syscall4(SYS_wait4, @bitCast(usize, @as(isize, pid)), @ptrToInt(status), flags, 0);
589}589}
590590
591pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) usize {
592 return syscall3(SYS_fcntl, @bitCast(usize, @as(isize, fd)), @bitCast(usize, @as(isize, cmd)), arg);
593}
594
591var vdso_clock_gettime = @ptrCast(?*const c_void, init_vdso_clock_gettime);595var vdso_clock_gettime = @ptrCast(?*const c_void, init_vdso_clock_gettime);
592596
593// We must follow the C calling convention when we call into the VDSO597// We must follow the C calling convention when we call into the VDSO
lib/std/os/test.zig+36-6
...@@ -1,7 +1,8 @@...@@ -1,7 +1,8 @@
1const std = @import("../std.zig");1const std = @import("../std.zig");
2const os = std.os;2const os = std.os;
3const testing = std.testing;3const testing = std.testing;
4const expect = std.testing.expect;4const expect = testing.expect;
5const expectEqual = testing.expectEqual;
5const io = std.io;6const io = std.io;
6const fs = std.fs;7const fs = std.fs;
7const mem = std.mem;8const mem = std.mem;
...@@ -19,8 +20,8 @@ test "makePath, put some files in it, deleteTree" {...@@ -19,8 +20,8 @@ test "makePath, put some files in it, deleteTree" {
19 try fs.cwd().makePath("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c");20 try fs.cwd().makePath("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c");
20 try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c" ++ fs.path.sep_str ++ "file.txt", "nonsense");21 try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c" ++ fs.path.sep_str ++ "file.txt", "nonsense");
21 try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "file2.txt", "blah");22 try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "file2.txt", "blah");
22 try fs.deleteTree("os_test_tmp");23 try fs.cwd().deleteTree("os_test_tmp");
23 if (fs.cwd().openDirTraverse("os_test_tmp")) |dir| {24 if (fs.cwd().openDir("os_test_tmp", .{})) |dir| {
24 @panic("expected error");25 @panic("expected error");
25 } else |err| {26 } else |err| {
26 expect(err == error.FileNotFound);27 expect(err == error.FileNotFound);
...@@ -37,7 +38,7 @@ test "access file" {...@@ -37,7 +38,7 @@ test "access file" {
3738
38 try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", "");39 try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", "");
39 try os.access("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", os.F_OK);40 try os.access("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", os.F_OK);
40 try fs.deleteTree("os_test_tmp");41 try fs.cwd().deleteTree("os_test_tmp");
41}42}
4243
43fn testThreadIdFn(thread_id: *Thread.Id) void {44fn testThreadIdFn(thread_id: *Thread.Id) void {
...@@ -46,9 +47,9 @@ fn testThreadIdFn(thread_id: *Thread.Id) void {...@@ -46,9 +47,9 @@ fn testThreadIdFn(thread_id: *Thread.Id) void {
4647
47test "sendfile" {48test "sendfile" {
48 try fs.cwd().makePath("os_test_tmp");49 try fs.cwd().makePath("os_test_tmp");
49 defer fs.deleteTree("os_test_tmp") catch {};50 defer fs.cwd().deleteTree("os_test_tmp") catch {};
5051
51 var dir = try fs.cwd().openDirList("os_test_tmp");52 var dir = try fs.cwd().openDir("os_test_tmp", .{});
52 defer dir.close();53 defer dir.close();
5354
54 const line1 = "line1\n";55 const line1 = "line1\n";
...@@ -446,3 +447,32 @@ test "getenv" {...@@ -446,3 +447,32 @@ test "getenv" {
446 expect(os.getenvZ("BOGUSDOESNOTEXISTENVVAR") == null);447 expect(os.getenvZ("BOGUSDOESNOTEXISTENVVAR") == null);
447 }448 }
448}449}
450
451test "fcntl" {
452 if (builtin.os.tag == .windows)
453 return error.SkipZigTest;
454
455 const test_out_file = "os_tmp_test";
456
457 const file = try fs.cwd().createFile(test_out_file, .{});
458 defer {
459 file.close();
460 fs.cwd().deleteFile(test_out_file) catch {};
461 }
462
463 // Note: The test assumes createFile opens the file with O_CLOEXEC
464 {
465 const flags = try os.fcntl(file.handle, os.F_GETFD, 0);
466 expect((flags & os.FD_CLOEXEC) != 0);
467 }
468 {
469 _ = try os.fcntl(file.handle, os.F_SETFD, 0);
470 const flags = try os.fcntl(file.handle, os.F_GETFD, 0);
471 expect((flags & os.FD_CLOEXEC) == 0);
472 }
473 {
474 _ = try os.fcntl(file.handle, os.F_SETFD, os.FD_CLOEXEC);
475 const flags = try os.fcntl(file.handle, os.F_GETFD, 0);
476 expect((flags & os.FD_CLOEXEC) != 0);
477 }
478}
lib/std/zig/system.zig+1-1
...@@ -754,7 +754,7 @@ pub const NativeTargetInfo = struct {...@@ -754,7 +754,7 @@ pub const NativeTargetInfo = struct {
754 const rpath_list = mem.toSliceConst(u8, @ptrCast([*:0]u8, strtab[rpoff..].ptr));754 const rpath_list = mem.toSliceConst(u8, @ptrCast([*:0]u8, strtab[rpoff..].ptr));
755 var it = mem.tokenize(rpath_list, ":");755 var it = mem.tokenize(rpath_list, ":");
756 while (it.next()) |rpath| {756 while (it.next()) |rpath| {
757 var dir = fs.cwd().openDirList(rpath) catch |err| switch (err) {757 var dir = fs.cwd().openDir(rpath, .{}) catch |err| switch (err) {
758 error.NameTooLong => unreachable,758 error.NameTooLong => unreachable,
759 error.InvalidUtf8 => unreachable,759 error.InvalidUtf8 => unreachable,
760 error.BadPathName => unreachable,760 error.BadPathName => unreachable,
src-self-hosted/compilation.zig+1-2
...@@ -520,8 +520,7 @@ pub const Compilation = struct {...@@ -520,8 +520,7 @@ pub const Compilation = struct {
520520
521 if (comp.tmp_dir.getOrNull()) |tmp_dir_result|521 if (comp.tmp_dir.getOrNull()) |tmp_dir_result|
522 if (tmp_dir_result.*) |tmp_dir| {522 if (tmp_dir_result.*) |tmp_dir| {
523 // TODO evented I/O?523 fs.cwd().deleteTree(tmp_dir) catch {};
524 fs.deleteTree(tmp_dir) catch {};
525 } else |_| {};524 } else |_| {};
526 }525 }
527526
src-self-hosted/libc_installation.zig+5-5
...@@ -280,7 +280,7 @@ pub const LibCInstallation = struct {...@@ -280,7 +280,7 @@ pub const LibCInstallation = struct {
280 // search in reverse order280 // search in reverse order
281 const search_path_untrimmed = search_paths.at(search_paths.len - path_i - 1);281 const search_path_untrimmed = search_paths.at(search_paths.len - path_i - 1);
282 const search_path = std.mem.trimLeft(u8, search_path_untrimmed, " ");282 const search_path = std.mem.trimLeft(u8, search_path_untrimmed, " ");
283 var search_dir = fs.cwd().openDirList(search_path) catch |err| switch (err) {283 var search_dir = fs.cwd().openDir(search_path, .{}) catch |err| switch (err) {
284 error.FileNotFound,284 error.FileNotFound,
285 error.NotDir,285 error.NotDir,
286 error.NoDevice,286 error.NoDevice,
...@@ -335,7 +335,7 @@ pub const LibCInstallation = struct {...@@ -335,7 +335,7 @@ pub const LibCInstallation = struct {
335 const stream = result_buf.outStream();335 const stream = result_buf.outStream();
336 try stream.print("{}\\Include\\{}\\ucrt", .{ search.path, search.version });336 try stream.print("{}\\Include\\{}\\ucrt", .{ search.path, search.version });
337337
338 var dir = fs.cwd().openDirList(result_buf.toSliceConst()) catch |err| switch (err) {338 var dir = fs.cwd().openDir(result_buf.toSliceConst(), .{}) catch |err| switch (err) {
339 error.FileNotFound,339 error.FileNotFound,
340 error.NotDir,340 error.NotDir,
341 error.NoDevice,341 error.NoDevice,
...@@ -382,7 +382,7 @@ pub const LibCInstallation = struct {...@@ -382,7 +382,7 @@ pub const LibCInstallation = struct {
382 const stream = result_buf.outStream();382 const stream = result_buf.outStream();
383 try stream.print("{}\\Lib\\{}\\ucrt\\{}", .{ search.path, search.version, arch_sub_dir });383 try stream.print("{}\\Lib\\{}\\ucrt\\{}", .{ search.path, search.version, arch_sub_dir });
384384
385 var dir = fs.cwd().openDirList(result_buf.toSliceConst()) catch |err| switch (err) {385 var dir = fs.cwd().openDir(result_buf.toSliceConst(), .{}) catch |err| switch (err) {
386 error.FileNotFound,386 error.FileNotFound,
387 error.NotDir,387 error.NotDir,
388 error.NoDevice,388 error.NoDevice,
...@@ -437,7 +437,7 @@ pub const LibCInstallation = struct {...@@ -437,7 +437,7 @@ pub const LibCInstallation = struct {
437 const stream = result_buf.outStream();437 const stream = result_buf.outStream();
438 try stream.print("{}\\Lib\\{}\\um\\{}", .{ search.path, search.version, arch_sub_dir });438 try stream.print("{}\\Lib\\{}\\um\\{}", .{ search.path, search.version, arch_sub_dir });
439439
440 var dir = fs.cwd().openDirList(result_buf.toSliceConst()) catch |err| switch (err) {440 var dir = fs.cwd().openDir(result_buf.toSliceConst(), .{}) catch |err| switch (err) {
441 error.FileNotFound,441 error.FileNotFound,
442 error.NotDir,442 error.NotDir,
443 error.NoDevice,443 error.NoDevice,
...@@ -475,7 +475,7 @@ pub const LibCInstallation = struct {...@@ -475,7 +475,7 @@ pub const LibCInstallation = struct {
475475
476 try result_buf.append("\\include");476 try result_buf.append("\\include");
477477
478 var dir = fs.cwd().openDirList(result_buf.toSliceConst()) catch |err| switch (err) {478 var dir = fs.cwd().openDir(result_buf.toSliceConst(), .{}) catch |err| switch (err) {
479 error.FileNotFound,479 error.FileNotFound,
480 error.NotDir,480 error.NotDir,
481 error.NoDevice,481 error.NoDevice,
src-self-hosted/main.zig+1-1
...@@ -734,7 +734,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro...@@ -734,7 +734,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro
734 max_src_size,734 max_src_size,
735 ) catch |err| switch (err) {735 ) catch |err| switch (err) {
736 error.IsDir, error.AccessDenied => {736 error.IsDir, error.AccessDenied => {
737 var dir = try fs.cwd().openDirList(file_path);737 var dir = try fs.cwd().openDir(file_path, .{ .iterate = true });
738 defer dir.close();738 defer dir.close();
739739
740 var group = event.Group(FmtError!void).init(fmt.allocator);740 var group = event.Group(FmtError!void).init(fmt.allocator);
src-self-hosted/print_targets.zig+1-1
...@@ -72,7 +72,7 @@ pub fn cmdTargets(...@@ -72,7 +72,7 @@ pub fn cmdTargets(
72 };72 };
73 defer allocator.free(zig_lib_dir);73 defer allocator.free(zig_lib_dir);
7474
75 var dir = try std.fs.cwd().openDirList(zig_lib_dir);75 var dir = try std.fs.cwd().openDir(zig_lib_dir, .{});
76 defer dir.close();76 defer dir.close();
7777
78 const vers_txt = try dir.readFileAlloc(allocator, "libc/glibc/vers.txt", 10 * 1024);78 const vers_txt = try dir.readFileAlloc(allocator, "libc/glibc/vers.txt", 10 * 1024);
src-self-hosted/stage2.zig+1-1
...@@ -319,7 +319,7 @@ fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool) FmtError!void {...@@ -319,7 +319,7 @@ fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool) FmtError!void {
319 const source_code = io.readFileAlloc(fmt.allocator, file_path) catch |err| switch (err) {319 const source_code = io.readFileAlloc(fmt.allocator, file_path) catch |err| switch (err) {
320 error.IsDir, error.AccessDenied => {320 error.IsDir, error.AccessDenied => {
321 // TODO make event based (and dir.next())321 // TODO make event based (and dir.next())
322 var dir = try fs.cwd().openDirList(file_path);322 var dir = try fs.cwd().openDir(file_path, .{ .iterate = true });
323 defer dir.close();323 defer dir.close();
324324
325 var dir_it = dir.iterate();325 var dir_it = dir.iterate();
src-self-hosted/test.zig+2-2
...@@ -57,11 +57,11 @@ pub const TestContext = struct {...@@ -57,11 +57,11 @@ pub const TestContext = struct {
57 errdefer allocator.free(self.zig_lib_dir);57 errdefer allocator.free(self.zig_lib_dir);
5858
59 try std.fs.cwd().makePath(tmp_dir_name);59 try std.fs.cwd().makePath(tmp_dir_name);
60 errdefer std.fs.deleteTree(tmp_dir_name) catch {};60 errdefer std.fs.cwd().deleteTree(tmp_dir_name) catch {};
61 }61 }
6262
63 fn deinit(self: *TestContext) void {63 fn deinit(self: *TestContext) void {
64 std.fs.deleteTree(tmp_dir_name) catch {};64 std.fs.cwd().deleteTree(tmp_dir_name) catch {};
65 allocator.free(self.zig_lib_dir);65 allocator.free(self.zig_lib_dir);
66 self.zig_compiler.deinit();66 self.zig_compiler.deinit();
67 }67 }
test/cli.zig+1-1
...@@ -36,7 +36,7 @@ pub fn main() !void {...@@ -36,7 +36,7 @@ pub fn main() !void {
36 testMissingOutputPath,36 testMissingOutputPath,
37 };37 };
38 for (test_fns) |testFn| {38 for (test_fns) |testFn| {
39 try fs.deleteTree(dir_path);39 try fs.cwd().deleteTree(dir_path);
40 try fs.cwd().makeDir(dir_path);40 try fs.cwd().makeDir(dir_path);
41 try testFn(zig_exe, dir_path);41 try testFn(zig_exe, dir_path);
42 }42 }