| ... | ... | @@ -708,7 +708,12 @@ pub const Dir = struct { |
| 708 | 708 | const access_mask = w.SYNCHRONIZE | |
| 709 | 709 | (if (flags.read) @as(u32, w.GENERIC_READ) else 0) | |
| 710 | 710 | (if (flags.write) @as(u32, w.GENERIC_WRITE) else 0); |
| 711 | | return self.openFileWindows(sub_path_w, access_mask, w.FILE_OPEN); |
| 711 | const share_access = if (flags.lock) |
| 712 | w.FILE_SHARE_DELETE | |
| 713 | (if (flags.write) @as(os.windows.ULONG, 0) else w.FILE_SHARE_READ) |
| 714 | else |
| 715 | null; |
| 716 | return self.openFileWindows(sub_path_w, access_mask, share_access, w.FILE_OPEN); |
| 712 | 717 | } |
| 713 | 718 | |
| 714 | 719 | /// Creates, opens, or overwrites a file with write access. |
| ... | ... | @@ -753,7 +758,7 @@ pub const Dir = struct { |
| 753 | 758 | @as(u32, w.FILE_OVERWRITE_IF) |
| 754 | 759 | else |
| 755 | 760 | @as(u32, w.FILE_OPEN_IF); |
| 756 | | return self.openFileWindows(sub_path_w, access_mask, creation); |
| 761 | return self.openFileWindows(sub_path_w, access_mask, null, creation); |
| 757 | 762 | } |
| 758 | 763 | |
| 759 | 764 | /// Deprecated; call `openFile` directly. |
| ... | ... | @@ -775,65 +780,80 @@ pub const Dir = struct { |
| 775 | 780 | self: Dir, |
| 776 | 781 | sub_path_w: [*:0]const u16, |
| 777 | 782 | access_mask: os.windows.ACCESS_MASK, |
| 783 | share_access_opt: ?os.windows.ULONG, |
| 778 | 784 | creation: os.windows.ULONG, |
| 779 | 785 | ) File.OpenError!File { |
| 780 | | const w = os.windows; |
| 786 | var delay: usize = 1; |
| 787 | while (true) { |
| 788 | const w = os.windows; |
| 781 | 789 | |
| 782 | | if (sub_path_w[0] == '.' and sub_path_w[1] == 0) { |
| 783 | | return error.IsDir; |
| 784 | | } |
| 785 | | if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) { |
| 786 | | return error.IsDir; |
| 787 | | } |
| 790 | if (sub_path_w[0] == '.' and sub_path_w[1] == 0) { |
| 791 | return error.IsDir; |
| 792 | } |
| 793 | if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) { |
| 794 | return error.IsDir; |
| 795 | } |
| 788 | 796 | |
| 789 | | var result = File{ |
| 790 | | .handle = undefined, |
| 791 | | .io_mode = .blocking, |
| 792 | | }; |
| 797 | var result = File{ |
| 798 | .handle = undefined, |
| 799 | .io_mode = .blocking, |
| 800 | }; |
| 793 | 801 | |
| 794 | | const path_len_bytes = math.cast(u16, mem.toSliceConst(u16, sub_path_w).len * 2) catch |err| switch (err) { |
| 795 | | error.Overflow => return error.NameTooLong, |
| 796 | | }; |
| 797 | | var nt_name = w.UNICODE_STRING{ |
| 798 | | .Length = path_len_bytes, |
| 799 | | .MaximumLength = path_len_bytes, |
| 800 | | .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)), |
| 801 | | }; |
| 802 | | var attr = w.OBJECT_ATTRIBUTES{ |
| 803 | | .Length = @sizeOf(w.OBJECT_ATTRIBUTES), |
| 804 | | .RootDirectory = if (path.isAbsoluteWindowsW(sub_path_w)) null else self.fd, |
| 805 | | .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here. |
| 806 | | .ObjectName = &nt_name, |
| 807 | | .SecurityDescriptor = null, |
| 808 | | .SecurityQualityOfService = null, |
| 809 | | }; |
| 810 | | var io: w.IO_STATUS_BLOCK = undefined; |
| 811 | | const rc = w.ntdll.NtCreateFile( |
| 812 | | &result.handle, |
| 813 | | access_mask, |
| 814 | | &attr, |
| 815 | | &io, |
| 816 | | null, |
| 817 | | w.FILE_ATTRIBUTE_NORMAL, |
| 818 | | w.FILE_SHARE_WRITE | w.FILE_SHARE_READ | w.FILE_SHARE_DELETE, |
| 819 | | creation, |
| 820 | | w.FILE_NON_DIRECTORY_FILE | w.FILE_SYNCHRONOUS_IO_NONALERT, |
| 821 | | null, |
| 822 | | 0, |
| 823 | | ); |
| 824 | | switch (rc) { |
| 825 | | .SUCCESS => return result, |
| 826 | | .OBJECT_NAME_INVALID => unreachable, |
| 827 | | .OBJECT_NAME_NOT_FOUND => return error.FileNotFound, |
| 828 | | .OBJECT_PATH_NOT_FOUND => return error.FileNotFound, |
| 829 | | .NO_MEDIA_IN_DEVICE => return error.NoDevice, |
| 830 | | .INVALID_PARAMETER => unreachable, |
| 831 | | .SHARING_VIOLATION => return error.SharingViolation, |
| 832 | | .ACCESS_DENIED => return error.AccessDenied, |
| 833 | | .PIPE_BUSY => return error.PipeBusy, |
| 834 | | .OBJECT_PATH_SYNTAX_BAD => unreachable, |
| 835 | | .OBJECT_NAME_COLLISION => return error.PathAlreadyExists, |
| 836 | | else => return w.unexpectedStatus(rc), |
| 802 | const path_len_bytes = math.cast(u16, mem.toSliceConst(u16, sub_path_w).len * 2) catch |err| switch (err) { |
| 803 | error.Overflow => return error.NameTooLong, |
| 804 | }; |
| 805 | var nt_name = w.UNICODE_STRING{ |
| 806 | .Length = path_len_bytes, |
| 807 | .MaximumLength = path_len_bytes, |
| 808 | .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)), |
| 809 | }; |
| 810 | var attr = w.OBJECT_ATTRIBUTES{ |
| 811 | .Length = @sizeOf(w.OBJECT_ATTRIBUTES), |
| 812 | .RootDirectory = if (path.isAbsoluteWindowsW(sub_path_w)) null else self.fd, |
| 813 | .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here. |
| 814 | .ObjectName = &nt_name, |
| 815 | .SecurityDescriptor = null, |
| 816 | .SecurityQualityOfService = null, |
| 817 | }; |
| 818 | var io: w.IO_STATUS_BLOCK = undefined; |
| 819 | const share_access = share_access_opt orelse w.FILE_SHARE_WRITE | w.FILE_SHARE_READ | w.FILE_SHARE_DELETE; |
| 820 | const rc = w.ntdll.NtCreateFile( |
| 821 | &result.handle, |
| 822 | access_mask, |
| 823 | &attr, |
| 824 | &io, |
| 825 | null, |
| 826 | w.FILE_ATTRIBUTE_NORMAL, |
| 827 | share_access, |
| 828 | creation, |
| 829 | w.FILE_NON_DIRECTORY_FILE | w.FILE_SYNCHRONOUS_IO_NONALERT, |
| 830 | null, |
| 831 | 0, |
| 832 | ); |
| 833 | switch (rc) { |
| 834 | .SUCCESS => return result, |
| 835 | .OBJECT_NAME_INVALID => unreachable, |
| 836 | .OBJECT_NAME_NOT_FOUND => return error.FileNotFound, |
| 837 | .OBJECT_PATH_NOT_FOUND => return error.FileNotFound, |
| 838 | .NO_MEDIA_IN_DEVICE => return error.NoDevice, |
| 839 | .INVALID_PARAMETER => unreachable, |
| 840 | .SHARING_VIOLATION => { |
| 841 | // TODO: check if async or blocking |
| 842 | //return error.SharingViolation |
| 843 | // Sleep so we don't consume a ton of CPU waiting to get lock on file |
| 844 | std.time.sleep(delay); |
| 845 | // Increase sleep time as long as it is less than 5 seconds |
| 846 | if (delay < 5 * std.time.ns_per_s) { |
| 847 | delay *= 2; |
| 848 | } |
| 849 | continue; |
| 850 | }, |
| 851 | .ACCESS_DENIED => return error.AccessDenied, |
| 852 | .PIPE_BUSY => return error.PipeBusy, |
| 853 | .OBJECT_PATH_SYNTAX_BAD => unreachable, |
| 854 | .OBJECT_NAME_COLLISION => return error.PathAlreadyExists, |
| 855 | else => return w.unexpectedStatus(rc), |
| 856 | } |
| 837 | 857 | } |
| 838 | 858 | } |
| 839 | 859 | |