authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-11-27 14:35:30-08:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-11-27 14:35:30-08:00
log9e4360c335c543ad4ff80791257eabfc556f3ac1
tree15ee4c35a1cc00dc84aa4956b26bd909422f9b89
parentf91df39ad2a54bc5367ba562ca703e564dd4a38d
parent8ed75614223ad86de26ddde9b9dda1ccbeb7d8d3
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #7198 from LemonBoy/freebsd-lock

std: Fix file locking logic for BSD targets

4 files changed, 57 insertions(+), 25 deletions(-)

lib/std/child_process.zig+1
...@@ -368,6 +368,7 @@ pub const ChildProcess = struct {...@@ -368,6 +368,7 @@ pub const ChildProcess = struct {
368 error.DeviceBusy => unreachable,368 error.DeviceBusy => unreachable,
369 error.FileLocksNotSupported => unreachable,369 error.FileLocksNotSupported => unreachable,
370 error.BadPathName => unreachable, // Windows-only370 error.BadPathName => unreachable, // Windows-only
371 error.WouldBlock => unreachable,
371 else => |e| return e,372 else => |e| return e,
372 }373 }
373 else374 else
lib/std/fs.zig+52-13
...@@ -405,7 +405,7 @@ pub const Dir = struct {...@@ -405,7 +405,7 @@ pub const Dir = struct {
405 else => false,405 else => false,
406 };406 };
407 if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..") or407 if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..") or
408 (skip_zero_fileno and bsd_entry.d_fileno == 0))408 (skip_zero_fileno and bsd_entry.d_fileno == 0))
409 {409 {
410 continue :start_over;410 continue :start_over;
411 }411 }
...@@ -729,14 +729,16 @@ pub const Dir = struct {...@@ -729,14 +729,16 @@ pub const Dir = struct {
729 }729 }
730730
731 var os_flags: u32 = os.O_CLOEXEC;731 var os_flags: u32 = os.O_CLOEXEC;
732 // Use the O_ locking flags if the os supports them732 // Use the O_ locking flags if the os supports them to acquire the lock
733 // (Or if it's darwin, as darwin's `open` doesn't support the O_SYNC flag)733 // atomically.
734 const has_flock_open_flags = @hasDecl(os, "O_EXLOCK") and !is_darwin;734 const has_flock_open_flags = @hasDecl(os, "O_EXLOCK");
735 if (has_flock_open_flags) {735 if (has_flock_open_flags) {
736 const nonblocking_lock_flag = if (flags.lock_nonblocking)736 // Note that the O_NONBLOCK flag is removed after the openat() call
737 os.O_NONBLOCK | os.O_SYNC737 // is successful.
738 const nonblocking_lock_flag: u32 = if (flags.lock_nonblocking)
739 os.O_NONBLOCK
738 else740 else
739 @as(u32, 0);741 0;
740 os_flags |= switch (flags.lock) {742 os_flags |= switch (flags.lock) {
741 .None => @as(u32, 0),743 .None => @as(u32, 0),
742 .Shared => os.O_SHLOCK | nonblocking_lock_flag,744 .Shared => os.O_SHLOCK | nonblocking_lock_flag,
...@@ -771,6 +773,22 @@ pub const Dir = struct {...@@ -771,6 +773,22 @@ pub const Dir = struct {
771 });773 });
772 }774 }
773775
776 if (has_flock_open_flags and flags.lock_nonblocking) {
777 var fl_flags = os.fcntl(fd, os.F_GETFL, 0) catch |err| switch (err) {
778 error.FileBusy => unreachable,
779 error.Locked => unreachable,
780 error.PermissionDenied => unreachable,
781 else => |e| return e,
782 };
783 fl_flags &= ~@as(usize, os.O_NONBLOCK);
784 _ = os.fcntl(fd, os.F_SETFL, fl_flags) catch |err| switch (err) {
785 error.FileBusy => unreachable,
786 error.Locked => unreachable,
787 error.PermissionDenied => unreachable,
788 else => |e| return e,
789 };
790 }
791
774 return File{792 return File{
775 .handle = fd,793 .handle = fd,
776 .capable_io_mode = .blocking,794 .capable_io_mode = .blocking,
...@@ -854,17 +872,19 @@ pub const Dir = struct {...@@ -854,17 +872,19 @@ pub const Dir = struct {
854 return self.createFileW(path_w.span(), flags);872 return self.createFileW(path_w.span(), flags);
855 }873 }
856874
857 // Use the O_ locking flags if the os supports them875 // Use the O_ locking flags if the os supports them to acquire the lock
858 // (Or if it's darwin, as darwin's `open` doesn't support the O_SYNC flag)876 // atomically.
859 const has_flock_open_flags = @hasDecl(os, "O_EXLOCK") and !is_darwin;877 const has_flock_open_flags = @hasDecl(os, "O_EXLOCK");
878 // Note that the O_NONBLOCK flag is removed after the openat() call
879 // is successful.
860 const nonblocking_lock_flag: u32 = if (has_flock_open_flags and flags.lock_nonblocking)880 const nonblocking_lock_flag: u32 = if (has_flock_open_flags and flags.lock_nonblocking)
861 os.O_NONBLOCK | os.O_SYNC881 os.O_NONBLOCK
862 else882 else
863 0;883 0;
864 const lock_flag: u32 = if (has_flock_open_flags) switch (flags.lock) {884 const lock_flag: u32 = if (has_flock_open_flags) switch (flags.lock) {
865 .None => @as(u32, 0),885 .None => @as(u32, 0),
866 .Shared => os.O_SHLOCK,886 .Shared => os.O_SHLOCK | nonblocking_lock_flag,
867 .Exclusive => os.O_EXLOCK,887 .Exclusive => os.O_EXLOCK | nonblocking_lock_flag,
868 } else 0;888 } else 0;
869889
870 const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0;890 const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0;
...@@ -876,6 +896,7 @@ pub const Dir = struct {...@@ -876,6 +896,7 @@ pub const Dir = struct {
876 try std.event.Loop.instance.?.openatZ(self.fd, sub_path_c, os_flags, flags.mode)896 try std.event.Loop.instance.?.openatZ(self.fd, sub_path_c, os_flags, flags.mode)
877 else897 else
878 try os.openatZ(self.fd, sub_path_c, os_flags, flags.mode);898 try os.openatZ(self.fd, sub_path_c, os_flags, flags.mode);
899 errdefer os.close(fd);
879900
880 if (!has_flock_open_flags and flags.lock != .None) {901 if (!has_flock_open_flags and flags.lock != .None) {
881 // TODO: integrate async I/O902 // TODO: integrate async I/O
...@@ -887,6 +908,22 @@ pub const Dir = struct {...@@ -887,6 +908,22 @@ pub const Dir = struct {
887 });908 });
888 }909 }
889910
911 if (has_flock_open_flags and flags.lock_nonblocking) {
912 var fl_flags = os.fcntl(fd, os.F_GETFL, 0) catch |err| switch (err) {
913 error.FileBusy => unreachable,
914 error.Locked => unreachable,
915 error.PermissionDenied => unreachable,
916 else => |e| return e,
917 };
918 fl_flags &= ~@as(usize, os.O_NONBLOCK);
919 _ = os.fcntl(fd, os.F_SETFL, fl_flags) catch |err| switch (err) {
920 error.FileBusy => unreachable,
921 error.Locked => unreachable,
922 error.PermissionDenied => unreachable,
923 else => |e| return e,
924 };
925 }
926
890 return File{927 return File{
891 .handle = fd,928 .handle = fd,
892 .capable_io_mode = .blocking,929 .capable_io_mode = .blocking,
...@@ -1178,6 +1215,7 @@ pub const Dir = struct {...@@ -1178,6 +1215,7 @@ pub const Dir = struct {
1178 error.NoSpaceLeft => unreachable, // not providing O_CREAT1215 error.NoSpaceLeft => unreachable, // not providing O_CREAT
1179 error.PathAlreadyExists => unreachable, // not providing O_CREAT1216 error.PathAlreadyExists => unreachable, // not providing O_CREAT
1180 error.FileLocksNotSupported => unreachable, // locking folders is not supported1217 error.FileLocksNotSupported => unreachable, // locking folders is not supported
1218 error.WouldBlock => unreachable, // can't happen for directories
1181 else => |e| return e,1219 else => |e| return e,
1182 };1220 };
1183 return Dir{ .fd = fd };1221 return Dir{ .fd = fd };
...@@ -1221,6 +1259,7 @@ pub const Dir = struct {...@@ -1221,6 +1259,7 @@ pub const Dir = struct {
1221 error.NoSpaceLeft => unreachable, // not providing O_CREAT1259 error.NoSpaceLeft => unreachable, // not providing O_CREAT
1222 error.PathAlreadyExists => unreachable, // not providing O_CREAT1260 error.PathAlreadyExists => unreachable, // not providing O_CREAT
1223 error.FileLocksNotSupported => unreachable, // locking folders is not supported1261 error.FileLocksNotSupported => unreachable, // locking folders is not supported
1262 error.WouldBlock => unreachable, // can't happen for directories
1224 else => |e| return e,1263 else => |e| return e,
1225 };1264 };
1226 return Dir{ .fd = fd };1265 return Dir{ .fd = fd };
lib/std/fs/test.zig-12
...@@ -691,9 +691,6 @@ test "realpath" {...@@ -691,9 +691,6 @@ test "realpath" {
691test "open file with exclusive nonblocking lock twice" {691test "open file with exclusive nonblocking lock twice" {
692 if (builtin.os.tag == .wasi) return error.SkipZigTest;692 if (builtin.os.tag == .wasi) return error.SkipZigTest;
693693
694 // TODO: fix this test on FreeBSD. https://github.com/ziglang/zig/issues/1759
695 if (builtin.os.tag == .freebsd) return error.SkipZigTest;
696
697 const filename = "file_nonblocking_lock_test.txt";694 const filename = "file_nonblocking_lock_test.txt";
698695
699 var tmp = tmpDir(.{});696 var tmp = tmpDir(.{});
...@@ -709,9 +706,6 @@ test "open file with exclusive nonblocking lock twice" {...@@ -709,9 +706,6 @@ test "open file with exclusive nonblocking lock twice" {
709test "open file with shared and exclusive nonblocking lock" {706test "open file with shared and exclusive nonblocking lock" {
710 if (builtin.os.tag == .wasi) return error.SkipZigTest;707 if (builtin.os.tag == .wasi) return error.SkipZigTest;
711708
712 // TODO: fix this test on FreeBSD. https://github.com/ziglang/zig/issues/1759
713 if (builtin.os.tag == .freebsd) return error.SkipZigTest;
714
715 const filename = "file_nonblocking_lock_test.txt";709 const filename = "file_nonblocking_lock_test.txt";
716710
717 var tmp = tmpDir(.{});711 var tmp = tmpDir(.{});
...@@ -727,9 +721,6 @@ test "open file with shared and exclusive nonblocking lock" {...@@ -727,9 +721,6 @@ test "open file with shared and exclusive nonblocking lock" {
727test "open file with exclusive and shared nonblocking lock" {721test "open file with exclusive and shared nonblocking lock" {
728 if (builtin.os.tag == .wasi) return error.SkipZigTest;722 if (builtin.os.tag == .wasi) return error.SkipZigTest;
729723
730 // TODO: fix this test on FreeBSD. https://github.com/ziglang/zig/issues/1759
731 if (builtin.os.tag == .freebsd) return error.SkipZigTest;
732
733 const filename = "file_nonblocking_lock_test.txt";724 const filename = "file_nonblocking_lock_test.txt";
734725
735 var tmp = tmpDir(.{});726 var tmp = tmpDir(.{});
...@@ -791,9 +782,6 @@ test "open file with exclusive lock twice, make sure it waits" {...@@ -791,9 +782,6 @@ test "open file with exclusive lock twice, make sure it waits" {
791test "open file with exclusive nonblocking lock twice (absolute paths)" {782test "open file with exclusive nonblocking lock twice (absolute paths)" {
792 if (builtin.os.tag == .wasi) return error.SkipZigTest;783 if (builtin.os.tag == .wasi) return error.SkipZigTest;
793784
794 // TODO: fix this test on FreeBSD. https://github.com/ziglang/zig/issues/1759
795 if (builtin.os.tag == .freebsd) return error.SkipZigTest;
796
797 const allocator = testing.allocator;785 const allocator = testing.allocator;
798786
799 const file_paths: [1][]const u8 = .{"zig-test-absolute-paths.txt"};787 const file_paths: [1][]const u8 = .{"zig-test-absolute-paths.txt"};
lib/std/os.zig+4
...@@ -1020,6 +1020,8 @@ pub const OpenError = error{...@@ -1020,6 +1020,8 @@ pub const OpenError = error{
10201020
1021 BadPathName,1021 BadPathName,
1022 InvalidUtf8,1022 InvalidUtf8,
1023
1024 WouldBlock,
1023} || UnexpectedError;1025} || UnexpectedError;
10241026
1025/// Open and possibly create a file. Keeps trying if it gets interrupted.1027/// Open and possibly create a file. Keeps trying if it gets interrupted.
...@@ -1201,6 +1203,7 @@ pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: u32, mode: mode_t)...@@ -1201,6 +1203,7 @@ pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: u32, mode: mode_t)
1201 EEXIST => return error.PathAlreadyExists,1203 EEXIST => return error.PathAlreadyExists,
1202 EBUSY => return error.DeviceBusy,1204 EBUSY => return error.DeviceBusy,
1203 EOPNOTSUPP => return error.FileLocksNotSupported,1205 EOPNOTSUPP => return error.FileLocksNotSupported,
1206 EWOULDBLOCK => return error.WouldBlock,
1204 else => |err| return unexpectedErrno(err),1207 else => |err| return unexpectedErrno(err),
1205 }1208 }
1206 }1209 }
...@@ -4187,6 +4190,7 @@ pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealP...@@ -4187,6 +4190,7 @@ pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealP
4187 const flags = if (builtin.os.tag == .linux) O_PATH | O_NONBLOCK | O_CLOEXEC else O_NONBLOCK | O_CLOEXEC;4190 const flags = if (builtin.os.tag == .linux) O_PATH | O_NONBLOCK | O_CLOEXEC else O_NONBLOCK | O_CLOEXEC;
4188 const fd = openZ(pathname, flags, 0) catch |err| switch (err) {4191 const fd = openZ(pathname, flags, 0) catch |err| switch (err) {
4189 error.FileLocksNotSupported => unreachable,4192 error.FileLocksNotSupported => unreachable,
4193 error.WouldBlock => unreachable,
4190 else => |e| return e,4194 else => |e| return e,
4191 };4195 };
4192 defer close(fd);4196 defer close(fd);