Implement blocking file locking API for windows

master
LeRoyce Pearson 2020-03-09 20:48:00 -06:00
parent 9af0590a28
commit e1868029e9
4 changed files with 80 additions and 59 deletions

View File

@ -708,7 +708,12 @@ pub const Dir = struct {
const access_mask = w.SYNCHRONIZE | const access_mask = w.SYNCHRONIZE |
(if (flags.read) @as(u32, w.GENERIC_READ) else 0) | (if (flags.read) @as(u32, w.GENERIC_READ) else 0) |
(if (flags.write) @as(u32, w.GENERIC_WRITE) else 0); (if (flags.write) @as(u32, w.GENERIC_WRITE) else 0);
return self.openFileWindows(sub_path_w, access_mask, w.FILE_OPEN); const share_access = if (flags.lock)
w.FILE_SHARE_DELETE |
(if (flags.write) @as(os.windows.ULONG, 0) else w.FILE_SHARE_READ)
else
null;
return self.openFileWindows(sub_path_w, access_mask, share_access, w.FILE_OPEN);
} }
/// Creates, opens, or overwrites a file with write access. /// Creates, opens, or overwrites a file with write access.
@ -753,7 +758,7 @@ pub const Dir = struct {
@as(u32, w.FILE_OVERWRITE_IF) @as(u32, w.FILE_OVERWRITE_IF)
else else
@as(u32, w.FILE_OPEN_IF); @as(u32, w.FILE_OPEN_IF);
return self.openFileWindows(sub_path_w, access_mask, creation); return self.openFileWindows(sub_path_w, access_mask, null, creation);
} }
/// Deprecated; call `openFile` directly. /// Deprecated; call `openFile` directly.
@ -775,65 +780,80 @@ pub const Dir = struct {
self: Dir, self: Dir,
sub_path_w: [*:0]const u16, sub_path_w: [*:0]const u16,
access_mask: os.windows.ACCESS_MASK, access_mask: os.windows.ACCESS_MASK,
share_access_opt: ?os.windows.ULONG,
creation: os.windows.ULONG, creation: os.windows.ULONG,
) File.OpenError!File { ) File.OpenError!File {
const w = os.windows; var delay: usize = 1;
while (true) {
const w = os.windows;
if (sub_path_w[0] == '.' and sub_path_w[1] == 0) { if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
return error.IsDir; return error.IsDir;
} }
if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) { if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) {
return error.IsDir; return error.IsDir;
} }
var result = File{ var result = File{
.handle = undefined, .handle = undefined,
.io_mode = .blocking, .io_mode = .blocking,
}; };
const path_len_bytes = math.cast(u16, mem.toSliceConst(u16, sub_path_w).len * 2) catch |err| switch (err) { const path_len_bytes = math.cast(u16, mem.toSliceConst(u16, sub_path_w).len * 2) catch |err| switch (err) {
error.Overflow => return error.NameTooLong, error.Overflow => return error.NameTooLong,
}; };
var nt_name = w.UNICODE_STRING{ var nt_name = w.UNICODE_STRING{
.Length = path_len_bytes, .Length = path_len_bytes,
.MaximumLength = path_len_bytes, .MaximumLength = path_len_bytes,
.Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)), .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)),
}; };
var attr = w.OBJECT_ATTRIBUTES{ var attr = w.OBJECT_ATTRIBUTES{
.Length = @sizeOf(w.OBJECT_ATTRIBUTES), .Length = @sizeOf(w.OBJECT_ATTRIBUTES),
.RootDirectory = if (path.isAbsoluteWindowsW(sub_path_w)) null else self.fd, .RootDirectory = if (path.isAbsoluteWindowsW(sub_path_w)) null else self.fd,
.Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here. .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
.ObjectName = &nt_name, .ObjectName = &nt_name,
.SecurityDescriptor = null, .SecurityDescriptor = null,
.SecurityQualityOfService = null, .SecurityQualityOfService = null,
}; };
var io: w.IO_STATUS_BLOCK = undefined; var io: w.IO_STATUS_BLOCK = undefined;
const rc = w.ntdll.NtCreateFile( const share_access = share_access_opt orelse w.FILE_SHARE_WRITE | w.FILE_SHARE_READ | w.FILE_SHARE_DELETE;
&result.handle, const rc = w.ntdll.NtCreateFile(
access_mask, &result.handle,
&attr, access_mask,
&io, &attr,
null, &io,
w.FILE_ATTRIBUTE_NORMAL, null,
w.FILE_SHARE_WRITE | w.FILE_SHARE_READ | w.FILE_SHARE_DELETE, w.FILE_ATTRIBUTE_NORMAL,
creation, share_access,
w.FILE_NON_DIRECTORY_FILE | w.FILE_SYNCHRONOUS_IO_NONALERT, creation,
null, w.FILE_NON_DIRECTORY_FILE | w.FILE_SYNCHRONOUS_IO_NONALERT,
0, null,
); 0,
switch (rc) { );
.SUCCESS => return result, switch (rc) {
.OBJECT_NAME_INVALID => unreachable, .SUCCESS => return result,
.OBJECT_NAME_NOT_FOUND => return error.FileNotFound, .OBJECT_NAME_INVALID => unreachable,
.OBJECT_PATH_NOT_FOUND => return error.FileNotFound, .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
.NO_MEDIA_IN_DEVICE => return error.NoDevice, .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
.INVALID_PARAMETER => unreachable, .NO_MEDIA_IN_DEVICE => return error.NoDevice,
.SHARING_VIOLATION => return error.SharingViolation, .INVALID_PARAMETER => unreachable,
.ACCESS_DENIED => return error.AccessDenied, .SHARING_VIOLATION => {
.PIPE_BUSY => return error.PipeBusy, // TODO: check if async or blocking
.OBJECT_PATH_SYNTAX_BAD => unreachable, //return error.SharingViolation
.OBJECT_NAME_COLLISION => return error.PathAlreadyExists, // Sleep so we don't consume a ton of CPU waiting to get lock on file
else => return w.unexpectedStatus(rc), std.time.sleep(delay);
// Increase sleep time as long as it is less than 5 seconds
if (delay < 5 * std.time.ns_per_s) {
delay *= 2;
}
continue;
},
.ACCESS_DENIED => return error.AccessDenied,
.PIPE_BUSY => return error.PipeBusy,
.OBJECT_PATH_SYNTAX_BAD => unreachable,
.OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
else => return w.unexpectedStatus(rc),
}
} }
} }

