mirror of
https://github.com/ziglang/zig.git
synced 2026-02-14 05:20:34 +00:00
Merge pull request #5677 from kubkon/fstatat
[libstd]: implement fstatat in WASI plus fix on macOS
This commit is contained in:
commit
41c6cc9001
@ -73,7 +73,6 @@ pub extern "c" fn abort() noreturn;
|
||||
pub extern "c" fn exit(code: c_int) noreturn;
|
||||
pub extern "c" fn isatty(fd: fd_t) c_int;
|
||||
pub extern "c" fn close(fd: fd_t) c_int;
|
||||
pub extern "c" fn fstatat(dirfd: fd_t, path: [*:0]const u8, stat_buf: *Stat, flags: u32) c_int;
|
||||
pub extern "c" fn lseek(fd: fd_t, offset: off_t, whence: c_int) off_t;
|
||||
pub extern "c" fn open(path: [*:0]const u8, oflag: c_uint, ...) c_int;
|
||||
pub extern "c" fn openat(fd: c_int, path: [*:0]const u8, oflag: c_uint, ...) c_int;
|
||||
@ -116,9 +115,11 @@ pub extern "c" fn readlinkat(dirfd: fd_t, noalias path: [*:0]const u8, noalias b
|
||||
pub usingnamespace switch (builtin.os.tag) {
|
||||
.macosx, .ios, .watchos, .tvos => struct {
|
||||
pub const realpath = @"realpath$DARWIN_EXTSN";
|
||||
pub const fstatat = @"fstatat$INODE64";
|
||||
},
|
||||
else => struct {
|
||||
pub extern "c" fn realpath(noalias file_name: [*:0]const u8, noalias resolved_name: [*]u8) ?[*:0]u8;
|
||||
pub extern "c" fn fstatat(dirfd: fd_t, path: [*:0]const u8, stat_buf: *Stat, flags: u32) c_int;
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@ -16,6 +16,7 @@ pub extern "c" fn @"realpath$DARWIN_EXTSN"(noalias file_name: [*:0]const u8, noa
|
||||
|
||||
pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: [*]u8, buf_len: usize, basep: *i64) isize;
|
||||
pub extern "c" fn @"fstat$INODE64"(fd: fd_t, buf: *Stat) c_int;
|
||||
pub extern "c" fn @"fstatat$INODE64"(dirfd: fd_t, path_name: [*:0]const u8, buf: *Stat, flags: u32) c_int;
|
||||
|
||||
pub extern "c" fn mach_absolute_time() u64;
|
||||
pub extern "c" fn mach_timebase_info(tinfo: ?*mach_timebase_info_data) void;
|
||||
|
||||
@ -1665,7 +1665,9 @@ pub const UnlinkError = error{
|
||||
/// Delete a name and possibly the file it refers to.
|
||||
/// See also `unlinkC`.
|
||||
pub fn unlink(file_path: []const u8) UnlinkError!void {
|
||||
if (builtin.os.tag == .windows) {
|
||||
if (builtin.os.tag == .wasi) {
|
||||
@compileError("unlink is not supported in WASI; use unlinkat instead");
|
||||
} else if (builtin.os.tag == .windows) {
|
||||
const file_path_w = try windows.sliceToPrefixedFileW(file_path);
|
||||
return windows.DeleteFileW(file_path_w.span().ptr);
|
||||
} else {
|
||||
@ -1722,6 +1724,8 @@ pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!vo
|
||||
|
||||
pub const unlinkatC = @compileError("deprecated: renamed to unlinkatZ");
|
||||
|
||||
/// WASI-only. Same as `unlinkat` but targeting WASI.
|
||||
/// See also `unlinkat`.
|
||||
pub fn unlinkatWasi(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void {
|
||||
const remove_dir = (flags & AT_REMOVEDIR) != 0;
|
||||
const res = if (remove_dir)
|
||||
@ -1868,7 +1872,9 @@ const RenameError = error{
|
||||
|
||||
/// Change the name or location of a file.
|
||||
pub fn rename(old_path: []const u8, new_path: []const u8) RenameError!void {
|
||||
if (builtin.os.tag == .windows) {
|
||||
if (builtin.os.tag == .wasi) {
|
||||
@compileError("rename is not supported in WASI; use renameat instead");
|
||||
} else if (builtin.os.tag == .windows) {
|
||||
const old_path_w = try windows.sliceToPrefixedFileW(old_path);
|
||||
const new_path_w = try windows.sliceToPrefixedFileW(new_path);
|
||||
return renameW(old_path_w.span().ptr, new_path_w.span().ptr);
|
||||
@ -1939,7 +1945,8 @@ pub fn renameat(
|
||||
}
|
||||
}
|
||||
|
||||
/// Same as `renameat` expect only WASI.
|
||||
/// WASI-only. Same as `renameat` expect targeting WASI.
|
||||
/// See also `renameat`.
|
||||
pub fn renameatWasi(old_dir_fd: fd_t, old_path: []const u8, new_dir_fd: fd_t, new_path: []const u8) RenameError!void {
|
||||
switch (wasi.path_rename(old_dir_fd, old_path.ptr, old_path.len, new_dir_fd, new_path.ptr, new_path.len)) {
|
||||
wasi.ESUCCESS => return,
|
||||
@ -2144,7 +2151,9 @@ pub fn mkdiratW(dir_fd: fd_t, sub_path_w: [*:0]const u16, mode: u32) MakeDirErro
|
||||
/// Create a directory.
|
||||
/// `mode` is ignored on Windows.
|
||||
pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {
|
||||
if (builtin.os.tag == .windows) {
|
||||
if (builtin.os.tag == .wasi) {
|
||||
@compileError("mkdir is not supported in WASI; use mkdirat instead");
|
||||
} else if (builtin.os.tag == .windows) {
|
||||
const sub_dir_handle = try windows.CreateDirectory(null, dir_path, null);
|
||||
windows.CloseHandle(sub_dir_handle);
|
||||
return;
|
||||
@ -2197,7 +2206,9 @@ pub const DeleteDirError = error{
|
||||
|
||||
/// Deletes an empty directory.
|
||||
pub fn rmdir(dir_path: []const u8) DeleteDirError!void {
|
||||
if (builtin.os.tag == .windows) {
|
||||
if (builtin.os.tag == .wasi) {
|
||||
@compileError("rmdir is not supported in WASI; use unlinkat instead");
|
||||
} else if (builtin.os.tag == .windows) {
|
||||
const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);
|
||||
return windows.RemoveDirectoryW(dir_path_w.span().ptr);
|
||||
} else {
|
||||
@ -2246,7 +2257,9 @@ pub const ChangeCurDirError = error{
|
||||
/// Changes the current working directory of the calling process.
|
||||
/// `dir_path` is recommended to be a UTF-8 encoded string.
|
||||
pub fn chdir(dir_path: []const u8) ChangeCurDirError!void {
|
||||
if (builtin.os.tag == .windows) {
|
||||
if (builtin.os.tag == .wasi) {
|
||||
@compileError("chdir is not supported in WASI");
|
||||
} else if (builtin.os.tag == .windows) {
|
||||
const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);
|
||||
@compileError("TODO implement chdir for Windows");
|
||||
} else {
|
||||
@ -2310,9 +2323,11 @@ pub const ReadLinkError = error{
|
||||
/// Read value of a symbolic link.
|
||||
/// The return value is a slice of `out_buffer` from index 0.
|
||||
pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
|
||||
if (builtin.os.tag == .windows) {
|
||||
if (builtin.os.tag == .wasi) {
|
||||
@compileError("readlink is not supported in WASI; use readlinkat instead");
|
||||
} else if (builtin.os.tag == .windows) {
|
||||
const file_path_w = try windows.sliceToPrefixedFileW(file_path);
|
||||
@compileError("TODO implement readlink for Windows");
|
||||
return readlinkW(file_path_w.span().ptr, out_buffer);
|
||||
} else {
|
||||
const file_path_c = try toPosixPath(file_path);
|
||||
return readlinkZ(&file_path_c, out_buffer);
|
||||
@ -2321,11 +2336,17 @@ pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
|
||||
|
||||
pub const readlinkC = @compileError("deprecated: renamed to readlinkZ");
|
||||
|
||||
/// Windows-only. Same as `readlink` expecte `file_path` is null-terminated, WTF16 encoded.
|
||||
/// Seel also `readlinkZ`.
|
||||
pub fn readlinkW(file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 {
|
||||
@compileError("TODO implement readlink for Windows");
|
||||
}
|
||||
|
||||
/// Same as `readlink` except `file_path` is null-terminated.
|
||||
pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const file_path_w = try windows.cStrToPrefixedFileW(file_path);
|
||||
@compileError("TODO implement readlink for Windows");
|
||||
return readlinkW(file_path_w.span().ptr, out_buffer);
|
||||
}
|
||||
const rc = system.readlink(file_path, out_buffer.ptr, out_buffer.len);
|
||||
switch (errno(rc)) {
|
||||
@ -3055,6 +3076,7 @@ pub const FStatError = error{
|
||||
AccessDenied,
|
||||
} || UnexpectedError;
|
||||
|
||||
/// Return information about a file descriptor.
|
||||
pub fn fstat(fd: fd_t) FStatError!Stat {
|
||||
if (builtin.os.tag == .wasi) {
|
||||
var stat: wasi.filestat_t = undefined;
|
||||
@ -3067,6 +3089,9 @@ pub fn fstat(fd: fd_t) FStatError!Stat {
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
if (builtin.os.tag == .windows) {
|
||||
@compileError("fstat is not yet implemented on Windows");
|
||||
}
|
||||
|
||||
var stat: Stat = undefined;
|
||||
switch (errno(system.fstat(fd, &stat))) {
|
||||
@ -3081,13 +3106,42 @@ pub fn fstat(fd: fd_t) FStatError!Stat {
|
||||
|
||||
pub const FStatAtError = FStatError || error{ NameTooLong, FileNotFound };
|
||||
|
||||
/// Similar to `fstat`, but returns stat of a resource pointed to by `pathname`
|
||||
/// which is relative to `dirfd` handle.
|
||||
/// See also `fstatatZ` and `fstatatWasi`.
|
||||
pub fn fstatat(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError!Stat {
|
||||
const pathname_c = try toPosixPath(pathname);
|
||||
return fstatatZ(dirfd, &pathname_c, flags);
|
||||
if (builtin.os.tag == .wasi) {
|
||||
return fstatatWasi(dirfd, pathname, flags);
|
||||
} else if (builtin.os.tag == .windows) {
|
||||
@compileError("fstatat is not yet implemented on Windows");
|
||||
} else {
|
||||
const pathname_c = try toPosixPath(pathname);
|
||||
return fstatatZ(dirfd, &pathname_c, flags);
|
||||
}
|
||||
}
|
||||
|
||||
pub const fstatatC = @compileError("deprecated: renamed to fstatatZ");
|
||||
|
||||
/// WASI-only. Same as `fstatat` but targeting WASI.
|
||||
/// See also `fstatat`.
|
||||
pub fn fstatatWasi(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError!Stat {
|
||||
var stat: wasi.filestat_t = undefined;
|
||||
switch (wasi.path_filestat_get(dirfd, flags, pathname.ptr, pathname.len, &stat)) {
|
||||
wasi.ESUCCESS => return Stat.fromFilestat(stat),
|
||||
wasi.EINVAL => unreachable,
|
||||
wasi.EBADF => unreachable, // Always a race condition.
|
||||
wasi.ENOMEM => return error.SystemResources,
|
||||
wasi.EACCES => return error.AccessDenied,
|
||||
wasi.EFAULT => unreachable,
|
||||
wasi.ENAMETOOLONG => return error.NameTooLong,
|
||||
wasi.ENOENT => return error.FileNotFound,
|
||||
wasi.ENOTDIR => return error.FileNotFound,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
|
||||
/// Same as `fstatat` but `pathname` is null-terminated.
|
||||
/// See also `fstatat`.
|
||||
pub fn fstatatZ(dirfd: fd_t, pathname: [*:0]const u8, flags: u32) FStatAtError!Stat {
|
||||
var stat: Stat = undefined;
|
||||
switch (errno(system.fstatat(dirfd, pathname, &stat, flags))) {
|
||||
|
||||
@ -18,6 +18,28 @@ const AtomicOrder = builtin.AtomicOrder;
|
||||
const tmpDir = std.testing.tmpDir;
|
||||
const Dir = std.fs.Dir;
|
||||
|
||||
test "fstatat" {
|
||||
// enable when `fstat` and `fstatat` are implemented on Windows
|
||||
if (builtin.os.tag == .windows) return error.SkipZigTest;
|
||||
|
||||
var tmp = tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
|
||||
// create dummy file
|
||||
const contents = "nonsense";
|
||||
try tmp.dir.writeFile("file.txt", contents);
|
||||
|
||||
// fetch file's info on the opened fd directly
|
||||
const file = try tmp.dir.openFile("file.txt", .{});
|
||||
const stat = try os.fstat(file.handle);
|
||||
defer file.close();
|
||||
|
||||
// now repeat but using `fstatat` instead
|
||||
const flags = if (builtin.os.tag == .wasi) 0x0 else os.AT_SYMLINK_NOFOLLOW;
|
||||
const statat = try os.fstatat(tmp.dir.fd, "file.txt", flags);
|
||||
expectEqual(stat, statat);
|
||||
}
|
||||
|
||||
test "readlinkat" {
|
||||
// enable when `readlinkat` and `symlinkat` are implemented on Windows
|
||||
if (builtin.os.tag == .windows) return error.SkipZigTest;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user