Commit 8186211404

Andrew Kelley <andrew@ziglang.org>
2019-12-31 22:34:14
improvements to memfd_create
* move test from std/io/test.zig to std/os/test.zig * do glibc version check, and make direct system call if glibc is too old * disable test when not linking libc, to avoid not working with outdated qemu version on the CI server. see #4019
1 parent a153a97
Changed files (3)
lib/std/io/test.zig
@@ -649,17 +649,3 @@ 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);
-    defer std.os.close(fd);
-    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/test.zig
@@ -237,3 +237,24 @@ test "argsAlloc" {
     var args = try std.process.argsAlloc(std.heap.page_allocator);
     std.process.argsFree(std.heap.page_allocator, args);
 }
+
+test "memfd_create" {
+    // memfd_create is linux specific.
+    if (builtin.os != .linux) return error.SkipZigTest;
+    // Zig's CI testing infrastructure uses QEMU. Currently the version is
+    // qemu 2.11 from Ubuntu 18.04, which does not have memfd_create support.
+    // memfd_create support is introduced in qemu 4.2. To avoid
+    // "invalid syscall" errors from qemu, we disable the test when not linking libc.
+    // https://github.com/ziglang/zig/issues/4019
+    if (!builtin.link_libc) return error.SkipZigTest;
+
+    const fd = try std.os.memfd_create("test", 0);
+    defer std.os.close(fd);
+    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.zig
@@ -3282,9 +3282,19 @@ pub fn setsockopt(fd: fd_t, level: u32, optname: u32, opt: []const u8) SetSockOp
     }
 }
 
+pub const MemFdCreateError = error{
+    SystemFdQuotaExceeded,
+    ProcessFdQuotaExceeded,
+    OutOfMemory,
+} || UnexpectedError;
+
 pub fn memfd_createC(name: [*:0]const u8, flags: u32) !fd_t {
-    const rc = system.memfd_create(name, flags);
-    switch (errno(rc)) {
+    // memfd_create is available only in glibc versions starting with 2.27.
+    const use_c = std.c.versionCheck(.{ .major = 2, .minor = 27, .patch = 0 }).ok;
+    const sys = if (use_c) std.c else linux;
+    const getErrno = if (use_c) std.c.getErrno else linux.getErrno;
+    const rc = sys.memfd_create(name, flags);
+    switch (getErrno(rc)) {
         0 => return @intCast(fd_t, rc),
         EFAULT => unreachable, // name has invalid memory
         EINVAL => unreachable, // name/flags are faulty
@@ -3308,5 +3318,5 @@ fn toMemFdPath(name: []const u8) ![MFD_MAX_NAME_LEN:0]u8 {
 
 pub fn memfd_create(name: []const u8, flags: u32) !fd_t {
     const name_t = try toMemFdPath(name);
-    return try memfd_createC(&name_t, flags);
+    return memfd_createC(&name_t, flags);
 }