Commit c950f0c6c3

Jakub Konka <kubkon@jakubkonka.com>
2020-06-22 09:40:06
Enhance std.os.readlinkat coverage
Adds Windows stub (still needs to be implemented on Windows), adds WASI implementation, adds unit test testing basic chain of ops: create file -> symlink -> readlink.
1 parent 64078ca
Changed files (2)
lib
lib/std/os/test.zig
@@ -19,6 +19,9 @@ const tmpDir = std.testing.tmpDir;
 const Dir = std.fs.Dir;
 
 test "readlinkat" {
+    // enable when `readlinkat` and `symlinkat` are implemented on Windows
+    if (builtin.os.tag == .windows) return error.SkipZigTest;
+
     var tmp = tmpDir(.{});
     defer tmp.cleanup();
 
@@ -28,7 +31,10 @@ test "readlinkat" {
     // create a symbolic link
     try os.symlinkat("file.txt", tmp.dir.fd, "link");
 
-    // TODO read the link
+    // read the link
+    var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
+    const read_link = try os.readlinkat(tmp.dir.fd, "link", buffer[0..]);
+    expect(mem.eql(u8, "file.txt", read_link));
 }
 
 test "makePath, put some files in it, deleteTree" {
lib/std/os.zig
@@ -1588,7 +1588,7 @@ pub const symlinkatC = @compileError("deprecated: renamed to symlinkatZ");
 /// WASI-only. The same as `symlinkat` but targeting WASI.
 /// See also `symlinkat`.
 pub fn symlinkatWasi(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkError!void {
-    switch (wasi.path_symlink(sym_link_path.ptr, sym_link_path.len, newdirfd, target_path.ptr, target_path.len)) {
+    switch (wasi.path_symlink(target_path.ptr, target_path.len, newdirfd, sym_link_path.ptr, sym_link_path.len)) {
         wasi.ESUCCESS => {},
         wasi.EFAULT => unreachable,
         wasi.EINVAL => unreachable,
@@ -2343,12 +2343,54 @@ pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8
     }
 }
 
+/// Similar to `readlink` except reads value of a symbolink link **relative** to `dirfd` directory handle.
+/// The return value is a slice of `out_buffer` from index 0.
+/// See also `readlinkatWasi`, `realinkatZ` and `realinkatW`.
+pub fn readlinkat(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
+    if (builtin.os.tag == .wasi) {
+        return readlinkatWasi(dirfd, file_path, out_buffer);
+    }
+    if (builtin.os.tag == .windows) {
+        const file_path_w = try windows.cStrToPrefixedFileW(file_path);
+        return readlinkatW(dirfd, file_path.span().ptr, out_buffer);
+    }
+    const file_path_c = try toPosixPath(file_path);
+    return readlinkatZ(dirfd, &file_path_c, out_buffer);
+}
+
 pub const readlinkatC = @compileError("deprecated: renamed to readlinkatZ");
 
+/// WASI-only. Same as `readlinkat` but targets WASI.
+/// See also `readlinkat`.
+pub fn readlinkatWasi(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
+    var bufused: usize = undefined;
+    switch (wasi.path_readlink(dirfd, file_path.ptr, file_path.len, out_buffer.ptr, out_buffer.len, &bufused)) {
+        wasi.ESUCCESS => return out_buffer[0..bufused],
+        wasi.EACCES => return error.AccessDenied,
+        wasi.EFAULT => unreachable,
+        wasi.EINVAL => unreachable,
+        wasi.EIO => return error.FileSystem,
+        wasi.ELOOP => return error.SymLinkLoop,
+        wasi.ENAMETOOLONG => return error.NameTooLong,
+        wasi.ENOENT => return error.FileNotFound,
+        wasi.ENOMEM => return error.SystemResources,
+        wasi.ENOTDIR => return error.NotDir,
+        else => |err| return unexpectedErrno(err),
+    }
+}
+
+/// Windows-only. Same as `readlinkat` except `file_path` is null-terminated, WTF16 encoded.
+/// See also `readlinkat`.
+pub fn readlinkatW(dirfd: fd_t, file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 {
+    @compileError("TODO implement on Windows");
+}
+
+/// Same as `readlinkat` except `file_path` is null-terminated.
+/// See also `readlinkat`.
 pub fn readlinkatZ(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
     if (builtin.os.tag == .windows) {
         const file_path_w = try windows.cStrToPrefixedFileW(file_path);
-        @compileError("TODO implement readlink for Windows");
+        return readlinkatW(dirfd, file_path_w.span().ptr, out_buffer);
     }
     const rc = system.readlinkat(dirfd, file_path, out_buffer.ptr, out_buffer.len);
     switch (errno(rc)) {