mirror of
https://github.com/ziglang/zig.git
synced 2025-12-30 18:13:19 +00:00
Merge pull request #4618 from ziglang/daurnimator-paths
improvements to std.fs, std.os
This commit is contained in:
commit
f6f0b019be
@ -50,7 +50,7 @@ pub fn main() !void {
|
||||
var tokenizer = Tokenizer.init(in_file_name, input_file_bytes);
|
||||
var toc = try genToc(allocator, &tokenizer);
|
||||
|
||||
try fs.makePath(allocator, tmp_dir_name);
|
||||
try fs.cwd().makePath(tmp_dir_name);
|
||||
defer fs.deleteTree(tmp_dir_name) catch {};
|
||||
|
||||
try genHtml(allocator, &tokenizer, &toc, &buffered_out_stream.stream, zig_exe);
|
||||
|
||||
@ -763,7 +763,7 @@ pub const Builder = struct {
|
||||
}
|
||||
|
||||
pub fn makePath(self: *Builder, path: []const u8) !void {
|
||||
fs.makePath(self.allocator, self.pathFromRoot(path)) catch |err| {
|
||||
fs.cwd().makePath(self.pathFromRoot(path)) catch |err| {
|
||||
warn("Unable to create path {}: {}\n", .{ path, @errorName(err) });
|
||||
return err;
|
||||
};
|
||||
@ -2311,7 +2311,7 @@ pub const InstallDirStep = struct {
|
||||
const rel_path = entry.path[full_src_dir.len + 1 ..];
|
||||
const dest_path = try fs.path.join(self.builder.allocator, &[_][]const u8{ dest_prefix, rel_path });
|
||||
switch (entry.kind) {
|
||||
.Directory => try fs.makePath(self.builder.allocator, dest_path),
|
||||
.Directory => try fs.cwd().makePath(dest_path),
|
||||
.File => try self.builder.updateFile(entry.path, dest_path),
|
||||
else => continue,
|
||||
}
|
||||
|
||||
@ -74,7 +74,7 @@ pub const WriteFileStep = struct {
|
||||
&hash_basename,
|
||||
});
|
||||
// TODO replace with something like fs.makePathAndOpenDir
|
||||
fs.makePath(self.builder.allocator, self.output_dir) catch |err| {
|
||||
fs.cwd().makePath(self.output_dir) catch |err| {
|
||||
warn("unable to make path {}: {}\n", .{ self.output_dir, @errorName(err) });
|
||||
return err;
|
||||
};
|
||||
|
||||
@ -75,6 +75,7 @@ pub extern "c" fn isatty(fd: fd_t) c_int;
|
||||
pub extern "c" fn close(fd: fd_t) c_int;
|
||||
pub extern "c" fn fstat(fd: fd_t, buf: *Stat) c_int;
|
||||
pub extern "c" fn @"fstat$INODE64"(fd: fd_t, buf: *Stat) 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;
|
||||
@ -101,9 +102,11 @@ pub extern "c" fn faccessat(dirfd: fd_t, path: [*:0]const u8, mode: c_uint, flag
|
||||
pub extern "c" fn pipe(fds: *[2]fd_t) c_int;
|
||||
pub extern "c" fn pipe2(fds: *[2]fd_t, flags: u32) c_int;
|
||||
pub extern "c" fn mkdir(path: [*:0]const u8, mode: c_uint) c_int;
|
||||
pub extern "c" fn mkdirat(dirfd: fd_t, path: [*:0]const u8, mode: u32) c_int;
|
||||
pub extern "c" fn symlink(existing: [*:0]const u8, new: [*:0]const u8) c_int;
|
||||
pub extern "c" fn rename(old: [*:0]const u8, new: [*:0]const u8) c_int;
|
||||
pub extern "c" fn chdir(path: [*:0]const u8) c_int;
|
||||
pub extern "c" fn fchdir(fd: fd_t) c_int;
|
||||
pub extern "c" fn execve(path: [*:0]const u8, argv: [*:null]const ?[*:0]const u8, envp: [*:null]const ?[*:0]const u8) c_int;
|
||||
pub extern "c" fn dup(fd: fd_t) c_int;
|
||||
pub extern "c" fn dup2(old_fd: fd_t, new_fd: fd_t) c_int;
|
||||
|
||||
189
lib/std/fs.zig
189
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;
|
||||
if (path.dirname(dest_path)) |dirname| {
|
||||
try cwd().makePath(dirname);
|
||||
}
|
||||
|
||||
var atomic_file = try AtomicFile.init(dest_path, actual_mode);
|
||||
defer atomic_file.deinit();
|
||||
|
||||
const in_stream = &src_file.inStream().stream;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@ -271,6 +271,8 @@ pub const File = struct {
|
||||
/// The `iovecs` parameter is mutable because this function needs to mutate the fields in
|
||||
/// order to handle partial reads from the underlying OS layer.
|
||||
pub fn readvAll(self: File, iovecs: []os.iovec) ReadError!void {
|
||||
if (iovecs.len == 0) return;
|
||||
|
||||
var i: usize = 0;
|
||||
while (true) {
|
||||
var amt = try self.readv(iovecs[i..]);
|
||||
@ -295,6 +297,8 @@ pub const File = struct {
|
||||
/// The `iovecs` parameter is mutable because this function needs to mutate the fields in
|
||||
/// order to handle partial reads from the underlying OS layer.
|
||||
pub fn preadvAll(self: File, iovecs: []const os.iovec, offset: u64) PReadError!void {
|
||||
if (iovecs.len == 0) return;
|
||||
|
||||
var i: usize = 0;
|
||||
var off: usize = 0;
|
||||
while (true) {
|
||||
@ -354,6 +358,8 @@ pub const File = struct {
|
||||
/// The `iovecs` parameter is mutable because this function needs to mutate the fields in
|
||||
/// order to handle partial writes from the underlying OS layer.
|
||||
pub fn writevAll(self: File, iovecs: []os.iovec_const) WriteError!void {
|
||||
if (iovecs.len == 0) return;
|
||||
|
||||
var i: usize = 0;
|
||||
while (true) {
|
||||
var amt = try self.writev(iovecs[i..]);
|
||||
@ -378,6 +384,8 @@ pub const File = struct {
|
||||
/// The `iovecs` parameter is mutable because this function needs to mutate the fields in
|
||||
/// order to handle partial writes from the underlying OS layer.
|
||||
pub fn pwritevAll(self: File, iovecs: []os.iovec_const, offset: usize) PWriteError!void {
|
||||
if (iovecs.len == 0) return;
|
||||
|
||||
var i: usize = 0;
|
||||
var off: usize = 0;
|
||||
while (true) {
|
||||
@ -393,6 +401,89 @@ pub const File = struct {
|
||||
}
|
||||
}
|
||||
|
||||
pub const WriteFileOptions = struct {
|
||||
in_offset: u64 = 0,
|
||||
|
||||
/// `null` means the entire file. `0` means no bytes from the file.
|
||||
/// When this is `null`, trailers must be sent in a separate writev() call
|
||||
/// due to a flaw in the BSD sendfile API. Other operating systems, such as
|
||||
/// Linux, already do this anyway due to API limitations.
|
||||
/// If the size of the source file is known, passing the size here will save one syscall.
|
||||
in_len: ?u64 = null,
|
||||
|
||||
headers_and_trailers: []os.iovec_const = &[0]os.iovec_const{},
|
||||
|
||||
/// The trailer count is inferred from `headers_and_trailers.len - header_count`
|
||||
header_count: usize = 0,
|
||||
};
|
||||
|
||||
pub const WriteFileError = os.SendFileError;
|
||||
|
||||
/// TODO integrate with async I/O
|
||||
pub fn writeFileAll(self: File, in_file: File, args: WriteFileOptions) WriteFileError!void {
|
||||
const count = blk: {
|
||||
if (args.in_len) |l| {
|
||||
if (l == 0) {
|
||||
return self.writevAll(args.headers_and_trailers);
|
||||
} else {
|
||||
break :blk l;
|
||||
}
|
||||
} else {
|
||||
break :blk 0;
|
||||
}
|
||||
};
|
||||
const headers = args.headers_and_trailers[0..args.header_count];
|
||||
const trailers = args.headers_and_trailers[args.header_count..];
|
||||
const zero_iovec = &[0]os.iovec_const{};
|
||||
// When reading the whole file, we cannot put the trailers in the sendfile() syscall,
|
||||
// because we have no way to determine whether a partial write is past the end of the file or not.
|
||||
const trls = if (count == 0) zero_iovec else trailers;
|
||||
const offset = args.in_offset;
|
||||
const out_fd = self.handle;
|
||||
const in_fd = in_file.handle;
|
||||
const flags = 0;
|
||||
var amt: usize = 0;
|
||||
hdrs: {
|
||||
var i: usize = 0;
|
||||
while (i < headers.len) {
|
||||
amt = try os.sendfile(out_fd, in_fd, offset, count, headers[i..], trls, flags);
|
||||
while (amt >= headers[i].iov_len) {
|
||||
amt -= headers[i].iov_len;
|
||||
i += 1;
|
||||
if (i >= headers.len) break :hdrs;
|
||||
}
|
||||
headers[i].iov_base += amt;
|
||||
headers[i].iov_len -= amt;
|
||||
}
|
||||
}
|
||||
if (count == 0) {
|
||||
var off: u64 = amt;
|
||||
while (true) {
|
||||
amt = try os.sendfile(out_fd, in_fd, offset + off, 0, zero_iovec, zero_iovec, flags);
|
||||
if (amt == 0) break;
|
||||
off += amt;
|
||||
}
|
||||
} else {
|
||||
var off: u64 = amt;
|
||||
while (off < count) {
|
||||
amt = try os.sendfile(out_fd, in_fd, offset + off, count - off, zero_iovec, trailers, flags);
|
||||
off += amt;
|
||||
}
|
||||
amt = @intCast(usize, off - count);
|
||||
}
|
||||
var i: usize = 0;
|
||||
while (i < trailers.len) {
|
||||
while (amt >= headers[i].iov_len) {
|
||||
amt -= trailers[i].iov_len;
|
||||
i += 1;
|
||||
if (i >= trailers.len) return;
|
||||
}
|
||||
trailers[i].iov_base += amt;
|
||||
trailers[i].iov_len -= amt;
|
||||
amt = try os.writev(self.handle, trailers[i..]);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn inStream(file: File) InStream {
|
||||
return InStream{
|
||||
.file = file,
|
||||
|
||||
@ -618,11 +618,10 @@ test "write a file, watch it, write it again" {
|
||||
// TODO re-enable this test
|
||||
if (true) return error.SkipZigTest;
|
||||
|
||||
const allocator = std.heap.page_allocator;
|
||||
|
||||
try os.makePath(allocator, test_tmp_dir);
|
||||
try fs.cwd().makePath(test_tmp_dir);
|
||||
defer os.deleteTree(test_tmp_dir) catch {};
|
||||
|
||||
const allocator = std.heap.page_allocator;
|
||||
return testFsWatch(&allocator);
|
||||
}
|
||||
|
||||
|
||||
140
lib/std/os.zig
140
lib/std/os.zig
@ -1355,7 +1355,6 @@ pub const UnlinkatError = UnlinkError || error{
|
||||
/// Delete a file name and possibly the file it refers to, based on an open directory handle.
|
||||
/// Asserts that the path parameter has no null bytes.
|
||||
pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void {
|
||||
if (std.debug.runtime_safety) for (file_path) |byte| assert(byte != 0);
|
||||
if (builtin.os.tag == .windows) {
|
||||
const file_path_w = try windows.sliceToPrefixedFileW(file_path);
|
||||
return unlinkatW(dirfd, &file_path_w, flags);
|
||||
@ -1540,25 +1539,69 @@ pub const MakeDirError = error{
|
||||
ReadOnlyFileSystem,
|
||||
InvalidUtf8,
|
||||
BadPathName,
|
||||
NoDevice,
|
||||
} || UnexpectedError;
|
||||
|
||||
pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!void {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const sub_dir_path_w = try windows.sliceToPrefixedFileW(sub_dir_path);
|
||||
return mkdiratW(dir_fd, &sub_dir_path_w, mode);
|
||||
} else {
|
||||
const sub_dir_path_c = try toPosixPath(sub_dir_path);
|
||||
return mkdiratC(dir_fd, &sub_dir_path_c, mode);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mkdiratC(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: u32) MakeDirError!void {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const sub_dir_path_w = try windows.cStrToPrefixedFileW(sub_dir_path);
|
||||
return mkdiratW(dir_fd, &sub_dir_path_w, mode);
|
||||
}
|
||||
switch (errno(system.mkdirat(dir_fd, sub_dir_path, mode))) {
|
||||
0 => return,
|
||||
EACCES => return error.AccessDenied,
|
||||
EBADF => unreachable,
|
||||
EPERM => return error.AccessDenied,
|
||||
EDQUOT => return error.DiskQuota,
|
||||
EEXIST => return error.PathAlreadyExists,
|
||||
EFAULT => unreachable,
|
||||
ELOOP => return error.SymLinkLoop,
|
||||
EMLINK => return error.LinkQuotaExceeded,
|
||||
ENAMETOOLONG => return error.NameTooLong,
|
||||
ENOENT => return error.FileNotFound,
|
||||
ENOMEM => return error.SystemResources,
|
||||
ENOSPC => return error.NoSpaceLeft,
|
||||
ENOTDIR => return error.NotDir,
|
||||
EROFS => return error.ReadOnlyFileSystem,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mkdiratW(dir_fd: fd_t, sub_path_w: [*:0]const u16, mode: u32) MakeDirError!void {
|
||||
const sub_dir_handle = try windows.CreateDirectoryW(dir_fd, sub_path_w, null);
|
||||
windows.CloseHandle(sub_dir_handle);
|
||||
}
|
||||
|
||||
/// Create a directory.
|
||||
/// `mode` is ignored on Windows.
|
||||
pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);
|
||||
return windows.CreateDirectoryW(&dir_path_w, null);
|
||||
const sub_dir_handle = try windows.CreateDirectory(null, dir_path, null);
|
||||
windows.CloseHandle(sub_dir_handle);
|
||||
return;
|
||||
} else {
|
||||
const dir_path_c = try toPosixPath(dir_path);
|
||||
return mkdirC(&dir_path_c, mode);
|
||||
return mkdirZ(&dir_path_c, mode);
|
||||
}
|
||||
}
|
||||
|
||||
/// Same as `mkdir` but the parameter is a null-terminated UTF8-encoded string.
|
||||
pub fn mkdirC(dir_path: [*:0]const u8, mode: u32) MakeDirError!void {
|
||||
pub fn mkdirZ(dir_path: [*:0]const u8, mode: u32) MakeDirError!void {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const dir_path_w = try windows.cStrToPrefixedFileW(dir_path);
|
||||
return windows.CreateDirectoryW(&dir_path_w, null);
|
||||
const sub_dir_handle = try windows.CreateDirectoryW(null, &dir_path_w, null);
|
||||
windows.CloseHandle(sub_dir_handle);
|
||||
return;
|
||||
}
|
||||
switch (errno(system.mkdir(dir_path, mode))) {
|
||||
0 => return,
|
||||
@ -1671,6 +1714,26 @@ pub fn chdirC(dir_path: [*:0]const u8) ChangeCurDirError!void {
|
||||
}
|
||||
}
|
||||
|
||||
pub const FchdirError = error{
|
||||
AccessDenied,
|
||||
NotDir,
|
||||
FileSystem,
|
||||
} || UnexpectedError;
|
||||
|
||||
pub fn fchdir(dirfd: fd_t) FchdirError!void {
|
||||
while (true) {
|
||||
switch (errno(system.fchdir(dirfd))) {
|
||||
0 => return,
|
||||
EACCES => return error.AccessDenied,
|
||||
EBADF => unreachable,
|
||||
ENOTDIR => return error.NotDir,
|
||||
EINTR => continue,
|
||||
EIO => return error.FileSystem,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const ReadLinkError = error{
|
||||
AccessDenied,
|
||||
FileSystem,
|
||||
@ -2322,6 +2385,29 @@ pub fn fstat(fd: fd_t) FStatError!Stat {
|
||||
}
|
||||
}
|
||||
|
||||
const FStatAtError = FStatError || error{NameTooLong};
|
||||
|
||||
pub fn fstatat(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError![]Stat {
|
||||
const pathname_c = try toPosixPath(pathname);
|
||||
return fstatatC(dirfd, &pathname_c, flags);
|
||||
}
|
||||
|
||||
pub fn fstatatC(dirfd: fd_t, pathname: [*:0]const u8, flags: u32) FStatAtError!Stat {
|
||||
var stat: Stat = undefined;
|
||||
switch (errno(system.fstatat(dirfd, pathname, &stat, flags))) {
|
||||
0 => return stat,
|
||||
EINVAL => unreachable,
|
||||
EBADF => unreachable, // Always a race condition.
|
||||
ENOMEM => return error.SystemResources,
|
||||
EACCES => return error.AccessDenied,
|
||||
EFAULT => unreachable,
|
||||
ENAMETOOLONG => return error.NameTooLong,
|
||||
ENOENT => return error.FileNotFound,
|
||||
ENOTDIR => return error.FileNotFound,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
|
||||
pub const KQueueError = error{
|
||||
/// The per-process limit on the number of open file descriptors has been reached.
|
||||
ProcessFdQuotaExceeded,
|
||||
@ -3169,6 +3255,7 @@ pub fn sched_getaffinity(pid: pid_t) SchedGetAffinityError!cpu_set_t {
|
||||
/// Used to convert a slice to a null terminated slice on the stack.
|
||||
/// TODO https://github.com/ziglang/zig/issues/287
|
||||
pub fn toPosixPath(file_path: []const u8) ![PATH_MAX - 1:0]u8 {
|
||||
if (std.debug.runtime_safety) assert(std.mem.indexOfScalar(u8, file_path, 0) == null);
|
||||
var path_with_null: [PATH_MAX - 1:0]u8 = undefined;
|
||||
// >= rather than > to make room for the null byte
|
||||
if (file_path.len >= PATH_MAX) return error.NameTooLong;
|
||||
@ -3492,12 +3579,12 @@ fn count_iovec_bytes(iovs: []const iovec_const) usize {
|
||||
}
|
||||
|
||||
/// Transfer data between file descriptors, with optional headers and trailers.
|
||||
/// Returns the number of bytes written. This will be zero if `in_offset` falls beyond the end of the file.
|
||||
/// Returns the number of bytes written, which can be zero.
|
||||
///
|
||||
/// The `sendfile` call copies `count` bytes from one file descriptor to another. When possible,
|
||||
/// The `sendfile` call copies `in_len` bytes from one file descriptor to another. When possible,
|
||||
/// this is done within the operating system kernel, which can provide better performance
|
||||
/// characteristics than transferring data from kernel to user space and back, such as with
|
||||
/// `read` and `write` calls. When `count` is `0`, it means to copy until the end of the input file has been
|
||||
/// `read` and `write` calls. When `in_len` is `0`, it means to copy until the end of the input file has been
|
||||
/// reached. Note, however, that partial writes are still possible in this case.
|
||||
///
|
||||
/// `in_fd` must be a file descriptor opened for reading, and `out_fd` must be a file descriptor
|
||||
@ -3506,7 +3593,8 @@ fn count_iovec_bytes(iovs: []const iovec_const) usize {
|
||||
/// atomicity guarantees no longer apply.
|
||||
///
|
||||
/// Copying begins reading at `in_offset`. The input file descriptor seek position is ignored and not updated.
|
||||
/// If the output file descriptor has a seek position, it is updated as bytes are written.
|
||||
/// If the output file descriptor has a seek position, it is updated as bytes are written. When
|
||||
/// `in_offset` is past the end of the input file, it successfully reads 0 bytes.
|
||||
///
|
||||
/// `flags` has different meanings per operating system; refer to the respective man pages.
|
||||
///
|
||||
@ -3527,7 +3615,7 @@ pub fn sendfile(
|
||||
out_fd: fd_t,
|
||||
in_fd: fd_t,
|
||||
in_offset: u64,
|
||||
count: usize,
|
||||
in_len: u64,
|
||||
headers: []const iovec_const,
|
||||
trailers: []const iovec_const,
|
||||
flags: u32,
|
||||
@ -3536,9 +3624,15 @@ pub fn sendfile(
|
||||
var total_written: usize = 0;
|
||||
|
||||
// Prevents EOVERFLOW.
|
||||
const size_t = @Type(std.builtin.TypeInfo{
|
||||
.Int = .{
|
||||
.is_signed = false,
|
||||
.bits = @typeInfo(usize).Int.bits - 1,
|
||||
},
|
||||
});
|
||||
const max_count = switch (std.Target.current.os.tag) {
|
||||
.linux => 0x7ffff000,
|
||||
else => math.maxInt(isize),
|
||||
else => math.maxInt(size_t),
|
||||
};
|
||||
|
||||
switch (std.Target.current.os.tag) {
|
||||
@ -3558,7 +3652,7 @@ pub fn sendfile(
|
||||
}
|
||||
|
||||
// Here we match BSD behavior, making a zero count value send as many bytes as possible.
|
||||
const adjusted_count = if (count == 0) max_count else math.min(count, max_count);
|
||||
const adjusted_count = if (in_len == 0) max_count else math.min(in_len, @as(size_t, max_count));
|
||||
|
||||
while (true) {
|
||||
var offset: off_t = @bitCast(off_t, in_offset);
|
||||
@ -3567,10 +3661,10 @@ pub fn sendfile(
|
||||
0 => {
|
||||
const amt = @bitCast(usize, rc);
|
||||
total_written += amt;
|
||||
if (count == 0 and amt == 0) {
|
||||
if (in_len == 0 and amt == 0) {
|
||||
// We have detected EOF from `in_fd`.
|
||||
break;
|
||||
} else if (amt < count) {
|
||||
} else if (amt < in_len) {
|
||||
return total_written;
|
||||
} else {
|
||||
break;
|
||||
@ -3636,7 +3730,7 @@ pub fn sendfile(
|
||||
hdtr = &hdtr_data;
|
||||
}
|
||||
|
||||
const adjusted_count = math.min(count, max_count);
|
||||
const adjusted_count = math.min(in_len, max_count);
|
||||
|
||||
while (true) {
|
||||
var sbytes: off_t = undefined;
|
||||
@ -3714,7 +3808,7 @@ pub fn sendfile(
|
||||
hdtr = &hdtr_data;
|
||||
}
|
||||
|
||||
const adjusted_count = math.min(count, @as(u63, max_count));
|
||||
const adjusted_count = math.min(in_len, @as(u63, max_count));
|
||||
|
||||
while (true) {
|
||||
var sbytes: off_t = adjusted_count;
|
||||
@ -3724,12 +3818,14 @@ pub fn sendfile(
|
||||
switch (err) {
|
||||
0 => return amt,
|
||||
|
||||
EBADF => unreachable, // Always a race condition.
|
||||
EFAULT => unreachable, // Segmentation fault.
|
||||
EINVAL => unreachable,
|
||||
ENOTCONN => unreachable, // `out_fd` is an unconnected socket.
|
||||
|
||||
ENOTSUP, ENOTSOCK, ENOSYS => break :sf,
|
||||
// On macOS version 10.14.6, I observed Darwin return EBADF when
|
||||
// using sendfile on a valid open file descriptor of a file
|
||||
// system file.
|
||||
ENOTSUP, ENOTSOCK, ENOSYS, EBADF => break :sf,
|
||||
|
||||
EINTR => if (amt != 0) return amt else continue,
|
||||
|
||||
@ -3768,10 +3864,10 @@ pub fn sendfile(
|
||||
rw: {
|
||||
var buf: [8 * 4096]u8 = undefined;
|
||||
// Here we match BSD behavior, making a zero count value send as many bytes as possible.
|
||||
const adjusted_count = if (count == 0) buf.len else math.min(buf.len, count);
|
||||
const adjusted_count = if (in_len == 0) buf.len else math.min(buf.len, in_len);
|
||||
const amt_read = try pread(in_fd, buf[0..adjusted_count], in_offset);
|
||||
if (amt_read == 0) {
|
||||
if (count == 0) {
|
||||
if (in_len == 0) {
|
||||
// We have detected EOF from `in_fd`.
|
||||
break :rw;
|
||||
} else {
|
||||
@ -3780,7 +3876,7 @@ pub fn sendfile(
|
||||
}
|
||||
const amt_written = try write(out_fd, buf[0..amt_read]);
|
||||
total_written += amt_written;
|
||||
if (amt_written < count or count == 0) return total_written;
|
||||
if (amt_written < in_len or in_len == 0) return total_written;
|
||||
}
|
||||
|
||||
if (trailers.len != 0) {
|
||||
|
||||
@ -76,6 +76,10 @@ pub fn chdir(path: [*:0]const u8) usize {
|
||||
return syscall1(SYS_chdir, @ptrToInt(path));
|
||||
}
|
||||
|
||||
pub fn fchdir(fd: fd_t) usize {
|
||||
return syscall1(SYS_fchdir, @bitCast(usize, @as(isize, fd)));
|
||||
}
|
||||
|
||||
pub fn chroot(path: [*:0]const u8) usize {
|
||||
return syscall1(SYS_chroot, @ptrToInt(path));
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@ const AtomicRmwOp = builtin.AtomicRmwOp;
|
||||
const AtomicOrder = builtin.AtomicOrder;
|
||||
|
||||
test "makePath, put some files in it, deleteTree" {
|
||||
try fs.makePath(a, "os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c");
|
||||
try fs.cwd().makePath("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c");
|
||||
try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c" ++ fs.path.sep_str ++ "file.txt", "nonsense");
|
||||
try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "file2.txt", "blah");
|
||||
try fs.deleteTree("os_test_tmp");
|
||||
@ -28,7 +28,7 @@ test "makePath, put some files in it, deleteTree" {
|
||||
}
|
||||
|
||||
test "access file" {
|
||||
try fs.makePath(a, "os_test_tmp");
|
||||
try fs.cwd().makePath("os_test_tmp");
|
||||
if (fs.cwd().access("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", .{})) |ok| {
|
||||
@panic("expected error");
|
||||
} else |err| {
|
||||
@ -45,7 +45,7 @@ fn testThreadIdFn(thread_id: *Thread.Id) void {
|
||||
}
|
||||
|
||||
test "sendfile" {
|
||||
try fs.makePath(a, "os_test_tmp");
|
||||
try fs.cwd().makePath("os_test_tmp");
|
||||
defer fs.deleteTree("os_test_tmp") catch {};
|
||||
|
||||
var dir = try fs.cwd().openDirList("os_test_tmp");
|
||||
@ -74,7 +74,9 @@ test "sendfile" {
|
||||
|
||||
const header1 = "header1\n";
|
||||
const header2 = "second header\n";
|
||||
var headers = [_]os.iovec_const{
|
||||
const trailer1 = "trailer1\n";
|
||||
const trailer2 = "second trailer\n";
|
||||
var hdtr = [_]os.iovec_const{
|
||||
.{
|
||||
.iov_base = header1,
|
||||
.iov_len = header1.len,
|
||||
@ -83,11 +85,6 @@ test "sendfile" {
|
||||
.iov_base = header2,
|
||||
.iov_len = header2.len,
|
||||
},
|
||||
};
|
||||
|
||||
const trailer1 = "trailer1\n";
|
||||
const trailer2 = "second trailer\n";
|
||||
var trailers = [_]os.iovec_const{
|
||||
.{
|
||||
.iov_base = trailer1,
|
||||
.iov_len = trailer1.len,
|
||||
@ -99,59 +96,16 @@ test "sendfile" {
|
||||
};
|
||||
|
||||
var written_buf: [header1.len + header2.len + 10 + trailer1.len + trailer2.len]u8 = undefined;
|
||||
try sendfileAll(dest_file.handle, src_file.handle, 1, 10, &headers, &trailers, 0);
|
||||
|
||||
try dest_file.writeFileAll(src_file, .{
|
||||
.in_offset = 1,
|
||||
.in_len = 10,
|
||||
.headers_and_trailers = &hdtr,
|
||||
.header_count = 2,
|
||||
});
|
||||
try dest_file.preadAll(&written_buf, 0);
|
||||
expect(mem.eql(u8, &written_buf, "header1\nsecond header\nine1\nsecontrailer1\nsecond trailer\n"));
|
||||
}
|
||||
|
||||
fn sendfileAll(
|
||||
out_fd: os.fd_t,
|
||||
in_fd: os.fd_t,
|
||||
offset: u64,
|
||||
count: usize,
|
||||
headers: []os.iovec_const,
|
||||
trailers: []os.iovec_const,
|
||||
flags: u32,
|
||||
) os.SendFileError!void {
|
||||
var amt: usize = undefined;
|
||||
hdrs: {
|
||||
var i: usize = 0;
|
||||
while (i < headers.len) {
|
||||
amt = try os.sendfile(out_fd, in_fd, offset, count, headers[i..], trailers, flags);
|
||||
while (amt >= headers[i].iov_len) {
|
||||
amt -= headers[i].iov_len;
|
||||
i += 1;
|
||||
if (i >= headers.len) break :hdrs;
|
||||
}
|
||||
headers[i].iov_base += amt;
|
||||
headers[i].iov_len -= amt;
|
||||
}
|
||||
}
|
||||
var off = amt;
|
||||
while (off < count) {
|
||||
amt = try os.sendfile(out_fd, in_fd, offset + off, count - off, &[0]os.iovec_const{}, trailers, flags);
|
||||
off += amt;
|
||||
}
|
||||
amt = off - count;
|
||||
var i: usize = 0;
|
||||
while (i < trailers.len) {
|
||||
while (amt >= headers[i].iov_len) {
|
||||
amt -= trailers[i].iov_len;
|
||||
i += 1;
|
||||
if (i >= trailers.len) return;
|
||||
}
|
||||
trailers[i].iov_base += amt;
|
||||
trailers[i].iov_len -= amt;
|
||||
if (std.Target.current.os.tag == .windows) {
|
||||
amt = try os.writev(out_fd, trailers[i..]);
|
||||
} else {
|
||||
// Here we must use send because it's the only way to give the flags.
|
||||
amt = try os.send(out_fd, trailers[i].iov_base[0..trailers[i].iov_len], flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test "std.Thread.getCurrentId" {
|
||||
if (builtin.single_threaded) return error.SkipZigTest;
|
||||
|
||||
|
||||
@ -337,7 +337,7 @@ pub fn GetQueuedCompletionStatus(
|
||||
}
|
||||
|
||||
pub fn CloseHandle(hObject: HANDLE) void {
|
||||
assert(kernel32.CloseHandle(hObject) != 0);
|
||||
assert(ntdll.NtClose(hObject) == .SUCCESS);
|
||||
}
|
||||
|
||||
pub fn FindClose(hFindFile: HANDLE) void {
|
||||
@ -586,23 +586,74 @@ pub fn MoveFileExW(old_path: [*:0]const u16, new_path: [*:0]const u16, flags: DW
|
||||
}
|
||||
|
||||
pub const CreateDirectoryError = error{
|
||||
NameTooLong,
|
||||
PathAlreadyExists,
|
||||
FileNotFound,
|
||||
NoDevice,
|
||||
AccessDenied,
|
||||
Unexpected,
|
||||
};
|
||||
|
||||
pub fn CreateDirectory(pathname: []const u8, attrs: ?*SECURITY_ATTRIBUTES) CreateDirectoryError!void {
|
||||
/// Returns an open directory handle which the caller is responsible for closing with `CloseHandle`.
|
||||
pub fn CreateDirectory(dir: ?HANDLE, pathname: []const u8, sa: ?*SECURITY_ATTRIBUTES) CreateDirectoryError!HANDLE {
|
||||
const pathname_w = try sliceToPrefixedFileW(pathname);
|
||||
return CreateDirectoryW(&pathname_w, attrs);
|
||||
return CreateDirectoryW(dir, &pathname_w, sa);
|
||||
}
|
||||
|
||||
pub fn CreateDirectoryW(pathname: [*:0]const u16, attrs: ?*SECURITY_ATTRIBUTES) CreateDirectoryError!void {
|
||||
if (kernel32.CreateDirectoryW(pathname, attrs) == 0) {
|
||||
switch (kernel32.GetLastError()) {
|
||||
.ALREADY_EXISTS => return error.PathAlreadyExists,
|
||||
.PATH_NOT_FOUND => return error.FileNotFound,
|
||||
else => |err| return unexpectedError(err),
|
||||
}
|
||||
/// Same as `CreateDirectory` except takes a WTF-16 encoded path.
|
||||
pub fn CreateDirectoryW(
|
||||
dir: ?HANDLE,
|
||||
sub_path_w: [*:0]const u16,
|
||||
sa: ?*SECURITY_ATTRIBUTES,
|
||||
) CreateDirectoryError!HANDLE {
|
||||
const path_len_bytes = math.cast(u16, mem.toSliceConst(u16, sub_path_w).len * 2) catch |err| switch (err) {
|
||||
error.Overflow => return error.NameTooLong,
|
||||
};
|
||||
var nt_name = UNICODE_STRING{
|
||||
.Length = path_len_bytes,
|
||||
.MaximumLength = path_len_bytes,
|
||||
.Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)),
|
||||
};
|
||||
|
||||
if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
|
||||
// Windows does not recognize this, but it does work with empty string.
|
||||
nt_name.Length = 0;
|
||||
}
|
||||
|
||||
var attr = OBJECT_ATTRIBUTES{
|
||||
.Length = @sizeOf(OBJECT_ATTRIBUTES),
|
||||
.RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sub_path_w)) null else dir,
|
||||
.Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
|
||||
.ObjectName = &nt_name,
|
||||
.SecurityDescriptor = if (sa) |ptr| ptr.lpSecurityDescriptor else null,
|
||||
.SecurityQualityOfService = null,
|
||||
};
|
||||
var io: IO_STATUS_BLOCK = undefined;
|
||||
var result_handle: HANDLE = undefined;
|
||||
const rc = ntdll.NtCreateFile(
|
||||
&result_handle,
|
||||
GENERIC_READ | SYNCHRONIZE,
|
||||
&attr,
|
||||
&io,
|
||||
null,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
FILE_SHARE_READ,
|
||||
FILE_CREATE,
|
||||
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
|
||||
null,
|
||||
0,
|
||||
);
|
||||
switch (rc) {
|
||||
.SUCCESS => return result_handle,
|
||||
.OBJECT_NAME_INVALID => unreachable,
|
||||
.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
|
||||
.OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
|
||||
.NO_MEDIA_IN_DEVICE => return error.NoDevice,
|
||||
.INVALID_PARAMETER => unreachable,
|
||||
.ACCESS_DENIED => return error.AccessDenied,
|
||||
.OBJECT_PATH_SYNTAX_BAD => unreachable,
|
||||
.OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
|
||||
else => return unexpectedStatus(rc),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1179,7 +1179,7 @@ pub const Compilation = struct {
|
||||
defer self.gpa().free(zig_dir_path);
|
||||
|
||||
const tmp_dir = try fs.path.join(self.arena(), &[_][]const u8{ zig_dir_path, comp_dir_name[0..] });
|
||||
try fs.makePath(self.gpa(), tmp_dir);
|
||||
try fs.cwd().makePath(tmp_dir);
|
||||
return tmp_dir;
|
||||
}
|
||||
|
||||
|
||||
@ -56,7 +56,7 @@ pub const TestContext = struct {
|
||||
self.zig_lib_dir = try introspect.resolveZigLibDir(allocator);
|
||||
errdefer allocator.free(self.zig_lib_dir);
|
||||
|
||||
try std.fs.makePath(allocator, tmp_dir_name);
|
||||
try std.fs.cwd().makePath(tmp_dir_name);
|
||||
errdefer std.fs.deleteTree(tmp_dir_name) catch {};
|
||||
}
|
||||
|
||||
@ -85,7 +85,7 @@ pub const TestContext = struct {
|
||||
const file1_path = try std.fs.path.join(allocator, [_][]const u8{ tmp_dir_name, file_index, file1 });
|
||||
|
||||
if (std.fs.path.dirname(file1_path)) |dirname| {
|
||||
try std.fs.makePath(allocator, dirname);
|
||||
try std.fs.cwd().makePath(dirname);
|
||||
}
|
||||
|
||||
// TODO async I/O
|
||||
@ -119,7 +119,7 @@ pub const TestContext = struct {
|
||||
|
||||
const output_file = try std.fmt.allocPrint(allocator, "{}-out{}", .{ file1_path, (Target{ .Native = {} }).exeFileExt() });
|
||||
if (std.fs.path.dirname(file1_path)) |dirname| {
|
||||
try std.fs.makePath(allocator, dirname);
|
||||
try std.fs.cwd().makePath(dirname);
|
||||
}
|
||||
|
||||
// TODO async I/O
|
||||
|
||||
@ -37,7 +37,7 @@ pub fn main() !void {
|
||||
};
|
||||
for (test_fns) |testFn| {
|
||||
try fs.deleteTree(dir_path);
|
||||
try fs.makeDir(dir_path);
|
||||
try fs.cwd().makeDir(dir_path);
|
||||
try testFn(zig_exe, dir_path);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user