authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-04-04 00:08:48-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-04-04 00:08:48-04:00
log3d8541121b5a54b269d601d12426415f383a7007
tree68400346b0bbd5fb3b124742fab41c6b7dd788b8
parent6050b9d8359d1ae6b808c332aa27142b0064a6be
parentabd389209b2b25ac3d3567797bff580c2ce7d9ad

Merge branch 'hellerve-wip-macos-dirent'


7 files changed, 174 insertions(+), 12 deletions(-)

doc/docgen.zig+1-1
......@@ -55,7 +55,7 @@ pub fn main() !void {
5555 // TODO issue #709
5656 // disabled to pass CI tests, but obviously we want to implement this
5757 // and then remove this workaround
58 if (builtin.os == builtin.Os.linux) {
58 if (builtin.os != builtin.Os.windows) {
5959 os.deleteTree(allocator, tmp_dir_name) catch {};
6060 }
6161 }
std/c/darwin.zig+10
......@@ -1,6 +1,7 @@
11extern "c" fn __error() &c_int;
22pub extern "c" fn _NSGetExecutablePath(buf: &u8, bufsize: &u32) c_int;
33
4pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: &u8, buf_len: usize, basep: &i64) usize;
45
56pub use @import("../os/darwin_errno.zig");
67
......@@ -45,3 +46,12 @@ pub const Sigaction = extern struct {
4546 sa_mask: sigset_t,
4647 sa_flags: c_int,
4748};
49
50pub const dirent = extern struct {
51 d_ino: usize,
52 d_seekoff: usize,
53 d_reclen: u16,
54 d_namlen: u16,
55 d_type: u8,
56 d_name: u8, // field address is address of first byte of name
57};
std/c/index.zig+1
......@@ -44,6 +44,7 @@ pub extern "c" fn sigaction(sig: c_int, noalias act: &const Sigaction, noalias o
4444pub extern "c" fn nanosleep(rqtp: &const timespec, rmtp: ?&timespec) c_int;
4545pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int;
4646pub extern "c" fn setregid(rgid: c_uint, egid: c_uint) c_int;
47pub extern "c" fn rmdir(path: &const u8) c_int;
4748
4849pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?&c_void;
4950pub extern "c" fn malloc(usize) ?&c_void;
std/os/darwin.zig+32
......@@ -56,10 +56,32 @@ pub const O_SYMLINK = 0x200000; /// allow open of symlinks
5656pub const O_EVTONLY = 0x8000; /// descriptor requested for event notifications only
5757pub const O_CLOEXEC = 0x1000000; /// mark as close-on-exec
5858
59pub const O_ACCMODE = 3;
60pub const O_ALERT = 536870912;
61pub const O_ASYNC = 64;
62pub const O_DIRECTORY = 1048576;
63pub const O_DP_GETRAWENCRYPTED = 1;
64pub const O_DP_GETRAWUNENCRYPTED = 2;
65pub const O_DSYNC = 4194304;
66pub const O_FSYNC = O_SYNC;
67pub const O_NOCTTY = 131072;
68pub const O_POPUP = 2147483648;
69pub const O_SYNC = 128;
70
5971pub const SEEK_SET = 0x0;
6072pub const SEEK_CUR = 0x1;
6173pub const SEEK_END = 0x2;
6274
75pub const DT_UNKNOWN = 0;
76pub const DT_FIFO = 1;
77pub const DT_CHR = 2;
78pub const DT_DIR = 4;
79pub const DT_BLK = 6;
80pub const DT_REG = 8;
81pub const DT_LNK = 10;
82pub const DT_SOCK = 12;
83pub const DT_WHT = 14;
84
6385pub const SIG_BLOCK = 1; /// block specified signal set
6486pub const SIG_UNBLOCK = 2; /// unblock specified signal set
6587pub const SIG_SETMASK = 3; /// set specified signal set
......@@ -192,6 +214,11 @@ pub fn pipe(fds: &[2]i32) usize {
192214 return errnoWrap(c.pipe(@ptrCast(&c_int, fds)));
193215}
194216
217
218pub fn getdirentries64(fd: i32, buf_ptr: &u8, buf_len: usize, basep: &i64) usize {
219 return errnoWrap(@bitCast(isize, c.__getdirentries64(fd, buf_ptr, buf_len, basep)));
220}
221
195222pub fn mkdir(path: &const u8, mode: u32) usize {
196223 return errnoWrap(c.mkdir(path, mode));
197224}
......@@ -204,6 +231,10 @@ pub fn rename(old: &const u8, new: &const u8) usize {
204231 return errnoWrap(c.rename(old, new));
205232}
206233
234pub fn rmdir(path: &const u8) usize {
235 return errnoWrap(c.rmdir(path));
236}
237
207238pub fn chdir(path: &const u8) usize {
208239 return errnoWrap(c.chdir(path));
209240}
......@@ -268,6 +299,7 @@ pub const empty_sigset = sigset_t(0);
268299
269300pub const timespec = c.timespec;
270301pub const Stat = c.Stat;
302pub const dirent = c.dirent;
271303
272304/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall.
273305pub const Sigaction = struct {
std/os/index.zig+97-11
......@@ -1050,15 +1050,16 @@ const DeleteTreeError = error {
10501050};
10511051pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError!void {
10521052 start_over: while (true) {
1053 var got_access_denied = false;
10531054 // First, try deleting the item as a file. This way we don't follow sym links.
10541055 if (deleteFile(allocator, full_path)) {
10551056 return;
10561057 } else |err| switch (err) {
10571058 error.FileNotFound => return,
10581059 error.IsDir => {},
1060 error.AccessDenied => got_access_denied = true,
10591061
10601062 error.OutOfMemory,
1061 error.AccessDenied,
10621063 error.SymLinkLoop,
10631064 error.NameTooLong,
10641065 error.SystemResources,
......@@ -1071,7 +1072,12 @@ pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError!
10711072 }
10721073 {
10731074 var dir = Dir.open(allocator, full_path) catch |err| switch (err) {
1074 error.NotDir => continue :start_over,
1075 error.NotDir => {
1076 if (got_access_denied) {
1077 return error.AccessDenied;
1078 }
1079 continue :start_over;
1080 },
10751081
10761082 error.OutOfMemory,
10771083 error.AccessDenied,
......@@ -1109,18 +1115,16 @@ pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError!
11091115}
11101116
11111117pub const Dir = struct {
1112 // See man getdents
11131118 fd: i32,
1119 darwin_seek: darwin_seek_t,
11141120 allocator: &Allocator,
11151121 buf: []u8,
11161122 index: usize,
11171123 end_index: usize,
11181124
1119 const LinuxEntry = extern struct {
1120 d_ino: usize,
1121 d_off: usize,
1122 d_reclen: u16,
1123 d_name: u8, // field address is the address of first byte of name
1125 const darwin_seek_t = switch (builtin.os) {
1126 Os.macosx, Os.ios => i64,
1127 else => void,
11241128 };
11251129
11261130 pub const Entry = struct {
......@@ -1135,15 +1139,26 @@ pub const Dir = struct {
11351139 SymLink,
11361140 File,
11371141 UnixDomainSocket,
1142 Whiteout,
11381143 Unknown,
11391144 };
11401145 };
11411146
11421147 pub fn open(allocator: &Allocator, dir_path: []const u8) !Dir {
1143 const fd = try posixOpen(allocator, dir_path, posix.O_RDONLY|posix.O_DIRECTORY|posix.O_CLOEXEC, 0);
1148 const fd = switch (builtin.os) {
1149 Os.windows => @compileError("TODO support Dir.open for windows"),
1150 Os.linux => try posixOpen(allocator, dir_path, posix.O_RDONLY|posix.O_DIRECTORY|posix.O_CLOEXEC, 0),
1151 Os.macosx, Os.ios => try posixOpen(allocator, dir_path, posix.O_RDONLY|posix.O_NONBLOCK|posix.O_DIRECTORY|posix.O_CLOEXEC, 0),
1152 else => @compileError("Dir.open is not supported for this platform"),
1153 };
1154 const darwin_seek_init = switch (builtin.os) {
1155 Os.macosx, Os.ios => 0,
1156 else => {},
1157 };
11441158 return Dir {
11451159 .allocator = allocator,
11461160 .fd = fd,
1161 .darwin_seek = darwin_seek_init,
11471162 .index = 0,
11481163 .end_index = 0,
11491164 .buf = []u8{},
......@@ -1158,6 +1173,76 @@ pub const Dir = struct {
11581173 /// Memory such as file names referenced in this returned entry becomes invalid
11591174 /// with subsequent calls to next, as well as when this ::Dir is deinitialized.
11601175 pub fn next(self: &Dir) !?Entry {
1176 switch (builtin.os) {
1177 Os.linux => return self.nextLinux(),
1178 Os.macosx, Os.ios => return self.nextDarwin(),
1179 Os.windows => return self.nextWindows(),
1180 else => @compileError("Dir.next not supported on " ++ @tagName(builtin.os)),
1181 }
1182 }
1183
1184 fn nextDarwin(self: &Dir) !?Entry {
1185 start_over: while (true) {
1186 if (self.index >= self.end_index) {
1187 if (self.buf.len == 0) {
1188 self.buf = try self.allocator.alloc(u8, page_size);
1189 }
1190
1191 while (true) {
1192 const result = posix.getdirentries64(self.fd, self.buf.ptr, self.buf.len,
1193 &self.darwin_seek);
1194 const err = posix.getErrno(result);
1195 if (err > 0) {
1196 switch (err) {
1197 posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,
1198 posix.EINVAL => {
1199 self.buf = try self.allocator.realloc(u8, self.buf, self.buf.len * 2);
1200 continue;
1201 },
1202 else => return unexpectedErrorPosix(err),
1203 }
1204 }
1205 if (result == 0)
1206 return null;
1207 self.index = 0;
1208 self.end_index = result;
1209 break;
1210 }
1211 }
1212 const darwin_entry = @ptrCast(& align(1) posix.dirent, &self.buf[self.index]);
1213 const next_index = self.index + darwin_entry.d_reclen;
1214 self.index = next_index;
1215
1216 const name = (&darwin_entry.d_name)[0..darwin_entry.d_namlen];
1217
1218 // skip . and .. entries
1219 if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {
1220 continue :start_over;
1221 }
1222
1223 const entry_kind = switch (darwin_entry.d_type) {
1224 posix.DT_BLK => Entry.Kind.BlockDevice,
1225 posix.DT_CHR => Entry.Kind.CharacterDevice,
1226 posix.DT_DIR => Entry.Kind.Directory,
1227 posix.DT_FIFO => Entry.Kind.NamedPipe,
1228 posix.DT_LNK => Entry.Kind.SymLink,
1229 posix.DT_REG => Entry.Kind.File,
1230 posix.DT_SOCK => Entry.Kind.UnixDomainSocket,
1231 posix.DT_WHT => Entry.Kind.Whiteout,
1232 else => Entry.Kind.Unknown,
1233 };
1234 return Entry {
1235 .name = name,
1236 .kind = entry_kind,
1237 };
1238 }
1239 }
1240
1241 fn nextWindows(self: &Dir) !?Entry {
1242 @compileError("TODO support Dir.next for windows");
1243 }
1244
1245 fn nextLinux(self: &Dir) !?Entry {
11611246 start_over: while (true) {
11621247 if (self.index >= self.end_index) {
11631248 if (self.buf.len == 0) {
......@@ -1166,7 +1251,7 @@ pub const Dir = struct {
11661251
11671252 while (true) {
11681253 const result = posix.getdents(self.fd, self.buf.ptr, self.buf.len);
1169 const err = linux.getErrno(result);
1254 const err = posix.getErrno(result);
11701255 if (err > 0) {
11711256 switch (err) {
11721257 posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,
......@@ -1184,7 +1269,7 @@ pub const Dir = struct {
11841269 break;
11851270 }
11861271 }
1187 const linux_entry = @ptrCast(& align(1) LinuxEntry, &self.buf[self.index]);
1272 const linux_entry = @ptrCast(& align(1) posix.dirent, &self.buf[self.index]);
11881273 const next_index = self.index + linux_entry.d_reclen;
11891274 self.index = next_index;
11901275
......@@ -1679,6 +1764,7 @@ test "std.os" {
16791764 _ = @import("linux/index.zig");
16801765 _ = @import("path.zig");
16811766 _ = @import("windows/index.zig");
1767 _ = @import("test.zig");
16821768}
16831769
16841770
std/os/linux/x86_64.zig+8
......@@ -488,3 +488,11 @@ pub const timespec = extern struct {
488488 tv_sec: isize,
489489 tv_nsec: isize,
490490};
491
492pub const dirent = extern struct {
493 d_ino: usize,
494 d_off: usize,
495 d_reclen: u16,
496 d_name: u8, // field address is the address of first byte of name
497};
498
std/os/test.zig created+25
......@@ -0,0 +1,25 @@
1const std = @import("../index.zig");
2const os = std.os;
3const assert = std.debug.assert;
4const io = std.io;
5
6const a = std.debug.global_allocator;
7
8const builtin = @import("builtin");
9
10test "makePath, put some files in it, deleteTree" {
11 if (builtin.os == builtin.Os.windows) {
12 // TODO implement os.Dir for windows
13 // https://github.com/zig-lang/zig/issues/709
14 return;
15 }
16 try os.makePath(a, "os_test_tmp/b/c");
17 try io.writeFile(a, "os_test_tmp/b/c/file.txt", "nonsense");
18 try io.writeFile(a, "os_test_tmp/b/file2.txt", "blah");
19 try os.deleteTree(a, "os_test_tmp");
20 if (os.Dir.open(a, "os_test_tmp")) |dir| {
21 @panic("expected error");
22 } else |err| {
23 assert(err == error.PathNotFound);
24 }
25}