mirror of
https://github.com/ziglang/zig.git
synced 2026-02-14 13:30:45 +00:00
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.
This commit is contained in:
parent
c6cd919a18
commit
078aa5f7b2
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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,
|
||||
};
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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,
|
||||
};
|
||||
|
||||
|
||||
@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
@ -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,
|
||||
};
|
||||
|
||||
|
||||
@ -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,
|
||||
};
|
||||
|
||||
|
||||
@ -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,
|
||||
};
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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,
|
||||
};
|
||||
|
||||
|
||||
@ -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,
|
||||
};
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user