aboutsummaryrefslogtreecommitdiff
path: root/lib/std/fs.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2020-03-03 17:05:14 -0500
committerGitHub <noreply@github.com>2020-03-03 17:05:14 -0500
commitf6f0b019bee7910702e113be6d33865c592afa2a (patch)
tree65cd5209cf69c03445f85bb244d15ca00bb51a32 /lib/std/fs.zig
parent582db68a157520a0cf7777807f466d1f6a2e31e7 (diff)
parent1141bfb21b82f8d3fc353e968a591f2ad9aaa571 (diff)
downloadzig-f6f0b019bee7910702e113be6d33865c592afa2a.tar.gz
zig-f6f0b019bee7910702e113be6d33865c592afa2a.zip
Merge pull request #4618 from ziglang/daurnimator-paths
improvements to std.fs, std.os
Diffstat (limited to 'lib/std/fs.zig')
-rw-r--r--lib/std/fs.zig189
1 files changed, 95 insertions, 94 deletions
diff --git a/lib/std/fs.zig b/lib/std/fs.zig
index 769d4b395c..056a2f9def 100644
--- a/lib/std/fs.zig
+++ b/lib/std/fs.zig
@@ -123,47 +123,21 @@ pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?Fil
}
const actual_mode = mode orelse src_stat.mode;
- // TODO this logic could be made more efficient by calling makePath, once
- // that API does not require an allocator
- var atomic_file = make_atomic_file: while (true) {
- const af = AtomicFile.init(dest_path, actual_mode) catch |err| switch (err) {
- error.FileNotFound => {
- var p = dest_path;
- while (path.dirname(p)) |dirname| {
- makeDir(dirname) catch |e| switch (e) {
- error.FileNotFound => {
- p = dirname;
- continue;
- },
- else => return e,
- };
- continue :make_atomic_file;
- } else {
- return err;
- }
- },
- else => |e| return e,
- };
- break af;
- } else unreachable;
- defer atomic_file.deinit();
+ if (path.dirname(dest_path)) |dirname| {
+ try cwd().makePath(dirname);
+ }
- const in_stream = &src_file.inStream().stream;
+ var atomic_file = try AtomicFile.init(dest_path, actual_mode);
+ defer atomic_file.deinit();
- var buf: [mem.page_size * 6]u8 = undefined;
- while (true) {
- const amt = try in_stream.readFull(buf[0..]);
- try atomic_file.file.writeAll(buf[0..amt]);
- if (amt != buf.len) {
- try atomic_file.file.updateTimes(src_stat.atime, src_stat.mtime);
- try atomic_file.finish();
- return PrevStatus.stale;
- }
- }
+ try atomic_file.file.writeFileAll(src_file, .{ .in_len = src_stat.size });
+ try atomic_file.file.updateTimes(src_stat.atime, src_stat.mtime);
+ try atomic_file.finish();
+ return PrevStatus.stale;
}
-/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is
-/// merged and readily available,
+/// Guaranteed to be atomic.
+/// On Linux, until https://patchwork.kernel.org/patch/9636735/ is merged and readily available,
/// there is a possibility of power loss or application termination leaving temporary files present
/// in the same directory as dest_path.
/// Destination file will have the same mode as the source file.
@@ -207,6 +181,9 @@ pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.M
}
}
+/// TODO update this API to avoid a getrandom syscall for every operation. It
+/// should accept a random interface.
+/// TODO rework this to integrate with Dir
pub const AtomicFile = struct {
file: File,
tmp_path_buf: [MAX_PATH_BYTES]u8,
@@ -268,70 +245,42 @@ pub const AtomicFile = struct {
pub fn finish(self: *AtomicFile) !void {
assert(!self.finished);
- self.file.close();
- self.finished = true;
- if (builtin.os.tag == .windows) {
+ if (std.Target.current.os.tag == .windows) {
const dest_path_w = try os.windows.sliceToPrefixedFileW(self.dest_path);
const tmp_path_w = try os.windows.cStrToPrefixedFileW(@ptrCast([*:0]u8, &self.tmp_path_buf));
+ self.file.close();
+ self.finished = true;
return os.renameW(&tmp_path_w, &dest_path_w);
+ } else {
+ const dest_path_c = try os.toPosixPath(self.dest_path);
+ self.file.close();
+ self.finished = true;
+ return os.renameC(@ptrCast([*:0]u8, &self.tmp_path_buf), &dest_path_c);
}
- const dest_path_c = try os.toPosixPath(self.dest_path);
- return os.renameC(@ptrCast([*:0]u8, &self.tmp_path_buf), &dest_path_c);
}
};
const default_new_dir_mode = 0o755;
-/// Create a new directory.
-pub fn makeDir(dir_path: []const u8) !void {
- return os.mkdir(dir_path, default_new_dir_mode);
+/// 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.
+pub fn makeDirAbsolute(absolute_path: []const u8) !void {
+ assert(path.isAbsoluteC(absolute_path));
+ return os.mkdir(absolute_path, default_new_dir_mode);
}
-/// Same as `makeDir` except the parameter is a null-terminated UTF8-encoded string.
-pub fn makeDirC(dir_path: [*:0]const u8) !void {
- return os.mkdirC(dir_path, default_new_dir_mode);
+/// Same as `makeDirAbsolute` except the parameter is a null-terminated UTF8-encoded string.
+pub fn makeDirAbsoluteZ(absolute_path_z: [*:0]const u8) !void {
+ assert(path.isAbsoluteC(absolute_path_z));
+ return os.mkdirZ(absolute_path_z, default_new_dir_mode);
}
-/// Same as `makeDir` except the parameter is a null-terminated UTF16LE-encoded string.
-pub fn makeDirW(dir_path: [*:0]const u16) !void {
- return os.mkdirW(dir_path, default_new_dir_mode);
-}
-
-/// Calls makeDir recursively to make an entire path. Returns success if the path
-/// already exists and is a directory.
-/// This function is not atomic, and if it returns an error, the file system may
-/// have been modified regardless.
-/// TODO determine if we can remove the allocator requirement from this function
-pub fn makePath(allocator: *Allocator, full_path: []const u8) !void {
- const resolved_path = try path.resolve(allocator, &[_][]const u8{full_path});
- defer allocator.free(resolved_path);
-
- var end_index: usize = resolved_path.len;
- while (true) {
- makeDir(resolved_path[0..end_index]) catch |err| switch (err) {
- error.PathAlreadyExists => {
- // TODO stat the file and return an error if it's not a directory
- // this is important because otherwise a dangling symlink
- // could cause an infinite loop
- if (end_index == resolved_path.len) return;
- },
- error.FileNotFound => {
- // march end_index backward until next path component
- while (true) {
- end_index -= 1;
- if (path.isSep(resolved_path[end_index])) break;
- }
- continue;
- },
- else => return err,
- };
- if (end_index == resolved_path.len) return;
- // march end_index forward until next path component
- while (true) {
- end_index += 1;
- if (end_index == resolved_path.len or path.isSep(resolved_path[end_index])) break;
- }
- }
+/// Same as `makeDirAbsolute` except the parameter is a null-terminated WTF-16 encoded string.
+pub fn makeDirAbsoluteW(absolute_path_w: [*:0]const u16) !void {
+ assert(path.isAbsoluteWindowsW(absolute_path_w));
+ const handle = try os.windows.CreateDirectoryW(null, absolute_path_w, null);
+ os.windows.CloseHandle(handle);
}
/// Returns `error.DirNotEmpty` if the directory is not empty.
@@ -709,7 +658,6 @@ pub const Dir = struct {
/// Call `File.close` to release the resource.
/// Asserts that the path parameter has no null bytes.
pub fn openFile(self: Dir, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File {
- if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0);
if (builtin.os.tag == .windows) {
const path_w = try os.windows.sliceToPrefixedFileW(sub_path);
return self.openFileW(&path_w, flags);
@@ -759,7 +707,6 @@ pub const Dir = struct {
/// Call `File.close` on the result when done.
/// Asserts that the path parameter has no null bytes.
pub fn createFile(self: Dir, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File {
- if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0);
if (builtin.os.tag == .windows) {
const path_w = try os.windows.sliceToPrefixedFileW(sub_path);
return self.createFileW(&path_w, flags);
@@ -882,6 +829,64 @@ pub const Dir = struct {
}
}
+ pub fn makeDir(self: Dir, sub_path: []const u8) !void {
+ try os.mkdirat(self.fd, sub_path, default_new_dir_mode);
+ }
+
+ pub fn makeDirZ(self: Dir, sub_path: [*:0]const u8) !void {
+ try os.mkdiratC(self.fd, sub_path, default_new_dir_mode);
+ }
+
+ pub fn makeDirW(self: Dir, sub_path: [*:0]const u16) !void {
+ const handle = try os.windows.CreateDirectoryW(self.fd, sub_path, null);
+ os.windows.CloseHandle(handle);
+ }
+
+ /// Calls makeDir recursively to make an entire path. Returns success if the path
+ /// already exists and is a directory.
+ /// This function is not atomic, and if it returns an error, the file system may
+ /// have been modified regardless.
+ pub fn makePath(self: Dir, sub_path: []const u8) !void {
+ var end_index: usize = sub_path.len;
+ while (true) {
+ self.makeDir(sub_path[0..end_index]) catch |err| switch (err) {
+ error.PathAlreadyExists => {
+ // TODO stat the file and return an error if it's not a directory
+ // this is important because otherwise a dangling symlink
+ // could cause an infinite loop
+ if (end_index == sub_path.len) return;
+ },
+ error.FileNotFound => {
+ if (end_index == 0) return err;
+ // march end_index backward until next path component
+ while (true) {
+ end_index -= 1;
+ if (path.isSep(sub_path[end_index])) break;
+ }
+ continue;
+ },
+ else => return err,
+ };
+ if (end_index == sub_path.len) return;
+ // march end_index forward until next path component
+ while (true) {
+ end_index += 1;
+ if (end_index == sub_path.len or path.isSep(sub_path[end_index])) break;
+ }
+ }
+ }
+
+ /// Changes the current working directory to the open directory handle.
+ /// This modifies global state and can have surprising effects in multi-
+ /// threaded applications. Most applications and especially libraries should
+ /// not call this function as a general rule, however it can have use cases
+ /// in, for example, implementing a shell, or child process execution.
+ /// Not all targets support this. For example, WASI does not have the concept
+ /// of a current working directory.
+ pub fn setAsCwd(self: Dir) !void {
+ try os.fchdir(self.fd);
+ }
+
/// Deprecated; call `openDirList` directly.
pub fn openDir(self: Dir, sub_path: []const u8) OpenError!Dir {
return self.openDirList(sub_path);
@@ -900,7 +905,6 @@ pub const Dir = struct {
///
/// Asserts that the path parameter has no null bytes.
pub fn openDirTraverse(self: Dir, sub_path: []const u8) OpenError!Dir {
- if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0);
if (builtin.os.tag == .windows) {
const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
return self.openDirTraverseW(&sub_path_w);
@@ -918,7 +922,6 @@ pub const Dir = struct {
///
/// Asserts that the path parameter has no null bytes.
pub fn openDirList(self: Dir, sub_path: []const u8) OpenError!Dir {
- if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0);
if (builtin.os.tag == .windows) {
const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
return self.openDirListW(&sub_path_w);
@@ -1082,7 +1085,6 @@ pub const Dir = struct {
/// To delete a directory recursively, see `deleteTree`.
/// Asserts that the path parameter has no null bytes.
pub fn deleteDir(self: Dir, sub_path: []const u8) DeleteDirError!void {
- if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0);
if (builtin.os.tag == .windows) {
const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
return self.deleteDirW(&sub_path_w);
@@ -1112,7 +1114,6 @@ pub const Dir = struct {
/// The return value is a slice of `buffer`, from index `0`.
/// Asserts that the path parameter has no null bytes.
pub fn readLink(self: Dir, sub_path: []const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
- if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0);
const sub_path_c = try os.toPosixPath(sub_path);
return self.readLinkC(&sub_path_c, buffer);
}