aboutsummaryrefslogtreecommitdiff
path: root/lib/std/fs.zig
diff options
context:
space:
mode:
authorLoris Cro <kappaloris@gmail.com>2023-06-18 09:06:40 +0200
committerGitHub <noreply@github.com>2023-06-18 09:06:40 +0200
commit216ef10dc471e4db60a30208be178d6c59efeaaf (patch)
tree8c239dab283ae9cb3b7fe099bae240bcc53f894e /lib/std/fs.zig
parent0fc1d396495c1ab482197021dedac8bea3f9401c (diff)
parent729a051e9e38674233190aea23c0ac8c134f2d67 (diff)
downloadzig-216ef10dc471e4db60a30208be178d6c59efeaaf.tar.gz
zig-216ef10dc471e4db60a30208be178d6c59efeaaf.zip
Merge branch 'master' into autodoc-searchkey
Diffstat (limited to 'lib/std/fs.zig')
-rw-r--r--lib/std/fs.zig207
1 files changed, 110 insertions, 97 deletions
diff --git a/lib/std/fs.zig b/lib/std/fs.zig
index 9f64387bd8..bb0890be4b 100644
--- a/lib/std/fs.zig
+++ b/lib/std/fs.zig
@@ -385,16 +385,16 @@ pub const IterableDir = struct {
continue :start_over;
}
- const entry_kind = switch (darwin_entry.d_type) {
- os.DT.BLK => Entry.Kind.BlockDevice,
- os.DT.CHR => Entry.Kind.CharacterDevice,
- os.DT.DIR => Entry.Kind.Directory,
- os.DT.FIFO => Entry.Kind.NamedPipe,
- os.DT.LNK => Entry.Kind.SymLink,
- os.DT.REG => Entry.Kind.File,
- os.DT.SOCK => Entry.Kind.UnixDomainSocket,
- os.DT.WHT => Entry.Kind.Whiteout,
- else => Entry.Kind.Unknown,
+ const entry_kind: Entry.Kind = switch (darwin_entry.d_type) {
+ os.DT.BLK => .block_device,
+ os.DT.CHR => .character_device,
+ os.DT.DIR => .directory,
+ os.DT.FIFO => .named_pipe,
+ os.DT.LNK => .sym_link,
+ os.DT.REG => .file,
+ os.DT.SOCK => .unix_domain_socket,
+ os.DT.WHT => .whiteout,
+ else => .unknown,
};
return Entry{
.name = name,
@@ -442,17 +442,17 @@ pub const IterableDir = struct {
error.FileNotFound => unreachable, // lost the race
else => |e| return e,
};
- const entry_kind = switch (stat_info.mode & os.S.IFMT) {
- os.S.IFIFO => Entry.Kind.NamedPipe,
- os.S.IFCHR => Entry.Kind.CharacterDevice,
- os.S.IFDIR => Entry.Kind.Directory,
- os.S.IFBLK => Entry.Kind.BlockDevice,
- os.S.IFREG => Entry.Kind.File,
- os.S.IFLNK => Entry.Kind.SymLink,
- os.S.IFSOCK => Entry.Kind.UnixDomainSocket,
- os.S.IFDOOR => Entry.Kind.Door,
- os.S.IFPORT => Entry.Kind.EventPort,
- else => Entry.Kind.Unknown,
+ const entry_kind: Entry.Kind = switch (stat_info.mode & os.S.IFMT) {
+ os.S.IFIFO => .named_pipe,
+ os.S.IFCHR => .character_device,
+ os.S.IFDIR => .directory,
+ os.S.IFBLK => .block_device,
+ os.S.IFREG => .file,
+ os.S.IFLNK => .sym_link,
+ os.S.IFSOCK => .unix_domain_socket,
+ os.S.IFDOOR => .door,
+ os.S.IFPORT => .event_port,
+ else => .unknown,
};
return Entry{
.name = name,
@@ -478,6 +478,9 @@ pub const IterableDir = struct {
.FAULT => unreachable,
.NOTDIR => unreachable,
.INVAL => unreachable,
+ // Introduced in freebsd 13.2: directory unlinked but still open.
+ // To be consistent, iteration ends if the directory being iterated is deleted during iteration.
+ .NOENT => return null,
else => |err| return os.unexpectedErrno(err),
}
if (rc == 0) return null;
@@ -501,16 +504,16 @@ pub const IterableDir = struct {
continue :start_over;
}
- const entry_kind = switch (bsd_entry.d_type) {
- os.DT.BLK => Entry.Kind.BlockDevice,
- os.DT.CHR => Entry.Kind.CharacterDevice,
- os.DT.DIR => Entry.Kind.Directory,
- os.DT.FIFO => Entry.Kind.NamedPipe,
- os.DT.LNK => Entry.Kind.SymLink,
- os.DT.REG => Entry.Kind.File,
- os.DT.SOCK => Entry.Kind.UnixDomainSocket,
- os.DT.WHT => Entry.Kind.Whiteout,
- else => Entry.Kind.Unknown,
+ const entry_kind: Entry.Kind = switch (bsd_entry.d_type) {
+ os.DT.BLK => .block_device,
+ os.DT.CHR => .character_device,
+ os.DT.DIR => .directory,
+ os.DT.FIFO => .named_pipe,
+ os.DT.LNK => .sym_link,
+ os.DT.REG => .file,
+ os.DT.SOCK => .unix_domain_socket,
+ os.DT.WHT => .whiteout,
+ else => .unknown,
};
return Entry{
.name = name,
@@ -595,14 +598,14 @@ pub const IterableDir = struct {
}
const statmode = stat_info.mode & os.S.IFMT;
- const entry_kind = switch (statmode) {
- os.S.IFDIR => Entry.Kind.Directory,
- os.S.IFBLK => Entry.Kind.BlockDevice,
- os.S.IFCHR => Entry.Kind.CharacterDevice,
- os.S.IFLNK => Entry.Kind.SymLink,
- os.S.IFREG => Entry.Kind.File,
- os.S.IFIFO => Entry.Kind.NamedPipe,
- else => Entry.Kind.Unknown,
+ const entry_kind: Entry.Kind = switch (statmode) {
+ os.S.IFDIR => .directory,
+ os.S.IFBLK => .block_device,
+ os.S.IFCHR => .character_device,
+ os.S.IFLNK => .sym_link,
+ os.S.IFREG => .file,
+ os.S.IFIFO => .named_pipe,
+ else => .unknown,
};
return Entry{
@@ -662,6 +665,7 @@ pub const IterableDir = struct {
.NOTDIR => unreachable,
.NOENT => return error.DirNotFound, // The directory being iterated was deleted during iteration.
.INVAL => return error.Unexpected, // Linux may in some cases return EINVAL when reading /proc/$PID/net.
+ .ACCES => return error.AccessDenied, // Do not have permission to iterate this directory.
else => |err| return os.unexpectedErrno(err),
}
if (rc == 0) return null;
@@ -679,15 +683,15 @@ pub const IterableDir = struct {
continue :start_over;
}
- const entry_kind = switch (linux_entry.d_type) {
- linux.DT.BLK => Entry.Kind.BlockDevice,
- linux.DT.CHR => Entry.Kind.CharacterDevice,
- linux.DT.DIR => Entry.Kind.Directory,
- linux.DT.FIFO => Entry.Kind.NamedPipe,
- linux.DT.LNK => Entry.Kind.SymLink,
- linux.DT.REG => Entry.Kind.File,
- linux.DT.SOCK => Entry.Kind.UnixDomainSocket,
- else => Entry.Kind.Unknown,
+ const entry_kind: Entry.Kind = switch (linux_entry.d_type) {
+ linux.DT.BLK => .block_device,
+ linux.DT.CHR => .character_device,
+ linux.DT.DIR => .directory,
+ linux.DT.FIFO => .named_pipe,
+ linux.DT.LNK => .sym_link,
+ linux.DT.REG => .file,
+ linux.DT.SOCK => .unix_domain_socket,
+ else => .unknown,
};
return Entry{
.name = name,
@@ -761,11 +765,11 @@ pub const IterableDir = struct {
// Trust that Windows gives us valid UTF-16LE
const name_utf8_len = std.unicode.utf16leToUtf8(self.name_data[0..], name_utf16le) catch unreachable;
const name_utf8 = self.name_data[0..name_utf8_len];
- const kind = blk: {
+ const kind: Entry.Kind = blk: {
const attrs = dir_info.FileAttributes;
- if (attrs & w.FILE_ATTRIBUTE_DIRECTORY != 0) break :blk Entry.Kind.Directory;
- if (attrs & w.FILE_ATTRIBUTE_REPARSE_POINT != 0) break :blk Entry.Kind.SymLink;
- break :blk Entry.Kind.File;
+ if (attrs & w.FILE_ATTRIBUTE_DIRECTORY != 0) break :blk .directory;
+ if (attrs & w.FILE_ATTRIBUTE_REPARSE_POINT != 0) break :blk .sym_link;
+ break :blk .file;
};
return Entry{
.name = name_utf8,
@@ -850,14 +854,14 @@ pub const IterableDir = struct {
continue :start_over;
}
- const entry_kind = switch (entry.d_type) {
- .BLOCK_DEVICE => Entry.Kind.BlockDevice,
- .CHARACTER_DEVICE => Entry.Kind.CharacterDevice,
- .DIRECTORY => Entry.Kind.Directory,
- .SYMBOLIC_LINK => Entry.Kind.SymLink,
- .REGULAR_FILE => Entry.Kind.File,
- .SOCKET_STREAM, .SOCKET_DGRAM => Entry.Kind.UnixDomainSocket,
- else => Entry.Kind.Unknown,
+ const entry_kind: Entry.Kind = switch (entry.d_type) {
+ .BLOCK_DEVICE => .block_device,
+ .CHARACTER_DEVICE => .character_device,
+ .DIRECTORY => .directory,
+ .SYMBOLIC_LINK => .sym_link,
+ .REGULAR_FILE => .file,
+ .SOCKET_STREAM, .SOCKET_DGRAM => .unix_domain_socket,
+ else => .unknown,
};
return Entry{
.name = name,
@@ -957,14 +961,24 @@ pub const IterableDir = struct {
var top = &self.stack.items[self.stack.items.len - 1];
var containing = top;
var dirname_len = top.dirname_len;
- if (try top.iter.next()) |base| {
+ if (top.iter.next() catch |err| {
+ // If we get an error, then we want the user to be able to continue
+ // walking if they want, which means that we need to pop the directory
+ // that errored from the stack. Otherwise, all future `next` calls would
+ // likely just fail with the same error.
+ var item = self.stack.pop();
+ if (self.stack.items.len != 0) {
+ item.iter.dir.close();
+ }
+ return err;
+ }) |base| {
self.name_buffer.shrinkRetainingCapacity(dirname_len);
if (self.name_buffer.items.len != 0) {
try self.name_buffer.append(path.sep);
dirname_len += 1;
}
try self.name_buffer.appendSlice(base.name);
- if (base.kind == .Directory) {
+ if (base.kind == .directory) {
var new_dir = top.iter.dir.openIterableDir(base.name, .{}) catch |err| switch (err) {
error.NameTooLong => unreachable, // no path sep in base.name
else => |e| return e,
@@ -972,7 +986,7 @@ pub const IterableDir = struct {
{
errdefer new_dir.close();
try self.stack.append(StackItem{
- .iter = new_dir.iterate(),
+ .iter = new_dir.iterateAssumeFirstIteration(),
.dirname_len = self.name_buffer.items.len,
});
top = &self.stack.items[self.stack.items.len - 1];
@@ -1157,9 +1171,9 @@ pub const Dir = struct {
else
0;
os_flags |= switch (flags.lock) {
- .None => @as(u32, 0),
- .Shared => os.O.SHLOCK | nonblocking_lock_flag,
- .Exclusive => os.O.EXLOCK | nonblocking_lock_flag,
+ .none => @as(u32, 0),
+ .shared => os.O.SHLOCK | nonblocking_lock_flag,
+ .exclusive => os.O.EXLOCK | nonblocking_lock_flag,
};
}
if (@hasDecl(os.O, "LARGEFILE")) {
@@ -1182,13 +1196,13 @@ pub const Dir = struct {
// WASI doesn't have os.flock so we intetinally check OS prior to the inner if block
// since it is not compiltime-known and we need to avoid undefined symbol in Wasm.
if (builtin.target.os.tag != .wasi) {
- if (!has_flock_open_flags and flags.lock != .None) {
+ if (!has_flock_open_flags and flags.lock != .none) {
// TODO: integrate async I/O
const lock_nonblocking = if (flags.lock_nonblocking) os.LOCK.NB else @as(i32, 0);
try os.flock(fd, switch (flags.lock) {
- .None => unreachable,
- .Shared => os.LOCK.SH | lock_nonblocking,
- .Exclusive => os.LOCK.EX | lock_nonblocking,
+ .none => unreachable,
+ .shared => os.LOCK.SH | lock_nonblocking,
+ .exclusive => os.LOCK.EX | lock_nonblocking,
});
}
}
@@ -1241,9 +1255,9 @@ pub const Dir = struct {
const range_off: w.LARGE_INTEGER = 0;
const range_len: w.LARGE_INTEGER = 1;
const exclusive = switch (flags.lock) {
- .None => return file,
- .Shared => false,
- .Exclusive => true,
+ .none => return file,
+ .shared => false,
+ .exclusive => true,
};
try w.LockFile(
file.handle,
@@ -1320,9 +1334,9 @@ pub const Dir = struct {
else
0;
const lock_flag: u32 = if (has_flock_open_flags) switch (flags.lock) {
- .None => @as(u32, 0),
- .Shared => os.O.SHLOCK | nonblocking_lock_flag,
- .Exclusive => os.O.EXLOCK | nonblocking_lock_flag,
+ .none => @as(u32, 0),
+ .shared => os.O.SHLOCK | nonblocking_lock_flag,
+ .exclusive => os.O.EXLOCK | nonblocking_lock_flag,
} else 0;
const O_LARGEFILE = if (@hasDecl(os.O, "LARGEFILE")) os.O.LARGEFILE else 0;
@@ -1339,13 +1353,13 @@ pub const Dir = struct {
// WASI doesn't have os.flock so we intetinally check OS prior to the inner if block
// since it is not compiltime-known and we need to avoid undefined symbol in Wasm.
if (builtin.target.os.tag != .wasi) {
- if (!has_flock_open_flags and flags.lock != .None) {
+ if (!has_flock_open_flags and flags.lock != .none) {
// TODO: integrate async I/O
const lock_nonblocking = if (flags.lock_nonblocking) os.LOCK.NB else @as(i32, 0);
try os.flock(fd, switch (flags.lock) {
- .None => unreachable,
- .Shared => os.LOCK.SH | lock_nonblocking,
- .Exclusive => os.LOCK.EX | lock_nonblocking,
+ .none => unreachable,
+ .shared => os.LOCK.SH | lock_nonblocking,
+ .exclusive => os.LOCK.EX | lock_nonblocking,
});
}
}
@@ -1402,9 +1416,9 @@ pub const Dir = struct {
const range_off: w.LARGE_INTEGER = 0;
const range_len: w.LARGE_INTEGER = 1;
const exclusive = switch (flags.lock) {
- .None => return file,
- .Shared => false,
- .Exclusive => true,
+ .none => return file,
+ .shared => false,
+ .exclusive => true,
};
try w.LockFile(
file.handle,
@@ -2106,7 +2120,7 @@ pub const Dir = struct {
/// this function recursively removes its entries and then tries again.
/// This operation is not atomic on most file systems.
pub fn deleteTree(self: Dir, sub_path: []const u8) DeleteTreeError!void {
- var initial_iterable_dir = (try self.deleteTreeOpenInitialSubpath(sub_path, .File)) orelse return;
+ var initial_iterable_dir = (try self.deleteTreeOpenInitialSubpath(sub_path, .file)) orelse return;
const StackItem = struct {
name: []const u8,
@@ -2130,7 +2144,7 @@ pub const Dir = struct {
process_stack: while (stack.len != 0) {
var top = &(stack.slice()[stack.len - 1]);
while (try top.iter.next()) |entry| {
- var treat_as_dir = entry.kind == .Directory;
+ var treat_as_dir = entry.kind == .directory;
handle_entry: while (true) {
if (treat_as_dir) {
if (stack.ensureUnusedCapacity(1)) {
@@ -2291,7 +2305,7 @@ pub const Dir = struct {
/// Like `deleteTree`, but only keeps one `Iterator` active at a time to minimize the function's stack size.
/// This is slower than `deleteTree` but uses less stack space.
pub fn deleteTreeMinStackSize(self: Dir, sub_path: []const u8) DeleteTreeError!void {
- return self.deleteTreeMinStackSizeWithKindHint(sub_path, .File);
+ return self.deleteTreeMinStackSizeWithKindHint(sub_path, .file);
}
fn deleteTreeMinStackSizeWithKindHint(self: Dir, sub_path: []const u8, kind_hint: File.Kind) DeleteTreeError!void {
@@ -2316,7 +2330,7 @@ pub const Dir = struct {
scan_dir: while (true) {
var dir_it = iterable_dir.iterateAssumeFirstIteration();
dir_it: while (try dir_it.next()) |entry| {
- var treat_as_dir = entry.kind == .Directory;
+ var treat_as_dir = entry.kind == .directory;
handle_entry: while (true) {
if (treat_as_dir) {
const new_dir = iterable_dir.dir.openIterableDir(entry.name, .{ .no_follow = true }) catch |err| switch (err) {
@@ -2407,7 +2421,7 @@ pub const Dir = struct {
fn deleteTreeOpenInitialSubpath(self: Dir, sub_path: []const u8, kind_hint: File.Kind) !?IterableDir {
return iterable_dir: {
// Treat as a file by default
- var treat_as_dir = kind_hint == .Directory;
+ var treat_as_dir = kind_hint == .directory;
handle_entry: while (true) {
if (treat_as_dir) {
@@ -2999,8 +3013,7 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 {
.haiku => {
// The only possible issue when looking for the self image path is
// when the buffer is too short.
- // TODO replace with proper constants
- if (os.find_path(null, 1000, null, out_buffer.ptr, out_buffer.len) != 0)
+ if (os.find_path(os.B_APP_IMAGE_SYMBOL, os.path_base_directory.B_FIND_IMAGE_PATH, null, out_buffer.ptr, out_buffer.len) != 0)
return error.Overflow;
return mem.sliceTo(out_buffer, 0);
},
@@ -3022,7 +3035,7 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 {
} else if (argv0.len != 0) {
// argv[0] is not empty (and not a path): search it inside PATH
const PATH = std.os.getenvZ("PATH") orelse return error.FileNotFound;
- var path_it = mem.tokenize(u8, PATH, &[_]u8{path.delimiter});
+ var path_it = mem.tokenizeScalar(u8, PATH, path.delimiter);
while (path_it.next()) |a_path| {
var resolved_path_buf: [MAX_PATH_BYTES - 1:0]u8 = undefined;
const resolved_path = std.fmt.bufPrintZ(&resolved_path_buf, "{s}/{s}", .{
@@ -3103,7 +3116,7 @@ const CopyFileRawError = error{SystemResources} || os.CopyFileRangeError || os.S
// No metadata is transferred over.
fn copy_file(fd_in: os.fd_t, fd_out: os.fd_t, maybe_size: ?u64) CopyFileRawError!void {
if (comptime builtin.target.isDarwin()) {
- const rc = os.system.fcopyfile(fd_in, fd_out, null, os.system.COPYFILE_DATA);
+ const rc = os.system.fcopyfile(fd_in, fd_out, null, os.system.COPYFILE.DATA);
switch (os.errno(rc)) {
.SUCCESS => return,
.INVAL => unreachable,
@@ -3151,12 +3164,12 @@ fn copy_file(fd_in: os.fd_t, fd_out: os.fd_t, maybe_size: ?u64) CopyFileRawError
test {
if (builtin.os.tag != .wasi) {
- _ = makeDirAbsolute;
- _ = makeDirAbsoluteZ;
- _ = copyFileAbsolute;
- _ = updateFileAbsolute;
+ _ = &makeDirAbsolute;
+ _ = &makeDirAbsoluteZ;
+ _ = &copyFileAbsolute;
+ _ = &updateFileAbsolute;
}
- _ = Dir.copyFile;
+ _ = &Dir.copyFile;
_ = @import("fs/test.zig");
_ = @import("fs/path.zig");
_ = @import("fs/file.zig");