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