Commit 4843c3b4c3

LemonBoy <thatlemon@gmail.com>
2020-03-16 11:39:18
std: Introduce fnctl wrapper
1 parent b1537b5
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;
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;
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;
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;
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
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);
+}
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,