| ... | @@ -21,9 +21,13 @@ const assert = std.debug.assert; | ... | @@ -21,9 +21,13 @@ const assert = std.debug.assert; |
| 21 | const math = std.math; | 21 | const math = std.math; |
| 22 | const mem = std.mem; | 22 | const mem = std.mem; |
| 23 | const elf = std.elf; | 23 | const elf = std.elf; |
| | 24 | const fs = std.fs; |
| 24 | const dl = @import("dynamic_library.zig"); | 25 | const dl = @import("dynamic_library.zig"); |
| 25 | const MAX_PATH_BYTES = std.fs.MAX_PATH_BYTES; | 26 | const MAX_PATH_BYTES = std.fs.MAX_PATH_BYTES; |
| 26 | const is_windows = builtin.os.tag == .windows; | 27 | const is_windows = builtin.os.tag == .windows; |
| | 28 | const Allocator = std.mem.Allocator; |
| | 29 | const Preopen = std.fs.wasi.Preopen; |
| | 30 | const PreopenList = std.fs.wasi.PreopenList; |
| 27 | | 31 | |
| 28 | pub const darwin = std.c; | 32 | pub const darwin = std.c; |
| 29 | pub const dragonfly = std.c; | 33 | pub const dragonfly = std.c; |
| ... | @@ -93,7 +97,12 @@ pub const MAX_ADDR_LEN = system.MAX_ADDR_LEN; | ... | @@ -93,7 +97,12 @@ pub const MAX_ADDR_LEN = system.MAX_ADDR_LEN; |
| 93 | pub const MMAP2_UNIT = system.MMAP2_UNIT; | 97 | pub const MMAP2_UNIT = system.MMAP2_UNIT; |
| 94 | pub const MSG = system.MSG; | 98 | pub const MSG = system.MSG; |
| 95 | pub const NAME_MAX = system.NAME_MAX; | 99 | pub const NAME_MAX = system.NAME_MAX; |
| 96 | pub const O = system.O; | 100 | pub const O = switch (builtin.os.tag) { |
| | 101 | // We want to expose the POSIX-like OFLAGS, so we use std.c.wasi.O instead |
| | 102 | // of std.os.wasi.O, which is for non-POSIX-like `wasi.path_open`, etc. |
| | 103 | .wasi => std.c.O, |
| | 104 | else => system.O, |
| | 105 | }; |
| 97 | pub const PATH_MAX = system.PATH_MAX; | 106 | pub const PATH_MAX = system.PATH_MAX; |
| 98 | pub const POLL = system.POLL; | 107 | pub const POLL = system.POLL; |
| 99 | pub const POSIX_FADV = system.POSIX_FADV; | 108 | pub const POSIX_FADV = system.POSIX_FADV; |
| ... | @@ -210,6 +219,17 @@ pub const LOG = struct { | ... | @@ -210,6 +219,17 @@ pub const LOG = struct { |
| 210 | pub const DEBUG = 7; | 219 | pub const DEBUG = 7; |
| 211 | }; | 220 | }; |
| 212 | | 221 | |
| | 222 | /// An fd-relative file path |
| | 223 | /// |
| | 224 | /// This is currently only used for WASI-specific functionality, but the concept |
| | 225 | /// is the same as the dirfd/pathname pairs in the `*at(...)` POSIX functions. |
| | 226 | pub const RelativePathWasi = struct { |
| | 227 | /// Handle to directory |
| | 228 | dir_fd: fd_t, |
| | 229 | /// Path to resource within `dir_fd`. |
| | 230 | relative_path: []const u8, |
| | 231 | }; |
| | 232 | |
| 213 | pub const socket_t = if (builtin.os.tag == .windows) windows.ws2_32.SOCKET else fd_t; | 233 | pub const socket_t = if (builtin.os.tag == .windows) windows.ws2_32.SOCKET else fd_t; |
| 214 | | 234 | |
| 215 | /// See also `getenv`. Populated by startup code before main(). | 235 | /// See also `getenv`. Populated by startup code before main(). |
| ... | @@ -1239,6 +1259,9 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usiz | ... | @@ -1239,6 +1259,9 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usiz |
| 1239 | } | 1259 | } |
| 1240 | | 1260 | |
| 1241 | pub const OpenError = error{ | 1261 | pub const OpenError = error{ |
| | 1262 | /// In WASI, this error may occur when the provided file handle is invalid. |
| | 1263 | InvalidHandle, |
| | 1264 | |
| 1242 | /// In WASI, this error may occur when the file descriptor does | 1265 | /// In WASI, this error may occur when the file descriptor does |
| 1243 | /// not hold the required rights to open a new resource relative to it. | 1266 | /// not hold the required rights to open a new resource relative to it. |
| 1244 | AccessDenied, | 1267 | AccessDenied, |
| ... | @@ -1300,6 +1323,8 @@ pub fn open(file_path: []const u8, flags: u32, perm: mode_t) OpenError!fd_t { | ... | @@ -1300,6 +1323,8 @@ pub fn open(file_path: []const u8, flags: u32, perm: mode_t) OpenError!fd_t { |
| 1300 | if (builtin.os.tag == .windows) { | 1323 | if (builtin.os.tag == .windows) { |
| 1301 | const file_path_w = try windows.sliceToPrefixedFileW(file_path); | 1324 | const file_path_w = try windows.sliceToPrefixedFileW(file_path); |
| 1302 | return openW(file_path_w.span(), flags, perm); | 1325 | return openW(file_path_w.span(), flags, perm); |
| | 1326 | } else if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| | 1327 | return openat(wasi.AT.FDCWD, file_path, flags, perm); |
| 1303 | } | 1328 | } |
| 1304 | const file_path_c = try toPosixPath(file_path); | 1329 | const file_path_c = try toPosixPath(file_path); |
| 1305 | return openZ(&file_path_c, flags, perm); | 1330 | return openZ(&file_path_c, flags, perm); |
| ... | @@ -1311,6 +1336,8 @@ pub fn openZ(file_path: [*:0]const u8, flags: u32, perm: mode_t) OpenError!fd_t | ... | @@ -1311,6 +1336,8 @@ pub fn openZ(file_path: [*:0]const u8, flags: u32, perm: mode_t) OpenError!fd_t |
| 1311 | if (builtin.os.tag == .windows) { | 1336 | if (builtin.os.tag == .windows) { |
| 1312 | const file_path_w = try windows.cStrToPrefixedFileW(file_path); | 1337 | const file_path_w = try windows.cStrToPrefixedFileW(file_path); |
| 1313 | return openW(file_path_w.span(), flags, perm); | 1338 | return openW(file_path_w.span(), flags, perm); |
| | 1339 | } else if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| | 1340 | return open(mem.sliceTo(file_path, 0), flags, perm); |
| 1314 | } | 1341 | } |
| 1315 | | 1342 | |
| 1316 | const open_sym = if (builtin.os.tag == .linux and builtin.link_libc) | 1343 | const open_sym = if (builtin.os.tag == .linux and builtin.link_libc) |
| ... | @@ -1347,7 +1374,7 @@ pub fn openZ(file_path: [*:0]const u8, flags: u32, perm: mode_t) OpenError!fd_t | ... | @@ -1347,7 +1374,7 @@ pub fn openZ(file_path: [*:0]const u8, flags: u32, perm: mode_t) OpenError!fd_t |
| 1347 | } | 1374 | } |
| 1348 | } | 1375 | } |
| 1349 | | 1376 | |
| 1350 | fn openOptionsFromFlags(flags: u32) windows.OpenFileOptions { | 1377 | fn openOptionsFromFlagsWindows(flags: u32) windows.OpenFileOptions { |
| 1351 | const w = windows; | 1378 | const w = windows; |
| 1352 | | 1379 | |
| 1353 | var access_mask: w.ULONG = w.READ_CONTROL | w.FILE_WRITE_ATTRIBUTES | w.SYNCHRONIZE; | 1380 | var access_mask: w.ULONG = w.READ_CONTROL | w.FILE_WRITE_ATTRIBUTES | w.SYNCHRONIZE; |
| ... | @@ -1387,7 +1414,7 @@ fn openOptionsFromFlags(flags: u32) windows.OpenFileOptions { | ... | @@ -1387,7 +1414,7 @@ fn openOptionsFromFlags(flags: u32) windows.OpenFileOptions { |
| 1387 | /// or makes use of perm argument. | 1414 | /// or makes use of perm argument. |
| 1388 | pub fn openW(file_path_w: []const u16, flags: u32, perm: mode_t) OpenError!fd_t { | 1415 | pub fn openW(file_path_w: []const u16, flags: u32, perm: mode_t) OpenError!fd_t { |
| 1389 | _ = perm; | 1416 | _ = perm; |
| 1390 | var options = openOptionsFromFlags(flags); | 1417 | var options = openOptionsFromFlagsWindows(flags); |
| 1391 | options.dir = std.fs.cwd().fd; | 1418 | options.dir = std.fs.cwd().fd; |
| 1392 | return windows.OpenFile(file_path_w, options) catch |err| switch (err) { | 1419 | return windows.OpenFile(file_path_w, options) catch |err| switch (err) { |
| 1393 | error.WouldBlock => unreachable, | 1420 | error.WouldBlock => unreachable, |
| ... | @@ -1396,21 +1423,204 @@ pub fn openW(file_path_w: []const u16, flags: u32, perm: mode_t) OpenError!fd_t | ... | @@ -1396,21 +1423,204 @@ pub fn openW(file_path_w: []const u16, flags: u32, perm: mode_t) OpenError!fd_t |
| 1396 | }; | 1423 | }; |
| 1397 | } | 1424 | } |
| 1398 | | 1425 | |
| | 1426 | var wasi_cwd = if (builtin.os.tag == .wasi and !builtin.link_libc) struct { |
| | 1427 | // List of available Preopens |
| | 1428 | preopens: ?PreopenList = null, |
| | 1429 | // Memory buffer for storing the relative portion of the CWD |
| | 1430 | path_buffer: [MAX_PATH_BYTES]u8 = undefined, |
| | 1431 | // Current Working Directory, stored as an fd_t and a relative path |
| | 1432 | cwd: ?RelativePathWasi = null, |
| | 1433 | // Preopen associated with `cwd`, if any |
| | 1434 | cwd_preopen: ?Preopen = null, |
| | 1435 | }{} else undefined; |
| | 1436 | |
| | 1437 | /// Initialize the available Preopen list on WASI and set the CWD to `cwd_init`. |
| | 1438 | /// Note that `cwd_init` corresponds to a Preopen directory, not necessarily |
| | 1439 | /// a POSIX path. For example, "." matches a Preopen provided with `--dir=.` |
| | 1440 | /// |
| | 1441 | /// This must be called before using any relative or absolute paths with `std.os` |
| | 1442 | /// functions, if you are on WASI without linking libc. |
| | 1443 | /// |
| | 1444 | /// `alloc` must not be a temporary or leak-detecting allocator, since `std.os` |
| | 1445 | /// retains ownership of allocations internally and may never call free(). |
| | 1446 | pub fn initPreopensWasi(alloc: Allocator, cwd_init: ?[]const u8) !void { |
| | 1447 | if (builtin.os.tag == .wasi) { |
| | 1448 | if (!builtin.link_libc) { |
| | 1449 | if (wasi_cwd.preopens == null) { |
| | 1450 | var preopen_list = PreopenList.init(alloc); |
| | 1451 | try preopen_list.populate(); |
| | 1452 | wasi_cwd.preopens = preopen_list; |
| | 1453 | } |
| | 1454 | if (cwd_init) |cwd| { |
| | 1455 | const preopen = wasi_cwd.preopens.?.findContaining(.{ .Dir = cwd }); |
| | 1456 | if (preopen) |po| { |
| | 1457 | wasi_cwd.cwd_preopen = po.base; |
| | 1458 | wasi_cwd.cwd = RelativePathWasi{ |
| | 1459 | .dir_fd = po.base.fd, |
| | 1460 | .relative_path = po.relative_path, |
| | 1461 | }; |
| | 1462 | } else { |
| | 1463 | // No matching preopen found |
| | 1464 | return error.FileNotFound; |
| | 1465 | } |
| | 1466 | } |
| | 1467 | } else { |
| | 1468 | if (cwd_init) |cwd| try chdir(cwd); |
| | 1469 | } |
| | 1470 | } |
| | 1471 | } |
| | 1472 | |
| | 1473 | /// Resolve a relative or absolute path to an handle (`fd_t`) and a relative subpath. |
| | 1474 | /// |
| | 1475 | /// For absolute paths, this automatically searches among available Preopens to find |
| | 1476 | /// a match. For relative paths, it uses the "emulated" CWD. |
| | 1477 | pub fn resolvePathWasi(path: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) !RelativePathWasi { |
| | 1478 | // Note: Due to WASI's "sandboxed" file handles, operations with this RelativePathWasi |
| | 1479 | // will fail if the relative path navigates outside of `dir_fd` using ".." |
| | 1480 | return resolvePathAndGetWasiPreopen(path, null, out_buffer); |
| | 1481 | } |
| | 1482 | |
| | 1483 | fn resolvePathAndGetWasiPreopen(path: []const u8, preopen: ?*?Preopen, out_buffer: *[MAX_PATH_BYTES]u8) !RelativePathWasi { |
| | 1484 | var allocator = std.heap.FixedBufferAllocator.init(out_buffer); |
| | 1485 | var alloc = allocator.allocator(); |
| | 1486 | |
| | 1487 | if (fs.path.isAbsolute(path) or wasi_cwd.cwd == null) { |
| | 1488 | if (wasi_cwd.preopens == null) @panic("On WASI, `initPreopensWasi` must be called to initialize preopens " ++ |
| | 1489 | "before using any CWD-relative or absolute paths.\n"); |
| | 1490 | |
| | 1491 | if (mem.startsWith(u8, path, "/preopens/fd/")) { |
| | 1492 | // "/preopens/fd/<N>" is a special prefix, which refers to a Preopen directly by fd |
| | 1493 | const fd_start = "/preopens/fd/".len; |
| | 1494 | const fd_end = mem.indexOfScalarPos(u8, path, fd_start, '/') orelse path.len; |
| | 1495 | const fd = std.fmt.parseUnsigned(fd_t, path[fd_start..fd_end], 10) catch unreachable; |
| | 1496 | const rel_path = if (path.len > fd_end + 1) path[fd_end + 1 ..] else "."; |
| | 1497 | |
| | 1498 | if (preopen) |p| p.* = wasi_cwd.preopens.?.findByFd(fd); |
| | 1499 | return RelativePathWasi{ |
| | 1500 | .dir_fd = fd, |
| | 1501 | .relative_path = alloc.dupe(u8, rel_path) catch return error.NameTooLong, |
| | 1502 | }; |
| | 1503 | } |
| | 1504 | |
| | 1505 | // For any other absolute path, we need to lookup a containing Preopen |
| | 1506 | const abs_path = fs.path.resolve(alloc, &.{ "/", path }) catch return error.NameTooLong; |
| | 1507 | const preopen_uri = wasi_cwd.preopens.?.findContaining(.{ .Dir = abs_path }); |
| | 1508 | |
| | 1509 | if (preopen_uri) |po| { |
| | 1510 | if (preopen) |p| p.* = po.base; |
| | 1511 | return RelativePathWasi{ |
| | 1512 | .dir_fd = po.base.fd, |
| | 1513 | .relative_path = po.relative_path, |
| | 1514 | }; |
| | 1515 | } else { |
| | 1516 | // No matching preopen found |
| | 1517 | return error.AccessDenied; |
| | 1518 | } |
| | 1519 | } else { |
| | 1520 | const cwd = wasi_cwd.cwd.?; |
| | 1521 | |
| | 1522 | // If the path is empty or "." or "./", return CWD |
| | 1523 | if (std.mem.eql(u8, path, ".") or std.mem.eql(u8, path, "./")) { |
| | 1524 | return cwd; |
| | 1525 | } |
| | 1526 | |
| | 1527 | // First resolve a combined path, where the "/" corresponds to `cwd.dir_fd` |
| | 1528 | // not the true filesystem root |
| | 1529 | const paths = &.{ "/", cwd.relative_path, path }; |
| | 1530 | const resolved_path = fs.path.resolve(alloc, paths) catch return error.NameTooLong; |
| | 1531 | |
| | 1532 | // Strip off the fake root to get the relative path w.r.t. `cwd.dir_fd` |
| | 1533 | const resolved_relative_path = resolved_path[1..]; |
| | 1534 | |
| | 1535 | if (preopen) |p| p.* = wasi_cwd.cwd_preopen; |
| | 1536 | return RelativePathWasi{ |
| | 1537 | .dir_fd = cwd.dir_fd, |
| | 1538 | .relative_path = resolved_relative_path, |
| | 1539 | }; |
| | 1540 | } |
| | 1541 | } |
| | 1542 | |
| 1399 | /// Open and possibly create a file. Keeps trying if it gets interrupted. | 1543 | /// Open and possibly create a file. Keeps trying if it gets interrupted. |
| 1400 | /// `file_path` is relative to the open directory handle `dir_fd`. | 1544 | /// `file_path` is relative to the open directory handle `dir_fd`. |
| 1401 | /// See also `openatZ`. | 1545 | /// See also `openatZ`. |
| 1402 | pub fn openat(dir_fd: fd_t, file_path: []const u8, flags: u32, mode: mode_t) OpenError!fd_t { | 1546 | pub fn openat(dir_fd: fd_t, file_path: []const u8, flags: u32, mode: mode_t) OpenError!fd_t { |
| 1403 | if (builtin.os.tag == .wasi and !builtin.link_libc) { | | |
| 1404 | @compileError("use openatWasi instead"); | | |
| 1405 | } | | |
| 1406 | if (builtin.os.tag == .windows) { | 1547 | if (builtin.os.tag == .windows) { |
| 1407 | const file_path_w = try windows.sliceToPrefixedFileW(file_path); | 1548 | const file_path_w = try windows.sliceToPrefixedFileW(file_path); |
| 1408 | return openatW(dir_fd, file_path_w.span(), flags, mode); | 1549 | return openatW(dir_fd, file_path_w.span(), flags, mode); |
| | 1550 | } else if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| | 1551 | // `mode` is ignored on WASI, which does not support unix-style file permissions |
| | 1552 | const fd = if (dir_fd == wasi.AT.FDCWD or fs.path.isAbsolute(file_path)) blk: { |
| | 1553 | // Resolve absolute or CWD-relative paths to a path within a Preopen |
| | 1554 | var path_buf: [MAX_PATH_BYTES]u8 = undefined; |
| | 1555 | const path = try resolvePathWasi(file_path, &path_buf); |
| | 1556 | |
| | 1557 | const opts = try openOptionsFromFlagsWasi(path.dir_fd, flags); |
| | 1558 | break :blk try openatWasi(path.dir_fd, path.relative_path, opts.lookup_flags, opts.oflags, opts.fs_flags, opts.fs_rights_base, opts.fs_rights_inheriting); |
| | 1559 | } else blk: { |
| | 1560 | const opts = try openOptionsFromFlagsWasi(dir_fd, flags); |
| | 1561 | break :blk try openatWasi(dir_fd, file_path, opts.lookup_flags, opts.oflags, opts.fs_flags, opts.fs_rights_base, opts.fs_rights_inheriting); |
| | 1562 | }; |
| | 1563 | errdefer close(fd); |
| | 1564 | |
| | 1565 | const info = try fstat(fd); |
| | 1566 | if (flags & O.WRONLY != 0 and info.filetype == .DIRECTORY) |
| | 1567 | return error.IsDir; |
| | 1568 | |
| | 1569 | return fd; |
| 1409 | } | 1570 | } |
| 1410 | const file_path_c = try toPosixPath(file_path); | 1571 | const file_path_c = try toPosixPath(file_path); |
| 1411 | return openatZ(dir_fd, &file_path_c, flags, mode); | 1572 | return openatZ(dir_fd, &file_path_c, flags, mode); |
| 1412 | } | 1573 | } |
| 1413 | | 1574 | |
| | 1575 | /// A struct to contain all lookup/rights flags accepted by `wasi.path_open` |
| | 1576 | const WasiOpenOptions = struct { |
| | 1577 | oflags: wasi.oflags_t, |
| | 1578 | lookup_flags: wasi.lookupflags_t, |
| | 1579 | fs_rights_base: wasi.rights_t, |
| | 1580 | fs_rights_inheriting: wasi.rights_t, |
| | 1581 | fs_flags: wasi.fdflags_t, |
| | 1582 | }; |
| | 1583 | |
| | 1584 | /// Compute rights + flags corresponding to the provided POSIX access mode. |
| | 1585 | fn openOptionsFromFlagsWasi(fd: fd_t, oflag: u32) OpenError!WasiOpenOptions { |
| | 1586 | const w = std.os.wasi; |
| | 1587 | |
| | 1588 | // First, discover the rights that we can derive from `fd` |
| | 1589 | var fsb_cur: wasi.fdstat_t = undefined; |
| | 1590 | _ = switch (w.fd_fdstat_get(fd, &fsb_cur)) { |
| | 1591 | .SUCCESS => .{}, |
| | 1592 | .BADF => return error.InvalidHandle, |
| | 1593 | else => |err| return unexpectedErrno(err), |
| | 1594 | }; |
| | 1595 | |
| | 1596 | // Next, calculate the read/write rights to request, depending on the |
| | 1597 | // provided POSIX access mode |
| | 1598 | var rights: w.rights_t = 0; |
| | 1599 | if (oflag & O.RDONLY != 0) { |
| | 1600 | rights |= w.RIGHT.FD_READ | w.RIGHT.FD_READDIR; |
| | 1601 | } |
| | 1602 | if (oflag & O.WRONLY != 0) { |
| | 1603 | rights |= w.RIGHT.FD_DATASYNC | w.RIGHT.FD_WRITE | |
| | 1604 | w.RIGHT.FD_ALLOCATE | w.RIGHT.FD_FILESTAT_SET_SIZE; |
| | 1605 | } |
| | 1606 | |
| | 1607 | // Request all other rights unconditionally |
| | 1608 | rights |= ~(w.RIGHT.FD_DATASYNC | w.RIGHT.FD_READ | |
| | 1609 | w.RIGHT.FD_WRITE | w.RIGHT.FD_ALLOCATE | |
| | 1610 | w.RIGHT.FD_READDIR | w.RIGHT.FD_FILESTAT_SET_SIZE); |
| | 1611 | |
| | 1612 | // But only take rights that we can actually inherit |
| | 1613 | rights &= fsb_cur.fs_rights_inheriting; |
| | 1614 | |
| | 1615 | return WasiOpenOptions{ |
| | 1616 | .oflags = @truncate(w.oflags_t, (oflag >> 12)) & 0xfff, |
| | 1617 | .lookup_flags = if (oflag & O.NOFOLLOW == 0) w.LOOKUP_SYMLINK_FOLLOW else 0, |
| | 1618 | .fs_rights_base = rights, |
| | 1619 | .fs_rights_inheriting = fsb_cur.fs_rights_inheriting, |
| | 1620 | .fs_flags = @truncate(w.fdflags_t, oflag & 0xfff), |
| | 1621 | }; |
| | 1622 | } |
| | 1623 | |
| 1414 | /// Open and possibly create a file in WASI. | 1624 | /// Open and possibly create a file in WASI. |
| 1415 | pub fn openatWasi(dir_fd: fd_t, file_path: []const u8, lookup_flags: lookupflags_t, oflags: oflags_t, fdflags: fdflags_t, base: rights_t, inheriting: rights_t) OpenError!fd_t { | 1625 | pub fn openatWasi(dir_fd: fd_t, file_path: []const u8, lookup_flags: lookupflags_t, oflags: oflags_t, fdflags: fdflags_t, base: rights_t, inheriting: rights_t) OpenError!fd_t { |
| 1416 | while (true) { | 1626 | while (true) { |
| ... | @@ -1450,6 +1660,8 @@ pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: u32, mode: mode_t) | ... | @@ -1450,6 +1660,8 @@ pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: u32, mode: mode_t) |
| 1450 | if (builtin.os.tag == .windows) { | 1660 | if (builtin.os.tag == .windows) { |
| 1451 | const file_path_w = try windows.cStrToPrefixedFileW(file_path); | 1661 | const file_path_w = try windows.cStrToPrefixedFileW(file_path); |
| 1452 | return openatW(dir_fd, file_path_w.span(), flags, mode); | 1662 | return openatW(dir_fd, file_path_w.span(), flags, mode); |
| | 1663 | } else if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| | 1664 | return openat(dir_fd, mem.sliceTo(file_path, 0), flags, mode); |
| 1453 | } | 1665 | } |
| 1454 | | 1666 | |
| 1455 | const openat_sym = if (builtin.os.tag == .linux and builtin.link_libc) | 1667 | const openat_sym = if (builtin.os.tag == .linux and builtin.link_libc) |
| ... | @@ -1496,7 +1708,7 @@ pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: u32, mode: mode_t) | ... | @@ -1496,7 +1708,7 @@ pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: u32, mode: mode_t) |
| 1496 | /// or makes use of perm argument. | 1708 | /// or makes use of perm argument. |
| 1497 | pub fn openatW(dir_fd: fd_t, file_path_w: []const u16, flags: u32, mode: mode_t) OpenError!fd_t { | 1709 | pub fn openatW(dir_fd: fd_t, file_path_w: []const u16, flags: u32, mode: mode_t) OpenError!fd_t { |
| 1498 | _ = mode; | 1710 | _ = mode; |
| 1499 | var options = openOptionsFromFlags(flags); | 1711 | var options = openOptionsFromFlagsWindows(flags); |
| 1500 | options.dir = dir_fd; | 1712 | options.dir = dir_fd; |
| 1501 | return windows.OpenFile(file_path_w, options) catch |err| switch (err) { | 1713 | return windows.OpenFile(file_path_w, options) catch |err| switch (err) { |
| 1502 | error.WouldBlock => unreachable, | 1714 | error.WouldBlock => unreachable, |
| ... | @@ -1764,9 +1976,15 @@ pub const GetCwdError = error{ | ... | @@ -1764,9 +1976,15 @@ pub const GetCwdError = error{ |
| 1764 | pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 { | 1976 | pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 { |
| 1765 | if (builtin.os.tag == .windows) { | 1977 | if (builtin.os.tag == .windows) { |
| 1766 | return windows.GetCurrentDirectory(out_buffer); | 1978 | return windows.GetCurrentDirectory(out_buffer); |
| 1767 | } | 1979 | } else if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| 1768 | if (builtin.os.tag == .wasi and !builtin.link_libc) { | 1980 | var buf: [MAX_PATH_BYTES]u8 = undefined; |
| 1769 | @compileError("WASI doesn't have a concept of cwd(); use std.fs.wasi.PreopenList to get available Dir handles instead"); | 1981 | const path = realpathWasi(".", &buf) catch |err| switch (err) { |
| | 1982 | error.NameTooLong => return error.NameTooLong, |
| | 1983 | error.InvalidHandle => return error.CurrentWorkingDirectoryUnlinked, |
| | 1984 | }; |
| | 1985 | if (out_buffer.len < path.len) return error.NameTooLong; |
| | 1986 | std.mem.copy(u8, out_buffer, path); |
| | 1987 | return out_buffer[0..path.len]; |
| 1770 | } | 1988 | } |
| 1771 | | 1989 | |
| 1772 | const err = if (builtin.link_libc) blk: { | 1990 | const err = if (builtin.link_libc) blk: { |
| ... | @@ -1809,11 +2027,10 @@ pub const SymLinkError = error{ | ... | @@ -1809,11 +2027,10 @@ pub const SymLinkError = error{ |
| 1809 | /// If `sym_link_path` exists, it will not be overwritten. | 2027 | /// If `sym_link_path` exists, it will not be overwritten. |
| 1810 | /// See also `symlinkZ. | 2028 | /// See also `symlinkZ. |
| 1811 | pub fn symlink(target_path: []const u8, sym_link_path: []const u8) SymLinkError!void { | 2029 | pub fn symlink(target_path: []const u8, sym_link_path: []const u8) SymLinkError!void { |
| 1812 | if (builtin.os.tag == .wasi and !builtin.link_libc) { | | |
| 1813 | @compileError("symlink is not supported in WASI; use symlinkat instead"); | | |
| 1814 | } | | |
| 1815 | if (builtin.os.tag == .windows) { | 2030 | if (builtin.os.tag == .windows) { |
| 1816 | @compileError("symlink is not supported on Windows; use std.os.windows.CreateSymbolicLink instead"); | 2031 | @compileError("symlink is not supported on Windows; use std.os.windows.CreateSymbolicLink instead"); |
| | 2032 | } else if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| | 2033 | return symlinkat(target_path, wasi.AT.FDCWD, sym_link_path); |
| 1817 | } | 2034 | } |
| 1818 | const target_path_c = try toPosixPath(target_path); | 2035 | const target_path_c = try toPosixPath(target_path); |
| 1819 | const sym_link_path_c = try toPosixPath(sym_link_path); | 2036 | const sym_link_path_c = try toPosixPath(sym_link_path); |
| ... | @@ -1825,6 +2042,8 @@ pub fn symlink(target_path: []const u8, sym_link_path: []const u8) SymLinkError! | ... | @@ -1825,6 +2042,8 @@ pub fn symlink(target_path: []const u8, sym_link_path: []const u8) SymLinkError! |
| 1825 | pub fn symlinkZ(target_path: [*:0]const u8, sym_link_path: [*:0]const u8) SymLinkError!void { | 2042 | pub fn symlinkZ(target_path: [*:0]const u8, sym_link_path: [*:0]const u8) SymLinkError!void { |
| 1826 | if (builtin.os.tag == .windows) { | 2043 | if (builtin.os.tag == .windows) { |
| 1827 | @compileError("symlink is not supported on Windows; use std.os.windows.CreateSymbolicLink instead"); | 2044 | @compileError("symlink is not supported on Windows; use std.os.windows.CreateSymbolicLink instead"); |
| | 2045 | } else if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| | 2046 | return symlink(mem.sliceTo(target_path, 0), mem.sliceTo(sym_link_path, 0)); |
| 1828 | } | 2047 | } |
| 1829 | switch (errno(system.symlink(target_path, sym_link_path))) { | 2048 | switch (errno(system.symlink(target_path, sym_link_path))) { |
| 1830 | .SUCCESS => return, | 2049 | .SUCCESS => return, |
| ... | @@ -1853,11 +2072,16 @@ pub fn symlinkZ(target_path: [*:0]const u8, sym_link_path: [*:0]const u8) SymLin | ... | @@ -1853,11 +2072,16 @@ pub fn symlinkZ(target_path: [*:0]const u8, sym_link_path: [*:0]const u8) SymLin |
| 1853 | /// If `sym_link_path` exists, it will not be overwritten. | 2072 | /// If `sym_link_path` exists, it will not be overwritten. |
| 1854 | /// See also `symlinkatWasi`, `symlinkatZ` and `symlinkatW`. | 2073 | /// See also `symlinkatWasi`, `symlinkatZ` and `symlinkatW`. |
| 1855 | pub fn symlinkat(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkError!void { | 2074 | pub fn symlinkat(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkError!void { |
| 1856 | if (builtin.os.tag == .wasi and !builtin.link_libc) { | | |
| 1857 | return symlinkatWasi(target_path, newdirfd, sym_link_path); | | |
| 1858 | } | | |
| 1859 | if (builtin.os.tag == .windows) { | 2075 | if (builtin.os.tag == .windows) { |
| 1860 | @compileError("symlinkat is not supported on Windows; use std.os.windows.CreateSymbolicLink instead"); | 2076 | @compileError("symlinkat is not supported on Windows; use std.os.windows.CreateSymbolicLink instead"); |
| | 2077 | } else if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| | 2078 | if (newdirfd == wasi.AT.FDCWD or fs.path.isAbsolute(target_path)) { |
| | 2079 | // Resolve absolute or CWD-relative paths to a path within a Preopen |
| | 2080 | var path_buf: [MAX_PATH_BYTES]u8 = undefined; |
| | 2081 | const path = try resolvePathWasi(sym_link_path, &path_buf); |
| | 2082 | return symlinkatWasi(target_path, path.dir_fd, path.relative_path); |
| | 2083 | } |
| | 2084 | return symlinkatWasi(target_path, newdirfd, sym_link_path); |
| 1861 | } | 2085 | } |
| 1862 | const target_path_c = try toPosixPath(target_path); | 2086 | const target_path_c = try toPosixPath(target_path); |
| 1863 | const sym_link_path_c = try toPosixPath(sym_link_path); | 2087 | const sym_link_path_c = try toPosixPath(sym_link_path); |
| ... | @@ -1893,6 +2117,8 @@ pub fn symlinkatWasi(target_path: []const u8, newdirfd: fd_t, sym_link_path: []c | ... | @@ -1893,6 +2117,8 @@ pub fn symlinkatWasi(target_path: []const u8, newdirfd: fd_t, sym_link_path: []c |
| 1893 | pub fn symlinkatZ(target_path: [*:0]const u8, newdirfd: fd_t, sym_link_path: [*:0]const u8) SymLinkError!void { | 2117 | pub fn symlinkatZ(target_path: [*:0]const u8, newdirfd: fd_t, sym_link_path: [*:0]const u8) SymLinkError!void { |
| 1894 | if (builtin.os.tag == .windows) { | 2118 | if (builtin.os.tag == .windows) { |
| 1895 | @compileError("symlinkat is not supported on Windows; use std.os.windows.CreateSymbolicLink instead"); | 2119 | @compileError("symlinkat is not supported on Windows; use std.os.windows.CreateSymbolicLink instead"); |
| | 2120 | } else if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| | 2121 | return symlinkat(mem.sliceTo(target_path, 0), newdirfd, mem.sliceTo(sym_link_path, 0)); |
| 1896 | } | 2122 | } |
| 1897 | switch (errno(system.symlinkat(target_path, newdirfd, sym_link_path))) { | 2123 | switch (errno(system.symlinkat(target_path, newdirfd, sym_link_path))) { |
| 1898 | .SUCCESS => return, | 2124 | .SUCCESS => return, |
| ... | @@ -1930,6 +2156,9 @@ pub const LinkError = UnexpectedError || error{ | ... | @@ -1930,6 +2156,9 @@ pub const LinkError = UnexpectedError || error{ |
| 1930 | }; | 2156 | }; |
| 1931 | | 2157 | |
| 1932 | pub fn linkZ(oldpath: [*:0]const u8, newpath: [*:0]const u8, flags: i32) LinkError!void { | 2158 | pub fn linkZ(oldpath: [*:0]const u8, newpath: [*:0]const u8, flags: i32) LinkError!void { |
| | 2159 | if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| | 2160 | return link(mem.sliceTo(oldpath, 0), mem.sliceTo(newpath, 0), flags); |
| | 2161 | } |
| 1933 | switch (errno(system.link(oldpath, newpath, flags))) { | 2162 | switch (errno(system.link(oldpath, newpath, flags))) { |
| 1934 | .SUCCESS => return, | 2163 | .SUCCESS => return, |
| 1935 | .ACCES => return error.AccessDenied, | 2164 | .ACCES => return error.AccessDenied, |
| ... | @@ -1952,6 +2181,12 @@ pub fn linkZ(oldpath: [*:0]const u8, newpath: [*:0]const u8, flags: i32) LinkErr | ... | @@ -1952,6 +2181,12 @@ pub fn linkZ(oldpath: [*:0]const u8, newpath: [*:0]const u8, flags: i32) LinkErr |
| 1952 | } | 2181 | } |
| 1953 | | 2182 | |
| 1954 | pub fn link(oldpath: []const u8, newpath: []const u8, flags: i32) LinkError!void { | 2183 | pub fn link(oldpath: []const u8, newpath: []const u8, flags: i32) LinkError!void { |
| | 2184 | if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| | 2185 | return linkat(wasi.AT.FDCWD, oldpath, wasi.AT.FDCWD, newpath, flags) catch |err| switch (err) { |
| | 2186 | error.NotDir => unreachable, // link() does not support directories |
| | 2187 | else => |e| return e, |
| | 2188 | }; |
| | 2189 | } |
| 1955 | const old = try toPosixPath(oldpath); | 2190 | const old = try toPosixPath(oldpath); |
| 1956 | const new = try toPosixPath(newpath); | 2191 | const new = try toPosixPath(newpath); |
| 1957 | return try linkZ(&old, &new, flags); | 2192 | return try linkZ(&old, &new, flags); |
| ... | @@ -1966,6 +2201,9 @@ pub fn linkatZ( | ... | @@ -1966,6 +2201,9 @@ pub fn linkatZ( |
| 1966 | newpath: [*:0]const u8, | 2201 | newpath: [*:0]const u8, |
| 1967 | flags: i32, | 2202 | flags: i32, |
| 1968 | ) LinkatError!void { | 2203 | ) LinkatError!void { |
| | 2204 | if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| | 2205 | return linkat(olddir, mem.sliceTo(oldpath, 0), newdir, mem.sliceTo(newpath, 0), flags); |
| | 2206 | } |
| 1969 | switch (errno(system.linkat(olddir, oldpath, newdir, newpath, flags))) { | 2207 | switch (errno(system.linkat(olddir, oldpath, newdir, newpath, flags))) { |
| 1970 | .SUCCESS => return, | 2208 | .SUCCESS => return, |
| 1971 | .ACCES => return error.AccessDenied, | 2209 | .ACCES => return error.AccessDenied, |
| ... | @@ -1995,11 +2233,62 @@ pub fn linkat( | ... | @@ -1995,11 +2233,62 @@ pub fn linkat( |
| 1995 | newpath: []const u8, | 2233 | newpath: []const u8, |
| 1996 | flags: i32, | 2234 | flags: i32, |
| 1997 | ) LinkatError!void { | 2235 | ) LinkatError!void { |
| | 2236 | if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| | 2237 | var resolve_olddir: bool = (olddir == wasi.AT.FDCWD or fs.path.isAbsolute(oldpath)); |
| | 2238 | var resolve_newdir: bool = (newdir == wasi.AT.FDCWD or fs.path.isAbsolute(newpath)); |
| | 2239 | |
| | 2240 | var old: RelativePathWasi = .{ .dir_fd = olddir, .relative_path = oldpath }; |
| | 2241 | var new: RelativePathWasi = .{ .dir_fd = newdir, .relative_path = newpath }; |
| | 2242 | |
| | 2243 | // Resolve absolute or CWD-relative paths to a path within a Preopen |
| | 2244 | if (resolve_olddir or resolve_newdir) { |
| | 2245 | var buf_old: [MAX_PATH_BYTES]u8 = undefined; |
| | 2246 | var buf_new: [MAX_PATH_BYTES]u8 = undefined; |
| | 2247 | |
| | 2248 | if (resolve_olddir) |
| | 2249 | old = try resolvePathWasi(oldpath, &buf_old); |
| | 2250 | |
| | 2251 | if (resolve_newdir) |
| | 2252 | new = try resolvePathWasi(newpath, &buf_new); |
| | 2253 | |
| | 2254 | return linkatWasi(old, new, flags); |
| | 2255 | } |
| | 2256 | return linkatWasi(old, new, flags); |
| | 2257 | } |
| 1998 | const old = try toPosixPath(oldpath); | 2258 | const old = try toPosixPath(oldpath); |
| 1999 | const new = try toPosixPath(newpath); | 2259 | const new = try toPosixPath(newpath); |
| 2000 | return try linkatZ(olddir, &old, newdir, &new, flags); | 2260 | return try linkatZ(olddir, &old, newdir, &new, flags); |
| 2001 | } | 2261 | } |
| 2002 | | 2262 | |
| | 2263 | /// WASI-only. The same as `linkat` but targeting WASI. |
| | 2264 | /// See also `linkat`. |
| | 2265 | pub fn linkatWasi(old: RelativePathWasi, new: RelativePathWasi, flags: i32) LinkatError!void { |
| | 2266 | var old_flags: wasi.lookupflags_t = 0; |
| | 2267 | // TODO: Why is this not defined in wasi-libc? |
| | 2268 | if (flags & linux.AT.SYMLINK_FOLLOW != 0) old_flags |= wasi.LOOKUP_SYMLINK_FOLLOW; |
| | 2269 | |
| | 2270 | switch (wasi.path_link(old.dir_fd, old_flags, old.relative_path.ptr, old.relative_path.len, new.dir_fd, new.relative_path.ptr, new.relative_path.len)) { |
| | 2271 | .SUCCESS => return, |
| | 2272 | .ACCES => return error.AccessDenied, |
| | 2273 | .DQUOT => return error.DiskQuota, |
| | 2274 | .EXIST => return error.PathAlreadyExists, |
| | 2275 | .FAULT => unreachable, |
| | 2276 | .IO => return error.FileSystem, |
| | 2277 | .LOOP => return error.SymLinkLoop, |
| | 2278 | .MLINK => return error.LinkQuotaExceeded, |
| | 2279 | .NAMETOOLONG => return error.NameTooLong, |
| | 2280 | .NOENT => return error.FileNotFound, |
| | 2281 | .NOMEM => return error.SystemResources, |
| | 2282 | .NOSPC => return error.NoSpaceLeft, |
| | 2283 | .NOTDIR => return error.NotDir, |
| | 2284 | .PERM => return error.AccessDenied, |
| | 2285 | .ROFS => return error.ReadOnlyFileSystem, |
| | 2286 | .XDEV => return error.NotSameFileSystem, |
| | 2287 | .INVAL => unreachable, |
| | 2288 | else => |err| return unexpectedErrno(err), |
| | 2289 | } |
| | 2290 | } |
| | 2291 | |
| 2003 | pub const UnlinkError = error{ | 2292 | pub const UnlinkError = error{ |
| 2004 | FileNotFound, | 2293 | FileNotFound, |
| 2005 | | 2294 | |
| ... | @@ -2027,7 +2316,10 @@ pub const UnlinkError = error{ | ... | @@ -2027,7 +2316,10 @@ pub const UnlinkError = error{ |
| 2027 | /// See also `unlinkZ`. | 2316 | /// See also `unlinkZ`. |
| 2028 | pub fn unlink(file_path: []const u8) UnlinkError!void { | 2317 | pub fn unlink(file_path: []const u8) UnlinkError!void { |
| 2029 | if (builtin.os.tag == .wasi and !builtin.link_libc) { | 2318 | if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| 2030 | @compileError("unlink is not supported in WASI; use unlinkat instead"); | 2319 | return unlinkat(wasi.AT.FDCWD, file_path, 0) catch |err| switch (err) { |
| | 2320 | error.DirNotEmpty => unreachable, // only occurs when targeting directories |
| | 2321 | else => |e| return e, |
| | 2322 | }; |
| 2031 | } else if (builtin.os.tag == .windows) { | 2323 | } else if (builtin.os.tag == .windows) { |
| 2032 | const file_path_w = try windows.sliceToPrefixedFileW(file_path); | 2324 | const file_path_w = try windows.sliceToPrefixedFileW(file_path); |
| 2033 | return unlinkW(file_path_w.span()); | 2325 | return unlinkW(file_path_w.span()); |
| ... | @@ -2042,6 +2334,8 @@ pub fn unlinkZ(file_path: [*:0]const u8) UnlinkError!void { | ... | @@ -2042,6 +2334,8 @@ pub fn unlinkZ(file_path: [*:0]const u8) UnlinkError!void { |
| 2042 | if (builtin.os.tag == .windows) { | 2334 | if (builtin.os.tag == .windows) { |
| 2043 | const file_path_w = try windows.cStrToPrefixedFileW(file_path); | 2335 | const file_path_w = try windows.cStrToPrefixedFileW(file_path); |
| 2044 | return unlinkW(file_path_w.span()); | 2336 | return unlinkW(file_path_w.span()); |
| | 2337 | } else if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| | 2338 | return unlink(mem.sliceTo(file_path, 0)); |
| 2045 | } | 2339 | } |
| 2046 | switch (errno(system.unlink(file_path))) { | 2340 | switch (errno(system.unlink(file_path))) { |
| 2047 | .SUCCESS => return, | 2341 | .SUCCESS => return, |
| ... | @@ -2079,6 +2373,12 @@ pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!vo | ... | @@ -2079,6 +2373,12 @@ pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!vo |
| 2079 | const file_path_w = try windows.sliceToPrefixedFileW(file_path); | 2373 | const file_path_w = try windows.sliceToPrefixedFileW(file_path); |
| 2080 | return unlinkatW(dirfd, file_path_w.span(), flags); | 2374 | return unlinkatW(dirfd, file_path_w.span(), flags); |
| 2081 | } else if (builtin.os.tag == .wasi and !builtin.link_libc) { | 2375 | } else if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| | 2376 | if (dirfd == wasi.AT.FDCWD or fs.path.isAbsolute(file_path)) { |
| | 2377 | // Resolve absolute or CWD-relative paths to a path within a Preopen |
| | 2378 | var path_buf: [MAX_PATH_BYTES]u8 = undefined; |
| | 2379 | const path = try resolvePathWasi(file_path, &path_buf); |
| | 2380 | return unlinkatWasi(path.dir_fd, path.relative_path, flags); |
| | 2381 | } |
| 2082 | return unlinkatWasi(dirfd, file_path, flags); | 2382 | return unlinkatWasi(dirfd, file_path, flags); |
| 2083 | } else { | 2383 | } else { |
| 2084 | const file_path_c = try toPosixPath(file_path); | 2384 | const file_path_c = try toPosixPath(file_path); |
| ... | @@ -2123,6 +2423,8 @@ pub fn unlinkatZ(dirfd: fd_t, file_path_c: [*:0]const u8, flags: u32) UnlinkatEr | ... | @@ -2123,6 +2423,8 @@ pub fn unlinkatZ(dirfd: fd_t, file_path_c: [*:0]const u8, flags: u32) UnlinkatEr |
| 2123 | if (builtin.os.tag == .windows) { | 2423 | if (builtin.os.tag == .windows) { |
| 2124 | const file_path_w = try windows.cStrToPrefixedFileW(file_path_c); | 2424 | const file_path_w = try windows.cStrToPrefixedFileW(file_path_c); |
| 2125 | return unlinkatW(dirfd, file_path_w.span(), flags); | 2425 | return unlinkatW(dirfd, file_path_w.span(), flags); |
| | 2426 | } else if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| | 2427 | return unlinkat(dirfd, mem.sliceTo(file_path_c, 0), flags); |
| 2126 | } | 2428 | } |
| 2127 | switch (errno(system.unlinkat(dirfd, file_path_c, flags))) { | 2429 | switch (errno(system.unlinkat(dirfd, file_path_c, flags))) { |
| 2128 | .SUCCESS => return, | 2430 | .SUCCESS => return, |
| ... | @@ -2181,7 +2483,7 @@ pub const RenameError = error{ | ... | @@ -2181,7 +2483,7 @@ pub const RenameError = error{ |
| 2181 | /// Change the name or location of a file. | 2483 | /// Change the name or location of a file. |
| 2182 | pub fn rename(old_path: []const u8, new_path: []const u8) RenameError!void { | 2484 | pub fn rename(old_path: []const u8, new_path: []const u8) RenameError!void { |
| 2183 | if (builtin.os.tag == .wasi and !builtin.link_libc) { | 2485 | if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| 2184 | @compileError("rename is not supported in WASI; use renameat instead"); | 2486 | return renameat(wasi.AT.FDCWD, old_path, wasi.AT.FDCWD, new_path); |
| 2185 | } else if (builtin.os.tag == .windows) { | 2487 | } else if (builtin.os.tag == .windows) { |
| 2186 | const old_path_w = try windows.sliceToPrefixedFileW(old_path); | 2488 | const old_path_w = try windows.sliceToPrefixedFileW(old_path); |
| 2187 | const new_path_w = try windows.sliceToPrefixedFileW(new_path); | 2489 | const new_path_w = try windows.sliceToPrefixedFileW(new_path); |
| ... | @@ -2199,6 +2501,8 @@ pub fn renameZ(old_path: [*:0]const u8, new_path: [*:0]const u8) RenameError!voi | ... | @@ -2199,6 +2501,8 @@ pub fn renameZ(old_path: [*:0]const u8, new_path: [*:0]const u8) RenameError!voi |
| 2199 | const old_path_w = try windows.cStrToPrefixedFileW(old_path); | 2501 | const old_path_w = try windows.cStrToPrefixedFileW(old_path); |
| 2200 | const new_path_w = try windows.cStrToPrefixedFileW(new_path); | 2502 | const new_path_w = try windows.cStrToPrefixedFileW(new_path); |
| 2201 | return renameW(old_path_w.span().ptr, new_path_w.span().ptr); | 2503 | return renameW(old_path_w.span().ptr, new_path_w.span().ptr); |
| | 2504 | } else if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| | 2505 | return rename(mem.sliceTo(old_path, 0), mem.sliceTo(new_path, 0)); |
| 2202 | } | 2506 | } |
| 2203 | switch (errno(system.rename(old_path, new_path))) { | 2507 | switch (errno(system.rename(old_path, new_path))) { |
| 2204 | .SUCCESS => return, | 2508 | .SUCCESS => return, |
| ... | @@ -2243,7 +2547,25 @@ pub fn renameat( | ... | @@ -2243,7 +2547,25 @@ pub fn renameat( |
| 2243 | const new_path_w = try windows.sliceToPrefixedFileW(new_path); | 2547 | const new_path_w = try windows.sliceToPrefixedFileW(new_path); |
| 2244 | return renameatW(old_dir_fd, old_path_w.span(), new_dir_fd, new_path_w.span(), windows.TRUE); | 2548 | return renameatW(old_dir_fd, old_path_w.span(), new_dir_fd, new_path_w.span(), windows.TRUE); |
| 2245 | } else if (builtin.os.tag == .wasi and !builtin.link_libc) { | 2549 | } else if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| 2246 | return renameatWasi(old_dir_fd, old_path, new_dir_fd, new_path); | 2550 | var resolve_old: bool = (old_dir_fd == wasi.AT.FDCWD or fs.path.isAbsolute(old_path)); |
| | 2551 | var resolve_new: bool = (new_dir_fd == wasi.AT.FDCWD or fs.path.isAbsolute(new_path)); |
| | 2552 | |
| | 2553 | var old: RelativePathWasi = .{ .dir_fd = old_dir_fd, .relative_path = old_path }; |
| | 2554 | var new: RelativePathWasi = .{ .dir_fd = new_dir_fd, .relative_path = new_path }; |
| | 2555 | |
| | 2556 | // Resolve absolute or CWD-relative paths to a path within a Preopen |
| | 2557 | if (resolve_old or resolve_new) { |
| | 2558 | var buf_old: [MAX_PATH_BYTES]u8 = undefined; |
| | 2559 | var buf_new: [MAX_PATH_BYTES]u8 = undefined; |
| | 2560 | |
| | 2561 | if (resolve_old) |
| | 2562 | old = try resolvePathWasi(old_path, &buf_old); |
| | 2563 | if (resolve_new) |
| | 2564 | new = try resolvePathWasi(new_path, &buf_new); |
| | 2565 | |
| | 2566 | return renameatWasi(old, new); |
| | 2567 | } |
| | 2568 | return renameatWasi(old, new); |
| 2247 | } else { | 2569 | } else { |
| 2248 | const old_path_c = try toPosixPath(old_path); | 2570 | const old_path_c = try toPosixPath(old_path); |
| 2249 | const new_path_c = try toPosixPath(new_path); | 2571 | const new_path_c = try toPosixPath(new_path); |
| ... | @@ -2253,8 +2575,8 @@ pub fn renameat( | ... | @@ -2253,8 +2575,8 @@ pub fn renameat( |
| 2253 | | 2575 | |
| 2254 | /// WASI-only. Same as `renameat` expect targeting WASI. | 2576 | /// WASI-only. Same as `renameat` expect targeting WASI. |
| 2255 | /// See also `renameat`. | 2577 | /// See also `renameat`. |
| 2256 | pub fn renameatWasi(old_dir_fd: fd_t, old_path: []const u8, new_dir_fd: fd_t, new_path: []const u8) RenameError!void { | 2578 | pub fn renameatWasi(old: RelativePathWasi, new: RelativePathWasi) RenameError!void { |
| 2257 | switch (wasi.path_rename(old_dir_fd, old_path.ptr, old_path.len, new_dir_fd, new_path.ptr, new_path.len)) { | 2579 | switch (wasi.path_rename(old.dir_fd, old.relative_path.ptr, old.relative_path.len, new.dir_fd, new.relative_path.ptr, new.relative_path.len)) { |
| 2258 | .SUCCESS => return, | 2580 | .SUCCESS => return, |
| 2259 | .ACCES => return error.AccessDenied, | 2581 | .ACCES => return error.AccessDenied, |
| 2260 | .PERM => return error.AccessDenied, | 2582 | .PERM => return error.AccessDenied, |
| ... | @@ -2290,6 +2612,8 @@ pub fn renameatZ( | ... | @@ -2290,6 +2612,8 @@ pub fn renameatZ( |
| 2290 | const old_path_w = try windows.cStrToPrefixedFileW(old_path); | 2612 | const old_path_w = try windows.cStrToPrefixedFileW(old_path); |
| 2291 | const new_path_w = try windows.cStrToPrefixedFileW(new_path); | 2613 | const new_path_w = try windows.cStrToPrefixedFileW(new_path); |
| 2292 | return renameatW(old_dir_fd, old_path_w.span(), new_dir_fd, new_path_w.span(), windows.TRUE); | 2614 | return renameatW(old_dir_fd, old_path_w.span(), new_dir_fd, new_path_w.span(), windows.TRUE); |
| | 2615 | } else if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| | 2616 | return renameat(old_dir_fd, mem.sliceTo(old_path, 0), new_dir_fd, mem.sliceTo(new_path, 0)); |
| 2293 | } | 2617 | } |
| 2294 | | 2618 | |
| 2295 | switch (errno(system.renameat(old_dir_fd, old_path, new_dir_fd, new_path))) { | 2619 | switch (errno(system.renameat(old_dir_fd, old_path, new_dir_fd, new_path))) { |
| ... | @@ -2380,6 +2704,12 @@ pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!v | ... | @@ -2380,6 +2704,12 @@ pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!v |
| 2380 | const sub_dir_path_w = try windows.sliceToPrefixedFileW(sub_dir_path); | 2704 | const sub_dir_path_w = try windows.sliceToPrefixedFileW(sub_dir_path); |
| 2381 | return mkdiratW(dir_fd, sub_dir_path_w.span(), mode); | 2705 | return mkdiratW(dir_fd, sub_dir_path_w.span(), mode); |
| 2382 | } else if (builtin.os.tag == .wasi and !builtin.link_libc) { | 2706 | } else if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| | 2707 | if (dir_fd == wasi.AT.FDCWD or fs.path.isAbsolute(sub_dir_path)) { |
| | 2708 | // Resolve absolute or CWD-relative paths to a path within a Preopen |
| | 2709 | var path_buf: [MAX_PATH_BYTES]u8 = undefined; |
| | 2710 | const path = try resolvePathWasi(sub_dir_path, &path_buf); |
| | 2711 | return mkdiratWasi(path.dir_fd, path.relative_path, mode); |
| | 2712 | } |
| 2383 | return mkdiratWasi(dir_fd, sub_dir_path, mode); | 2713 | return mkdiratWasi(dir_fd, sub_dir_path, mode); |
| 2384 | } else { | 2714 | } else { |
| 2385 | const sub_dir_path_c = try toPosixPath(sub_dir_path); | 2715 | const sub_dir_path_c = try toPosixPath(sub_dir_path); |
| ... | @@ -2414,6 +2744,8 @@ pub fn mkdiratZ(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: u32) MakeDirErr | ... | @@ -2414,6 +2744,8 @@ pub fn mkdiratZ(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: u32) MakeDirErr |
| 2414 | if (builtin.os.tag == .windows) { | 2744 | if (builtin.os.tag == .windows) { |
| 2415 | const sub_dir_path_w = try windows.cStrToPrefixedFileW(sub_dir_path); | 2745 | const sub_dir_path_w = try windows.cStrToPrefixedFileW(sub_dir_path); |
| 2416 | return mkdiratW(dir_fd, sub_dir_path_w.span().ptr, mode); | 2746 | return mkdiratW(dir_fd, sub_dir_path_w.span().ptr, mode); |
| | 2747 | } else if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| | 2748 | return mkdirat(dir_fd, mem.sliceTo(sub_dir_path, 0), mode); |
| 2417 | } | 2749 | } |
| 2418 | switch (errno(system.mkdirat(dir_fd, sub_dir_path, mode))) { | 2750 | switch (errno(system.mkdirat(dir_fd, sub_dir_path, mode))) { |
| 2419 | .SUCCESS => return, | 2751 | .SUCCESS => return, |
| ... | @@ -2472,10 +2804,10 @@ pub const MakeDirError = error{ | ... | @@ -2472,10 +2804,10 @@ pub const MakeDirError = error{ |
| 2472 | } || UnexpectedError; | 2804 | } || UnexpectedError; |
| 2473 | | 2805 | |
| 2474 | /// Create a directory. | 2806 | /// Create a directory. |
| 2475 | /// `mode` is ignored on Windows. | 2807 | /// `mode` is ignored on Windows and WASI. |
| 2476 | pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void { | 2808 | pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void { |
| 2477 | if (builtin.os.tag == .wasi and !builtin.link_libc) { | 2809 | if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| 2478 | @compileError("mkdir is not supported in WASI; use mkdirat instead"); | 2810 | return mkdirat(wasi.AT.FDCWD, dir_path, mode); |
| 2479 | } else if (builtin.os.tag == .windows) { | 2811 | } else if (builtin.os.tag == .windows) { |
| 2480 | const dir_path_w = try windows.sliceToPrefixedFileW(dir_path); | 2812 | const dir_path_w = try windows.sliceToPrefixedFileW(dir_path); |
| 2481 | return mkdirW(dir_path_w.span(), mode); | 2813 | return mkdirW(dir_path_w.span(), mode); |
| ... | @@ -2490,6 +2822,8 @@ pub fn mkdirZ(dir_path: [*:0]const u8, mode: u32) MakeDirError!void { | ... | @@ -2490,6 +2822,8 @@ pub fn mkdirZ(dir_path: [*:0]const u8, mode: u32) MakeDirError!void { |
| 2490 | if (builtin.os.tag == .windows) { | 2822 | if (builtin.os.tag == .windows) { |
| 2491 | const dir_path_w = try windows.cStrToPrefixedFileW(dir_path); | 2823 | const dir_path_w = try windows.cStrToPrefixedFileW(dir_path); |
| 2492 | return mkdirW(dir_path_w.span(), mode); | 2824 | return mkdirW(dir_path_w.span(), mode); |
| | 2825 | } else if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| | 2826 | return mkdir(mem.sliceTo(dir_path, 0), mode); |
| 2493 | } | 2827 | } |
| 2494 | switch (errno(system.mkdir(dir_path, mode))) { | 2828 | switch (errno(system.mkdir(dir_path, mode))) { |
| 2495 | .SUCCESS => return, | 2829 | .SUCCESS => return, |
| ... | @@ -2545,7 +2879,11 @@ pub const DeleteDirError = error{ | ... | @@ -2545,7 +2879,11 @@ pub const DeleteDirError = error{ |
| 2545 | /// Deletes an empty directory. | 2879 | /// Deletes an empty directory. |
| 2546 | pub fn rmdir(dir_path: []const u8) DeleteDirError!void { | 2880 | pub fn rmdir(dir_path: []const u8) DeleteDirError!void { |
| 2547 | if (builtin.os.tag == .wasi and !builtin.link_libc) { | 2881 | if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| 2548 | @compileError("rmdir is not supported in WASI; use unlinkat instead"); | 2882 | return unlinkat(wasi.AT.FDCWD, dir_path, AT.REMOVEDIR) catch |err| switch (err) { |
| | 2883 | error.FileSystem => unreachable, // only occurs when targeting files |
| | 2884 | error.IsDir => unreachable, // only occurs when targeting files |
| | 2885 | else => |e| return e, |
| | 2886 | }; |
| 2549 | } else if (builtin.os.tag == .windows) { | 2887 | } else if (builtin.os.tag == .windows) { |
| 2550 | const dir_path_w = try windows.sliceToPrefixedFileW(dir_path); | 2888 | const dir_path_w = try windows.sliceToPrefixedFileW(dir_path); |
| 2551 | return rmdirW(dir_path_w.span()); | 2889 | return rmdirW(dir_path_w.span()); |
| ... | @@ -2560,6 +2898,8 @@ pub fn rmdirZ(dir_path: [*:0]const u8) DeleteDirError!void { | ... | @@ -2560,6 +2898,8 @@ pub fn rmdirZ(dir_path: [*:0]const u8) DeleteDirError!void { |
| 2560 | if (builtin.os.tag == .windows) { | 2898 | if (builtin.os.tag == .windows) { |
| 2561 | const dir_path_w = try windows.cStrToPrefixedFileW(dir_path); | 2899 | const dir_path_w = try windows.cStrToPrefixedFileW(dir_path); |
| 2562 | return rmdirW(dir_path_w.span()); | 2900 | return rmdirW(dir_path_w.span()); |
| | 2901 | } else if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| | 2902 | return rmdir(mem.sliceTo(dir_path, 0)); |
| 2563 | } | 2903 | } |
| 2564 | switch (errno(system.rmdir(dir_path))) { | 2904 | switch (errno(system.rmdir(dir_path))) { |
| 2565 | .SUCCESS => return, | 2905 | .SUCCESS => return, |
| ... | @@ -2606,7 +2946,17 @@ pub const ChangeCurDirError = error{ | ... | @@ -2606,7 +2946,17 @@ pub const ChangeCurDirError = error{ |
| 2606 | /// `dir_path` is recommended to be a UTF-8 encoded string. | 2946 | /// `dir_path` is recommended to be a UTF-8 encoded string. |
| 2607 | pub fn chdir(dir_path: []const u8) ChangeCurDirError!void { | 2947 | pub fn chdir(dir_path: []const u8) ChangeCurDirError!void { |
| 2608 | if (builtin.os.tag == .wasi and !builtin.link_libc) { | 2948 | if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| 2609 | @compileError("chdir is not supported in WASI"); | 2949 | var preopen: ?Preopen = null; |
| | 2950 | const path = try resolvePathAndGetWasiPreopen(dir_path, &preopen, &wasi_cwd.path_buffer); |
| | 2951 | |
| | 2952 | const dirinfo = try fstatat(path.dir_fd, path.relative_path, 0); |
| | 2953 | if (dirinfo.filetype != .DIRECTORY) { |
| | 2954 | return error.NotDir; |
| | 2955 | } |
| | 2956 | |
| | 2957 | wasi_cwd.cwd_preopen = preopen; |
| | 2958 | wasi_cwd.cwd = path; |
| | 2959 | return; |
| 2610 | } else if (builtin.os.tag == .windows) { | 2960 | } else if (builtin.os.tag == .windows) { |
| 2611 | var utf16_dir_path: [windows.PATH_MAX_WIDE]u16 = undefined; | 2961 | var utf16_dir_path: [windows.PATH_MAX_WIDE]u16 = undefined; |
| 2612 | const len = try std.unicode.utf8ToUtf16Le(utf16_dir_path[0..], dir_path); | 2962 | const len = try std.unicode.utf8ToUtf16Le(utf16_dir_path[0..], dir_path); |
| ... | @@ -2625,6 +2975,8 @@ pub fn chdirZ(dir_path: [*:0]const u8) ChangeCurDirError!void { | ... | @@ -2625,6 +2975,8 @@ pub fn chdirZ(dir_path: [*:0]const u8) ChangeCurDirError!void { |
| 2625 | const len = try std.unicode.utf8ToUtf16Le(utf16_dir_path[0..], dir_path); | 2975 | const len = try std.unicode.utf8ToUtf16Le(utf16_dir_path[0..], dir_path); |
| 2626 | if (len > utf16_dir_path.len) return error.NameTooLong; | 2976 | if (len > utf16_dir_path.len) return error.NameTooLong; |
| 2627 | return chdirW(utf16_dir_path[0..len]); | 2977 | return chdirW(utf16_dir_path[0..len]); |
| | 2978 | } else if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| | 2979 | return chdir(mem.sliceTo(dir_path, 0)); |
| 2628 | } | 2980 | } |
| 2629 | switch (errno(system.chdir(dir_path))) { | 2981 | switch (errno(system.chdir(dir_path))) { |
| 2630 | .SUCCESS => return, | 2982 | .SUCCESS => return, |
| ... | @@ -2655,15 +3007,29 @@ pub const FchdirError = error{ | ... | @@ -2655,15 +3007,29 @@ pub const FchdirError = error{ |
| 2655 | } || UnexpectedError; | 3007 | } || UnexpectedError; |
| 2656 | | 3008 | |
| 2657 | pub fn fchdir(dirfd: fd_t) FchdirError!void { | 3009 | pub fn fchdir(dirfd: fd_t) FchdirError!void { |
| 2658 | while (true) { | 3010 | if (builtin.os.tag == .wasi) { |
| 2659 | switch (errno(system.fchdir(dirfd))) { | 3011 | // Check that this is a directory |
| 2660 | .SUCCESS => return, | 3012 | const dirinfo = fstatat(dirfd, ".", 0) catch unreachable; |
| 2661 | .ACCES => return error.AccessDenied, | 3013 | if (dirinfo.filetype != .DIRECTORY) { |
| 2662 | .BADF => unreachable, | 3014 | return error.NotDir; |
| 2663 | .NOTDIR => return error.NotDir, | 3015 | } |
| 2664 | .INTR => continue, | 3016 | |
| 2665 | .IO => return error.FileSystem, | 3017 | wasi_cwd.cwd = .{ |
| 2666 | else => |err| return unexpectedErrno(err), | 3018 | .dir_fd = dirfd, |
| | 3019 | .relative_path = ".", |
| | 3020 | }; |
| | 3021 | wasi_cwd.cwd_preopen = null; |
| | 3022 | } else { |
| | 3023 | while (true) { |
| | 3024 | switch (errno(system.fchdir(dirfd))) { |
| | 3025 | .SUCCESS => return, |
| | 3026 | .ACCES => return error.AccessDenied, |
| | 3027 | .BADF => unreachable, |
| | 3028 | .NOTDIR => return error.NotDir, |
| | 3029 | .INTR => continue, |
| | 3030 | .IO => return error.FileSystem, |
| | 3031 | else => |err| return unexpectedErrno(err), |
| | 3032 | } |
| 2667 | } | 3033 | } |
| 2668 | } | 3034 | } |
| 2669 | } | 3035 | } |
| ... | @@ -2690,7 +3056,7 @@ pub const ReadLinkError = error{ | ... | @@ -2690,7 +3056,7 @@ pub const ReadLinkError = error{ |
| 2690 | /// The return value is a slice of `out_buffer` from index 0. | 3056 | /// The return value is a slice of `out_buffer` from index 0. |
| 2691 | pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 { | 3057 | pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 { |
| 2692 | if (builtin.os.tag == .wasi and !builtin.link_libc) { | 3058 | if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| 2693 | @compileError("readlink is not supported in WASI; use readlinkat instead"); | 3059 | return readlinkat(wasi.AT.FDCWD, file_path, out_buffer); |
| 2694 | } else if (builtin.os.tag == .windows) { | 3060 | } else if (builtin.os.tag == .windows) { |
| 2695 | const file_path_w = try windows.sliceToPrefixedFileW(file_path); | 3061 | const file_path_w = try windows.sliceToPrefixedFileW(file_path); |
| 2696 | return readlinkW(file_path_w.span(), out_buffer); | 3062 | return readlinkW(file_path_w.span(), out_buffer); |
| ... | @@ -2711,6 +3077,8 @@ pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 | ... | @@ -2711,6 +3077,8 @@ pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 |
| 2711 | if (builtin.os.tag == .windows) { | 3077 | if (builtin.os.tag == .windows) { |
| 2712 | const file_path_w = try windows.cStrToWin32PrefixedFileW(file_path); | 3078 | const file_path_w = try windows.cStrToWin32PrefixedFileW(file_path); |
| 2713 | return readlinkW(file_path_w.span(), out_buffer); | 3079 | return readlinkW(file_path_w.span(), out_buffer); |
| | 3080 | } else if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| | 3081 | return readlink(mem.sliceTo(file_path, 0), out_buffer); |
| 2714 | } | 3082 | } |
| 2715 | const rc = system.readlink(file_path, out_buffer.ptr, out_buffer.len); | 3083 | const rc = system.readlink(file_path, out_buffer.ptr, out_buffer.len); |
| 2716 | switch (errno(rc)) { | 3084 | switch (errno(rc)) { |
| ... | @@ -2733,6 +3101,12 @@ pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 | ... | @@ -2733,6 +3101,12 @@ pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 |
| 2733 | /// See also `readlinkatWasi`, `realinkatZ` and `realinkatW`. | 3101 | /// See also `readlinkatWasi`, `realinkatZ` and `realinkatW`. |
| 2734 | pub fn readlinkat(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 { | 3102 | pub fn readlinkat(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 { |
| 2735 | if (builtin.os.tag == .wasi and !builtin.link_libc) { | 3103 | if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| | 3104 | if (dirfd == wasi.AT.FDCWD or fs.path.isAbsolute(file_path)) { |
| | 3105 | // Resolve absolute or CWD-relative paths to a path within a Preopen |
| | 3106 | var path_buf: [MAX_PATH_BYTES]u8 = undefined; |
| | 3107 | var path = try resolvePathWasi(file_path, &path_buf); |
| | 3108 | return readlinkatWasi(path.dir_fd, path.relative_path, out_buffer); |
| | 3109 | } |
| 2736 | return readlinkatWasi(dirfd, file_path, out_buffer); | 3110 | return readlinkatWasi(dirfd, file_path, out_buffer); |
| 2737 | } | 3111 | } |
| 2738 | if (builtin.os.tag == .windows) { | 3112 | if (builtin.os.tag == .windows) { |
| ... | @@ -2775,6 +3149,8 @@ pub fn readlinkatZ(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) Read | ... | @@ -2775,6 +3149,8 @@ pub fn readlinkatZ(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) Read |
| 2775 | if (builtin.os.tag == .windows) { | 3149 | if (builtin.os.tag == .windows) { |
| 2776 | const file_path_w = try windows.cStrToPrefixedFileW(file_path); | 3150 | const file_path_w = try windows.cStrToPrefixedFileW(file_path); |
| 2777 | return readlinkatW(dirfd, file_path_w.span(), out_buffer); | 3151 | return readlinkatW(dirfd, file_path_w.span(), out_buffer); |
| | 3152 | } else if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| | 3153 | return readlinkat(dirfd, mem.sliceTo(file_path, 0), out_buffer); |
| 2778 | } | 3154 | } |
| 2779 | const rc = system.readlinkat(dirfd, file_path, out_buffer.ptr, out_buffer.len); | 3155 | const rc = system.readlinkat(dirfd, file_path, out_buffer.ptr, out_buffer.len); |
| 2780 | switch (errno(rc)) { | 3156 | switch (errno(rc)) { |
| ... | @@ -3727,7 +4103,14 @@ pub const FStatAtError = FStatError || error{ NameTooLong, FileNotFound, SymLink | ... | @@ -3727,7 +4103,14 @@ pub const FStatAtError = FStatError || error{ NameTooLong, FileNotFound, SymLink |
| 3727 | /// See also `fstatatZ` and `fstatatWasi`. | 4103 | /// See also `fstatatZ` and `fstatatWasi`. |
| 3728 | pub fn fstatat(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError!Stat { | 4104 | pub fn fstatat(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError!Stat { |
| 3729 | if (builtin.os.tag == .wasi and !builtin.link_libc) { | 4105 | if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| 3730 | return fstatatWasi(dirfd, pathname, flags); | 4106 | const wasi_flags = if (flags & linux.AT.SYMLINK_NOFOLLOW == 0) wasi.LOOKUP_SYMLINK_FOLLOW else 0; |
| | 4107 | if (dirfd == wasi.AT.FDCWD or fs.path.isAbsolute(pathname)) { |
| | 4108 | // Resolve absolute or CWD-relative paths to a path within a Preopen |
| | 4109 | var path_buf: [MAX_PATH_BYTES]u8 = undefined; |
| | 4110 | const path = try resolvePathWasi(pathname, &path_buf); |
| | 4111 | return fstatatWasi(path.dir_fd, path.relative_path, wasi_flags); |
| | 4112 | } |
| | 4113 | return fstatatWasi(dirfd, pathname, wasi_flags); |
| 3731 | } else if (builtin.os.tag == .windows) { | 4114 | } else if (builtin.os.tag == .windows) { |
| 3732 | @compileError("fstatat is not yet implemented on Windows"); | 4115 | @compileError("fstatat is not yet implemented on Windows"); |
| 3733 | } else { | 4116 | } else { |
| ... | @@ -3758,6 +4141,10 @@ pub fn fstatatWasi(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError!S | ... | @@ -3758,6 +4141,10 @@ pub fn fstatatWasi(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError!S |
| 3758 | /// Same as `fstatat` but `pathname` is null-terminated. | 4141 | /// Same as `fstatat` but `pathname` is null-terminated. |
| 3759 | /// See also `fstatat`. | 4142 | /// See also `fstatat`. |
| 3760 | pub fn fstatatZ(dirfd: fd_t, pathname: [*:0]const u8, flags: u32) FStatAtError!Stat { | 4143 | pub fn fstatatZ(dirfd: fd_t, pathname: [*:0]const u8, flags: u32) FStatAtError!Stat { |
| | 4144 | if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| | 4145 | return fstatatWasi(dirfd, mem.sliceTo(pathname), flags); |
| | 4146 | } |
| | 4147 | |
| 3761 | const fstatat_sym = if (builtin.os.tag == .linux and builtin.link_libc) | 4148 | const fstatat_sym = if (builtin.os.tag == .linux and builtin.link_libc) |
| 3762 | system.fstatat64 | 4149 | system.fstatat64 |
| 3763 | else | 4150 | else |
| ... | @@ -4056,6 +4443,8 @@ pub fn access(path: []const u8, mode: u32) AccessError!void { | ... | @@ -4056,6 +4443,8 @@ pub fn access(path: []const u8, mode: u32) AccessError!void { |
| 4056 | const path_w = try windows.sliceToPrefixedFileW(path); | 4443 | const path_w = try windows.sliceToPrefixedFileW(path); |
| 4057 | _ = try windows.GetFileAttributesW(path_w.span().ptr); | 4444 | _ = try windows.GetFileAttributesW(path_w.span().ptr); |
| 4058 | return; | 4445 | return; |
| | 4446 | } else if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| | 4447 | return faccessat(wasi.AT.FDCWD, path, mode, 0); |
| 4059 | } | 4448 | } |
| 4060 | const path_c = try toPosixPath(path); | 4449 | const path_c = try toPosixPath(path); |
| 4061 | return accessZ(&path_c, mode); | 4450 | return accessZ(&path_c, mode); |
| ... | @@ -4067,6 +4456,8 @@ pub fn accessZ(path: [*:0]const u8, mode: u32) AccessError!void { | ... | @@ -4067,6 +4456,8 @@ pub fn accessZ(path: [*:0]const u8, mode: u32) AccessError!void { |
| 4067 | const path_w = try windows.cStrToPrefixedFileW(path); | 4456 | const path_w = try windows.cStrToPrefixedFileW(path); |
| 4068 | _ = try windows.GetFileAttributesW(path_w.span().ptr); | 4457 | _ = try windows.GetFileAttributesW(path_w.span().ptr); |
| 4069 | return; | 4458 | return; |
| | 4459 | } else if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| | 4460 | return access(mem.sliceTo(path, 0), mode); |
| 4070 | } | 4461 | } |
| 4071 | switch (errno(system.access(path, mode))) { | 4462 | switch (errno(system.access(path, mode))) { |
| 4072 | .SUCCESS => return, | 4463 | .SUCCESS => return, |
| ... | @@ -4108,6 +4499,45 @@ pub fn faccessat(dirfd: fd_t, path: []const u8, mode: u32, flags: u32) AccessErr | ... | @@ -4108,6 +4499,45 @@ pub fn faccessat(dirfd: fd_t, path: []const u8, mode: u32, flags: u32) AccessErr |
| 4108 | if (builtin.os.tag == .windows) { | 4499 | if (builtin.os.tag == .windows) { |
| 4109 | const path_w = try windows.sliceToPrefixedFileW(path); | 4500 | const path_w = try windows.sliceToPrefixedFileW(path); |
| 4110 | return faccessatW(dirfd, path_w.span().ptr, mode, flags); | 4501 | return faccessatW(dirfd, path_w.span().ptr, mode, flags); |
| | 4502 | } else if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| | 4503 | var resolved = RelativePathWasi{ .dir_fd = dirfd, .relative_path = path }; |
| | 4504 | |
| | 4505 | const file = blk: { |
| | 4506 | if (dirfd == wasi.AT.FDCWD or fs.path.isAbsolute(path)) { |
| | 4507 | // Resolve absolute or CWD-relative paths to a path within a Preopen |
| | 4508 | var path_buf: [MAX_PATH_BYTES]u8 = undefined; |
| | 4509 | resolved = resolvePathWasi(path, &path_buf) catch |err| break :blk @as(FStatAtError!Stat, err); |
| | 4510 | break :blk fstatat(resolved.dir_fd, resolved.relative_path, flags); |
| | 4511 | } |
| | 4512 | break :blk fstatat(dirfd, path, flags); |
| | 4513 | } catch |err| switch (err) { |
| | 4514 | error.AccessDenied => return error.PermissionDenied, |
| | 4515 | else => |e| return e, |
| | 4516 | }; |
| | 4517 | |
| | 4518 | if (mode != F_OK) { |
| | 4519 | var directory: wasi.fdstat_t = undefined; |
| | 4520 | if (wasi.fd_fdstat_get(resolved.dir_fd, &directory) != .SUCCESS) { |
| | 4521 | return error.PermissionDenied; |
| | 4522 | } |
| | 4523 | |
| | 4524 | var rights: wasi.rights_t = 0; |
| | 4525 | if (mode & R_OK != 0) { |
| | 4526 | rights |= if (file.filetype == .DIRECTORY) |
| | 4527 | wasi.RIGHT.FD_READDIR |
| | 4528 | else |
| | 4529 | wasi.RIGHT.FD_READ; |
| | 4530 | } |
| | 4531 | if (mode & W_OK != 0) { |
| | 4532 | rights |= wasi.RIGHT.FD_WRITE; |
| | 4533 | } |
| | 4534 | // No validation for X_OK |
| | 4535 | |
| | 4536 | if ((rights & directory.fs_rights_inheriting) != rights) { |
| | 4537 | return error.PermissionDenied; |
| | 4538 | } |
| | 4539 | } |
| | 4540 | return; |
| 4111 | } | 4541 | } |
| 4112 | const path_c = try toPosixPath(path); | 4542 | const path_c = try toPosixPath(path); |
| 4113 | return faccessatZ(dirfd, &path_c, mode, flags); | 4543 | return faccessatZ(dirfd, &path_c, mode, flags); |
| ... | @@ -4118,6 +4548,8 @@ pub fn faccessatZ(dirfd: fd_t, path: [*:0]const u8, mode: u32, flags: u32) Acces | ... | @@ -4118,6 +4548,8 @@ pub fn faccessatZ(dirfd: fd_t, path: [*:0]const u8, mode: u32, flags: u32) Acces |
| 4118 | if (builtin.os.tag == .windows) { | 4548 | if (builtin.os.tag == .windows) { |
| 4119 | const path_w = try windows.cStrToPrefixedFileW(path); | 4549 | const path_w = try windows.cStrToPrefixedFileW(path); |
| 4120 | return faccessatW(dirfd, path_w.span().ptr, mode, flags); | 4550 | return faccessatW(dirfd, path_w.span().ptr, mode, flags); |
| | 4551 | } else if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| | 4552 | return faccessat(dirfd, mem.sliceTo(path, 0), mode, flags); |
| 4121 | } | 4553 | } |
| 4122 | switch (errno(system.faccessat(dirfd, path, mode, flags))) { | 4554 | switch (errno(system.faccessat(dirfd, path, mode, flags))) { |
| 4123 | .SUCCESS => return, | 4555 | .SUCCESS => return, |
| ... | @@ -4645,6 +5077,9 @@ pub const RealPathError = error{ | ... | @@ -4645,6 +5077,9 @@ pub const RealPathError = error{ |
| 4645 | SharingViolation, | 5077 | SharingViolation, |
| 4646 | PipeBusy, | 5078 | PipeBusy, |
| 4647 | | 5079 | |
| | 5080 | /// On WASI, the current CWD may not be associated with an absolute path. |
| | 5081 | InvalidHandle, |
| | 5082 | |
| 4648 | /// On Windows, file paths must be valid Unicode. | 5083 | /// On Windows, file paths must be valid Unicode. |
| 4649 | InvalidUtf8, | 5084 | InvalidUtf8, |
| 4650 | | 5085 | |
| ... | @@ -4660,19 +5095,55 @@ pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathE | ... | @@ -4660,19 +5095,55 @@ pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathE |
| 4660 | if (builtin.os.tag == .windows) { | 5095 | if (builtin.os.tag == .windows) { |
| 4661 | const pathname_w = try windows.sliceToPrefixedFileW(pathname); | 5096 | const pathname_w = try windows.sliceToPrefixedFileW(pathname); |
| 4662 | return realpathW(pathname_w.span(), out_buffer); | 5097 | return realpathW(pathname_w.span(), out_buffer); |
| 4663 | } | 5098 | } else if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| 4664 | if (builtin.os.tag == .wasi and !builtin.link_libc) { | 5099 | return realpathWasi(pathname, out_buffer); |
| 4665 | @compileError("Use std.fs.wasi.PreopenList to obtain valid Dir handles instead of using absolute paths"); | | |
| 4666 | } | 5100 | } |
| 4667 | const pathname_c = try toPosixPath(pathname); | 5101 | const pathname_c = try toPosixPath(pathname); |
| 4668 | return realpathZ(&pathname_c, out_buffer); | 5102 | return realpathZ(&pathname_c, out_buffer); |
| 4669 | } | 5103 | } |
| 4670 | | 5104 | |
| | 5105 | /// Return an emulated canonicalized absolute pathname on WASI. |
| | 5106 | /// |
| | 5107 | /// NOTE: This emulation is incomplete. Symbolic links are not |
| | 5108 | /// currently expanded during path canonicalization. |
| | 5109 | fn realpathWasi(pathname: []const u8, out_buffer: []u8) ![]u8 { |
| | 5110 | var alloc = std.heap.FixedBufferAllocator.init(out_buffer); |
| | 5111 | if (fs.path.isAbsolute(pathname)) |
| | 5112 | return try fs.path.resolve(alloc.allocator(), &.{pathname}) catch error.NameTooLong; |
| | 5113 | if (wasi_cwd.cwd) |cwd| { |
| | 5114 | if (wasi_cwd.cwd_preopen) |po| { |
| | 5115 | var base_cwd_dir = switch (po.@"type") { |
| | 5116 | .Dir => |dir| dir, |
| | 5117 | }; |
| | 5118 | const paths: [][]const u8 = if (fs.path.isAbsolute(base_cwd_dir)) blk: { |
| | 5119 | break :blk &.{ base_cwd_dir, cwd.relative_path, pathname }; |
| | 5120 | } else blk: { |
| | 5121 | // No absolute path is associated with this preopen, so |
| | 5122 | // instead we use a special "/preopens/fd/<N>/" prefix |
| | 5123 | var buf: [16]u8 = undefined; |
| | 5124 | var fbs = std.io.fixedBufferStream(&buf); |
| | 5125 | std.fmt.formatInt(po.fd, 10, .lower, .{}, fbs.writer()) catch return error.NameTooLong; |
| | 5126 | break :blk &.{ "/preopens/fd/", fbs.getWritten(), cwd.relative_path, pathname }; |
| | 5127 | }; |
| | 5128 | |
| | 5129 | return fs.path.resolve(alloc.allocator(), paths) catch error.NameTooLong; |
| | 5130 | } else { |
| | 5131 | // The CWD is not rooted to an existing Preopen, |
| | 5132 | // so we have no way to know its absolute path |
| | 5133 | return error.InvalidHandle; |
| | 5134 | } |
| | 5135 | } else { |
| | 5136 | return try fs.path.resolve(alloc.allocator(), &.{ "/", pathname }) catch error.NameTooLong; |
| | 5137 | } |
| | 5138 | } |
| | 5139 | |
| 4671 | /// Same as `realpath` except `pathname` is null-terminated. | 5140 | /// Same as `realpath` except `pathname` is null-terminated. |
| 4672 | pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { | 5141 | pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { |
| 4673 | if (builtin.os.tag == .windows) { | 5142 | if (builtin.os.tag == .windows) { |
| 4674 | const pathname_w = try windows.cStrToPrefixedFileW(pathname); | 5143 | const pathname_w = try windows.cStrToPrefixedFileW(pathname); |
| 4675 | return realpathW(pathname_w.span(), out_buffer); | 5144 | return realpathW(pathname_w.span(), out_buffer); |
| | 5145 | } else if (builtin.os.tag == .wasi and !builtin.link_libc) { |
| | 5146 | return realpath(mem.sliceTo(pathname, 0), out_buffer); |
| 4676 | } | 5147 | } |
| 4677 | if (!builtin.link_libc) { | 5148 | if (!builtin.link_libc) { |
| 4678 | const flags = if (builtin.os.tag == .linux) O.PATH | O.NONBLOCK | O.CLOEXEC else O.NONBLOCK | O.CLOEXEC; | 5149 | const flags = if (builtin.os.tag == .linux) O.PATH | O.NONBLOCK | O.CLOEXEC else O.NONBLOCK | O.CLOEXEC; |
| ... | @@ -4680,6 +5151,7 @@ pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealP | ... | @@ -4680,6 +5151,7 @@ pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealP |
| 4680 | error.FileLocksNotSupported => unreachable, | 5151 | error.FileLocksNotSupported => unreachable, |
| 4681 | error.WouldBlock => unreachable, | 5152 | error.WouldBlock => unreachable, |
| 4682 | error.FileBusy => unreachable, // not asking for write permissions | 5153 | error.FileBusy => unreachable, // not asking for write permissions |
| | 5154 | error.InvalidHandle => unreachable, // WASI-only |
| 4683 | else => |e| return e, | 5155 | else => |e| return e, |
| 4684 | }; | 5156 | }; |
| 4685 | defer close(fd); | 5157 | defer close(fd); |