| ... | ... | @@ -951,6 +951,9 @@ pub const Dir = struct { |
| 951 | 951 | /// `true` means the opened directory can be scanned for the files and sub-directories |
| 952 | 952 | /// of the result. It means the `iterate` function can be called. |
| 953 | 953 | iterate: bool = false, |
| 954 | |
| 955 | /// `true` means it won't dereference the symlink. |
| 956 | no_follow: bool = false, |
| 954 | 957 | }; |
| 955 | 958 | |
| 956 | 959 | /// Opens a directory at the given path. The directory is a system resource that remains |
| ... | ... | @@ -994,10 +997,11 @@ pub const Dir = struct { |
| 994 | 997 | w.RIGHT_PATH_REMOVE_DIRECTORY | |
| 995 | 998 | w.RIGHT_PATH_UNLINK_FILE; |
| 996 | 999 | } |
| 1000 | const symlink_flags: w.lookupflags_t = if (args.no_follow) 0x0 else w.LOOKUP_SYMLINK_FOLLOW; |
| 997 | 1001 | // TODO do we really need all the rights here? |
| 998 | 1002 | const inheriting: w.rights_t = w.RIGHT_ALL ^ w.RIGHT_SOCK_SHUTDOWN; |
| 999 | 1003 | |
| 1000 | | const result = os.openatWasi(self.fd, sub_path, w.O_DIRECTORY, 0x0, base, inheriting); |
| 1004 | const result = os.openatWasi(self.fd, sub_path, w.O_DIRECTORY, symlink_flags, base, inheriting); |
| 1001 | 1005 | const fd = result catch |err| switch (err) { |
| 1002 | 1006 | error.FileTooBig => unreachable, // can't happen for directories |
| 1003 | 1007 | error.IsDir => unreachable, // we're providing O_DIRECTORY |
| ... | ... | @@ -1014,11 +1018,13 @@ pub const Dir = struct { |
| 1014 | 1018 | if (builtin.os.tag == .windows) { |
| 1015 | 1019 | const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c); |
| 1016 | 1020 | return self.openDirW(sub_path_w.span().ptr, args); |
| 1017 | | } else if (!args.iterate) { |
| 1021 | } |
| 1022 | const symlink_flags: u32 = if (args.no_follow) os.O_NOFOLLOW else 0x0; |
| 1023 | if (!args.iterate) { |
| 1018 | 1024 | const O_PATH = if (@hasDecl(os, "O_PATH")) os.O_PATH else 0; |
| 1019 | | return self.openDirFlagsZ(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | os.O_CLOEXEC | O_PATH); |
| 1025 | return self.openDirFlagsZ(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | os.O_CLOEXEC | O_PATH | symlink_flags); |
| 1020 | 1026 | } else { |
| 1021 | | return self.openDirFlagsZ(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | os.O_CLOEXEC); |
| 1027 | return self.openDirFlagsZ(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | os.O_CLOEXEC | symlink_flags); |
| 1022 | 1028 | } |
| 1023 | 1029 | } |
| 1024 | 1030 | |
| ... | ... | @@ -1030,7 +1036,7 @@ pub const Dir = struct { |
| 1030 | 1036 | const base_flags = w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA | |
| 1031 | 1037 | w.SYNCHRONIZE | w.FILE_TRAVERSE; |
| 1032 | 1038 | const flags: u32 = if (args.iterate) base_flags | w.FILE_LIST_DIRECTORY else base_flags; |
| 1033 | | return self.openDirAccessMaskW(sub_path_w, flags); |
| 1039 | return self.openDirAccessMaskW(sub_path_w, flags, args.no_follow); |
| 1034 | 1040 | } |
| 1035 | 1041 | |
| 1036 | 1042 | /// `flags` must contain `os.O_DIRECTORY`. |
| ... | ... | @@ -1050,7 +1056,7 @@ pub const Dir = struct { |
| 1050 | 1056 | return Dir{ .fd = fd }; |
| 1051 | 1057 | } |
| 1052 | 1058 | |
| 1053 | | fn openDirAccessMaskW(self: Dir, sub_path_w: [*:0]const u16, access_mask: u32) OpenError!Dir { |
| 1059 | fn openDirAccessMaskW(self: Dir, sub_path_w: [*:0]const u16, access_mask: u32, no_follow: bool) OpenError!Dir { |
| 1054 | 1060 | const w = os.windows; |
| 1055 | 1061 | |
| 1056 | 1062 | var result = Dir{ |
| ... | ... | @@ -1080,6 +1086,7 @@ pub const Dir = struct { |
| 1080 | 1086 | // implement this: https://git.midipix.org/ntapi/tree/src/fs/ntapi_tt_open_physical_parent_directory.c |
| 1081 | 1087 | @panic("TODO opening '..' with a relative directory handle is not yet implemented on Windows"); |
| 1082 | 1088 | } |
| 1089 | const open_reparse_point: w.DWORD = if (no_follow) w.FILE_OPEN_REPARSE_POINT else 0x0; |
| 1083 | 1090 | var io: w.IO_STATUS_BLOCK = undefined; |
| 1084 | 1091 | const rc = w.ntdll.NtCreateFile( |
| 1085 | 1092 | &result.fd, |
| ... | ... | @@ -1090,7 +1097,7 @@ pub const Dir = struct { |
| 1090 | 1097 | 0, |
| 1091 | 1098 | w.FILE_SHARE_READ | w.FILE_SHARE_WRITE, |
| 1092 | 1099 | w.FILE_OPEN, |
| 1093 | | w.FILE_DIRECTORY_FILE | w.FILE_SYNCHRONOUS_IO_NONALERT | w.FILE_OPEN_FOR_BACKUP_INTENT, |
| 1100 | w.FILE_DIRECTORY_FILE | w.FILE_SYNCHRONOUS_IO_NONALERT | w.FILE_OPEN_FOR_BACKUP_INTENT | open_reparse_point, |
| 1094 | 1101 | null, |
| 1095 | 1102 | 0, |
| 1096 | 1103 | ); |
| ... | ... | @@ -1277,6 +1284,7 @@ pub const Dir = struct { |
| 1277 | 1284 | pub fn deleteTree(self: Dir, sub_path: []const u8) DeleteTreeError!void { |
| 1278 | 1285 | start_over: while (true) { |
| 1279 | 1286 | var got_access_denied = false; |
| 1287 | |
| 1280 | 1288 | // First, try deleting the item as a file. This way we don't follow sym links. |
| 1281 | 1289 | if (self.deleteFile(sub_path)) { |
| 1282 | 1290 | return; |
| ... | ... | @@ -1297,7 +1305,8 @@ pub const Dir = struct { |
| 1297 | 1305 | error.Unexpected, |
| 1298 | 1306 | => |e| return e, |
| 1299 | 1307 | } |
| 1300 | | var dir = self.openDir(sub_path, .{ .iterate = true }) catch |err| switch (err) { |
| 1308 | |
| 1309 | var dir = self.openDir(sub_path, .{ .iterate = true, .no_follow = true }) catch |err| switch (err) { |
| 1301 | 1310 | error.NotDir => { |
| 1302 | 1311 | if (got_access_denied) { |
| 1303 | 1312 | return error.AccessDenied; |
| ... | ... | @@ -1364,7 +1373,7 @@ pub const Dir = struct { |
| 1364 | 1373 | => |e| return e, |
| 1365 | 1374 | } |
| 1366 | 1375 | |
| 1367 | | const new_dir = dir.openDir(entry.name, .{ .iterate = true }) catch |err| switch (err) { |
| 1376 | const new_dir = dir.openDir(entry.name, .{ .iterate = true, .no_follow = true }) catch |err| switch (err) { |
| 1368 | 1377 | error.NotDir => { |
| 1369 | 1378 | if (got_access_denied) { |
| 1370 | 1379 | return error.AccessDenied; |