Commit 997812e8fb

Luna <git@l4.pm>
2019-11-14 01:25:19
os: add memfd_create
currently only linux is supported
1 parent 2505183
Changed files (4)
lib/std/io/test.zig
@@ -649,3 +649,16 @@ test "updateTimes" {
     std.testing.expect(stat_new.atime < stat_old.atime);
     std.testing.expect(stat_new.mtime < stat_old.mtime);
 }
+
+test "memfd_create" {
+    if (builtin.os != .linux) return error.SkipZigTest;
+
+    const fd = try std.os.memfd_create("test", 0);
+    try std.os.write(fd, "test");
+    try std.os.lseek_SET(fd, 0);
+
+    var buf: [10]u8 = undefined;
+    const bytes_read = try std.os.read(fd, &buf);
+    expect(bytes_read == 4);
+    expect(mem.eql(u8, buf[0..4], "test"));
+}
lib/std/os/bits/linux.zig
@@ -26,6 +26,7 @@ pub const uid_t = i32;
 pub const gid_t = u32;
 pub const clock_t = isize;
 
+pub const NAME_MAX = 255;
 pub const PATH_MAX = 4096;
 pub const IOV_MAX = 1024;
 
@@ -1417,3 +1418,38 @@ pub const POLLHUP = 0x010;
 pub const POLLNVAL = 0x020;
 pub const POLLRDNORM = 0x040;
 pub const POLLRDBAND = 0x080;
+
+pub const MFD_CLOEXEC = 0x0001;
+pub const MFD_ALLOW_SEALING = 0x0002;
+pub const MFD_HUGETLB = 0x0004;
+pub const MFD_ALL_FLAGS = MFD_CLOEXEC | MFD_ALLOW_SEALING | MFD_HUGETLB;
+
+pub const HUGETLB_FLAG_ENCODE_SHIFT = 26;
+pub const HUGETLB_FLAG_ENCODE_MASK = 0x3f;
+pub const HUGETLB_FLAG_ENCODE_64KB = 16 << HUGETLB_FLAG_ENCODE_SHIFT;
+pub const HUGETLB_FLAG_ENCODE_512KB = 19 << HUGETLB_FLAG_ENCODE_SHIFT;
+pub const HUGETLB_FLAG_ENCODE_1MB = 20 << HUGETLB_FLAG_ENCODE_SHIFT;
+pub const HUGETLB_FLAG_ENCODE_2MB = 21 << HUGETLB_FLAG_ENCODE_SHIFT;
+pub const HUGETLB_FLAG_ENCODE_8MB = 23 << HUGETLB_FLAG_ENCODE_SHIFT;
+pub const HUGETLB_FLAG_ENCODE_16MB = 24 << HUGETLB_FLAG_ENCODE_SHIFT;
+pub const HUGETLB_FLAG_ENCODE_32MB = 25 << HUGETLB_FLAG_ENCODE_SHIFT;
+pub const HUGETLB_FLAG_ENCODE_256MB = 28 << HUGETLB_FLAG_ENCODE_SHIFT;
+pub const HUGETLB_FLAG_ENCODE_512MB = 29 << HUGETLB_FLAG_ENCODE_SHIFT;
+pub const HUGETLB_FLAG_ENCODE_1GB = 30 << HUGETLB_FLAG_ENCODE_SHIFT;
+pub const HUGETLB_FLAG_ENCODE_2GB = 31 << HUGETLB_FLAG_ENCODE_SHIFT;
+pub const HUGETLB_FLAG_ENCODE_16GB = 34 << HUGETLB_FLAG_ENCODE_SHIFT;
+
+pub const MFD_HUGE_SHIFT = HUGETLB_FLAG_ENCODE_SHIFT;
+pub const MFD_HUGE_MASK = HUGETLB_FLAG_ENCODE_MASK;
+pub const MFD_HUGE_64KB = HUGETLB_FLAG_ENCODE_64KB;
+pub const MFD_HUGE_512KB = HUGETLB_FLAG_ENCODE_512KB;
+pub const MFD_HUGE_1MB = HUGETLB_FLAG_ENCODE_1MB;
+pub const MFD_HUGE_2MB = HUGETLB_FLAG_ENCODE_2MB;
+pub const MFD_HUGE_8MB = HUGETLB_FLAG_ENCODE_8MB;
+pub const MFD_HUGE_16MB = HUGETLB_FLAG_ENCODE_16MB;
+pub const MFD_HUGE_32MB = HUGETLB_FLAG_ENCODE_32MB;
+pub const MFD_HUGE_256MB = HUGETLB_FLAG_ENCODE_256MB;
+pub const MFD_HUGE_512MB = HUGETLB_FLAG_ENCODE_512MB;
+pub const MFD_HUGE_1GB = HUGETLB_FLAG_ENCODE_1GB;
+pub const MFD_HUGE_2GB = HUGETLB_FLAG_ENCODE_2GB;
+pub const MFD_HUGE_16GB = HUGETLB_FLAG_ENCODE_16GB;
lib/std/os/linux.zig
@@ -1110,6 +1110,10 @@ pub fn io_uring_register(fd: i32, opcode: u32, arg: ?*const c_void, nr_args: u32
     return syscall4(SYS_io_uring_register, @bitCast(usize, @as(isize, fd)), opcode, @ptrToInt(arg), nr_args);
 }
 
+pub fn memfd_create(name: [*:0]const u8, flags: usize) usize {
+    return syscall2(SYS_memfd_create, @ptrToInt(name), flags);
+}
+
 test "" {
     if (builtin.os == .linux) {
         _ = @import("linux/test.zig");
lib/std/os.zig
@@ -3281,3 +3281,35 @@ pub fn setsockopt(fd: fd_t, level: u32, optname: u32, opt: []const u8) SetSockOp
         else => |err| return unexpectedErrno(err),
     }
 }
+
+// TODO support for non-null terminated strings?
+pub fn memfd_createC(name: [*:0]const u8, flags: usize) !fd_t {
+    if (builtin.os != .linux) @compileError("memfd_create() not implemented for this target");
+
+    const rc = linux.memfd_create(name, flags);
+    switch (linux.getErrno(rc)) {
+        0 => return @intCast(fd_t, rc),
+        EFAULT => unreachable, // name has invalid memory
+        EINVAL => unreachable, // name/flags are faulty
+        ENFILE => return error.SystemFdQuotaExceeded,
+        EMFILE => return error.ProcessFdQuotaExceeded,
+        ENOMEM => return error.OutOfMemory,
+        else => |err| return unexpectedErrno(err),
+    }
+}
+
+pub const MFD_NAME_PREFIX = "memfd:";
+pub const MFD_MAX_NAME_LEN = NAME_MAX - MFD_NAME_PREFIX.len;
+fn toMemFdPath(name: []const u8) ![MFD_MAX_NAME_LEN:0]u8 {
+    var path_with_null: [MFD_MAX_NAME_LEN:0]u8 = undefined;
+    // >= rather than > to make room for the null byte
+    if (name.len >= MFD_MAX_NAME_LEN) return error.NameTooLong;
+    mem.copy(u8, &path_with_null, name);
+    path_with_null[name.len] = 0;
+    return path_with_null;
+}
+
+pub fn memfd_create(name: []const u8, flags: usize) !fd_t {
+    const name_t = try toMemFdPath(name);
+    return try memfd_createC(&name_t, flags);
+}