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;...@@ -357,11 +357,11 @@ pub const off_t = i64;
357pub const ino_t = u64;357pub const ino_t = u64;
358358
359pub const Flock = extern struct {359pub const Flock = extern struct {
360 l_start: off_t,360 start: off_t,
361 l_len: off_t,361 len: off_t,
362 l_pid: pid_t,362 pid: pid_t,
363 l_type: i16,363 type: i16,
364 l_whence: i16,364 whence: i16,
365};365};
366366
367pub const Stat = extern struct {367pub const Stat = extern struct {
lib/std/c/dragonfly.zig+5-5
...@@ -929,11 +929,11 @@ pub const LOCK = struct {...@@ -929,11 +929,11 @@ pub const LOCK = struct {
929};929};
930930
931pub const Flock = extern struct {931pub const Flock = extern struct {
932 l_start: off_t,932 start: off_t,
933 l_len: off_t,933 len: off_t,
934 l_pid: pid_t,934 pid: pid_t,
935 l_type: c_short,935 type: c_short,
936 l_whence: c_short,936 whence: c_short,
937};937};
938938
939pub const addrinfo = extern struct {939pub const addrinfo = extern struct {
lib/std/c/freebsd.zig+6-6
...@@ -193,12 +193,12 @@ pub const dl_phdr_info = extern struct {...@@ -193,12 +193,12 @@ pub const dl_phdr_info = extern struct {
193};193};
194194
195pub const Flock = extern struct {195pub const Flock = extern struct {
196 l_start: off_t,196 start: off_t,
197 l_len: off_t,197 len: off_t,
198 l_pid: pid_t,198 pid: pid_t,
199 l_type: i16,199 type: i16,
200 l_whence: i16,200 whence: i16,
201 l_sysid: i32,201 sysid: i32,
202 __unused: [4]u8,202 __unused: [4]u8,
203};203};
204204
lib/std/c/haiku.zig+5-5
...@@ -159,11 +159,11 @@ pub const dl_phdr_info = extern struct {...@@ -159,11 +159,11 @@ pub const dl_phdr_info = extern struct {
159};159};
160160
161pub const Flock = extern struct {161pub const Flock = extern struct {
162 l_type: c_short,162 type: c_short,
163 l_whence: c_short,163 whence: c_short,
164 l_start: off_t,164 start: off_t,
165 l_len: off_t,165 len: off_t,
166 l_pid: pid_t,166 pid: pid_t,
167};167};
168168
169pub const msghdr = extern struct {169pub const msghdr = extern struct {
lib/std/c/netbsd.zig+5-5
...@@ -169,11 +169,11 @@ pub const dl_phdr_info = extern struct {...@@ -169,11 +169,11 @@ pub const dl_phdr_info = extern struct {
169};169};
170170
171pub const Flock = extern struct {171pub const Flock = extern struct {
172 l_start: off_t,172 start: off_t,
173 l_len: off_t,173 len: off_t,
174 l_pid: pid_t,174 pid: pid_t,
175 l_type: i16,175 type: i16,
176 l_whence: i16,176 whence: i16,
177};177};
178178
179pub const addrinfo = extern struct {179pub const addrinfo = extern struct {
lib/std/c/openbsd.zig+5-5
...@@ -94,11 +94,11 @@ pub const dl_phdr_info = extern struct {...@@ -94,11 +94,11 @@ pub const dl_phdr_info = extern struct {
94};94};
9595
96pub const Flock = extern struct {96pub const Flock = extern struct {
97 l_start: off_t,97 start: off_t,
98 l_len: off_t,98 len: off_t,
99 l_pid: pid_t,99 pid: pid_t,
100 l_type: c_short,100 type: c_short,
101 l_whence: c_short,101 whence: c_short,
102};102};
103103
104pub const addrinfo = extern struct {104pub const addrinfo = extern struct {
lib/std/c/solaris.zig+6-6
...@@ -116,13 +116,13 @@ pub const RTLD = struct {...@@ -116,13 +116,13 @@ pub const RTLD = struct {
116};116};
117117
118pub const Flock = extern struct {118pub const Flock = extern struct {
119 l_type: c_short,119 type: c_short,
120 l_whence: c_short,120 whence: c_short,
121 l_start: off_t,121 start: off_t,
122 // len == 0 means until end of file.122 // len == 0 means until end of file.
123 l_len: off_t,123 len: off_t,
124 l_sysid: c_int,124 sysid: c_int,
125 l_pid: pid_t,125 pid: pid_t,
126 __pad: [4]c_long,126 __pad: [4]c_long,
127};127};
128128
lib/std/fs.zig+8
...@@ -1045,6 +1045,8 @@ pub const Dir = struct {...@@ -1045,6 +1045,8 @@ pub const Dir = struct {
1045 error.FileBusy => unreachable,1045 error.FileBusy => unreachable,
1046 error.Locked => unreachable,1046 error.Locked => unreachable,
1047 error.PermissionDenied => unreachable,1047 error.PermissionDenied => unreachable,
1048 error.DeadLock => unreachable,
1049 error.LockedRegionLimitExceeded => unreachable,
1048 else => |e| return e,1050 else => |e| return e,
1049 };1051 };
1050 fl_flags &= ~@as(usize, os.O.NONBLOCK);1052 fl_flags &= ~@as(usize, os.O.NONBLOCK);
...@@ -1052,6 +1054,8 @@ pub const Dir = struct {...@@ -1052,6 +1054,8 @@ pub const Dir = struct {
1052 error.FileBusy => unreachable,1054 error.FileBusy => unreachable,
1053 error.Locked => unreachable,1055 error.Locked => unreachable,
1054 error.PermissionDenied => unreachable,1056 error.PermissionDenied => unreachable,
1057 error.DeadLock => unreachable,
1058 error.LockedRegionLimitExceeded => unreachable,
1055 else => |e| return e,1059 else => |e| return e,
1056 };1060 };
1057 }1061 }
...@@ -1197,6 +1201,8 @@ pub const Dir = struct {...@@ -1197,6 +1201,8 @@ pub const Dir = struct {
1197 error.FileBusy => unreachable,1201 error.FileBusy => unreachable,
1198 error.Locked => unreachable,1202 error.Locked => unreachable,
1199 error.PermissionDenied => unreachable,1203 error.PermissionDenied => unreachable,
1204 error.DeadLock => unreachable,
1205 error.LockedRegionLimitExceeded => unreachable,
1200 else => |e| return e,1206 else => |e| return e,
1201 };1207 };
1202 fl_flags &= ~@as(usize, os.O.NONBLOCK);1208 fl_flags &= ~@as(usize, os.O.NONBLOCK);
...@@ -1204,6 +1210,8 @@ pub const Dir = struct {...@@ -1204,6 +1210,8 @@ pub const Dir = struct {
1204 error.FileBusy => unreachable,1210 error.FileBusy => unreachable,
1205 error.Locked => unreachable,1211 error.Locked => unreachable,
1206 error.PermissionDenied => unreachable,1212 error.PermissionDenied => unreachable,
1213 error.DeadLock => unreachable,
1214 error.LockedRegionLimitExceeded => unreachable,
1207 else => |e| return e,1215 else => |e| return e,
1208 };1216 };
1209 }1217 }
lib/std/os.zig+13-1
...@@ -4513,6 +4513,8 @@ pub const FcntlError = error{...@@ -4513,6 +4513,8 @@ pub const FcntlError = error{
4513 FileBusy,4513 FileBusy,
4514 ProcessFdQuotaExceeded,4514 ProcessFdQuotaExceeded,
4515 Locked,4515 Locked,
4516 DeadLock,
4517 LockedRegionLimitExceeded,
4516} || UnexpectedError;4518} || UnexpectedError;
45174519
4518pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) FcntlError!usize {4520pub 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 {...@@ -4521,13 +4523,15 @@ pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) FcntlError!usize {
4521 switch (errno(rc)) {4523 switch (errno(rc)) {
4522 .SUCCESS => return @intCast(usize, rc),4524 .SUCCESS => return @intCast(usize, rc),
4523 .INTR => continue,4525 .INTR => continue,
4524 .ACCES => return error.Locked,4526 .AGAIN, .ACCES => return error.Locked,
4525 .BADF => unreachable,4527 .BADF => unreachable,
4526 .BUSY => return error.FileBusy,4528 .BUSY => return error.FileBusy,
4527 .INVAL => unreachable, // invalid parameters4529 .INVAL => unreachable, // invalid parameters
4528 .PERM => return error.PermissionDenied,4530 .PERM => return error.PermissionDenied,
4529 .MFILE => return error.ProcessFdQuotaExceeded,4531 .MFILE => return error.ProcessFdQuotaExceeded,
4530 .NOTDIR => unreachable, // invalid parameter4532 .NOTDIR => unreachable, // invalid parameter
4533 .DEADLK => return error.DeadLock,
4534 .NOLCK => return error.LockedRegionLimitExceeded,
4531 else => |err| return unexpectedErrno(err),4535 else => |err| return unexpectedErrno(err),
4532 }4536 }
4533 }4537 }
...@@ -4542,6 +4546,8 @@ fn setSockFlags(sock: socket_t, flags: u32) !void {...@@ -4542,6 +4546,8 @@ fn setSockFlags(sock: socket_t, flags: u32) !void {
4542 error.FileBusy => unreachable,4546 error.FileBusy => unreachable,
4543 error.Locked => unreachable,4547 error.Locked => unreachable,
4544 error.PermissionDenied => unreachable,4548 error.PermissionDenied => unreachable,
4549 error.DeadLock => unreachable,
4550 error.LockedRegionLimitExceeded => unreachable,
4545 else => |e| return e,4551 else => |e| return e,
4546 };4552 };
4547 fd_flags |= FD_CLOEXEC;4553 fd_flags |= FD_CLOEXEC;
...@@ -4549,6 +4555,8 @@ fn setSockFlags(sock: socket_t, flags: u32) !void {...@@ -4549,6 +4555,8 @@ fn setSockFlags(sock: socket_t, flags: u32) !void {
4549 error.FileBusy => unreachable,4555 error.FileBusy => unreachable,
4550 error.Locked => unreachable,4556 error.Locked => unreachable,
4551 error.PermissionDenied => unreachable,4557 error.PermissionDenied => unreachable,
4558 error.DeadLock => unreachable,
4559 error.LockedRegionLimitExceeded => unreachable,
4552 else => |e| return e,4560 else => |e| return e,
4553 };4561 };
4554 }4562 }
...@@ -4570,6 +4578,8 @@ fn setSockFlags(sock: socket_t, flags: u32) !void {...@@ -4570,6 +4578,8 @@ fn setSockFlags(sock: socket_t, flags: u32) !void {
4570 error.FileBusy => unreachable,4578 error.FileBusy => unreachable,
4571 error.Locked => unreachable,4579 error.Locked => unreachable,
4572 error.PermissionDenied => unreachable,4580 error.PermissionDenied => unreachable,
4581 error.DeadLock => unreachable,
4582 error.LockedRegionLimitExceeded => unreachable,
4573 else => |e| return e,4583 else => |e| return e,
4574 };4584 };
4575 fl_flags |= O.NONBLOCK;4585 fl_flags |= O.NONBLOCK;
...@@ -4577,6 +4587,8 @@ fn setSockFlags(sock: socket_t, flags: u32) !void {...@@ -4577,6 +4587,8 @@ fn setSockFlags(sock: socket_t, flags: u32) !void {
4577 error.FileBusy => unreachable,4587 error.FileBusy => unreachable,
4578 error.Locked => unreachable,4588 error.Locked => unreachable,
4579 error.PermissionDenied => unreachable,4589 error.PermissionDenied => unreachable,
4590 error.DeadLock => unreachable,
4591 error.LockedRegionLimitExceeded => unreachable,
4580 else => |e| return e,4592 else => |e| return e,
4581 };4593 };
4582 }4594 }
lib/std/os/linux/arm-eabi.zig+5-5
...@@ -638,12 +638,12 @@ pub const HWCAP = struct {...@@ -638,12 +638,12 @@ pub const HWCAP = struct {
638};638};
639639
640pub const Flock = extern struct {640pub const Flock = extern struct {
641 l_type: i16,641 type: i16,
642 l_whence: i16,642 whence: i16,
643 __pad0: [4]u8,643 __pad0: [4]u8,
644 l_start: off_t,644 start: off_t,
645 l_len: off_t,645 len: off_t,
646 l_pid: pid_t,646 pid: pid_t,
647 __unused: [4]u8,647 __unused: [4]u8,
648};648};
649649
lib/std/os/linux/arm64.zig+5-5
...@@ -490,11 +490,11 @@ pub const VDSO = struct {...@@ -490,11 +490,11 @@ pub const VDSO = struct {
490};490};
491491
492pub const Flock = extern struct {492pub const Flock = extern struct {
493 l_type: i16,493 type: i16,
494 l_whence: i16,494 whence: i16,
495 l_start: off_t,495 start: off_t,
496 l_len: off_t,496 len: off_t,
497 l_pid: pid_t,497 pid: pid_t,
498 __unused: [4]u8,498 __unused: [4]u8,
499};499};
500500
lib/std/os/linux/mips.zig+5-5
...@@ -707,12 +707,12 @@ pub const VDSO = struct {...@@ -707,12 +707,12 @@ pub const VDSO = struct {
707};707};
708708
709pub const Flock = extern struct {709pub const Flock = extern struct {
710 l_type: i16,710 type: i16,
711 l_whence: i16,711 whence: i16,
712 __pad0: [4]u8,712 __pad0: [4]u8,
713 l_start: off_t,713 start: off_t,
714 l_len: off_t,714 len: off_t,
715 l_pid: pid_t,715 pid: pid_t,
716 __unused: [4]u8,716 __unused: [4]u8,
717};717};
718718
lib/std/os/linux/powerpc.zig+5-5
...@@ -643,11 +643,11 @@ pub const VDSO = struct {...@@ -643,11 +643,11 @@ pub const VDSO = struct {
643};643};
644644
645pub const Flock = extern struct {645pub const Flock = extern struct {
646 l_type: i16,646 type: i16,
647 l_whence: i16,647 whence: i16,
648 l_start: off_t,648 start: off_t,
649 l_len: off_t,649 len: off_t,
650 l_pid: pid_t,650 pid: pid_t,
651};651};
652652
653pub const msghdr = extern struct {653pub const msghdr = extern struct {
lib/std/os/linux/powerpc64.zig+5-5
...@@ -617,11 +617,11 @@ pub const VDSO = struct {...@@ -617,11 +617,11 @@ pub const VDSO = struct {
617};617};
618618
619pub const Flock = extern struct {619pub const Flock = extern struct {
620 l_type: i16,620 type: i16,
621 l_whence: i16,621 whence: i16,
622 l_start: off_t,622 start: off_t,
623 l_len: off_t,623 len: off_t,
624 l_pid: pid_t,624 pid: pid_t,
625 __unused: [4]u8,625 __unused: [4]u8,
626};626};
627627
lib/std/os/linux/riscv64.zig+5-5
...@@ -480,11 +480,11 @@ pub const timeval = extern struct {...@@ -480,11 +480,11 @@ pub const timeval = extern struct {
480};480};
481481
482pub const Flock = extern struct {482pub const Flock = extern struct {
483 l_type: i16,483 type: i16,
484 l_whence: i16,484 whence: i16,
485 l_start: off_t,485 start: off_t,
486 l_len: off_t,486 len: off_t,
487 l_pid: pid_t,487 pid: pid_t,
488 __unused: [4]u8,488 __unused: [4]u8,
489};489};
490490
lib/std/os/linux/sparc64.zig+5-5
...@@ -648,11 +648,11 @@ pub const VDSO = struct {...@@ -648,11 +648,11 @@ pub const VDSO = struct {
648};648};
649649
650pub const Flock = extern struct {650pub const Flock = extern struct {
651 l_type: i16,651 type: i16,
652 l_whence: i16,652 whence: i16,
653 l_start: off_t,653 start: off_t,
654 l_len: off_t,654 len: off_t,
655 l_pid: pid_t,655 pid: pid_t,
656};656};
657657
658pub const msghdr = extern struct {658pub const msghdr = extern struct {
lib/std/os/test.zig+57
...@@ -821,3 +821,60 @@ test "writev longer than IOV_MAX" {...@@ -821,3 +821,60 @@ test "writev longer than IOV_MAX" {
821 const amt = try file.writev(&iovecs);821 const amt = try file.writev(&iovecs);
822 try testing.expectEqual(@as(usize, os.IOV_MAX), amt);822 try testing.expectEqual(@as(usize, os.IOV_MAX), amt);
823}823}
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}