From 078aa5f7b20e6e705378713a717bc2dd9afb8835 Mon Sep 17 00:00:00 2001 From: Anthony Carrico Date: Sat, 25 Sep 2021 17:32:18 -0400 Subject: [PATCH] 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. --- lib/std/c/darwin.zig | 10 +++--- lib/std/c/dragonfly.zig | 10 +++--- lib/std/c/freebsd.zig | 12 +++---- lib/std/c/haiku.zig | 10 +++--- lib/std/c/netbsd.zig | 10 +++--- lib/std/c/openbsd.zig | 10 +++--- lib/std/c/solaris.zig | 12 +++---- lib/std/fs.zig | 8 +++++ lib/std/os.zig | 14 ++++++++- lib/std/os/linux/arm-eabi.zig | 10 +++--- lib/std/os/linux/arm64.zig | 10 +++--- lib/std/os/linux/mips.zig | 10 +++--- lib/std/os/linux/powerpc.zig | 10 +++--- lib/std/os/linux/powerpc64.zig | 10 +++--- lib/std/os/linux/riscv64.zig | 10 +++--- lib/std/os/linux/sparc64.zig | 10 +++--- lib/std/os/test.zig | 57 ++++++++++++++++++++++++++++++++++ 17 files changed, 150 insertions(+), 73 deletions(-) diff --git a/lib/std/c/darwin.zig b/lib/std/c/darwin.zig index bd5d3aeead17e93530ef4a844e9b6f2e54f470dd..308fb106a8e01fc8588adb19f1bb6b36141850b9 100644 --- a/lib/std/c/darwin.zig +++ b/lib/std/c/darwin.zig @@ -357,11 +357,11 @@ pub const off_t = i64; pub const ino_t = u64; pub const Flock = extern struct { - l_start: off_t, - l_len: off_t, - l_pid: pid_t, - l_type: i16, - l_whence: i16, + start: off_t, + len: off_t, + pid: pid_t, + type: i16, + whence: i16, }; pub const Stat = extern struct { diff --git a/lib/std/c/dragonfly.zig b/lib/std/c/dragonfly.zig index 75e84f637d6952bc61443bba3d3fa5db1618b508..186525a9cea464af392c401380168931fec6016c 100644 --- a/lib/std/c/dragonfly.zig +++ b/lib/std/c/dragonfly.zig @@ -929,11 +929,11 @@ pub const LOCK = struct { }; pub const Flock = extern struct { - l_start: off_t, - l_len: off_t, - l_pid: pid_t, - l_type: c_short, - l_whence: c_short, + start: off_t, + len: off_t, + pid: pid_t, + type: c_short, + whence: c_short, }; pub const addrinfo = extern struct { diff --git a/lib/std/c/freebsd.zig b/lib/std/c/freebsd.zig index d5b6e4972deb75db40356f9e24596f305e8b83fd..8972b6d6dc05a7a2f582d330cd48f2387e33b283 100644 --- a/lib/std/c/freebsd.zig +++ b/lib/std/c/freebsd.zig @@ -193,12 +193,12 @@ pub const dl_phdr_info = extern struct { }; pub const Flock = extern struct { - l_start: off_t, - l_len: off_t, - l_pid: pid_t, - l_type: i16, - l_whence: i16, - l_sysid: i32, + start: off_t, + len: off_t, + pid: pid_t, + type: i16, + whence: i16, + sysid: i32, __unused: [4]u8, }; diff --git a/lib/std/c/haiku.zig b/lib/std/c/haiku.zig index 176d53e6ae31eadb4ed5399962e3abc502a853bd..f3f7cd3081367729ff8f9473efc5db3a6d0a2c08 100644 --- a/lib/std/c/haiku.zig +++ b/lib/std/c/haiku.zig @@ -159,11 +159,11 @@ pub const dl_phdr_info = extern struct { }; pub const Flock = extern struct { - l_type: c_short, - l_whence: c_short, - l_start: off_t, - l_len: off_t, - l_pid: pid_t, + type: c_short, + whence: c_short, + start: off_t, + len: off_t, + pid: pid_t, }; pub const msghdr = extern struct { diff --git a/lib/std/c/netbsd.zig b/lib/std/c/netbsd.zig index b0708107ad42bb11b4be9354769fba257b5e3504..b1f8e8846ecb2606ba90df07ababdf0ccaf56d7e 100644 --- a/lib/std/c/netbsd.zig +++ b/lib/std/c/netbsd.zig @@ -169,11 +169,11 @@ pub const dl_phdr_info = extern struct { }; pub const Flock = extern struct { - l_start: off_t, - l_len: off_t, - l_pid: pid_t, - l_type: i16, - l_whence: i16, + start: off_t, + len: off_t, + pid: pid_t, + type: i16, + whence: i16, }; pub const addrinfo = extern struct { diff --git a/lib/std/c/openbsd.zig b/lib/std/c/openbsd.zig index 51cdbe3180730cfc0f4cddefe2a0b29f4d255c4f..5e2cd9d9d4055a074a673ff2a417e9a648c79f8e 100644 --- a/lib/std/c/openbsd.zig +++ b/lib/std/c/openbsd.zig @@ -94,11 +94,11 @@ pub const dl_phdr_info = extern struct { }; pub const Flock = extern struct { - l_start: off_t, - l_len: off_t, - l_pid: pid_t, - l_type: c_short, - l_whence: c_short, + start: off_t, + len: off_t, + pid: pid_t, + type: c_short, + whence: c_short, }; pub const addrinfo = extern struct { diff --git a/lib/std/c/solaris.zig b/lib/std/c/solaris.zig index 5f44dc53506a6e06c3a5aed7250ea6245991bada..7026cf5dc4786c3ee51c2f807fdecd51b9e625c7 100644 --- a/lib/std/c/solaris.zig +++ b/lib/std/c/solaris.zig @@ -116,13 +116,13 @@ pub const RTLD = struct { }; pub const Flock = extern struct { - l_type: c_short, - l_whence: c_short, - l_start: off_t, + type: c_short, + whence: c_short, + start: off_t, // len == 0 means until end of file. - l_len: off_t, - l_sysid: c_int, - l_pid: pid_t, + len: off_t, + sysid: c_int, + pid: pid_t, __pad: [4]c_long, }; diff --git a/lib/std/fs.zig b/lib/std/fs.zig index 7b577b1b621021d8289dd3ff5016f9ab65668ed2..758a2bd132591c0ac67d8f9e4eea8ebae5a6d22c 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -1045,6 +1045,8 @@ pub const Dir = struct { error.FileBusy => unreachable, error.Locked => unreachable, error.PermissionDenied => unreachable, + error.DeadLock => unreachable, + error.LockedRegionLimitExceeded => unreachable, else => |e| return e, }; fl_flags &= ~@as(usize, os.O.NONBLOCK); @@ -1052,6 +1054,8 @@ pub const Dir = struct { error.FileBusy => unreachable, error.Locked => unreachable, error.PermissionDenied => unreachable, + error.DeadLock => unreachable, + error.LockedRegionLimitExceeded => unreachable, else => |e| return e, }; } @@ -1197,6 +1201,8 @@ pub const Dir = struct { error.FileBusy => unreachable, error.Locked => unreachable, error.PermissionDenied => unreachable, + error.DeadLock => unreachable, + error.LockedRegionLimitExceeded => unreachable, else => |e| return e, }; fl_flags &= ~@as(usize, os.O.NONBLOCK); @@ -1204,6 +1210,8 @@ pub const Dir = struct { error.FileBusy => unreachable, error.Locked => unreachable, error.PermissionDenied => unreachable, + error.DeadLock => unreachable, + error.LockedRegionLimitExceeded => unreachable, else => |e| return e, }; } diff --git a/lib/std/os.zig b/lib/std/os.zig index d7f60c1e1ab19c5dc2feb1f62d780e068d6bd7f8..3567420a51ea3f2b96d1666866de846b034b721d 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -4513,6 +4513,8 @@ pub const FcntlError = error{ FileBusy, ProcessFdQuotaExceeded, Locked, + DeadLock, + LockedRegionLimitExceeded, } || UnexpectedError; 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 { switch (errno(rc)) { .SUCCESS => return @intCast(usize, rc), .INTR => continue, - .ACCES => return error.Locked, + .AGAIN, .ACCES => return error.Locked, .BADF => unreachable, .BUSY => return error.FileBusy, .INVAL => unreachable, // invalid parameters .PERM => return error.PermissionDenied, .MFILE => return error.ProcessFdQuotaExceeded, .NOTDIR => unreachable, // invalid parameter + .DEADLK => return error.DeadLock, + .NOLCK => return error.LockedRegionLimitExceeded, else => |err| return unexpectedErrno(err), } } @@ -4542,6 +4546,8 @@ fn setSockFlags(sock: socket_t, flags: u32) !void { error.FileBusy => unreachable, error.Locked => unreachable, error.PermissionDenied => unreachable, + error.DeadLock => unreachable, + error.LockedRegionLimitExceeded => unreachable, else => |e| return e, }; fd_flags |= FD_CLOEXEC; @@ -4549,6 +4555,8 @@ fn setSockFlags(sock: socket_t, flags: u32) !void { error.FileBusy => unreachable, error.Locked => unreachable, error.PermissionDenied => unreachable, + error.DeadLock => unreachable, + error.LockedRegionLimitExceeded => unreachable, else => |e| return e, }; } @@ -4570,6 +4578,8 @@ fn setSockFlags(sock: socket_t, flags: u32) !void { error.FileBusy => unreachable, error.Locked => unreachable, error.PermissionDenied => unreachable, + error.DeadLock => unreachable, + error.LockedRegionLimitExceeded => unreachable, else => |e| return e, }; fl_flags |= O.NONBLOCK; @@ -4577,6 +4587,8 @@ fn setSockFlags(sock: socket_t, flags: u32) !void { error.FileBusy => unreachable, error.Locked => unreachable, error.PermissionDenied => unreachable, + error.DeadLock => unreachable, + error.LockedRegionLimitExceeded => unreachable, else => |e| return e, }; } diff --git a/lib/std/os/linux/arm-eabi.zig b/lib/std/os/linux/arm-eabi.zig index 852f6e9283e8755c2d38e8257f04d9be371a8eb2..cea3188b3dafa742807950d0859e98bb1060ffee 100644 --- a/lib/std/os/linux/arm-eabi.zig +++ b/lib/std/os/linux/arm-eabi.zig @@ -638,12 +638,12 @@ pub const HWCAP = struct { }; pub const Flock = extern struct { - l_type: i16, - l_whence: i16, + type: i16, + whence: i16, __pad0: [4]u8, - l_start: off_t, - l_len: off_t, - l_pid: pid_t, + start: off_t, + len: off_t, + pid: pid_t, __unused: [4]u8, }; diff --git a/lib/std/os/linux/arm64.zig b/lib/std/os/linux/arm64.zig index 16e3b897de3b56aff650bc4b7dc81548e52c432f..815a640b79f2e441bb056fcf3153b5c1871779a2 100644 --- a/lib/std/os/linux/arm64.zig +++ b/lib/std/os/linux/arm64.zig @@ -490,11 +490,11 @@ pub const VDSO = struct { }; pub const Flock = extern struct { - l_type: i16, - l_whence: i16, - l_start: off_t, - l_len: off_t, - l_pid: pid_t, + type: i16, + whence: i16, + start: off_t, + len: off_t, + pid: pid_t, __unused: [4]u8, }; diff --git a/lib/std/os/linux/mips.zig b/lib/std/os/linux/mips.zig index a7235f4de2949a08fa3123c18102b3567907a025..ef586cba1969900fb8f6ff8de9e00fa839a71c25 100644 --- a/lib/std/os/linux/mips.zig +++ b/lib/std/os/linux/mips.zig @@ -707,12 +707,12 @@ pub const VDSO = struct { }; pub const Flock = extern struct { - l_type: i16, - l_whence: i16, + type: i16, + whence: i16, __pad0: [4]u8, - l_start: off_t, - l_len: off_t, - l_pid: pid_t, + start: off_t, + len: off_t, + pid: pid_t, __unused: [4]u8, }; diff --git a/lib/std/os/linux/powerpc.zig b/lib/std/os/linux/powerpc.zig index f5ef42680bb3b6053f3c0c2b5ad74cc237273d7e..5decf83a50c45e1981449048057919a880b3eca0 100644 --- a/lib/std/os/linux/powerpc.zig +++ b/lib/std/os/linux/powerpc.zig @@ -643,11 +643,11 @@ pub const VDSO = struct { }; pub const Flock = extern struct { - l_type: i16, - l_whence: i16, - l_start: off_t, - l_len: off_t, - l_pid: pid_t, + type: i16, + whence: i16, + start: off_t, + len: off_t, + pid: pid_t, }; pub const msghdr = extern struct { diff --git a/lib/std/os/linux/powerpc64.zig b/lib/std/os/linux/powerpc64.zig index 165ca848c6a31a3c1cece7d77d8e5b34179a269e..74caa71f3d90f3b09a360e673e53edaa1a8a56d9 100644 --- a/lib/std/os/linux/powerpc64.zig +++ b/lib/std/os/linux/powerpc64.zig @@ -617,11 +617,11 @@ pub const VDSO = struct { }; pub const Flock = extern struct { - l_type: i16, - l_whence: i16, - l_start: off_t, - l_len: off_t, - l_pid: pid_t, + type: i16, + whence: i16, + start: off_t, + len: off_t, + pid: pid_t, __unused: [4]u8, }; diff --git a/lib/std/os/linux/riscv64.zig b/lib/std/os/linux/riscv64.zig index 75505442c071c471b09ba41fde3e424dda4c96d7..1ce3bed4e29a303fec625aa0fb24f953e2a2af60 100644 --- a/lib/std/os/linux/riscv64.zig +++ b/lib/std/os/linux/riscv64.zig @@ -480,11 +480,11 @@ pub const timeval = extern struct { }; pub const Flock = extern struct { - l_type: i16, - l_whence: i16, - l_start: off_t, - l_len: off_t, - l_pid: pid_t, + type: i16, + whence: i16, + start: off_t, + len: off_t, + pid: pid_t, __unused: [4]u8, }; diff --git a/lib/std/os/linux/sparc64.zig b/lib/std/os/linux/sparc64.zig index 3ec9a67210ce44da5e3c85fddb54f7b1be32aec5..146ac8d2ee052446354bad30f69ce4f4653ac33d 100644 --- a/lib/std/os/linux/sparc64.zig +++ b/lib/std/os/linux/sparc64.zig @@ -648,11 +648,11 @@ pub const VDSO = struct { }; pub const Flock = extern struct { - l_type: i16, - l_whence: i16, - l_start: off_t, - l_len: off_t, - l_pid: pid_t, + type: i16, + whence: i16, + start: off_t, + len: off_t, + pid: pid_t, }; pub const msghdr = extern struct { diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig index b502a8090a3cfd588608340335175370b24ed107..cc62e6161c132f229087af71a5b571c591abafe0 100644 --- a/lib/std/os/test.zig +++ b/lib/std/os/test.zig @@ -821,3 +821,60 @@ test "writev longer than IOV_MAX" { const amt = try file.writev(&iovecs); try testing.expectEqual(@as(usize, os.IOV_MAX), amt); } + +test "POSIX file locking with fcntl" { + if (native_os == .windows or native_os == .wasi) return error.SkipZigTest; + + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); + + // Create a temporary lock file + var file = try tmp.dir.createFile("lock", .{ .read = true }); + defer file.close(); + try file.setEndPos(2); + const fd = file.handle; + + // Place an exclusive lock on the first byte, and a shared lock on the second byte: + var struct_flock = std.mem.zeroInit(std.os.Flock, .{ .type = std.os.F.WRLCK }); + _ = try std.os.fcntl(fd, std.os.F.SETLK, @ptrToInt(&struct_flock)); + struct_flock.start = 1; + struct_flock.type = std.os.F.RDLCK; + _ = try std.os.fcntl(fd, std.os.F.SETLK, @ptrToInt(&struct_flock)); + + // Check the locks in a child process: + const pid = try std.os.fork(); + if (pid == 0) { + // child expects be denied the exclusive lock: + struct_flock.start = 0; + struct_flock.type = std.os.F.WRLCK; + try expectError(error.Locked, std.os.fcntl(fd, std.os.F.SETLK, @ptrToInt(&struct_flock))); + // child expects to get the shared lock: + struct_flock.start = 1; + struct_flock.type = std.os.F.RDLCK; + _ = try std.os.fcntl(fd, std.os.F.SETLK, @ptrToInt(&struct_flock)); + // child waits for the exclusive lock in order to test deadlock: + struct_flock.start = 0; + struct_flock.type = std.os.F.WRLCK; + _ = try std.os.fcntl(fd, std.os.F.SETLKW, @ptrToInt(&struct_flock)); + // child exits without continuing: + std.os.exit(0); + } else { + // parent waits for child to get shared lock: + std.time.sleep(1 * std.time.ns_per_ms); + // parent expects deadlock when attempting to upgrade the shared lock to exclusive: + struct_flock.start = 1; + struct_flock.type = std.os.F.WRLCK; + try expectError(error.DeadLock, std.os.fcntl(fd, std.os.F.SETLKW, @ptrToInt(&struct_flock))); + // parent releases exclusive lock: + struct_flock.start = 0; + struct_flock.type = std.os.F.UNLCK; + _ = try std.os.fcntl(fd, std.os.F.SETLK, @ptrToInt(&struct_flock)); + // parent releases shared lock: + struct_flock.start = 1; + struct_flock.type = std.os.F.UNLCK; + _ = try std.os.fcntl(fd, std.os.F.SETLK, @ptrToInt(&struct_flock)); + // parent waits for child: + const result = std.os.waitpid(pid, 0); + try expect(result.status == 0 * 256); + } +} -- 2.54.0