diff options
Diffstat (limited to 'lib/std/fs.zig')
| -rw-r--r-- | lib/std/fs.zig | 177 |
1 files changed, 139 insertions, 38 deletions
diff --git a/lib/std/fs.zig b/lib/std/fs.zig index 9b88f4dd52..cfb4d7958b 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -31,18 +31,21 @@ pub const realpathW = os.realpathW; pub const getAppDataDir = @import("fs/get_app_data_dir.zig").getAppDataDir; pub const GetAppDataDirError = @import("fs/get_app_data_dir.zig").GetAppDataDirError; -/// This represents the maximum size of a UTF-8 encoded file path that the +/// This represents the maximum size of a `[]u8` file path that the /// operating system will accept. Paths, including those returned from file /// system operations, may be longer than this length, but such paths cannot /// be successfully passed back in other file system operations. However, /// all path components returned by file system operations are assumed to -/// fit into a UTF-8 encoded array of this length. +/// fit into a `u8` array of this length. /// The byte count includes room for a null sentinel byte. +/// On Windows, `[]u8` file paths are encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). +/// On WASI, `[]u8` file paths are encoded as valid UTF-8. +/// On other platforms, `[]u8` file paths are opaque sequences of bytes with no particular encoding. pub const MAX_PATH_BYTES = switch (builtin.os.tag) { .linux, .macos, .ios, .freebsd, .openbsd, .netbsd, .dragonfly, .haiku, .solaris, .illumos, .plan9, .emscripten => os.PATH_MAX, - // Each UTF-16LE character may be expanded to 3 UTF-8 bytes. - // If it would require 4 UTF-8 bytes, then there would be a surrogate - // pair in the UTF-16LE, and we (over)account 3 bytes for it that way. + // Each WTF-16LE code unit may be expanded to 3 WTF-8 bytes. + // If it would require 4 WTF-8 bytes, then there would be a surrogate + // pair in the WTF-16LE, and we (over)account 3 bytes for it that way. // +1 for the null byte at the end, which can be encoded in 1 byte. .windows => os.windows.PATH_MAX_WIDE * 3 + 1, // TODO work out what a reasonable value we should use here @@ -53,18 +56,21 @@ pub const MAX_PATH_BYTES = switch (builtin.os.tag) { @compileError("PATH_MAX not implemented for " ++ @tagName(builtin.os.tag)), }; -/// This represents the maximum size of a UTF-8 encoded file name component that +/// This represents the maximum size of a `[]u8` file name component that /// the platform's common file systems support. File name components returned by file system -/// operations are likely to fit into a UTF-8 encoded array of this length, but +/// operations are likely to fit into a `u8` array of this length, but /// (depending on the platform) this assumption may not hold for every configuration. /// The byte count does not include a null sentinel byte. +/// On Windows, `[]u8` file name components are encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). +/// On WASI, file name components are encoded as valid UTF-8. +/// On other platforms, `[]u8` components are an opaque sequence of bytes with no particular encoding. pub const MAX_NAME_BYTES = switch (builtin.os.tag) { .linux, .macos, .ios, .freebsd, .openbsd, .netbsd, .dragonfly, .solaris, .illumos => os.NAME_MAX, // Haiku's NAME_MAX includes the null terminator, so subtract one. .haiku => os.NAME_MAX - 1, - // Each UTF-16LE character may be expanded to 3 UTF-8 bytes. - // If it would require 4 UTF-8 bytes, then there would be a surrogate - // pair in the UTF-16LE, and we (over)account 3 bytes for it that way. + // Each WTF-16LE character may be expanded to 3 WTF-8 bytes. + // If it would require 4 WTF-8 bytes, then there would be a surrogate + // pair in the WTF-16LE, and we (over)account 3 bytes for it that way. .windows => os.windows.NAME_MAX * 3, // For WASI, the MAX_NAME will depend on the host OS, so it needs to be // as large as the largest MAX_NAME_BYTES (Windows) in order to work on any host OS. @@ -86,6 +92,9 @@ pub const base64_decoder = base64.Base64Decoder.init(base64_alphabet, null); /// TODO remove the allocator requirement from this API /// TODO move to Dir +/// On Windows, both paths should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). +/// On WASI, both paths should be encoded as valid UTF-8. +/// On other platforms, both paths are an opaque sequence of bytes with no particular encoding. pub fn atomicSymLink(allocator: Allocator, existing_path: []const u8, new_path: []const u8) !void { if (cwd().symLink(existing_path, new_path, .{})) { return; @@ -117,6 +126,9 @@ pub fn atomicSymLink(allocator: Allocator, existing_path: []const u8, new_path: /// Same as `Dir.updateFile`, except asserts that both `source_path` and `dest_path` /// are absolute. See `Dir.updateFile` for a function that operates on both /// absolute and relative paths. +/// On Windows, both paths should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). +/// On WASI, both paths should be encoded as valid UTF-8. +/// On other platforms, both paths are an opaque sequence of bytes with no particular encoding. pub fn updateFileAbsolute( source_path: []const u8, dest_path: []const u8, @@ -131,6 +143,9 @@ pub fn updateFileAbsolute( /// Same as `Dir.copyFile`, except asserts that both `source_path` and `dest_path` /// are absolute. See `Dir.copyFile` for a function that operates on both /// absolute and relative paths. +/// On Windows, both paths should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). +/// On WASI, both paths should be encoded as valid UTF-8. +/// On other platforms, both paths are an opaque sequence of bytes with no particular encoding. pub fn copyFileAbsolute( source_path: []const u8, dest_path: []const u8, @@ -145,24 +160,30 @@ pub fn copyFileAbsolute( /// Create a new directory, based on an absolute path. /// Asserts that the path is absolute. See `Dir.makeDir` for a function that operates /// on both absolute and relative paths. +/// On Windows, `absolute_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). +/// On WASI, `absolute_path` should be encoded as valid UTF-8. +/// On other platforms, `absolute_path` is an opaque sequence of bytes with no particular encoding. pub fn makeDirAbsolute(absolute_path: []const u8) !void { assert(path.isAbsolute(absolute_path)); return os.mkdir(absolute_path, Dir.default_mode); } -/// Same as `makeDirAbsolute` except the parameter is a null-terminated UTF-8-encoded string. +/// Same as `makeDirAbsolute` except the parameter is null-terminated. pub fn makeDirAbsoluteZ(absolute_path_z: [*:0]const u8) !void { assert(path.isAbsoluteZ(absolute_path_z)); return os.mkdirZ(absolute_path_z, Dir.default_mode); } -/// Same as `makeDirAbsolute` except the parameter is a null-terminated WTF-16-encoded string. +/// Same as `makeDirAbsolute` except the parameter is a null-terminated WTF-16 LE-encoded string. pub fn makeDirAbsoluteW(absolute_path_w: [*:0]const u16) !void { assert(path.isAbsoluteWindowsW(absolute_path_w)); return os.mkdirW(absolute_path_w, Dir.default_mode); } /// Same as `Dir.deleteDir` except the path is absolute. +/// On Windows, `dir_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). +/// On WASI, `dir_path` should be encoded as valid UTF-8. +/// On other platforms, `dir_path` is an opaque sequence of bytes with no particular encoding. pub fn deleteDirAbsolute(dir_path: []const u8) !void { assert(path.isAbsolute(dir_path)); return os.rmdir(dir_path); @@ -181,6 +202,9 @@ pub fn deleteDirAbsoluteW(dir_path: [*:0]const u16) !void { } /// Same as `Dir.rename` except the paths are absolute. +/// On Windows, both paths should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). +/// On WASI, both paths should be encoded as valid UTF-8. +/// On other platforms, both paths are an opaque sequence of bytes with no particular encoding. pub fn renameAbsolute(old_path: []const u8, new_path: []const u8) !void { assert(path.isAbsolute(old_path)); assert(path.isAbsolute(new_path)); @@ -211,7 +235,7 @@ pub fn renameZ(old_dir: Dir, old_sub_path_z: [*:0]const u8, new_dir: Dir, new_su return os.renameatZ(old_dir.fd, old_sub_path_z, new_dir.fd, new_sub_path_z); } -/// Same as `rename` except the parameters are UTF16LE, NT prefixed. +/// Same as `rename` except the parameters are WTF16LE, NT prefixed. /// This function is Windows-only. pub fn renameW(old_dir: Dir, old_sub_path_w: []const u16, new_dir: Dir, new_sub_path_w: []const u16) !void { return os.renameatW(old_dir.fd, old_sub_path_w, new_dir.fd, new_sub_path_w); @@ -240,6 +264,9 @@ pub fn defaultWasiCwd() std.os.wasi.fd_t { /// See `openDirAbsoluteZ` for a function that accepts a null-terminated path. /// /// Asserts that the path parameter has no null bytes. +/// On Windows, `absolute_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). +/// On WASI, `absolute_path` should be encoded as valid UTF-8. +/// On other platforms, `absolute_path` is an opaque sequence of bytes with no particular encoding. pub fn openDirAbsolute(absolute_path: []const u8, flags: Dir.OpenDirOptions) File.OpenError!Dir { assert(path.isAbsolute(absolute_path)); return cwd().openDir(absolute_path, flags); @@ -262,6 +289,9 @@ pub fn openDirAbsoluteW(absolute_path_c: [*:0]const u16, flags: Dir.OpenDirOptio /// operates on both absolute and relative paths. /// Asserts that the path parameter has no null bytes. See `openFileAbsoluteZ` for a function /// that accepts a null-terminated path. +/// On Windows, `absolute_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). +/// On WASI, `absolute_path` should be encoded as valid UTF-8. +/// On other platforms, `absolute_path` is an opaque sequence of bytes with no particular encoding. pub fn openFileAbsolute(absolute_path: []const u8, flags: File.OpenFlags) File.OpenError!File { assert(path.isAbsolute(absolute_path)); return cwd().openFile(absolute_path, flags); @@ -280,11 +310,13 @@ pub fn openFileAbsoluteW(absolute_path_w: []const u16, flags: File.OpenFlags) Fi } /// Test accessing `path`. -/// `path` is UTF-8-encoded. /// Be careful of Time-Of-Check-Time-Of-Use race conditions when using this function. /// For example, instead of testing if a file exists and then opening it, just /// open it and handle the error for file not found. /// See `accessAbsoluteZ` for a function that accepts a null-terminated path. +/// On Windows, `absolute_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). +/// On WASI, `absolute_path` should be encoded as valid UTF-8. +/// On other platforms, `absolute_path` is an opaque sequence of bytes with no particular encoding. pub fn accessAbsolute(absolute_path: []const u8, flags: File.OpenFlags) Dir.AccessError!void { assert(path.isAbsolute(absolute_path)); try cwd().access(absolute_path, flags); @@ -306,6 +338,9 @@ pub fn accessAbsoluteW(absolute_path: [*:0]const u16, flags: File.OpenFlags) Dir /// operates on both absolute and relative paths. /// Asserts that the path parameter has no null bytes. See `createFileAbsoluteC` for a function /// that accepts a null-terminated path. +/// On Windows, `absolute_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). +/// On WASI, `absolute_path` should be encoded as valid UTF-8. +/// On other platforms, `absolute_path` is an opaque sequence of bytes with no particular encoding. pub fn createFileAbsolute(absolute_path: []const u8, flags: File.CreateFlags) File.OpenError!File { assert(path.isAbsolute(absolute_path)); return cwd().createFile(absolute_path, flags); @@ -327,6 +362,9 @@ pub fn createFileAbsoluteW(absolute_path_w: [*:0]const u16, flags: File.CreateFl /// Asserts that the path is absolute. See `Dir.deleteFile` for a function that /// operates on both absolute and relative paths. /// Asserts that the path parameter has no null bytes. +/// On Windows, `absolute_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). +/// On WASI, `absolute_path` should be encoded as valid UTF-8. +/// On other platforms, `absolute_path` is an opaque sequence of bytes with no particular encoding. pub fn deleteFileAbsolute(absolute_path: []const u8) Dir.DeleteFileError!void { assert(path.isAbsolute(absolute_path)); return cwd().deleteFile(absolute_path); @@ -349,6 +387,9 @@ pub fn deleteFileAbsoluteW(absolute_path_w: [*:0]const u16) Dir.DeleteFileError! /// Asserts that the path is absolute. See `Dir.deleteTree` for a function that /// operates on both absolute and relative paths. /// Asserts that the path parameter has no null bytes. +/// On Windows, `absolute_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). +/// On WASI, `absolute_path` should be encoded as valid UTF-8. +/// On other platforms, `absolute_path` is an opaque sequence of bytes with no particular encoding. pub fn deleteTreeAbsolute(absolute_path: []const u8) !void { assert(path.isAbsolute(absolute_path)); const dirname = path.dirname(absolute_path) orelse return error{ @@ -364,6 +405,9 @@ pub fn deleteTreeAbsolute(absolute_path: []const u8) !void { } /// Same as `Dir.readLink`, except it asserts the path is absolute. +/// On Windows, `pathname` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). +/// On WASI, `pathname` should be encoded as valid UTF-8. +/// On other platforms, `pathname` is an opaque sequence of bytes with no particular encoding. pub fn readLinkAbsolute(pathname: []const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 { assert(path.isAbsolute(pathname)); return os.readlink(pathname, buffer); @@ -387,6 +431,9 @@ pub fn readLinkAbsoluteZ(pathname_c: [*:0]const u8, buffer: *[MAX_PATH_BYTES]u8) /// one; the latter case is known as a dangling link. /// If `sym_link_path` exists, it will not be overwritten. /// See also `symLinkAbsoluteZ` and `symLinkAbsoluteW`. +/// On Windows, both paths should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). +/// On WASI, both paths should be encoded as valid UTF-8. +/// On other platforms, both paths are an opaque sequence of bytes with no particular encoding. pub fn symLinkAbsolute( target_path: []const u8, sym_link_path: []const u8, @@ -402,7 +449,7 @@ pub fn symLinkAbsolute( return os.symlink(target_path, sym_link_path); } -/// Windows-only. Same as `symLinkAbsolute` except the parameters are null-terminated, WTF16 encoded. +/// Windows-only. Same as `symLinkAbsolute` except the parameters are null-terminated, WTF16 LE encoded. /// Note that this function will by default try creating a symbolic link to a file. If you would /// like to create a symbolic link to a directory, specify this with `SymLinkFlags{ .is_directory = true }`. /// See also `symLinkAbsolute`, `symLinkAbsoluteZ`. @@ -426,27 +473,14 @@ pub fn symLinkAbsoluteZ( assert(path.isAbsoluteZ(target_path_c)); assert(path.isAbsoluteZ(sym_link_path_c)); if (builtin.os.tag == .windows) { - const target_path_w = try os.windows.cStrToWin32PrefixedFileW(target_path_c); - const sym_link_path_w = try os.windows.cStrToWin32PrefixedFileW(sym_link_path_c); - return os.windows.CreateSymbolicLink(sym_link_path_w.span(), target_path_w.span(), flags.is_directory); + const target_path_w = try os.windows.cStrToPrefixedFileW(null, target_path_c); + const sym_link_path_w = try os.windows.cStrToPrefixedFileW(null, sym_link_path_c); + return os.windows.CreateSymbolicLink(null, sym_link_path_w.span(), target_path_w.span(), flags.is_directory); } return os.symlinkZ(target_path_c, sym_link_path_c); } -pub const OpenSelfExeError = error{ - SharingViolation, - PathAlreadyExists, - FileNotFound, - AccessDenied, - PipeBusy, - NameTooLong, - /// On Windows, file paths must be valid Unicode. - InvalidUtf8, - /// On Windows, file paths cannot contain these characters: - /// '/', '*', '?', '"', '<', '>', '|' - BadPathName, - Unexpected, -} || os.OpenError || SelfExePathError || os.FlockError; +pub const OpenSelfExeError = os.OpenError || SelfExePathError || os.FlockError; pub fn openSelfExe(flags: File.OpenFlags) OpenSelfExeError!File { if (builtin.os.tag == .linux) { @@ -469,7 +503,45 @@ pub fn openSelfExe(flags: File.OpenFlags) OpenSelfExeError!File { return openFileAbsoluteZ(buf[0..self_exe_path.len :0].ptr, flags); } -pub const SelfExePathError = os.ReadLinkError || os.SysCtlError || os.RealPathError; +// This is os.ReadLinkError || os.RealPathError with impossible errors excluded +pub const SelfExePathError = error{ + FileNotFound, + AccessDenied, + NameTooLong, + NotSupported, + NotDir, + SymLinkLoop, + InputOutput, + FileTooBig, + IsDir, + ProcessFdQuotaExceeded, + SystemFdQuotaExceeded, + NoDevice, + SystemResources, + NoSpaceLeft, + FileSystem, + BadPathName, + DeviceBusy, + SharingViolation, + PipeBusy, + NotLink, + PathAlreadyExists, + InvalidHandle, + + /// On Windows, `\\server` or `\\server\share` was not found. + NetworkNotFound, + + /// On Windows, antivirus software is enabled by default. It can be + /// disabled, but Windows Update sometimes ignores the user's preference + /// and re-enables it. When enabled, antivirus software on Windows + /// intercepts file system operations and makes them significantly slower + /// in addition to possibly failing with this error code. + AntivirusInterference, + + /// On Windows, the volume does not contain a recognized file system. File + /// system drivers might not be loaded, or the volume may be corrupt. + UnrecognizedVolume, +} || os.SysCtlError; /// `selfExePath` except allocates the result on the heap. /// Caller owns returned memory. @@ -491,6 +563,8 @@ pub fn selfExePathAlloc(allocator: Allocator) ![]u8 { /// This function may return an error if the current executable /// was deleted after spawning. /// Returned value is a slice of out_buffer. +/// On Windows, the result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). +/// On other platforms, the result is an opaque sequence of bytes with no particular encoding. /// /// On Linux, depends on procfs being mounted. If the currently executing binary has /// been deleted, the file path looks something like `/a/b/c/exe (deleted)`. @@ -505,15 +579,31 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 { if (rc != 0) return error.NameTooLong; var real_path_buf: [MAX_PATH_BYTES]u8 = undefined; - const real_path = try std.os.realpathZ(&symlink_path_buf, &real_path_buf); + const real_path = std.os.realpathZ(&symlink_path_buf, &real_path_buf) catch |err| switch (err) { + error.InvalidWtf8 => unreachable, // Windows-only + error.NetworkNotFound => unreachable, // Windows-only + else => |e| return e, + }; if (real_path.len > out_buffer.len) return error.NameTooLong; const result = out_buffer[0..real_path.len]; @memcpy(result, real_path); return result; } switch (builtin.os.tag) { - .linux => return os.readlinkZ("/proc/self/exe", out_buffer), - .solaris, .illumos => return os.readlinkZ("/proc/self/path/a.out", out_buffer), + .linux => return os.readlinkZ("/proc/self/exe", out_buffer) catch |err| switch (err) { + error.InvalidUtf8 => unreachable, // WASI-only + error.InvalidWtf8 => unreachable, // Windows-only + error.UnsupportedReparsePointType => unreachable, // Windows-only + error.NetworkNotFound => unreachable, // Windows-only + else => |e| return e, + }, + .solaris, .illumos => return os.readlinkZ("/proc/self/path/a.out", out_buffer) catch |err| switch (err) { + error.InvalidUtf8 => unreachable, // WASI-only + error.InvalidWtf8 => unreachable, // Windows-only + error.UnsupportedReparsePointType => unreachable, // Windows-only + error.NetworkNotFound => unreachable, // Windows-only + else => |e| return e, + }, .freebsd, .dragonfly => { var mib = [4]c_int{ os.CTL.KERN, os.KERN.PROC, os.KERN.PROC_PATHNAME, -1 }; var out_len: usize = out_buffer.len; @@ -537,7 +627,11 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 { if (mem.indexOf(u8, argv0, "/") != null) { // argv[0] is a path (relative or absolute): use realpath(3) directly var real_path_buf: [MAX_PATH_BYTES]u8 = undefined; - const real_path = try os.realpathZ(os.argv[0], &real_path_buf); + const real_path = os.realpathZ(os.argv[0], &real_path_buf) catch |err| switch (err) { + error.InvalidWtf8 => unreachable, // Windows-only + error.NetworkNotFound => unreachable, // Windows-only + else => |e| return e, + }; if (real_path.len > out_buffer.len) return error.NameTooLong; const result = out_buffer[0..real_path.len]; @@ -575,7 +669,10 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 { // symlink, not the path that the symlink points to. We want the path // that the symlink points to, though, so we need to get the realpath. const pathname_w = try os.windows.wToPrefixedFileW(null, image_path_name); - return std.fs.cwd().realpathW(pathname_w.span(), out_buffer); + return std.fs.cwd().realpathW(pathname_w.span(), out_buffer) catch |err| switch (err) { + error.InvalidWtf8 => unreachable, + else => |e| return e, + }; }, else => @compileError("std.fs.selfExePath not supported for this target"), } @@ -599,6 +696,8 @@ pub fn selfExeDirPathAlloc(allocator: Allocator) ![]u8 { /// Get the directory path that contains the current executable. /// Returned value is a slice of out_buffer. +/// On Windows, the result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). +/// On other platforms, the result is an opaque sequence of bytes with no particular encoding. pub fn selfExeDirPath(out_buffer: []u8) SelfExePathError![]const u8 { const self_exe_path = try selfExePath(out_buffer); // Assume that the OS APIs return absolute paths, and therefore dirname @@ -607,6 +706,8 @@ pub fn selfExeDirPath(out_buffer: []u8) SelfExePathError![]const u8 { } /// `realpath`, except caller must free the returned memory. +/// On Windows, the result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/). +/// On other platforms, the result is an opaque sequence of bytes with no particular encoding. /// See also `Dir.realpath`. pub fn realpathAlloc(allocator: Allocator, pathname: []const u8) ![]u8 { // Use of MAX_PATH_BYTES here is valid as the realpath function does not |