View File

@ -1141,8 +1141,8 @@ pub fn freeNullDelimitedEnvMap(allocator: *mem.Allocator, envp_buf: []?[*:0]u8)
} }
/// Attempts to get lock the file, blocking if the file is locked. /// Attempts to get lock the file, blocking if the file is locked.
pub fn fcntlFlockBlocking(fd: fd_t, flock_p: *flock) OpenError!void { pub fn fcntlFlockBlocking(fd: fd_t, flock_p: *const flock) OpenError!void {
const rc = system.fcntlFlock(fd, F_SETLKW, flock_p); const rc = system.fcntl(fd, F_SETLKW, flock_p);
if (rc < 0) { if (rc < 0) {
std.debug.panic("fcntl error: {}\n", .{rc}); std.debug.panic("fcntl error: {}\n", .{rc});
} }

View File

@ -8,6 +8,7 @@ const iovec = linux.iovec;
const iovec_const = linux.iovec_const; const iovec_const = linux.iovec_const;
const uid_t = linux.uid_t; const uid_t = linux.uid_t;
const gid_t = linux.gid_t; const gid_t = linux.gid_t;
const pid_t = linux.pid_t;
const stack_t = linux.stack_t; const stack_t = linux.stack_t;
const sigset_t = linux.sigset_t; const sigset_t = linux.sigset_t;

View File

@ -219,7 +219,7 @@ pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, of
} }
} }
pub fn fcntlFlock(fd: fd_t, cmd: i32, flock_p: *flock) usize { pub fn fcntl(fd: fd_t, cmd: i32, flock_p: *const c_void) usize {
return syscall3(SYS_fcntl, @bitCast(usize, @as(isize, fd)), @bitCast(usize, @as(isize, cmd)), @ptrToInt(flock_p)); return syscall3(SYS_fcntl, @bitCast(usize, @as(isize, fd)), @bitCast(usize, @as(isize, cmd)), @ptrToInt(flock_p));
} }