authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-06-22 20:02:27-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-06-22 20:02:27-04:00
log6ff6ac866dee20f4a39fe92d2fa11605d94e2aec
tree02ca5d10429d58784859b3d732b433afd8a79284
parent44bd0a267096b54ac4dd40af31138bab1919e4d7
parentc950f0c6c3d5472a635ba8b971f46200d41221fd
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #5666 from kubkon/symlinkat-readlinkat

[libstd]: enhance std.os.{symlinkat, readlinkat} coverage

3 files changed, 120 insertions(+), 6 deletions(-)

lib/std/c.zig+1
...@@ -102,6 +102,7 @@ pub extern "c" fn pipe2(fds: *[2]fd_t, flags: u32) c_int;...@@ -102,6 +102,7 @@ pub extern "c" fn pipe2(fds: *[2]fd_t, flags: u32) c_int;
102pub extern "c" fn mkdir(path: [*:0]const u8, mode: c_uint) c_int;102pub extern "c" fn mkdir(path: [*:0]const u8, mode: c_uint) c_int;
103pub extern "c" fn mkdirat(dirfd: fd_t, path: [*:0]const u8, mode: u32) c_int;103pub extern "c" fn mkdirat(dirfd: fd_t, path: [*:0]const u8, mode: u32) c_int;
104pub extern "c" fn symlink(existing: [*:0]const u8, new: [*:0]const u8) c_int;104pub extern "c" fn symlink(existing: [*:0]const u8, new: [*:0]const u8) c_int;
105pub extern "c" fn symlinkat(oldpath: [*:0]const u8, newdirfd: fd_t, newpath: [*:0]const u8) c_int;
105pub extern "c" fn rename(old: [*:0]const u8, new: [*:0]const u8) c_int;106pub extern "c" fn rename(old: [*:0]const u8, new: [*:0]const u8) c_int;
106pub extern "c" fn renameat(olddirfd: fd_t, old: [*:0]const u8, newdirfd: fd_t, new: [*:0]const u8) c_int;107pub extern "c" fn renameat(olddirfd: fd_t, old: [*:0]const u8, newdirfd: fd_t, new: [*:0]const u8) c_int;
107pub extern "c" fn chdir(path: [*:0]const u8) c_int;108pub extern "c" fn chdir(path: [*:0]const u8) c_int;
lib/std/os.zig+100-6
...@@ -1520,15 +1520,17 @@ pub const SymLinkError = error{...@@ -1520,15 +1520,17 @@ pub const SymLinkError = error{
1520/// If `sym_link_path` exists, it will not be overwritten.1520/// If `sym_link_path` exists, it will not be overwritten.
1521/// See also `symlinkC` and `symlinkW`.1521/// See also `symlinkC` and `symlinkW`.
1522pub fn symlink(target_path: []const u8, sym_link_path: []const u8) SymLinkError!void {1522pub fn symlink(target_path: []const u8, sym_link_path: []const u8) SymLinkError!void {
1523 if (builtin.os.tag == .wasi) {
1524 @compileError("symlink is not supported in WASI; use symlinkat instead");
1525 }
1523 if (builtin.os.tag == .windows) {1526 if (builtin.os.tag == .windows) {
1524 const target_path_w = try windows.sliceToPrefixedFileW(target_path);1527 const target_path_w = try windows.sliceToPrefixedFileW(target_path);
1525 const sym_link_path_w = try windows.sliceToPrefixedFileW(sym_link_path);1528 const sym_link_path_w = try windows.sliceToPrefixedFileW(sym_link_path);
1526 return windows.CreateSymbolicLinkW(sym_link_path_w.span().ptr, target_path_w.span().ptr, 0);1529 return windows.CreateSymbolicLinkW(sym_link_path_w.span().ptr, target_path_w.span().ptr, 0);
1527 } else {
1528 const target_path_c = try toPosixPath(target_path);
1529 const sym_link_path_c = try toPosixPath(sym_link_path);
1530 return symlinkZ(&target_path_c, &sym_link_path_c);
1531 }1530 }
1531 const target_path_c = try toPosixPath(target_path);
1532 const sym_link_path_c = try toPosixPath(sym_link_path);
1533 return symlinkZ(&target_path_c, &sym_link_path_c);
1532}1534}
15331535
1534pub const symlinkC = @compileError("deprecated: renamed to symlinkZ");1536pub const symlinkC = @compileError("deprecated: renamed to symlinkZ");
...@@ -1561,15 +1563,65 @@ pub fn symlinkZ(target_path: [*:0]const u8, sym_link_path: [*:0]const u8) SymLin...@@ -1561,15 +1563,65 @@ pub fn symlinkZ(target_path: [*:0]const u8, sym_link_path: [*:0]const u8) SymLin
1561 }1563 }
1562}1564}
15631565
1566/// Similar to `symlink`, however, creates a symbolic link named `sym_link_path` which contains the string
1567/// `target_path` **relative** to `newdirfd` directory handle.
1568/// A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent
1569/// one; the latter case is known as a dangling link.
1570/// If `sym_link_path` exists, it will not be overwritten.
1571/// See also `symlinkatWasi`, `symlinkatZ` and `symlinkatW`.
1564pub fn symlinkat(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkError!void {1572pub fn symlinkat(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkError!void {
1573 if (builtin.os.tag == .wasi) {
1574 return symlinkatWasi(target_path, newdirfd, sym_link_path);
1575 }
1576 if (builtin.os.tag == .windows) {
1577 const target_path_w = try windows.sliceToPrefixedFileW(target_path);
1578 const sym_link_path_w = try windows.sliceToPrefixedFileW(sym_link_path);
1579 return symlinkatW(target_path_w.span().ptr, newdirfd, sym_link_path_w.span().ptr);
1580 }
1565 const target_path_c = try toPosixPath(target_path);1581 const target_path_c = try toPosixPath(target_path);
1566 const sym_link_path_c = try toPosixPath(sym_link_path);1582 const sym_link_path_c = try toPosixPath(sym_link_path);
1567 return symlinkatZ(target_path_c, newdirfd, sym_link_path_c);1583 return symlinkatZ(&target_path_c, newdirfd, &sym_link_path_c);
1568}1584}
15691585
1570pub const symlinkatC = @compileError("deprecated: renamed to symlinkatZ");1586pub const symlinkatC = @compileError("deprecated: renamed to symlinkatZ");
15711587
1588/// WASI-only. The same as `symlinkat` but targeting WASI.
1589/// See also `symlinkat`.
1590pub fn symlinkatWasi(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkError!void {
1591 switch (wasi.path_symlink(target_path.ptr, target_path.len, newdirfd, sym_link_path.ptr, sym_link_path.len)) {
1592 wasi.ESUCCESS => {},
1593 wasi.EFAULT => unreachable,
1594 wasi.EINVAL => unreachable,
1595 wasi.EACCES => return error.AccessDenied,
1596 wasi.EPERM => return error.AccessDenied,
1597 wasi.EDQUOT => return error.DiskQuota,
1598 wasi.EEXIST => return error.PathAlreadyExists,
1599 wasi.EIO => return error.FileSystem,
1600 wasi.ELOOP => return error.SymLinkLoop,
1601 wasi.ENAMETOOLONG => return error.NameTooLong,
1602 wasi.ENOENT => return error.FileNotFound,
1603 wasi.ENOTDIR => return error.NotDir,
1604 wasi.ENOMEM => return error.SystemResources,
1605 wasi.ENOSPC => return error.NoSpaceLeft,
1606 wasi.EROFS => return error.ReadOnlyFileSystem,
1607 else => |err| return unexpectedErrno(err),
1608 }
1609}
1610
1611/// Windows-only. The same as `symlinkat` except the paths are null-terminated, WTF-16 encoded.
1612/// See also `symlinkat`.
1613pub fn symlinkatW(target_path: [*:0]const u16, newdirfd: fd_t, sym_link_path: [*:0]const u16) SymlinkError!void {
1614 @compileError("TODO implement on Windows");
1615}
1616
1617/// The same as `symlinkat` except the parameters are null-terminated pointers.
1618/// See also `symlinkat`.
1572pub fn symlinkatZ(target_path: [*:0]const u8, newdirfd: fd_t, sym_link_path: [*:0]const u8) SymLinkError!void {1619pub fn symlinkatZ(target_path: [*:0]const u8, newdirfd: fd_t, sym_link_path: [*:0]const u8) SymLinkError!void {
1620 if (builtin.os.tag == .windows) {
1621 const target_path_w = try windows.cStrToPrefixedFileW(target_path);
1622 const sym_link_path_w = try windows.cStrToPrefixedFileW(sym_link_path);
1623 return symlinkatW(target_path_w.span().ptr, newdirfd, sym_link_path.span().ptr);
1624 }
1573 switch (errno(system.symlinkat(target_path, newdirfd, sym_link_path))) {1625 switch (errno(system.symlinkat(target_path, newdirfd, sym_link_path))) {
1574 0 => return,1626 0 => return,
1575 EFAULT => unreachable,1627 EFAULT => unreachable,
...@@ -2291,12 +2343,54 @@ pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8...@@ -2291,12 +2343,54 @@ pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8
2291 }2343 }
2292}2344}
22932345
2346/// Similar to `readlink` except reads value of a symbolink link **relative** to `dirfd` directory handle.
2347/// The return value is a slice of `out_buffer` from index 0.
2348/// See also `readlinkatWasi`, `realinkatZ` and `realinkatW`.
2349pub fn readlinkat(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
2350 if (builtin.os.tag == .wasi) {
2351 return readlinkatWasi(dirfd, file_path, out_buffer);
2352 }
2353 if (builtin.os.tag == .windows) {
2354 const file_path_w = try windows.cStrToPrefixedFileW(file_path);
2355 return readlinkatW(dirfd, file_path.span().ptr, out_buffer);
2356 }
2357 const file_path_c = try toPosixPath(file_path);
2358 return readlinkatZ(dirfd, &file_path_c, out_buffer);
2359}
2360
2294pub const readlinkatC = @compileError("deprecated: renamed to readlinkatZ");2361pub const readlinkatC = @compileError("deprecated: renamed to readlinkatZ");
22952362
2363/// WASI-only. Same as `readlinkat` but targets WASI.
2364/// See also `readlinkat`.
2365pub fn readlinkatWasi(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
2366 var bufused: usize = undefined;
2367 switch (wasi.path_readlink(dirfd, file_path.ptr, file_path.len, out_buffer.ptr, out_buffer.len, &bufused)) {
2368 wasi.ESUCCESS => return out_buffer[0..bufused],
2369 wasi.EACCES => return error.AccessDenied,
2370 wasi.EFAULT => unreachable,
2371 wasi.EINVAL => unreachable,
2372 wasi.EIO => return error.FileSystem,
2373 wasi.ELOOP => return error.SymLinkLoop,
2374 wasi.ENAMETOOLONG => return error.NameTooLong,
2375 wasi.ENOENT => return error.FileNotFound,
2376 wasi.ENOMEM => return error.SystemResources,
2377 wasi.ENOTDIR => return error.NotDir,
2378 else => |err| return unexpectedErrno(err),
2379 }
2380}
2381
2382/// Windows-only. Same as `readlinkat` except `file_path` is null-terminated, WTF16 encoded.
2383/// See also `readlinkat`.
2384pub fn readlinkatW(dirfd: fd_t, file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 {
2385 @compileError("TODO implement on Windows");
2386}
2387
2388/// Same as `readlinkat` except `file_path` is null-terminated.
2389/// See also `readlinkat`.
2296pub fn readlinkatZ(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {2390pub fn readlinkatZ(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
2297 if (builtin.os.tag == .windows) {2391 if (builtin.os.tag == .windows) {
2298 const file_path_w = try windows.cStrToPrefixedFileW(file_path);2392 const file_path_w = try windows.cStrToPrefixedFileW(file_path);
2299 @compileError("TODO implement readlink for Windows");2393 return readlinkatW(dirfd, file_path_w.span().ptr, out_buffer);
2300 }2394 }
2301 const rc = system.readlinkat(dirfd, file_path, out_buffer.ptr, out_buffer.len);2395 const rc = system.readlinkat(dirfd, file_path, out_buffer.ptr, out_buffer.len);
2302 switch (errno(rc)) {2396 switch (errno(rc)) {
lib/std/os/test.zig+19
...@@ -18,6 +18,25 @@ const AtomicOrder = builtin.AtomicOrder;...@@ -18,6 +18,25 @@ const AtomicOrder = builtin.AtomicOrder;
18const tmpDir = std.testing.tmpDir;18const tmpDir = std.testing.tmpDir;
19const Dir = std.fs.Dir;19const Dir = std.fs.Dir;
2020
21test "readlinkat" {
22 // enable when `readlinkat` and `symlinkat` are implemented on Windows
23 if (builtin.os.tag == .windows) return error.SkipZigTest;
24
25 var tmp = tmpDir(.{});
26 defer tmp.cleanup();
27
28 // create file
29 try tmp.dir.writeFile("file.txt", "nonsense");
30
31 // create a symbolic link
32 try os.symlinkat("file.txt", tmp.dir.fd, "link");
33
34 // read the link
35 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
36 const read_link = try os.readlinkat(tmp.dir.fd, "link", buffer[0..]);
37 expect(mem.eql(u8, "file.txt", read_link));
38}
39
21test "makePath, put some files in it, deleteTree" {40test "makePath, put some files in it, deleteTree" {
22 var tmp = tmpDir(.{});41 var tmp = tmpDir(.{});
23 defer tmp.cleanup();42 defer tmp.cleanup();