| ... | @@ -951,6 +951,9 @@ pub const Dir = struct { | ... | @@ -951,6 +951,9 @@ pub const Dir = struct { |
| 951 | /// `true` means the opened directory can be scanned for the files and sub-directories | 951 | /// `true` means the opened directory can be scanned for the files and sub-directories |
| 952 | /// of the result. It means the `iterate` function can be called. | 952 | /// of the result. It means the `iterate` function can be called. |
| 953 | iterate: bool = false, | 953 | iterate: bool = false, |
| | 954 | |
| | 955 | /// `true` means it won't dereference the symlink. |
| | 956 | no_follow: bool = false, |
| 954 | }; | 957 | }; |
| 955 | | 958 | |
| 956 | /// Opens a directory at the given path. The directory is a system resource that remains | 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,10 +997,11 @@ pub const Dir = struct { |
| 994 | w.RIGHT_PATH_REMOVE_DIRECTORY | | 997 | w.RIGHT_PATH_REMOVE_DIRECTORY | |
| 995 | w.RIGHT_PATH_UNLINK_FILE; | 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 | // TODO do we really need all the rights here? | 1001 | // TODO do we really need all the rights here? |
| 998 | const inheriting: w.rights_t = w.RIGHT_ALL ^ w.RIGHT_SOCK_SHUTDOWN; | 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 | const fd = result catch |err| switch (err) { | 1005 | const fd = result catch |err| switch (err) { |
| 1002 | error.FileTooBig => unreachable, // can't happen for directories | 1006 | error.FileTooBig => unreachable, // can't happen for directories |
| 1003 | error.IsDir => unreachable, // we're providing O_DIRECTORY | 1007 | error.IsDir => unreachable, // we're providing O_DIRECTORY |
| ... | @@ -1014,11 +1018,13 @@ pub const Dir = struct { | ... | @@ -1014,11 +1018,13 @@ pub const Dir = struct { |
| 1014 | if (builtin.os.tag == .windows) { | 1018 | if (builtin.os.tag == .windows) { |
| 1015 | const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c); | 1019 | const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c); |
| 1016 | return self.openDirW(sub_path_w.span().ptr, args); | 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 | const O_PATH = if (@hasDecl(os, "O_PATH")) os.O_PATH else 0; | 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 | } else { | 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,7 +1036,7 @@ pub const Dir = struct { |
| 1030 | const base_flags = w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA | | 1036 | const base_flags = w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA | |
| 1031 | w.SYNCHRONIZE | w.FILE_TRAVERSE; | 1037 | w.SYNCHRONIZE | w.FILE_TRAVERSE; |
| 1032 | const flags: u32 = if (args.iterate) base_flags | w.FILE_LIST_DIRECTORY else base_flags; | 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 | /// `flags` must contain `os.O_DIRECTORY`. | 1042 | /// `flags` must contain `os.O_DIRECTORY`. |
| ... | @@ -1050,7 +1056,7 @@ pub const Dir = struct { | ... | @@ -1050,7 +1056,7 @@ pub const Dir = struct { |
| 1050 | return Dir{ .fd = fd }; | 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 | const w = os.windows; | 1060 | const w = os.windows; |
| 1055 | | 1061 | |
| 1056 | var result = Dir{ | 1062 | var result = Dir{ |
| ... | @@ -1080,6 +1086,7 @@ pub const Dir = struct { | ... | @@ -1080,6 +1086,7 @@ pub const Dir = struct { |
| 1080 | // implement this: https://git.midipix.org/ntapi/tree/src/fs/ntapi_tt_open_physical_parent_directory.c | 1086 | // implement this: https://git.midipix.org/ntapi/tree/src/fs/ntapi_tt_open_physical_parent_directory.c |
| 1081 | @panic("TODO opening '..' with a relative directory handle is not yet implemented on Windows"); | 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 | var io: w.IO_STATUS_BLOCK = undefined; | 1090 | var io: w.IO_STATUS_BLOCK = undefined; |
| 1084 | const rc = w.ntdll.NtCreateFile( | 1091 | const rc = w.ntdll.NtCreateFile( |
| 1085 | &result.fd, | 1092 | &result.fd, |
| ... | @@ -1090,7 +1097,7 @@ pub const Dir = struct { | ... | @@ -1090,7 +1097,7 @@ pub const Dir = struct { |
| 1090 | 0, | 1097 | 0, |
| 1091 | w.FILE_SHARE_READ | w.FILE_SHARE_WRITE, | 1098 | w.FILE_SHARE_READ | w.FILE_SHARE_WRITE, |
| 1092 | w.FILE_OPEN, | 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 | null, | 1101 | null, |
| 1095 | 0, | 1102 | 0, |
| 1096 | ); | 1103 | ); |
| ... | @@ -1277,6 +1284,7 @@ pub const Dir = struct { | ... | @@ -1277,6 +1284,7 @@ pub const Dir = struct { |
| 1277 | pub fn deleteTree(self: Dir, sub_path: []const u8) DeleteTreeError!void { | 1284 | pub fn deleteTree(self: Dir, sub_path: []const u8) DeleteTreeError!void { |
| 1278 | start_over: while (true) { | 1285 | start_over: while (true) { |
| 1279 | var got_access_denied = false; | 1286 | var got_access_denied = false; |
| | 1287 | |
| 1280 | // First, try deleting the item as a file. This way we don't follow sym links. | 1288 | // First, try deleting the item as a file. This way we don't follow sym links. |
| 1281 | if (self.deleteFile(sub_path)) { | 1289 | if (self.deleteFile(sub_path)) { |
| 1282 | return; | 1290 | return; |
| ... | @@ -1297,7 +1305,8 @@ pub const Dir = struct { | ... | @@ -1297,7 +1305,8 @@ pub const Dir = struct { |
| 1297 | error.Unexpected, | 1305 | error.Unexpected, |
| 1298 | => |e| return e, | 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 | error.NotDir => { | 1310 | error.NotDir => { |
| 1302 | if (got_access_denied) { | 1311 | if (got_access_denied) { |
| 1303 | return error.AccessDenied; | 1312 | return error.AccessDenied; |
| ... | @@ -1364,7 +1373,7 @@ pub const Dir = struct { | ... | @@ -1364,7 +1373,7 @@ pub const Dir = struct { |
| 1364 | => |e| return e, | 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 | error.NotDir => { | 1377 | error.NotDir => { |
| 1369 | if (got_access_denied) { | 1378 | if (got_access_denied) { |
| 1370 | return error.AccessDenied; | 1379 | return error.AccessDenied; |