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
signaturebadge-question-mark 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 {
368368 error.DeviceBusy => unreachable,
369369 error.FileLocksNotSupported => unreachable,
370370 error.BadPathName => unreachable, // Windows-only
371 error.WouldBlock => unreachable,
371372 else => |e| return e,
372373 }
373374 else
lib/std/fs.zig+52-13
......@@ -405,7 +405,7 @@ pub const Dir = struct {
405405 else => false,
406406 };
407407 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))
409409 {
410410 continue :start_over;
411411 }
......@@ -729,14 +729,16 @@ pub const Dir = struct {
729729 }
730730
731731 var os_flags: u32 = os.O_CLOEXEC;
732 // Use the O_ locking flags if the os supports them
733 // (Or if it's darwin, as darwin's `open` doesn't support the O_SYNC flag)
734 const has_flock_open_flags = @hasDecl(os, "O_EXLOCK") and !is_darwin;
732 // Use the O_ locking flags if the os supports them to acquire the lock
733 // atomically.
734 const has_flock_open_flags = @hasDecl(os, "O_EXLOCK");
735735 if (has_flock_open_flags) {
736 const nonblocking_lock_flag = if (flags.lock_nonblocking)
737 os.O_NONBLOCK | os.O_SYNC
736 // Note that the O_NONBLOCK flag is removed after the openat() call
737 // is successful.
738 const nonblocking_lock_flag: u32 = if (flags.lock_nonblocking)
739 os.O_NONBLOCK
738740 else
739 @as(u32, 0);
741 0;
740742 os_flags |= switch (flags.lock) {
741743 .None => @as(u32, 0),
742744 .Shared => os.O_SHLOCK | nonblocking_lock_flag,
......@@ -771,6 +773,22 @@ pub const Dir = struct {
771773 });
772774 }
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
774792 return File{
775793 .handle = fd,
776794 .capable_io_mode = .blocking,
......@@ -854,17 +872,19 @@ pub const Dir = struct {
854872 return self.createFileW(path_w.span(), flags);
855873 }
856874
857 // Use the O_ locking flags if the os supports them
858 // (Or if it's darwin, as darwin's `open` doesn't support the O_SYNC flag)
859 const has_flock_open_flags = @hasDecl(os, "O_EXLOCK") and !is_darwin;
875 // Use the O_ locking flags if the os supports them to acquire the lock
876 // atomically.
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.
860880 const nonblocking_lock_flag: u32 = if (has_flock_open_flags and flags.lock_nonblocking)
861 os.O_NONBLOCK | os.O_SYNC
881 os.O_NONBLOCK
862882 else
863883 0;
864884 const lock_flag: u32 = if (has_flock_open_flags) switch (flags.lock) {
865885 .None => @as(u32, 0),
866 .Shared => os.O_SHLOCK,
867 .Exclusive => os.O_EXLOCK,
886 .Shared => os.O_SHLOCK | nonblocking_lock_flag,
887 .Exclusive => os.O_EXLOCK | nonblocking_lock_flag,
868888 } else 0;
869889
870890 const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0;
......@@ -876,6 +896,7 @@ pub const Dir = struct {
876896 try std.event.Loop.instance.?.openatZ(self.fd, sub_path_c, os_flags, flags.mode)
877897 else
878898 try os.openatZ(self.fd, sub_path_c, os_flags, flags.mode);
899 errdefer os.close(fd);
879900
880901 if (!has_flock_open_flags and flags.lock != .None) {
881902 // TODO: integrate async I/O
......@@ -887,6 +908,22 @@ pub const Dir = struct {
887908 });
888909 }
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
890927 return File{
891928 .handle = fd,
892929 .capable_io_mode = .blocking,
......@@ -1178,6 +1215,7 @@ pub const Dir = struct {
11781215 error.NoSpaceLeft => unreachable, // not providing O_CREAT
11791216 error.PathAlreadyExists => unreachable, // not providing O_CREAT
11801217 error.FileLocksNotSupported => unreachable, // locking folders is not supported
1218 error.WouldBlock => unreachable, // can't happen for directories
11811219 else => |e| return e,
11821220 };
11831221 return Dir{ .fd = fd };
......@@ -1221,6 +1259,7 @@ pub const Dir = struct {
12211259 error.NoSpaceLeft => unreachable, // not providing O_CREAT
12221260 error.PathAlreadyExists => unreachable, // not providing O_CREAT
12231261 error.FileLocksNotSupported => unreachable, // locking folders is not supported
1262 error.WouldBlock => unreachable, // can't happen for directories
12241263 else => |e| return e,
12251264 };
12261265 return Dir{ .fd = fd };
lib/std/fs/test.zig-12
......@@ -691,9 +691,6 @@ test "realpath" {
691691test "open file with exclusive nonblocking lock twice" {
692692 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
697694 const filename = "file_nonblocking_lock_test.txt";
698695
699696 var tmp = tmpDir(.{});
......@@ -709,9 +706,6 @@ test "open file with exclusive nonblocking lock twice" {
709706test "open file with shared and exclusive nonblocking lock" {
710707 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
715709 const filename = "file_nonblocking_lock_test.txt";
716710
717711 var tmp = tmpDir(.{});
......@@ -727,9 +721,6 @@ test "open file with shared and exclusive nonblocking lock" {
727721test "open file with exclusive and shared nonblocking lock" {
728722 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
733724 const filename = "file_nonblocking_lock_test.txt";
734725
735726 var tmp = tmpDir(.{});
......@@ -791,9 +782,6 @@ test "open file with exclusive lock twice, make sure it waits" {
791782test "open file with exclusive nonblocking lock twice (absolute paths)" {
792783 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
797785 const allocator = testing.allocator;
798786
799787 const file_paths: [1][]const u8 = .{"zig-test-absolute-paths.txt"};
lib/std/os.zig+4
......@@ -1020,6 +1020,8 @@ pub const OpenError = error{
10201020
10211021 BadPathName,
10221022 InvalidUtf8,
1023
1024 WouldBlock,
10231025} || UnexpectedError;
10241026
10251027/// 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)
12011203 EEXIST => return error.PathAlreadyExists,
12021204 EBUSY => return error.DeviceBusy,
12031205 EOPNOTSUPP => return error.FileLocksNotSupported,
1206 EWOULDBLOCK => return error.WouldBlock,
12041207 else => |err| return unexpectedErrno(err),
12051208 }
12061209 }
......@@ -4187,6 +4190,7 @@ pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealP
41874190 const flags = if (builtin.os.tag == .linux) O_PATH | O_NONBLOCK | O_CLOEXEC else O_NONBLOCK | O_CLOEXEC;
41884191 const fd = openZ(pathname, flags, 0) catch |err| switch (err) {
41894192 error.FileLocksNotSupported => unreachable,
4193 error.WouldBlock => unreachable,
41904194 else => |e| return e,
41914195 };
41924196 defer close(fd);