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 {...@@ -55,7 +55,7 @@ pub fn main() !void {
55 // TODO issue #70955 // TODO issue #709
56 // disabled to pass CI tests, but obviously we want to implement this56 // disabled to pass CI tests, but obviously we want to implement this
57 // and then remove this workaround57 // and then remove this workaround
58 if (builtin.os == builtin.Os.linux) {58 if (builtin.os != builtin.Os.windows) {
59 os.deleteTree(allocator, tmp_dir_name) catch {};59 os.deleteTree(allocator, tmp_dir_name) catch {};
60 }60 }
61 }61 }
std/c/darwin.zig+10
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1extern "c" fn __error() &c_int;1extern "c" fn __error() &c_int;
2pub extern "c" fn _NSGetExecutablePath(buf: &u8, bufsize: &u32) c_int;2pub 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
5pub use @import("../os/darwin_errno.zig");6pub use @import("../os/darwin_errno.zig");
67
...@@ -45,3 +46,12 @@ pub const Sigaction = extern struct {...@@ -45,3 +46,12 @@ pub const Sigaction = extern struct {
45 sa_mask: sigset_t,46 sa_mask: sigset_t,
46 sa_flags: c_int,47 sa_flags: c_int,
47};48};
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...@@ -44,6 +44,7 @@ pub extern "c" fn sigaction(sig: c_int, noalias act: &const Sigaction, noalias o
44pub extern "c" fn nanosleep(rqtp: &const timespec, rmtp: ?&timespec) c_int;44pub extern "c" fn nanosleep(rqtp: &const timespec, rmtp: ?&timespec) c_int;
45pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int;45pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int;
46pub extern "c" fn setregid(rgid: c_uint, egid: c_uint) c_int;46pub extern "c" fn setregid(rgid: c_uint, egid: c_uint) c_int;
47pub extern "c" fn rmdir(path: &const u8) c_int;
4748
48pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?&c_void;49pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?&c_void;
49pub extern "c" fn malloc(usize) ?&c_void;50pub 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...@@ -56,10 +56,32 @@ pub const O_SYMLINK = 0x200000; /// allow open of symlinks
56pub const O_EVTONLY = 0x8000; /// descriptor requested for event notifications only56pub const O_EVTONLY = 0x8000; /// descriptor requested for event notifications only
57pub const O_CLOEXEC = 0x1000000; /// mark as close-on-exec57pub 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
59pub const SEEK_SET = 0x0;71pub const SEEK_SET = 0x0;
60pub const SEEK_CUR = 0x1;72pub const SEEK_CUR = 0x1;
61pub const SEEK_END = 0x2;73pub 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
63pub const SIG_BLOCK = 1; /// block specified signal set85pub const SIG_BLOCK = 1; /// block specified signal set
64pub const SIG_UNBLOCK = 2; /// unblock specified signal set86pub const SIG_UNBLOCK = 2; /// unblock specified signal set
65pub const SIG_SETMASK = 3; /// set specified signal set87pub const SIG_SETMASK = 3; /// set specified signal set
...@@ -192,6 +214,11 @@ pub fn pipe(fds: &[2]i32) usize {...@@ -192,6 +214,11 @@ pub fn pipe(fds: &[2]i32) usize {
192 return errnoWrap(c.pipe(@ptrCast(&c_int, fds)));214 return errnoWrap(c.pipe(@ptrCast(&c_int, fds)));
193}215}
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
195pub fn mkdir(path: &const u8, mode: u32) usize {222pub fn mkdir(path: &const u8, mode: u32) usize {
196 return errnoWrap(c.mkdir(path, mode));223 return errnoWrap(c.mkdir(path, mode));
197}224}
...@@ -204,6 +231,10 @@ pub fn rename(old: &const u8, new: &const u8) usize {...@@ -204,6 +231,10 @@ pub fn rename(old: &const u8, new: &const u8) usize {
204 return errnoWrap(c.rename(old, new));231 return errnoWrap(c.rename(old, new));
205}232}
206233
234pub fn rmdir(path: &const u8) usize {
235 return errnoWrap(c.rmdir(path));
236}
237
207pub fn chdir(path: &const u8) usize {238pub fn chdir(path: &const u8) usize {
208 return errnoWrap(c.chdir(path));239 return errnoWrap(c.chdir(path));
209}240}
...@@ -268,6 +299,7 @@ pub const empty_sigset = sigset_t(0);...@@ -268,6 +299,7 @@ pub const empty_sigset = sigset_t(0);
268299
269pub const timespec = c.timespec;300pub const timespec = c.timespec;
270pub const Stat = c.Stat;301pub const Stat = c.Stat;
302pub const dirent = c.dirent;
271303
272/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall.304/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall.
273pub const Sigaction = struct {305pub const Sigaction = struct {
std/os/index.zig+97-11
...@@ -1050,15 +1050,16 @@ const DeleteTreeError = error {...@@ -1050,15 +1050,16 @@ const DeleteTreeError = error {
1050};1050};
1051pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError!void {1051pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError!void {
1052 start_over: while (true) {1052 start_over: while (true) {
1053 var got_access_denied = false;
1053 // First, try deleting the item as a file. This way we don't follow sym links.1054 // First, try deleting the item as a file. This way we don't follow sym links.
1054 if (deleteFile(allocator, full_path)) {1055 if (deleteFile(allocator, full_path)) {
1055 return;1056 return;
1056 } else |err| switch (err) {1057 } else |err| switch (err) {
1057 error.FileNotFound => return,1058 error.FileNotFound => return,
1058 error.IsDir => {},1059 error.IsDir => {},
1060 error.AccessDenied => got_access_denied = true,
10591061
1060 error.OutOfMemory,1062 error.OutOfMemory,
1061 error.AccessDenied,
1062 error.SymLinkLoop,1063 error.SymLinkLoop,
1063 error.NameTooLong,1064 error.NameTooLong,
1064 error.SystemResources,1065 error.SystemResources,
...@@ -1071,7 +1072,12 @@ pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError!...@@ -1071,7 +1072,12 @@ pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError!
1071 }1072 }
1072 {1073 {
1073 var dir = Dir.open(allocator, full_path) catch |err| switch (err) {1074 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
1076 error.OutOfMemory,1082 error.OutOfMemory,
1077 error.AccessDenied,1083 error.AccessDenied,
...@@ -1109,18 +1115,16 @@ pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError!...@@ -1109,18 +1115,16 @@ pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError!
1109}1115}
11101116
1111pub const Dir = struct {1117pub const Dir = struct {
1112 // See man getdents
1113 fd: i32,1118 fd: i32,
1119 darwin_seek: darwin_seek_t,
1114 allocator: &Allocator,1120 allocator: &Allocator,
1115 buf: []u8,1121 buf: []u8,
1116 index: usize,1122 index: usize,
1117 end_index: usize,1123 end_index: usize,
11181124
1119 const LinuxEntry = extern struct {1125 const darwin_seek_t = switch (builtin.os) {
1120 d_ino: usize,1126 Os.macosx, Os.ios => i64,
1121 d_off: usize,1127 else => void,
1122 d_reclen: u16,
1123 d_name: u8, // field address is the address of first byte of name
1124 };1128 };
11251129
1126 pub const Entry = struct {1130 pub const Entry = struct {
...@@ -1135,15 +1139,26 @@ pub const Dir = struct {...@@ -1135,15 +1139,26 @@ pub const Dir = struct {
1135 SymLink,1139 SymLink,
1136 File,1140 File,
1137 UnixDomainSocket,1141 UnixDomainSocket,
1142 Whiteout,
1138 Unknown,1143 Unknown,
1139 };1144 };
1140 };1145 };
11411146
1142 pub fn open(allocator: &Allocator, dir_path: []const u8) !Dir {1147 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 };
1144 return Dir {1158 return Dir {
1145 .allocator = allocator,1159 .allocator = allocator,
1146 .fd = fd,1160 .fd = fd,
1161 .darwin_seek = darwin_seek_init,
1147 .index = 0,1162 .index = 0,
1148 .end_index = 0,1163 .end_index = 0,
1149 .buf = []u8{},1164 .buf = []u8{},
...@@ -1158,6 +1173,76 @@ pub const Dir = struct {...@@ -1158,6 +1173,76 @@ pub const Dir = struct {
1158 /// Memory such as file names referenced in this returned entry becomes invalid1173 /// Memory such as file names referenced in this returned entry becomes invalid
1159 /// with subsequent calls to next, as well as when this ::Dir is deinitialized.1174 /// with subsequent calls to next, as well as when this ::Dir is deinitialized.
1160 pub fn next(self: &Dir) !?Entry {1175 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 {
1161 start_over: while (true) {1246 start_over: while (true) {
1162 if (self.index >= self.end_index) {1247 if (self.index >= self.end_index) {
1163 if (self.buf.len == 0) {1248 if (self.buf.len == 0) {
...@@ -1166,7 +1251,7 @@ pub const Dir = struct {...@@ -1166,7 +1251,7 @@ pub const Dir = struct {
11661251
1167 while (true) {1252 while (true) {
1168 const result = posix.getdents(self.fd, self.buf.ptr, self.buf.len);1253 const result = posix.getdents(self.fd, self.buf.ptr, self.buf.len);
1169 const err = linux.getErrno(result);1254 const err = posix.getErrno(result);
1170 if (err > 0) {1255 if (err > 0) {
1171 switch (err) {1256 switch (err) {
1172 posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,1257 posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,
...@@ -1184,7 +1269,7 @@ pub const Dir = struct {...@@ -1184,7 +1269,7 @@ pub const Dir = struct {
1184 break;1269 break;
1185 }1270 }
1186 }1271 }
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]);
1188 const next_index = self.index + linux_entry.d_reclen;1273 const next_index = self.index + linux_entry.d_reclen;
1189 self.index = next_index;1274 self.index = next_index;
11901275
...@@ -1679,6 +1764,7 @@ test "std.os" {...@@ -1679,6 +1764,7 @@ test "std.os" {
1679 _ = @import("linux/index.zig");1764 _ = @import("linux/index.zig");
1680 _ = @import("path.zig");1765 _ = @import("path.zig");
1681 _ = @import("windows/index.zig");1766 _ = @import("windows/index.zig");
1767 _ = @import("test.zig");
1682}1768}
16831769
16841770
std/os/linux/x86_64.zig+8
...@@ -488,3 +488,11 @@ pub const timespec = extern struct {...@@ -488,3 +488,11 @@ pub const timespec = extern struct {
488 tv_sec: isize,488 tv_sec: isize,
489 tv_nsec: isize,489 tv_nsec: isize,
490};490};
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}