diff --git a/lib/std/os.zig b/lib/std/os.zig index 3333a91788f6d90689db8294236a3f6901d07db1..2dec2c4c1d785c7096aea426c83f349ca48a7c6a 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -3163,6 +3163,31 @@ pub fn lseek_CUR_get(fd: fd_t) SeekError!u64 { } } +pub const FcntlError = error{ + PermissionDenied, + FileBusy, + ProcessFdQuotaExceeded, + Locked, +} || UnexpectedError; + +pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) FcntlError!usize { + while (true) { + const rc = system.fcntl(fd, cmd, arg); + switch (errno(rc)) { + 0 => return @intCast(usize, rc), + EINTR => continue, + EACCES => return error.Locked, + EBADF => unreachable, + EBUSY => return error.FileBusy, + EINVAL => unreachable, // invalid parameters + EPERM => return error.PermissionDenied, + EMFILE => return error.ProcessFdQuotaExceeded, + ENOTDIR => unreachable, // invalid parameter + else => |err| return unexpectedErrno(err), + } + } +} + pub const RealPathError = error{ FileNotFound, AccessDenied, diff --git a/lib/std/os/bits/dragonfly.zig b/lib/std/os/bits/dragonfly.zig index 27b2733b76d7c418ffe58d50db6ed8788c1ef88d..4a9b51d472ad27ac4c8ac30570a18ce0ac4211dc 100644 --- a/lib/std/os/bits/dragonfly.zig +++ b/lib/std/os/bits/dragonfly.zig @@ -283,6 +283,8 @@ pub const F_LOCK = 1; pub const F_TLOCK = 2; pub const F_TEST = 3; +pub const FD_CLOEXEC = 1; + pub const AT_FDCWD = -328243; pub const AT_SYMLINK_NOFOLLOW = 1; pub const AT_REMOVEDIR = 2; diff --git a/lib/std/os/bits/freebsd.zig b/lib/std/os/bits/freebsd.zig index 02fe7e75b3f05f426f56985e9e952195598a11f6..6f97884e1613633b581ef064cbd93f53d93ba913 100644 --- a/lib/std/os/bits/freebsd.zig +++ b/lib/std/os/bits/freebsd.zig @@ -355,6 +355,8 @@ pub const F_GETOWN_EX = 16; pub const F_GETOWNER_UIDS = 17; +pub const FD_CLOEXEC = 1; + pub const SEEK_SET = 0; pub const SEEK_CUR = 1; pub const SEEK_END = 2; diff --git a/lib/std/os/bits/linux.zig b/lib/std/os/bits/linux.zig index 2a58c144900359e26172bca347f1f232d5a637db..89364909c9fd08942767a63556e7da1bbfcaf30b 100644 --- a/lib/std/os/bits/linux.zig +++ b/lib/std/os/bits/linux.zig @@ -136,6 +136,8 @@ pub const MAP_FIXED_NOREPLACE = 0x100000; /// For anonymous mmap, memory could be uninitialized pub const MAP_UNINITIALIZED = 0x4000000; +pub const FD_CLOEXEC = 1; + pub const F_OK = 0; pub const X_OK = 1; pub const W_OK = 2; diff --git a/lib/std/os/bits/netbsd.zig b/lib/std/os/bits/netbsd.zig index 735485695a4a591231b5a1b922e16aa1ba63403f..4abd4c8c5e553d7e279d153c8918b10197123c59 100644 --- a/lib/std/os/bits/netbsd.zig +++ b/lib/std/os/bits/netbsd.zig @@ -312,6 +312,8 @@ pub const F_GETLK = 7; pub const F_SETLK = 8; pub const F_SETLKW = 9; +pub const FD_CLOEXEC = 1; + pub const SEEK_SET = 0; pub const SEEK_CUR = 1; pub const SEEK_END = 2; diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index f3265eaa61416f678270eca853dafc22e8d71822..ae48d831cfdb53433bca1cacd792c9aa31415ae8 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -588,6 +588,10 @@ pub fn waitpid(pid: pid_t, status: *u32, flags: u32) usize { return syscall4(SYS_wait4, @bitCast(usize, @as(isize, pid)), @ptrToInt(status), flags, 0); } +pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) usize { + return syscall3(SYS_fcntl, @bitCast(usize, @as(isize, fd)), @bitCast(usize, @as(isize, cmd)), arg); +} + var vdso_clock_gettime = @ptrCast(?*const c_void, init_vdso_clock_gettime); // We must follow the C calling convention when we call into the VDSO diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig index 1ef6798b50f97f2d21911b6d9064e0608a895af9..b0452f4d6665ea3f481aa713fbf73d52fe5bbeb3 100644 --- a/lib/std/os/test.zig +++ b/lib/std/os/test.zig @@ -1,7 +1,8 @@ const std = @import("../std.zig"); const os = std.os; const testing = std.testing; -const expect = std.testing.expect; +const expect = testing.expect; +const expectEqual = testing.expectEqual; const io = std.io; const fs = std.fs; const mem = std.mem; @@ -446,3 +447,31 @@ test "getenv" { expect(os.getenvZ("BOGUSDOESNOTEXISTENVVAR") == null); } } + +test "fcntl" { + if (builtin.os.tag == .windows) + return error.SkipZigTest; + + const test_out_file = "os_tmp_test"; + + const file = try fs.cwd().createFile(test_out_file, .{}); + defer file.close(); + + // Note: The test assumes createFile opens the file with O_CLOEXEC + { + const flags = try os.fcntl(file.handle, os.F_GETFD, 0); + expect((flags & os.FD_CLOEXEC) != 0); + } + { + _ = try os.fcntl(file.handle, os.F_SETFD, 0); + const flags = try os.fcntl(file.handle, os.F_GETFD, 0); + expect((flags & os.FD_CLOEXEC) == 0); + } + { + _ = try os.fcntl(file.handle, os.F_SETFD, os.FD_CLOEXEC); + const flags = try os.fcntl(file.handle, os.F_GETFD, 0); + expect((flags & os.FD_CLOEXEC) != 0); + } + + try fs.cwd().deleteFile(test_out_file); +}