| author | |
| committer | |
| log | 840331ee48e54b1ce4a3c8196f90eec73ce6deea |
| tree | 674cd1223b59a58f7db0f944a65980cc97e3c56a |
| parent | c70832bc411388c3e9749013d7cf99704ff95a36 |
4 files changed, 188 insertions(+), 0 deletions(-)
lib/std/c.zig+2| ... | @@ -100,6 +100,8 @@ pub extern "c" fn pwrite(fd: fd_t, buf: [*]const u8, nbyte: usize, offset: u64) | ... | @@ -100,6 +100,8 @@ pub extern "c" fn pwrite(fd: fd_t, buf: [*]const u8, nbyte: usize, offset: u64) |
| 100 | pub extern "c" fn mmap(addr: ?*align(page_size) c_void, len: usize, prot: c_uint, flags: c_uint, fd: fd_t, offset: u64) *c_void; | 100 | pub extern "c" fn mmap(addr: ?*align(page_size) c_void, len: usize, prot: c_uint, flags: c_uint, fd: fd_t, offset: u64) *c_void; |
| 101 | pub extern "c" fn munmap(addr: *align(page_size) c_void, len: usize) c_int; | 101 | pub extern "c" fn munmap(addr: *align(page_size) c_void, len: usize) c_int; |
| 102 | pub extern "c" fn mprotect(addr: *align(page_size) c_void, len: usize, prot: c_uint) c_int; | 102 | pub extern "c" fn mprotect(addr: *align(page_size) c_void, len: usize, prot: c_uint) c_int; |
| 103 | pub extern "c" fn link(oldpath: [*:0]const u8, newpath: [*:0]const u8, flags: c_int) c_int; | ||
| 104 | pub extern "c" fn linkat(oldfd: fd_t, oldpath: [*:0]const u8, newfd: fd_t, newpath: [*:0]const u8, flags: c_int) c_int; | ||
| 103 | pub extern "c" fn unlink(path: [*:0]const u8) c_int; | 105 | pub extern "c" fn unlink(path: [*:0]const u8) c_int; |
| 104 | pub extern "c" fn unlinkat(dirfd: fd_t, path: [*:0]const u8, flags: c_uint) c_int; | 106 | pub extern "c" fn unlinkat(dirfd: fd_t, path: [*:0]const u8, flags: c_uint) c_int; |
| 105 | pub extern "c" fn getcwd(buf: [*]u8, size: usize) ?[*]u8; | 107 | pub extern "c" fn getcwd(buf: [*]u8, size: usize) ?[*]u8; |
lib/std/os.zig+86| ... | @@ -1634,6 +1634,92 @@ pub fn symlinkatZ(target_path: [*:0]const u8, newdirfd: fd_t, sym_link_path: [*: | ... | @@ -1634,6 +1634,92 @@ pub fn symlinkatZ(target_path: [*:0]const u8, newdirfd: fd_t, sym_link_path: [*: |
| 1634 | } | 1634 | } |
| 1635 | } | 1635 | } |
| 1636 | 1636 | ||
| 1637 | pub const LinkError = UnexpectedError || error{ | ||
| 1638 | AccessDenied, | ||
| 1639 | DiskQuota, | ||
| 1640 | PathAlreadyExists, | ||
| 1641 | FileSystem, | ||
| 1642 | SymLinkLoop, | ||
| 1643 | LinkQuotaExceeded, | ||
| 1644 | NameTooLong, | ||
| 1645 | FileNotFound, | ||
| 1646 | SystemResources, | ||
| 1647 | NoSpaceLeft, | ||
| 1648 | ReadOnlyFileSystem, | ||
| 1649 | NotSameFileSystem, | ||
| 1650 | }; | ||
| 1651 | |||
| 1652 | pub fn linkZ(oldpath: [*:0]const u8, newpath: [*:0]const u8, flags: i32) LinkError!void { | ||
| 1653 | switch (errno(system.link(oldpath, newpath, flags))) { | ||
| 1654 | 0 => return, | ||
| 1655 | EACCES => return error.AccessDenied, | ||
| 1656 | EDQUOT => return error.DiskQuota, | ||
| 1657 | EEXIST => return error.PathAlreadyExists, | ||
| 1658 | EFAULT => unreachable, | ||
| 1659 | EIO => return error.FileSystem, | ||
| 1660 | ELOOP => return error.SymLinkLoop, | ||
| 1661 | EMLINK => return error.LinkQuotaExceeded, | ||
| 1662 | ENAMETOOLONG => return error.NameTooLong, | ||
| 1663 | ENOENT => return error.FileNotFound, | ||
| 1664 | ENOMEM => return error.SystemResources, | ||
| 1665 | ENOSPC => return error.NoSpaceLeft, | ||
| 1666 | EPERM => return error.AccessDenied, | ||
| 1667 | EROFS => return error.ReadOnlyFileSystem, | ||
| 1668 | EXDEV => return error.NotSameFileSystem, | ||
| 1669 | EINVAL => unreachable, | ||
| 1670 | else => |err| return unexpectedErrno(err), | ||
| 1671 | } | ||
| 1672 | } | ||
| 1673 | |||
| 1674 | pub fn link(oldpath: []const u8, newpath: []const u8, flags: i32) LinkError!void { | ||
| 1675 | const old = try toPosixPath(oldpath); | ||
| 1676 | const new = try toPosixPath(newpath); | ||
| 1677 | return try linkZ(&old, &new, flags); | ||
| 1678 | } | ||
| 1679 | |||
| 1680 | pub const LinkatError = LinkError || error{NotDir}; | ||
| 1681 | |||
| 1682 | pub fn linkatZ( | ||
| 1683 | olddir: fd_t, | ||
| 1684 | oldpath: [*:0]const u8, | ||
| 1685 | newdir: fd_t, | ||
| 1686 | newpath: [*:0]const u8, | ||
| 1687 | flags: i32, | ||
| 1688 | ) LinkatError!void { | ||
| 1689 | switch (errno(system.linkat(olddir, oldpath, newdir, newpath, flags))) { | ||
| 1690 | 0 => return, | ||
| 1691 | EACCES => return error.AccessDenied, | ||
| 1692 | EDQUOT => return error.DiskQuota, | ||
| 1693 | EEXIST => return error.PathAlreadyExists, | ||
| 1694 | EFAULT => unreachable, | ||
| 1695 | EIO => return error.FileSystem, | ||
| 1696 | ELOOP => return error.SymLinkLoop, | ||
| 1697 | EMLINK => return error.LinkQuotaExceeded, | ||
| 1698 | ENAMETOOLONG => return error.NameTooLong, | ||
| 1699 | ENOENT => return error.FileNotFound, | ||
| 1700 | ENOMEM => return error.SystemResources, | ||
| 1701 | ENOSPC => return error.NoSpaceLeft, | ||
| 1702 | ENOTDIR => return error.NotDir, | ||
| 1703 | EPERM => return error.AccessDenied, | ||
| 1704 | EROFS => return error.ReadOnlyFileSystem, | ||
| 1705 | EXDEV => return error.NotSameFileSystem, | ||
| 1706 | EINVAL => unreachable, | ||
| 1707 | else => |err| return unexpectedErrno(err), | ||
| 1708 | } | ||
| 1709 | } | ||
| 1710 | |||
| 1711 | pub fn linkat( | ||
| 1712 | olddir: fd_t, | ||
| 1713 | oldpath: []const u8, | ||
| 1714 | newdir: fd_t, | ||
| 1715 | newpath: []const u8, | ||
| 1716 | flags: i32, | ||
| 1717 | ) LinkatError!void { | ||
| 1718 | const old = try toPosixPath(oldpath); | ||
| 1719 | const new = try toPosixPath(newpath); | ||
| 1720 | return try linkatZ(olddir, &old, newdir, &new, flags); | ||
| 1721 | } | ||
| 1722 | |||
| 1637 | pub const UnlinkError = error{ | 1723 | pub const UnlinkError = error{ |
| 1638 | FileNotFound, | 1724 | FileNotFound, |
| 1639 | 1725 |
lib/std/os/linux.zig+31| ... | @@ -634,6 +634,37 @@ pub fn tgkill(tgid: pid_t, tid: pid_t, sig: i32) usize { | ... | @@ -634,6 +634,37 @@ pub fn tgkill(tgid: pid_t, tid: pid_t, sig: i32) usize { |
| 634 | return syscall2(.tgkill, @bitCast(usize, @as(isize, tgid)), @bitCast(usize, @as(isize, tid)), @bitCast(usize, @as(isize, sig))); | 634 | return syscall2(.tgkill, @bitCast(usize, @as(isize, tgid)), @bitCast(usize, @as(isize, tid)), @bitCast(usize, @as(isize, sig))); |
| 635 | } | 635 | } |
| 636 | 636 | ||
| 637 | pub fn link(oldpath: [*:0]const u8, newpath: [*:0]const u8, flags: i32) usize { | ||
| 638 | if (@hasField(SYS, "link")) { | ||
| 639 | return syscall3( | ||
| 640 | .link, | ||
| 641 | @ptrToInt(oldpath), | ||
| 642 | @ptrToInt(newpath), | ||
| 643 | @bitCast(usize, @as(isize, flags)), | ||
| 644 | ); | ||
| 645 | } else { | ||
| 646 | return syscall5( | ||
| 647 | .linkat, | ||
| 648 | @bitCast(usize, @as(isize, AT_FDCWD)), | ||
| 649 | @ptrToInt(oldpath), | ||
| 650 | @bitCast(usize, @as(isize, AT_FDCWD)), | ||
| 651 | @ptrToInt(newpath), | ||
| 652 | @bitCast(usize, @as(isize, flags)), | ||
| 653 | ); | ||
| 654 | } | ||
| 655 | } | ||
| 656 | |||
| 657 | pub fn linkat(oldfd: fd_t, oldpath: [*:0]const u8, newfd: fd_t, newpath: [*:0]const u8, flags: i32) usize { | ||
| 658 | return syscall5( | ||
| 659 | .linkat, | ||
| 660 | @bitCast(usize, @as(isize, oldfd)), | ||
| 661 | @ptrToInt(oldpath), | ||
| 662 | @bitCast(usize, @as(isize, newfd)), | ||
| 663 | @ptrToInt(newpath), | ||
| 664 | @bitCast(usize, @as(isize, flags)), | ||
| 665 | ); | ||
| 666 | } | ||
| 667 | |||
| 637 | pub fn unlink(path: [*:0]const u8) usize { | 668 | pub fn unlink(path: [*:0]const u8) usize { |
| 638 | if (@hasField(SYS, "unlink")) { | 669 | if (@hasField(SYS, "unlink")) { |
| 639 | return syscall1(.unlink, @ptrToInt(path)); | 670 | return syscall1(.unlink, @ptrToInt(path)); |
lib/std/os/test.zig+69| ... | @@ -189,6 +189,75 @@ fn testReadlink(target_path: []const u8, symlink_path: []const u8) !void { | ... | @@ -189,6 +189,75 @@ fn testReadlink(target_path: []const u8, symlink_path: []const u8) !void { |
| 189 | expect(mem.eql(u8, target_path, given)); | 189 | expect(mem.eql(u8, target_path, given)); |
| 190 | } | 190 | } |
| 191 | 191 | ||
| 192 | test "link with relative paths" { | ||
| 193 | if (builtin.os.tag != .linux) return error.SkipZigTest; | ||
| 194 | var cwd = fs.cwd(); | ||
| 195 | |||
| 196 | cwd.deleteFile("example.txt") catch {}; | ||
| 197 | cwd.deleteFile("new.txt") catch {}; | ||
| 198 | |||
| 199 | try cwd.writeFile("example.txt", "example"); | ||
| 200 | try os.link("example.txt", "new.txt", 0); | ||
| 201 | |||
| 202 | const efd = try cwd.openFile("example.txt", .{}); | ||
| 203 | defer efd.close(); | ||
| 204 | |||
| 205 | const nfd = try cwd.openFile("new.txt", .{}); | ||
| 206 | defer nfd.close(); | ||
| 207 | |||
| 208 | { | ||
| 209 | const estat = try os.fstat(efd.handle); | ||
| 210 | const nstat = try os.fstat(nfd.handle); | ||
| 211 | |||
| 212 | testing.expectEqual(estat.ino, nstat.ino); | ||
| 213 | testing.expectEqual(@as(usize, 2), nstat.nlink); | ||
| 214 | } | ||
| 215 | |||
| 216 | try os.unlink("new.txt"); | ||
| 217 | |||
| 218 | { | ||
| 219 | const estat = try os.fstat(efd.handle); | ||
| 220 | testing.expectEqual(@as(usize, 1), estat.nlink); | ||
| 221 | } | ||
| 222 | |||
| 223 | try cwd.deleteFile("example.txt"); | ||
| 224 | } | ||
| 225 | |||
| 226 | test "linkat with different directories" { | ||
| 227 | if (builtin.os.tag != .linux) return error.SkipZigTest; | ||
| 228 | var cwd = fs.cwd(); | ||
| 229 | var tmp = tmpDir(.{}); | ||
| 230 | |||
| 231 | cwd.deleteFile("example.txt") catch {}; | ||
| 232 | tmp.dir.deleteFile("new.txt") catch {}; | ||
| 233 | |||
| 234 | try cwd.writeFile("example.txt", "example"); | ||
| 235 | try os.linkat(cwd.fd, "example.txt", tmp.dir.fd, "new.txt", 0); | ||
| 236 | |||
| 237 | const efd = try cwd.openFile("example.txt", .{}); | ||
| 238 | defer efd.close(); | ||
| 239 | |||
| 240 | const nfd = try tmp.dir.openFile("new.txt", .{}); | ||
| 241 | |||
| 242 | { | ||
| 243 | defer nfd.close(); | ||
| 244 | const estat = try os.fstat(efd.handle); | ||
| 245 | const nstat = try os.fstat(nfd.handle); | ||
| 246 | |||
| 247 | testing.expectEqual(estat.ino, nstat.ino); | ||
| 248 | testing.expectEqual(@as(usize, 2), nstat.nlink); | ||
| 249 | } | ||
| 250 | |||
| 251 | try os.unlinkat(tmp.dir.fd, "new.txt", 0); | ||
| 252 | |||
| 253 | { | ||
| 254 | const estat = try os.fstat(efd.handle); | ||
| 255 | testing.expectEqual(@as(usize, 1), estat.nlink); | ||
| 256 | } | ||
| 257 | |||
| 258 | try cwd.deleteFile("example.txt"); | ||
| 259 | } | ||
| 260 | |||
| 192 | test "fstatat" { | 261 | test "fstatat" { |
| 193 | // enable when `fstat` and `fstatat` are implemented on Windows | 262 | // enable when `fstat` and `fstatat` are implemented on Windows |
| 194 | if (builtin.os.tag == .windows) return error.SkipZigTest; | 263 | if (builtin.os.tag == .windows) return error.SkipZigTest; |