| ... | ... | @@ -1459,8 +1459,9 @@ pub const Dir = struct { |
| 1459 | 1459 | try os.mkdiratW(self.fd, sub_path, default_new_dir_mode); |
| 1460 | 1460 | } |
| 1461 | 1461 | |
| 1462 | | /// Calls makeDir recursively to make an entire path. Returns success if the path |
| 1463 | | /// already exists and is a directory. |
| 1462 | /// Calls makeDir iteratively to make an entire path |
| 1463 | /// (i.e. creating any parent directories that do not exist). |
| 1464 | /// Returns success if the path already exists and is a directory. |
| 1464 | 1465 | /// This function is not atomic, and if it returns an error, the file system may |
| 1465 | 1466 | /// have been modified regardless. |
| 1466 | 1467 | pub fn makePath(self: Dir, sub_path: []const u8) !void { |
| ... | ... | @@ -1483,22 +1484,89 @@ pub const Dir = struct { |
| 1483 | 1484 | } |
| 1484 | 1485 | } |
| 1485 | 1486 | |
| 1487 | /// Calls makeOpenDirAccessMaskW iteratively to make an entire path |
| 1488 | /// (i.e. creating any parent directories that do not exist). |
| 1489 | /// Opens the dir if the path already exists and is a directory. |
| 1490 | /// This function is not atomic, and if it returns an error, the file system may |
| 1491 | /// have been modified regardless. |
| 1492 | fn makeOpenPathAccessMaskW(self: Dir, sub_path: []const u8, access_mask: u32, no_follow: bool) OpenError!Dir { |
| 1493 | const w = os.windows; |
| 1494 | var it = try path.componentIterator(sub_path); |
| 1495 | // If there are no components in the path, then create a dummy component with the full path. |
| 1496 | var component = it.last() orelse path.NativeUtf8ComponentIterator.Component{ |
| 1497 | .name = "", |
| 1498 | .path = sub_path, |
| 1499 | }; |
| 1500 | |
| 1501 | while (true) { |
| 1502 | const sub_path_w = try w.sliceToPrefixedFileW(self.fd, component.path); |
| 1503 | const is_last = it.peekNext() == null; |
| 1504 | var result = self.makeOpenDirAccessMaskW(sub_path_w.span().ptr, access_mask, .{ |
| 1505 | .no_follow = no_follow, |
| 1506 | .create_disposition = if (is_last) w.FILE_OPEN_IF else w.FILE_CREATE, |
| 1507 | }) catch |err| switch (err) { |
| 1508 | error.FileNotFound => |e| { |
| 1509 | component = it.previous() orelse return e; |
| 1510 | continue; |
| 1511 | }, |
| 1512 | else => |e| return e, |
| 1513 | }; |
| 1514 | |
| 1515 | component = it.next() orelse return result; |
| 1516 | // Don't leak the intermediate file handles |
| 1517 | result.close(); |
| 1518 | } |
| 1519 | } |
| 1520 | |
| 1486 | 1521 | /// This function performs `makePath`, followed by `openDir`. |
| 1487 | 1522 | /// If supported by the OS, this operation is atomic. It is not atomic on |
| 1488 | 1523 | /// all operating systems. |
| 1524 | /// On Windows, this function performs `makeOpenPathAccessMaskW`. |
| 1489 | 1525 | 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); |
| 1526 | return switch (builtin.os.tag) { |
| 1527 | .windows => { |
| 1528 | const w = os.windows; |
| 1529 | const base_flags = w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA | |
| 1530 | w.SYNCHRONIZE | w.FILE_TRAVERSE; |
| 1531 | |
| 1532 | return self.makeOpenPathAccessMaskW(sub_path, base_flags, open_dir_options.no_follow); |
| 1533 | }, |
| 1534 | else => { |
| 1535 | return self.openDir(sub_path, open_dir_options) catch |err| switch (err) { |
| 1536 | error.FileNotFound => { |
| 1537 | try self.makePath(sub_path); |
| 1538 | return self.openDir(sub_path, open_dir_options); |
| 1539 | }, |
| 1540 | else => |e| return e, |
| 1541 | }; |
| 1542 | }, |
| 1543 | }; |
| 1493 | 1544 | } |
| 1494 | 1545 | |
| 1495 | 1546 | /// This function performs `makePath`, followed by `openIterableDir`. |
| 1496 | 1547 | /// If supported by the OS, this operation is atomic. It is not atomic on |
| 1497 | 1548 | /// all operating systems. |
| 1498 | 1549 | 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); |
| 1550 | return switch (builtin.os.tag) { |
| 1551 | .windows => { |
| 1552 | const w = os.windows; |
| 1553 | const base_flags = w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA | |
| 1554 | w.SYNCHRONIZE | w.FILE_TRAVERSE | w.FILE_LIST_DIRECTORY; |
| 1555 | |
| 1556 | return IterableDir{ |
| 1557 | .dir = try self.makeOpenPathAccessMaskW(sub_path, base_flags, open_dir_options.no_follow), |
| 1558 | }; |
| 1559 | }, |
| 1560 | else => { |
| 1561 | return self.openIterableDir(sub_path, open_dir_options) catch |err| switch (err) { |
| 1562 | error.FileNotFound => { |
| 1563 | try self.makePath(sub_path); |
| 1564 | return self.openIterableDir(sub_path, open_dir_options); |
| 1565 | }, |
| 1566 | else => |e| return e, |
| 1567 | }; |
| 1568 | }, |
| 1569 | }; |
| 1502 | 1570 | } |
| 1503 | 1571 | |
| 1504 | 1572 | /// This function returns the canonicalized absolute pathname of |
| ... | ... | @@ -1742,7 +1810,10 @@ pub const Dir = struct { |
| 1742 | 1810 | const base_flags = w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA | |
| 1743 | 1811 | w.SYNCHRONIZE | w.FILE_TRAVERSE; |
| 1744 | 1812 | 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); |
| 1813 | var dir = try self.makeOpenDirAccessMaskW(sub_path_w, flags, .{ |
| 1814 | .no_follow = args.no_follow, |
| 1815 | .create_disposition = w.FILE_OPEN, |
| 1816 | }); |
| 1746 | 1817 | return dir; |
| 1747 | 1818 | } |
| 1748 | 1819 | |
| ... | ... | @@ -1765,7 +1836,12 @@ pub const Dir = struct { |
| 1765 | 1836 | return Dir{ .fd = fd }; |
| 1766 | 1837 | } |
| 1767 | 1838 | |
| 1768 | | fn openDirAccessMaskW(self: Dir, sub_path_w: [*:0]const u16, access_mask: u32, no_follow: bool) OpenError!Dir { |
| 1839 | const MakeOpenDirAccessMaskWOptions = struct { |
| 1840 | no_follow: bool, |
| 1841 | create_disposition: u32, |
| 1842 | }; |
| 1843 | |
| 1844 | fn makeOpenDirAccessMaskW(self: Dir, sub_path_w: [*:0]const u16, access_mask: u32, flags: MakeOpenDirAccessMaskWOptions) OpenError!Dir { |
| 1769 | 1845 | const w = os.windows; |
| 1770 | 1846 | |
| 1771 | 1847 | var result = Dir{ |
| ... | ... | @@ -1786,7 +1862,7 @@ pub const Dir = struct { |
| 1786 | 1862 | .SecurityDescriptor = null, |
| 1787 | 1863 | .SecurityQualityOfService = null, |
| 1788 | 1864 | }; |
| 1789 | | const open_reparse_point: w.DWORD = if (no_follow) w.FILE_OPEN_REPARSE_POINT else 0x0; |
| 1865 | const open_reparse_point: w.DWORD = if (flags.no_follow) w.FILE_OPEN_REPARSE_POINT else 0x0; |
| 1790 | 1866 | var io: w.IO_STATUS_BLOCK = undefined; |
| 1791 | 1867 | const rc = w.ntdll.NtCreateFile( |
| 1792 | 1868 | &result.fd, |
| ... | ... | @@ -1794,13 +1870,14 @@ pub const Dir = struct { |
| 1794 | 1870 | &attr, |
| 1795 | 1871 | &io, |
| 1796 | 1872 | null, |
| 1797 | | 0, |
| 1873 | w.FILE_ATTRIBUTE_NORMAL, |
| 1798 | 1874 | w.FILE_SHARE_READ | w.FILE_SHARE_WRITE, |
| 1799 | | w.FILE_OPEN, |
| 1875 | flags.create_disposition, |
| 1800 | 1876 | w.FILE_DIRECTORY_FILE | w.FILE_SYNCHRONOUS_IO_NONALERT | w.FILE_OPEN_FOR_BACKUP_INTENT | open_reparse_point, |
| 1801 | 1877 | null, |
| 1802 | 1878 | 0, |
| 1803 | 1879 | ); |
| 1880 | |
| 1804 | 1881 | switch (rc) { |
| 1805 | 1882 | .SUCCESS => return result, |
| 1806 | 1883 | .OBJECT_NAME_INVALID => return error.BadPathName, |