aboutsummaryrefslogtreecommitdiff
path: root/lib/std/fs.zig
diff options
context:
space:
mode:
Diffstat (limited to 'lib/std/fs.zig')
-rw-r--r--lib/std/fs.zig177
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