| ... | ... | @@ -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 | 1522 | /// This function performs `makePath`, followed by `openDir`. |
| 1487 | 1523 | /// If supported by the OS, this operation is atomic. It is not atomic on |
| 1488 | 1524 | /// all operating systems. |
| 1525 | /// On Windows, this function performs `makeOpenPathAccessMaskW`. |
| 1489 | 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 |
| 1491 | | try self.makePath(sub_path); |
| 1492 | | return self.openDir(sub_path, open_dir_options); |
| 1527 | return switch (builtin.os.tag) { |
| 1528 | .windows => { |
| 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 | 1547 | /// This function performs `makePath`, followed by `openIterableDir`. |
| 1496 | 1548 | /// If supported by the OS, this operation is atomic. It is not atomic on |
| 1497 | 1549 | /// all operating systems. |
| 1498 | 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 |
| 1500 | | try self.makePath(sub_path); |
| 1501 | | return self.openIterableDir(sub_path, open_dir_options); |
| 1551 | return switch (builtin.os.tag) { |
| 1552 | .windows => { |
| 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 | 1573 | /// This function returns the canonicalized absolute pathname of |
| ... | ... | @@ -1742,7 +1811,10 @@ pub const Dir = struct { |
| 1742 | 1811 | const base_flags = w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA | |
| 1743 | 1812 | w.SYNCHRONIZE | w.FILE_TRAVERSE; |
| 1744 | 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 | 1818 | return dir; |
| 1747 | 1819 | } |
| 1748 | 1820 | |
| ... | ... | @@ -1765,7 +1837,12 @@ pub const Dir = struct { |
| 1765 | 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 | 1846 | const w = os.windows; |
| 1770 | 1847 | |
| 1771 | 1848 | var result = Dir{ |
| ... | ... | @@ -1786,7 +1863,7 @@ pub const Dir = struct { |
| 1786 | 1863 | .SecurityDescriptor = null, |
| 1787 | 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 | 1867 | var io: w.IO_STATUS_BLOCK = undefined; |
| 1791 | 1868 | const rc = w.ntdll.NtCreateFile( |
| 1792 | 1869 | &result.fd, |
| ... | ... | @@ -1794,13 +1871,14 @@ pub const Dir = struct { |
| 1794 | 1871 | &attr, |
| 1795 | 1872 | &io, |
| 1796 | 1873 | null, |
| 1797 | | 0, |
| 1874 | w.FILE_ATTRIBUTE_NORMAL, |
| 1798 | 1875 | w.FILE_SHARE_READ | w.FILE_SHARE_WRITE, |
| 1799 | | w.FILE_OPEN, |
| 1876 | flags.create_disposition, |
| 1800 | 1877 | w.FILE_DIRECTORY_FILE | w.FILE_SYNCHRONOUS_IO_NONALERT | w.FILE_OPEN_FOR_BACKUP_INTENT | open_reparse_point, |
| 1801 | 1878 | null, |
| 1802 | 1879 | 0, |
| 1803 | 1880 | ); |
| 1881 | |
| 1804 | 1882 | switch (rc) { |
| 1805 | 1883 | .SUCCESS => return result, |
| 1806 | 1884 | .OBJECT_NAME_INVALID => return error.BadPathName, |