| ... | @@ -1483,22 +1483,91 @@ pub const Dir = struct { | ... | @@ -1483,22 +1483,91 @@ pub const Dir = struct { |
| 1483 | } | 1483 | } |
| 1484 | } | 1484 | } |
| 1485 | | 1485 | |
| | 1486 | /// Calls makeOpenDirAccessMaskW recursively to make an entire path |
| | 1487 | /// (i.e. falling back if the parent directory does not exist). Opens the dir if the path |
| | 1488 | /// already exists and is a directory. |
| | 1489 | /// This function is not atomic, and if it returns an error, the file system may |
| | 1490 | /// have been modified regardless. |
| | 1491 | fn makeOpenPathAccessMaskW(self: Dir, sub_path: []const u8, access_mask: u32, no_follow: bool) OpenError!Dir { |
| | 1492 | const w = os.windows; |
| | 1493 | var end_index: usize = sub_path.len; |
| | 1494 | |
| | 1495 | return while (true) { |
| | 1496 | const sub_path_w = try w.sliceToPrefixedFileW(sub_path[0..end_index]); |
| | 1497 | const result = self.makeOpenDirAccessMaskW(sub_path_w.span().ptr, access_mask, .{ |
| | 1498 | .no_follow = no_follow, |
| | 1499 | .create_disposition = if (end_index == sub_path.len) w.FILE_OPEN_IF else w.FILE_CREATE, |
| | 1500 | }) catch |err| switch (err) { |
| | 1501 | error.FileNotFound => { |
| | 1502 | // march end_index backward until next path component |
| | 1503 | while (true) { |
| | 1504 | if (end_index == 0) return err; |
| | 1505 | end_index -= 1; |
| | 1506 | if (path.isSep(sub_path[end_index])) break; |
| | 1507 | } |
| | 1508 | continue; |
| | 1509 | }, |
| | 1510 | else => return err, |
| | 1511 | }; |
| | 1512 | |
| | 1513 | if (end_index == sub_path.len) return result; |
| | 1514 | // march end_index forward until next path component |
| | 1515 | while (true) { |
| | 1516 | end_index += 1; |
| | 1517 | if (end_index == sub_path.len or path.isSep(sub_path[end_index])) break; |
| | 1518 | } |
| | 1519 | }; |
| | 1520 | } |
| | 1521 | |
| 1486 | /// This function performs `makePath`, followed by `openDir`. | 1522 | /// This function performs `makePath`, followed by `openDir`. |
| 1487 | /// If supported by the OS, this operation is atomic. It is not atomic on | 1523 | /// If supported by the OS, this operation is atomic. It is not atomic on |
| 1488 | /// all operating systems. | 1524 | /// all operating systems. |
| | 1525 | /// On Windows, this function performs `makeOpenPathAccessMaskW`. |
| 1489 | pub fn makeOpenPath(self: Dir, sub_path: []const u8, open_dir_options: OpenDirOptions) !Dir { | 1526 | pub fn makeOpenPath(self: Dir, sub_path: []const u8, open_dir_options: OpenDirOptions) !Dir { |
| 1490 | // TODO improve this implementation on Windows; we can avoid 1 call to NtClose | 1527 | return switch (builtin.os.tag) { |
| 1491 | try self.makePath(sub_path); | 1528 | .windows => { |
| 1492 | return self.openDir(sub_path, open_dir_options); | 1529 | const w = os.windows; |
| | 1530 | const base_flags = w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA | |
| | 1531 | w.SYNCHRONIZE | w.FILE_TRAVERSE; |
| | 1532 | |
| | 1533 | return self.makeOpenPathAccessMaskW(sub_path, base_flags, open_dir_options.no_follow); |
| | 1534 | }, |
| | 1535 | else => { |
| | 1536 | return self.openDir(sub_path, open_dir_options) catch |err| switch (err) { |
| | 1537 | error.FileNotFound => { |
| | 1538 | try self.makePath(sub_path); |
| | 1539 | return self.openDir(sub_path, open_dir_options); |
| | 1540 | }, |
| | 1541 | else => |e| return e, |
| | 1542 | }; |
| | 1543 | }, |
| | 1544 | }; |
| 1493 | } | 1545 | } |
| 1494 | | 1546 | |
| 1495 | /// This function performs `makePath`, followed by `openIterableDir`. | 1547 | /// This function performs `makePath`, followed by `openIterableDir`. |
| 1496 | /// If supported by the OS, this operation is atomic. It is not atomic on | 1548 | /// If supported by the OS, this operation is atomic. It is not atomic on |
| 1497 | /// all operating systems. | 1549 | /// all operating systems. |
| 1498 | pub fn makeOpenPathIterable(self: Dir, sub_path: []const u8, open_dir_options: OpenDirOptions) !IterableDir { | 1550 | pub fn makeOpenPathIterable(self: Dir, sub_path: []const u8, open_dir_options: OpenDirOptions) !IterableDir { |
| 1499 | // TODO improve this implementation on Windows; we can avoid 1 call to NtClose | 1551 | return switch (builtin.os.tag) { |
| 1500 | try self.makePath(sub_path); | 1552 | .windows => { |
| 1501 | return self.openIterableDir(sub_path, open_dir_options); | 1553 | const w = os.windows; |
| | 1554 | const base_flags = w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA | |
| | 1555 | w.SYNCHRONIZE | w.FILE_TRAVERSE | w.FILE_LIST_DIRECTORY; |
| | 1556 | |
| | 1557 | return IterableDir{ |
| | 1558 | .dir = try self.makeOpenPathAccessMaskW(sub_path, base_flags, open_dir_options.no_follow), |
| | 1559 | }; |
| | 1560 | }, |
| | 1561 | else => { |
| | 1562 | return self.openIterableDir(sub_path, open_dir_options) catch |err| switch (err) { |
| | 1563 | error.FileNotFound => { |
| | 1564 | try self.makePath(sub_path); |
| | 1565 | return self.openIterableDir(sub_path, open_dir_options); |
| | 1566 | }, |
| | 1567 | else => |e| return e, |
| | 1568 | }; |
| | 1569 | }, |
| | 1570 | }; |
| 1502 | } | 1571 | } |
| 1503 | | 1572 | |
| 1504 | /// This function returns the canonicalized absolute pathname of | 1573 | /// This function returns the canonicalized absolute pathname of |
| ... | @@ -1742,7 +1811,10 @@ pub const Dir = struct { | ... | @@ -1742,7 +1811,10 @@ pub const Dir = struct { |
| 1742 | const base_flags = w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA | | 1811 | const base_flags = w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA | |
| 1743 | w.SYNCHRONIZE | w.FILE_TRAVERSE; | 1812 | w.SYNCHRONIZE | w.FILE_TRAVERSE; |
| 1744 | const flags: u32 = if (iterable) base_flags | w.FILE_LIST_DIRECTORY else base_flags; | 1813 | const flags: u32 = if (iterable) base_flags | w.FILE_LIST_DIRECTORY else base_flags; |
| 1745 | var dir = try self.openDirAccessMaskW(sub_path_w, flags, args.no_follow); | 1814 | var dir = try self.makeOpenDirAccessMaskW(sub_path_w, flags, .{ |
| | 1815 | .no_follow = args.no_follow, |
| | 1816 | .create_disposition = w.FILE_OPEN, |
| | 1817 | }); |
| 1746 | return dir; | 1818 | return dir; |
| 1747 | } | 1819 | } |
| 1748 | | 1820 | |
| ... | @@ -1765,7 +1837,12 @@ pub const Dir = struct { | ... | @@ -1765,7 +1837,12 @@ pub const Dir = struct { |
| 1765 | return Dir{ .fd = fd }; | 1837 | return Dir{ .fd = fd }; |
| 1766 | } | 1838 | } |
| 1767 | | 1839 | |
| 1768 | fn openDirAccessMaskW(self: Dir, sub_path_w: [*:0]const u16, access_mask: u32, no_follow: bool) OpenError!Dir { | 1840 | const MakeOpenDirAccessMaskWOptions = struct { |
| | 1841 | no_follow: bool, |
| | 1842 | create_disposition: u32, |
| | 1843 | }; |
| | 1844 | |
| | 1845 | fn makeOpenDirAccessMaskW(self: Dir, sub_path_w: [*:0]const u16, access_mask: u32, flags: MakeOpenDirAccessMaskWOptions) OpenError!Dir { |
| 1769 | const w = os.windows; | 1846 | const w = os.windows; |
| 1770 | | 1847 | |
| 1771 | var result = Dir{ | 1848 | var result = Dir{ |
| ... | @@ -1786,7 +1863,7 @@ pub const Dir = struct { | ... | @@ -1786,7 +1863,7 @@ pub const Dir = struct { |
| 1786 | .SecurityDescriptor = null, | 1863 | .SecurityDescriptor = null, |
| 1787 | .SecurityQualityOfService = null, | 1864 | .SecurityQualityOfService = null, |
| 1788 | }; | 1865 | }; |
| 1789 | const open_reparse_point: w.DWORD = if (no_follow) w.FILE_OPEN_REPARSE_POINT else 0x0; | 1866 | const open_reparse_point: w.DWORD = if (flags.no_follow) w.FILE_OPEN_REPARSE_POINT else 0x0; |
| 1790 | var io: w.IO_STATUS_BLOCK = undefined; | 1867 | var io: w.IO_STATUS_BLOCK = undefined; |
| 1791 | const rc = w.ntdll.NtCreateFile( | 1868 | const rc = w.ntdll.NtCreateFile( |
| 1792 | &result.fd, | 1869 | &result.fd, |
| ... | @@ -1794,13 +1871,14 @@ pub const Dir = struct { | ... | @@ -1794,13 +1871,14 @@ pub const Dir = struct { |
| 1794 | &attr, | 1871 | &attr, |
| 1795 | &io, | 1872 | &io, |
| 1796 | null, | 1873 | null, |
| 1797 | 0, | 1874 | w.FILE_ATTRIBUTE_NORMAL, |
| 1798 | w.FILE_SHARE_READ | w.FILE_SHARE_WRITE, | 1875 | w.FILE_SHARE_READ | w.FILE_SHARE_WRITE, |
| 1799 | w.FILE_OPEN, | 1876 | flags.create_disposition, |
| 1800 | w.FILE_DIRECTORY_FILE | w.FILE_SYNCHRONOUS_IO_NONALERT | w.FILE_OPEN_FOR_BACKUP_INTENT | open_reparse_point, | 1877 | w.FILE_DIRECTORY_FILE | w.FILE_SYNCHRONOUS_IO_NONALERT | w.FILE_OPEN_FOR_BACKUP_INTENT | open_reparse_point, |
| 1801 | null, | 1878 | null, |
| 1802 | 0, | 1879 | 0, |
| 1803 | ); | 1880 | ); |
| | 1881 | |
| 1804 | switch (rc) { | 1882 | switch (rc) { |
| 1805 | .SUCCESS => return result, | 1883 | .SUCCESS => return result, |
| 1806 | .OBJECT_NAME_INVALID => return error.BadPathName, | 1884 | .OBJECT_NAME_INVALID => return error.BadPathName, |