authorgravatar for acarrico@memebeam.orgAnthony Carrico <acarrico@memebeam.org> 2021-09-25 17:32:18-04:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-02-15 13:22:50+02:00
log078aa5f7b20e6e705378713a717bc2dd9afb8835
tree29c2dbc7e9072d35837664f799854237a4d76f47
parentc6cd919a1852e4737fb99aa952eaca8c4d0a51df

Adds Linux support for POSIX file locking with fcntl

On Linux, locking fails with EAGAIN (vs. EACCES on other systems). This commit also adds FcntlErrors for EDEADLK and ENOLCK.

17 files changed, 150 insertions(+), 73 deletions(-)

lib/std/c/darwin.zig+5-5
......@@ -357,11 +357,11 @@ pub const off_t = i64;
357357pub const ino_t = u64;
358358
359359pub const Flock = extern struct {
360 l_start: off_t,
361 l_len: off_t,
362 l_pid: pid_t,
363 l_type: i16,
364 l_whence: i16,
360 start: off_t,
361 len: off_t,
362 pid: pid_t,
363 type: i16,
364 whence: i16,
365365};
366366
367367pub const Stat = extern struct {
lib/std/c/dragonfly.zig+5-5
......@@ -929,11 +929,11 @@ pub const LOCK = struct {
929929};
930930
931931pub const Flock = extern struct {
932 l_start: off_t,
933 l_len: off_t,
934 l_pid: pid_t,
935 l_type: c_short,
936 l_whence: c_short,
932 start: off_t,
933 len: off_t,
934 pid: pid_t,
935 type: c_short,
936 whence: c_short,
937937};
938938
939939pub const addrinfo = extern struct {
lib/std/c/freebsd.zig+6-6
......@@ -193,12 +193,12 @@ pub const dl_phdr_info = extern struct {
193193};
194194
195195pub const Flock = extern struct {
196 l_start: off_t,
197 l_len: off_t,
198 l_pid: pid_t,
199 l_type: i16,
200 l_whence: i16,
201 l_sysid: i32,
196 start: off_t,
197 len: off_t,
198 pid: pid_t,
199 type: i16,
200 whence: i16,
201 sysid: i32,
202202 __unused: [4]u8,
203203};
204204
lib/std/c/haiku.zig+5-5
......@@ -159,11 +159,11 @@ pub const dl_phdr_info = extern struct {
159159};
160160
161161pub const Flock = extern struct {
162 l_type: c_short,
163 l_whence: c_short,
164 l_start: off_t,
165 l_len: off_t,
166 l_pid: pid_t,
162 type: c_short,
163 whence: c_short,
164 start: off_t,
165 len: off_t,
166 pid: pid_t,
167167};
168168
169169pub const msghdr = extern struct {
lib/std/c/netbsd.zig+5-5
......@@ -169,11 +169,11 @@ pub const dl_phdr_info = extern struct {
169169};
170170
171171pub const Flock = extern struct {
172 l_start: off_t,
173 l_len: off_t,
174 l_pid: pid_t,
175 l_type: i16,
176 l_whence: i16,
172 start: off_t,
173 len: off_t,
174 pid: pid_t,
175 type: i16,
176 whence: i16,
177177};
178178
179179pub const addrinfo = extern struct {
lib/std/c/openbsd.zig+5-5
......@@ -94,11 +94,11 @@ pub const dl_phdr_info = extern struct {
9494};
9595
9696pub const Flock = extern struct {
97 l_start: off_t,
98 l_len: off_t,
99 l_pid: pid_t,
100 l_type: c_short,
101 l_whence: c_short,
97 start: off_t,
98 len: off_t,
99 pid: pid_t,
100 type: c_short,
101 whence: c_short,
102102};
103103
104104pub const addrinfo = extern struct {
lib/std/c/solaris.zig+6-6
......@@ -116,13 +116,13 @@ pub const RTLD = struct {
116116};
117117
118118pub const Flock = extern struct {
119 l_type: c_short,
120 l_whence: c_short,
121 l_start: off_t,
119 type: c_short,
120 whence: c_short,
121 start: off_t,
122122 // len == 0 means until end of file.
123 l_len: off_t,
124 l_sysid: c_int,
125 l_pid: pid_t,
123 len: off_t,
124 sysid: c_int,
125 pid: pid_t,
126126 __pad: [4]c_long,
127127};
128128
lib/std/fs.zig+8
......@@ -1045,6 +1045,8 @@ pub const Dir = struct {
10451045 error.FileBusy => unreachable,
10461046 error.Locked => unreachable,
10471047 error.PermissionDenied => unreachable,
1048 error.DeadLock => unreachable,
1049 error.LockedRegionLimitExceeded => unreachable,
10481050 else => |e| return e,
10491051 };
10501052 fl_flags &= ~@as(usize, os.O.NONBLOCK);
......@@ -1052,6 +1054,8 @@ pub const Dir = struct {
10521054 error.FileBusy => unreachable,
10531055 error.Locked => unreachable,
10541056 error.PermissionDenied => unreachable,
1057 error.DeadLock => unreachable,
1058 error.LockedRegionLimitExceeded => unreachable,
10551059 else => |e| return e,
10561060 };
10571061 }
......@@ -1197,6 +1201,8 @@ pub const Dir = struct {
11971201 error.FileBusy => unreachable,
11981202 error.Locked => unreachable,
11991203 error.PermissionDenied => unreachable,
1204 error.DeadLock => unreachable,
1205 error.LockedRegionLimitExceeded => unreachable,
12001206 else => |e| return e,
12011207 };
12021208 fl_flags &= ~@as(usize, os.O.NONBLOCK);
......@@ -1204,6 +1210,8 @@ pub const Dir = struct {
12041210 error.FileBusy => unreachable,
12051211 error.Locked => unreachable,
12061212 error.PermissionDenied => unreachable,
1213 error.DeadLock => unreachable,
1214 error.LockedRegionLimitExceeded => unreachable,
12071215 else => |e| return e,
12081216 };
12091217 }
lib/std/os.zig+13-1
......@@ -4513,6 +4513,8 @@ pub const FcntlError = error{
45134513 FileBusy,
45144514 ProcessFdQuotaExceeded,
45154515 Locked,
4516 DeadLock,
4517 LockedRegionLimitExceeded,
45164518} || UnexpectedError;
45174519
45184520pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) FcntlError!usize {
......@@ -4521,13 +4523,15 @@ pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) FcntlError!usize {
45214523 switch (errno(rc)) {
45224524 .SUCCESS => return @intCast(usize, rc),
45234525 .INTR => continue,
4524 .ACCES => return error.Locked,
4526 .AGAIN, .ACCES => return error.Locked,
45254527 .BADF => unreachable,
45264528 .BUSY => return error.FileBusy,
45274529 .INVAL => unreachable, // invalid parameters
45284530 .PERM => return error.PermissionDenied,
45294531 .MFILE => return error.ProcessFdQuotaExceeded,
45304532 .NOTDIR => unreachable, // invalid parameter
4533 .DEADLK => return error.DeadLock,
4534 .NOLCK => return error.LockedRegionLimitExceeded,
45314535 else => |err| return unexpectedErrno(err),
45324536 }
45334537 }
......@@ -4542,6 +4546,8 @@ fn setSockFlags(sock: socket_t, flags: u32) !void {
45424546 error.FileBusy => unreachable,
45434547 error.Locked => unreachable,
45444548 error.PermissionDenied => unreachable,
4549 error.DeadLock => unreachable,
4550 error.LockedRegionLimitExceeded => unreachable,
45454551 else => |e| return e,
45464552 };
45474553 fd_flags |= FD_CLOEXEC;
......@@ -4549,6 +4555,8 @@ fn setSockFlags(sock: socket_t, flags: u32) !void {
45494555 error.FileBusy => unreachable,
45504556 error.Locked => unreachable,
45514557 error.PermissionDenied => unreachable,
4558 error.DeadLock => unreachable,
4559 error.LockedRegionLimitExceeded => unreachable,
45524560 else => |e| return e,
45534561 };
45544562 }
......@@ -4570,6 +4578,8 @@ fn setSockFlags(sock: socket_t, flags: u32) !void {
45704578 error.FileBusy => unreachable,
45714579 error.Locked => unreachable,
45724580 error.PermissionDenied => unreachable,
4581 error.DeadLock => unreachable,
4582 error.LockedRegionLimitExceeded => unreachable,
45734583 else => |e| return e,
45744584 };
45754585 fl_flags |= O.NONBLOCK;
......@@ -4577,6 +4587,8 @@ fn setSockFlags(sock: socket_t, flags: u32) !void {
45774587 error.FileBusy => unreachable,
45784588 error.Locked => unreachable,
45794589 error.PermissionDenied => unreachable,
4590 error.DeadLock => unreachable,
4591 error.LockedRegionLimitExceeded => unreachable,
45804592 else => |e| return e,
45814593 };
45824594 }
lib/std/os/linux/arm-eabi.zig+5-5
......@@ -638,12 +638,12 @@ pub const HWCAP = struct {
638638};
639639
640640pub const Flock = extern struct {
641 l_type: i16,
642 l_whence: i16,
641 type: i16,
642 whence: i16,
643643 __pad0: [4]u8,
644 l_start: off_t,
645 l_len: off_t,
646 l_pid: pid_t,
644 start: off_t,
645 len: off_t,
646 pid: pid_t,
647647 __unused: [4]u8,
648648};
649649
lib/std/os/linux/arm64.zig+5-5
......@@ -490,11 +490,11 @@ pub const VDSO = struct {
490490};
491491
492492pub const Flock = extern struct {
493 l_type: i16,
494 l_whence: i16,
495 l_start: off_t,
496 l_len: off_t,
497 l_pid: pid_t,
493 type: i16,
494 whence: i16,
495 start: off_t,
496 len: off_t,
497 pid: pid_t,
498498 __unused: [4]u8,
499499};
500500
lib/std/os/linux/mips.zig+5-5
......@@ -707,12 +707,12 @@ pub const VDSO = struct {
707707};
708708
709709pub const Flock = extern struct {
710 l_type: i16,
711 l_whence: i16,
710 type: i16,
711 whence: i16,
712712 __pad0: [4]u8,
713 l_start: off_t,
714 l_len: off_t,
715 l_pid: pid_t,
713 start: off_t,
714 len: off_t,
715 pid: pid_t,
716716 __unused: [4]u8,
717717};
718718
lib/std/os/linux/powerpc.zig+5-5
......@@ -643,11 +643,11 @@ pub const VDSO = struct {
643643};
644644
645645pub const Flock = extern struct {
646 l_type: i16,
647 l_whence: i16,
648 l_start: off_t,
649 l_len: off_t,
650 l_pid: pid_t,
646 type: i16,
647 whence: i16,
648 start: off_t,
649 len: off_t,
650 pid: pid_t,
651651};
652652
653653pub const msghdr = extern struct {
lib/std/os/linux/powerpc64.zig+5-5
......@@ -617,11 +617,11 @@ pub const VDSO = struct {
617617};
618618
619619pub const Flock = extern struct {
620 l_type: i16,
621 l_whence: i16,
622 l_start: off_t,
623 l_len: off_t,
624 l_pid: pid_t,
620 type: i16,
621 whence: i16,
622 start: off_t,
623 len: off_t,
624 pid: pid_t,
625625 __unused: [4]u8,
626626};
627627
lib/std/os/linux/riscv64.zig+5-5
......@@ -480,11 +480,11 @@ pub const timeval = extern struct {
480480};
481481
482482pub const Flock = extern struct {
483 l_type: i16,
484 l_whence: i16,
485 l_start: off_t,
486 l_len: off_t,
487 l_pid: pid_t,
483 type: i16,
484 whence: i16,
485 start: off_t,
486 len: off_t,
487 pid: pid_t,
488488 __unused: [4]u8,
489489};
490490
lib/std/os/linux/sparc64.zig+5-5
......@@ -648,11 +648,11 @@ pub const VDSO = struct {
648648};
649649
650650pub const Flock = extern struct {
651 l_type: i16,
652 l_whence: i16,
653 l_start: off_t,
654 l_len: off_t,
655 l_pid: pid_t,
651 type: i16,
652 whence: i16,
653 start: off_t,
654 len: off_t,
655 pid: pid_t,
656656};
657657
658658pub const msghdr = extern struct {
lib/std/os/test.zig+57
......@@ -821,3 +821,60 @@ test "writev longer than IOV_MAX" {
821821 const amt = try file.writev(&iovecs);
822822 try testing.expectEqual(@as(usize, os.IOV_MAX), amt);
823823}
824
825test "POSIX file locking with fcntl" {
826 if (native_os == .windows or native_os == .wasi) return error.SkipZigTest;
827
828 var tmp = std.testing.tmpDir(.{});
829 defer tmp.cleanup();
830
831 // Create a temporary lock file
832 var file = try tmp.dir.createFile("lock", .{ .read = true });
833 defer file.close();
834 try file.setEndPos(2);
835 const fd = file.handle;
836
837 // Place an exclusive lock on the first byte, and a shared lock on the second byte:
838 var struct_flock = std.mem.zeroInit(std.os.Flock, .{ .type = std.os.F.WRLCK });
839 _ = try std.os.fcntl(fd, std.os.F.SETLK, @ptrToInt(&struct_flock));
840 struct_flock.start = 1;
841 struct_flock.type = std.os.F.RDLCK;
842 _ = try std.os.fcntl(fd, std.os.F.SETLK, @ptrToInt(&struct_flock));
843
844 // Check the locks in a child process:
845 const pid = try std.os.fork();
846 if (pid == 0) {
847 // child expects be denied the exclusive lock:
848 struct_flock.start = 0;
849 struct_flock.type = std.os.F.WRLCK;
850 try expectError(error.Locked, std.os.fcntl(fd, std.os.F.SETLK, @ptrToInt(&struct_flock)));
851 // child expects to get the shared lock:
852 struct_flock.start = 1;
853 struct_flock.type = std.os.F.RDLCK;
854 _ = try std.os.fcntl(fd, std.os.F.SETLK, @ptrToInt(&struct_flock));
855 // child waits for the exclusive lock in order to test deadlock:
856 struct_flock.start = 0;
857 struct_flock.type = std.os.F.WRLCK;
858 _ = try std.os.fcntl(fd, std.os.F.SETLKW, @ptrToInt(&struct_flock));
859 // child exits without continuing:
860 std.os.exit(0);
861 } else {
862 // parent waits for child to get shared lock:
863 std.time.sleep(1 * std.time.ns_per_ms);
864 // parent expects deadlock when attempting to upgrade the shared lock to exclusive:
865 struct_flock.start = 1;
866 struct_flock.type = std.os.F.WRLCK;
867 try expectError(error.DeadLock, std.os.fcntl(fd, std.os.F.SETLKW, @ptrToInt(&struct_flock)));
868 // parent releases exclusive lock:
869 struct_flock.start = 0;
870 struct_flock.type = std.os.F.UNLCK;
871 _ = try std.os.fcntl(fd, std.os.F.SETLK, @ptrToInt(&struct_flock));
872 // parent releases shared lock:
873 struct_flock.start = 1;
874 struct_flock.type = std.os.F.UNLCK;
875 _ = try std.os.fcntl(fd, std.os.F.SETLK, @ptrToInt(&struct_flock));
876 // parent waits for child:
877 const result = std.os.waitpid(pid, 0);
878 try expect(result.status == 0 * 256);
879 }
880}