| author | |
| committer | |
| log | f6f0b019bee7910702e113be6d33865c592afa2a |
| tree | 65cd5209cf69c03445f85bb244d15ca00bb51a32 |
| parent | 582db68a157520a0cf7777807f466d1f6a2e31e7 |
| parent | 1141bfb21b82f8d3fc353e968a591f2ad9aaa571 |
| signature |
improvements to std.fs, std.os14 files changed, 395 insertions(+), 196 deletions(-)
doc/docgen.zig+1-1| ... | @@ -50,7 +50,7 @@ pub fn main() !void { | ... | @@ -50,7 +50,7 @@ pub fn main() !void { |
| 50 | var tokenizer = Tokenizer.init(in_file_name, input_file_bytes); | 50 | var tokenizer = Tokenizer.init(in_file_name, input_file_bytes); |
| 51 | var toc = try genToc(allocator, &tokenizer); | 51 | var toc = try genToc(allocator, &tokenizer); |
| 52 | 52 | ||
| 53 | try fs.makePath(allocator, tmp_dir_name); | 53 | try fs.cwd().makePath(tmp_dir_name); |
| 54 | defer fs.deleteTree(tmp_dir_name) catch {}; | 54 | defer fs.deleteTree(tmp_dir_name) catch {}; |
| 55 | 55 | ||
| 56 | try genHtml(allocator, &tokenizer, &toc, &buffered_out_stream.stream, zig_exe); | 56 | try genHtml(allocator, &tokenizer, &toc, &buffered_out_stream.stream, zig_exe); |
lib/std/build.zig+2-2| ... | @@ -763,7 +763,7 @@ pub const Builder = struct { | ... | @@ -763,7 +763,7 @@ pub const Builder = struct { |
| 763 | } | 763 | } |
| 764 | 764 | ||
| 765 | pub fn makePath(self: *Builder, path: []const u8) !void { | 765 | pub fn makePath(self: *Builder, path: []const u8) !void { |
| 766 | fs.makePath(self.allocator, self.pathFromRoot(path)) catch |err| { | 766 | fs.cwd().makePath(self.pathFromRoot(path)) catch |err| { |
| 767 | warn("Unable to create path {}: {}\n", .{ path, @errorName(err) }); | 767 | warn("Unable to create path {}: {}\n", .{ path, @errorName(err) }); |
| 768 | return err; | 768 | return err; |
| 769 | }; | 769 | }; |
| ... | @@ -2311,7 +2311,7 @@ pub const InstallDirStep = struct { | ... | @@ -2311,7 +2311,7 @@ pub const InstallDirStep = struct { |
| 2311 | const rel_path = entry.path[full_src_dir.len + 1 ..]; | 2311 | const rel_path = entry.path[full_src_dir.len + 1 ..]; |
| 2312 | const dest_path = try fs.path.join(self.builder.allocator, &[_][]const u8{ dest_prefix, rel_path }); | 2312 | const dest_path = try fs.path.join(self.builder.allocator, &[_][]const u8{ dest_prefix, rel_path }); |
| 2313 | switch (entry.kind) { | 2313 | switch (entry.kind) { |
| 2314 | .Directory => try fs.makePath(self.builder.allocator, dest_path), | 2314 | .Directory => try fs.cwd().makePath(dest_path), |
| 2315 | .File => try self.builder.updateFile(entry.path, dest_path), | 2315 | .File => try self.builder.updateFile(entry.path, dest_path), |
| 2316 | else => continue, | 2316 | else => continue, |
| 2317 | } | 2317 | } |
lib/std/build/write_file.zig+1-1| ... | @@ -74,7 +74,7 @@ pub const WriteFileStep = struct { | ... | @@ -74,7 +74,7 @@ pub const WriteFileStep = struct { |
| 74 | &hash_basename, | 74 | &hash_basename, |
| 75 | }); | 75 | }); |
| 76 | // TODO replace with something like fs.makePathAndOpenDir | 76 | // TODO replace with something like fs.makePathAndOpenDir |
| 77 | fs.makePath(self.builder.allocator, self.output_dir) catch |err| { | 77 | fs.cwd().makePath(self.output_dir) catch |err| { |
| 78 | warn("unable to make path {}: {}\n", .{ self.output_dir, @errorName(err) }); | 78 | warn("unable to make path {}: {}\n", .{ self.output_dir, @errorName(err) }); |
| 79 | return err; | 79 | return err; |
| 80 | }; | 80 | }; |
lib/std/c.zig+3| ... | @@ -75,6 +75,7 @@ pub extern "c" fn isatty(fd: fd_t) c_int; | ... | @@ -75,6 +75,7 @@ pub extern "c" fn isatty(fd: fd_t) c_int; |
| 75 | pub extern "c" fn close(fd: fd_t) c_int; | 75 | pub extern "c" fn close(fd: fd_t) c_int; |
| 76 | pub extern "c" fn fstat(fd: fd_t, buf: *Stat) c_int; | 76 | pub extern "c" fn fstat(fd: fd_t, buf: *Stat) c_int; |
| 77 | pub extern "c" fn @"fstat$INODE64"(fd: fd_t, buf: *Stat) c_int; | 77 | pub extern "c" fn @"fstat$INODE64"(fd: fd_t, buf: *Stat) c_int; |
| 78 | pub extern "c" fn fstatat(dirfd: fd_t, path: [*:0]const u8, stat_buf: *Stat, flags: u32) c_int; | ||
| 78 | pub extern "c" fn lseek(fd: fd_t, offset: off_t, whence: c_int) off_t; | 79 | pub extern "c" fn lseek(fd: fd_t, offset: off_t, whence: c_int) off_t; |
| 79 | pub extern "c" fn open(path: [*:0]const u8, oflag: c_uint, ...) c_int; | 80 | pub extern "c" fn open(path: [*:0]const u8, oflag: c_uint, ...) c_int; |
| 80 | pub extern "c" fn openat(fd: c_int, path: [*:0]const u8, oflag: c_uint, ...) c_int; | 81 | 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 | ... | @@ -101,9 +102,11 @@ pub extern "c" fn faccessat(dirfd: fd_t, path: [*:0]const u8, mode: c_uint, flag |
| 101 | pub extern "c" fn pipe(fds: *[2]fd_t) c_int; | 102 | pub extern "c" fn pipe(fds: *[2]fd_t) c_int; |
| 102 | pub extern "c" fn pipe2(fds: *[2]fd_t, flags: u32) c_int; | 103 | pub extern "c" fn pipe2(fds: *[2]fd_t, flags: u32) c_int; |
| 103 | pub extern "c" fn mkdir(path: [*:0]const u8, mode: c_uint) c_int; | 104 | pub extern "c" fn mkdir(path: [*:0]const u8, mode: c_uint) c_int; |
| 105 | pub extern "c" fn mkdirat(dirfd: fd_t, path: [*:0]const u8, mode: u32) c_int; | ||
| 104 | pub extern "c" fn symlink(existing: [*:0]const u8, new: [*:0]const u8) c_int; | 106 | pub extern "c" fn symlink(existing: [*:0]const u8, new: [*:0]const u8) c_int; |
| 105 | pub extern "c" fn rename(old: [*:0]const u8, new: [*:0]const u8) c_int; | 107 | pub extern "c" fn rename(old: [*:0]const u8, new: [*:0]const u8) c_int; |
| 106 | pub extern "c" fn chdir(path: [*:0]const u8) c_int; | 108 | pub extern "c" fn chdir(path: [*:0]const u8) c_int; |
| 109 | pub extern "c" fn fchdir(fd: fd_t) c_int; | ||
| 107 | pub extern "c" fn execve(path: [*:0]const u8, argv: [*:null]const ?[*:0]const u8, envp: [*:null]const ?[*:0]const u8) c_int; | 110 | pub extern "c" fn execve(path: [*:0]const u8, argv: [*:null]const ?[*:0]const u8, envp: [*:null]const ?[*:0]const u8) c_int; |
| 108 | pub extern "c" fn dup(fd: fd_t) c_int; | 111 | pub extern "c" fn dup(fd: fd_t) c_int; |
| 109 | pub extern "c" fn dup2(old_fd: fd_t, new_fd: fd_t) c_int; | 112 | pub extern "c" fn dup2(old_fd: fd_t, new_fd: fd_t) c_int; |
lib/std/fs.zig+95-94| ... | @@ -123,47 +123,21 @@ pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?Fil | ... | @@ -123,47 +123,21 @@ pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?Fil |
| 123 | } | 123 | } |
| 124 | const actual_mode = mode orelse src_stat.mode; | 124 | const actual_mode = mode orelse src_stat.mode; |
| 125 | 125 | ||
| 126 | // TODO this logic could be made more efficient by calling makePath, once | 126 | if (path.dirname(dest_path)) |dirname| { |
| 127 | // that API does not require an allocator | 127 | try cwd().makePath(dirname); |
| 128 | var atomic_file = make_atomic_file: while (true) { | 128 | } |
| 129 | const af = AtomicFile.init(dest_path, actual_mode) catch |err| switch (err) { | ||
| 130 | error.FileNotFound => { | ||
| 131 | var p = dest_path; | ||
| 132 | while (path.dirname(p)) |dirname| { | ||
| 133 | makeDir(dirname) catch |e| switch (e) { | ||
| 134 | error.FileNotFound => { | ||
| 135 | p = dirname; | ||
| 136 | continue; | ||
| 137 | }, | ||
| 138 | else => return e, | ||
| 139 | }; | ||
| 140 | continue :make_atomic_file; | ||
| 141 | } else { | ||
| 142 | return err; | ||
| 143 | } | ||
| 144 | }, | ||
| 145 | else => |e| return e, | ||
| 146 | }; | ||
| 147 | break af; | ||
| 148 | } else unreachable; | ||
| 149 | defer atomic_file.deinit(); | ||
| 150 | 129 | ||
| 151 | const in_stream = &src_file.inStream().stream; | 130 | var atomic_file = try AtomicFile.init(dest_path, actual_mode); |
| 131 | defer atomic_file.deinit(); | ||
| 152 | 132 | ||
| 153 | var buf: [mem.page_size * 6]u8 = undefined; | 133 | try atomic_file.file.writeFileAll(src_file, .{ .in_len = src_stat.size }); |
| 154 | while (true) { | 134 | try atomic_file.file.updateTimes(src_stat.atime, src_stat.mtime); |
| 155 | const amt = try in_stream.readFull(buf[0..]); | 135 | try atomic_file.finish(); |
| 156 | try atomic_file.file.writeAll(buf[0..amt]); | 136 | return PrevStatus.stale; |
| 157 | if (amt != buf.len) { | ||
| 158 | try atomic_file.file.updateTimes(src_stat.atime, src_stat.mtime); | ||
| 159 | try atomic_file.finish(); | ||
| 160 | return PrevStatus.stale; | ||
| 161 | } | ||
| 162 | } | ||
| 163 | } | 137 | } |
| 164 | 138 | ||
| 165 | /// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is | 139 | /// Guaranteed to be atomic. |
| 166 | /// merged and readily available, | 140 | /// On Linux, until https://patchwork.kernel.org/patch/9636735/ is merged and readily available, |
| 167 | /// there is a possibility of power loss or application termination leaving temporary files present | 141 | /// there is a possibility of power loss or application termination leaving temporary files present |
| 168 | /// in the same directory as dest_path. | 142 | /// in the same directory as dest_path. |
| 169 | /// Destination file will have the same mode as the source file. | 143 | /// 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 | ... | @@ -207,6 +181,9 @@ pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.M |
| 207 | } | 181 | } |
| 208 | } | 182 | } |
| 209 | 183 | ||
| 184 | /// TODO update this API to avoid a getrandom syscall for every operation. It | ||
| 185 | /// should accept a random interface. | ||
| 186 | /// TODO rework this to integrate with Dir | ||
| 210 | pub const AtomicFile = struct { | 187 | pub const AtomicFile = struct { |
| 211 | file: File, | 188 | file: File, |
| 212 | tmp_path_buf: [MAX_PATH_BYTES]u8, | 189 | tmp_path_buf: [MAX_PATH_BYTES]u8, |
| ... | @@ -268,70 +245,42 @@ pub const AtomicFile = struct { | ... | @@ -268,70 +245,42 @@ pub const AtomicFile = struct { |
| 268 | 245 | ||
| 269 | pub fn finish(self: *AtomicFile) !void { | 246 | pub fn finish(self: *AtomicFile) !void { |
| 270 | assert(!self.finished); | 247 | assert(!self.finished); |
| 271 | self.file.close(); | 248 | if (std.Target.current.os.tag == .windows) { |
| 272 | self.finished = true; | ||
| 273 | if (builtin.os.tag == .windows) { | ||
| 274 | const dest_path_w = try os.windows.sliceToPrefixedFileW(self.dest_path); | 249 | const dest_path_w = try os.windows.sliceToPrefixedFileW(self.dest_path); |
| 275 | const tmp_path_w = try os.windows.cStrToPrefixedFileW(@ptrCast([*:0]u8, &self.tmp_path_buf)); | 250 | const tmp_path_w = try os.windows.cStrToPrefixedFileW(@ptrCast([*:0]u8, &self.tmp_path_buf)); |
| 251 | self.file.close(); | ||
| 252 | self.finished = true; | ||
| 276 | return os.renameW(&tmp_path_w, &dest_path_w); | 253 | return os.renameW(&tmp_path_w, &dest_path_w); |
| 254 | } else { | ||
| 255 | const dest_path_c = try os.toPosixPath(self.dest_path); | ||
| 256 | self.file.close(); | ||
| 257 | self.finished = true; | ||
| 258 | return os.renameC(@ptrCast([*:0]u8, &self.tmp_path_buf), &dest_path_c); | ||
| 277 | } | 259 | } |
| 278 | const dest_path_c = try os.toPosixPath(self.dest_path); | ||
| 279 | return os.renameC(@ptrCast([*:0]u8, &self.tmp_path_buf), &dest_path_c); | ||
| 280 | } | 260 | } |
| 281 | }; | 261 | }; |
| 282 | 262 | ||
| 283 | const default_new_dir_mode = 0o755; | 263 | const default_new_dir_mode = 0o755; |
| 284 | 264 | ||
| 285 | /// Create a new directory. | 265 | /// Create a new directory, based on an absolute path. |
| 286 | pub fn makeDir(dir_path: []const u8) !void { | 266 | /// Asserts that the path is absolute. See `Dir.makeDir` for a function that operates |
| 287 | return os.mkdir(dir_path, default_new_dir_mode); | 267 | /// on both absolute and relative paths. |
| 268 | pub fn makeDirAbsolute(absolute_path: []const u8) !void { | ||
| 269 | assert(path.isAbsoluteC(absolute_path)); | ||
| 270 | return os.mkdir(absolute_path, default_new_dir_mode); | ||
| 288 | } | 271 | } |
| 289 | 272 | ||
| 290 | /// Same as `makeDir` except the parameter is a null-terminated UTF8-encoded string. | 273 | /// Same as `makeDirAbsolute` except the parameter is a null-terminated UTF8-encoded string. |
| 291 | pub fn makeDirC(dir_path: [*:0]const u8) !void { | 274 | pub fn makeDirAbsoluteZ(absolute_path_z: [*:0]const u8) !void { |
| 292 | return os.mkdirC(dir_path, default_new_dir_mode); | 275 | assert(path.isAbsoluteC(absolute_path_z)); |
| 276 | return os.mkdirZ(absolute_path_z, default_new_dir_mode); | ||
| 293 | } | 277 | } |
| 294 | 278 | ||
| 295 | /// Same as `makeDir` except the parameter is a null-terminated UTF16LE-encoded string. | 279 | /// Same as `makeDirAbsolute` except the parameter is a null-terminated WTF-16 encoded string. |
| 296 | pub fn makeDirW(dir_path: [*:0]const u16) !void { | 280 | pub fn makeDirAbsoluteW(absolute_path_w: [*:0]const u16) !void { |
| 297 | return os.mkdirW(dir_path, default_new_dir_mode); | 281 | assert(path.isAbsoluteWindowsW(absolute_path_w)); |
| 298 | } | 282 | const handle = try os.windows.CreateDirectoryW(null, absolute_path_w, null); |
| 299 | 283 | os.windows.CloseHandle(handle); | |
| 300 | /// Calls makeDir recursively to make an entire path. Returns success if the path | ||
| 301 | /// already exists and is a directory. | ||
| 302 | /// This function is not atomic, and if it returns an error, the file system may | ||
| 303 | /// have been modified regardless. | ||
| 304 | /// TODO determine if we can remove the allocator requirement from this function | ||
| 305 | pub fn makePath(allocator: *Allocator, full_path: []const u8) !void { | ||
| 306 | const resolved_path = try path.resolve(allocator, &[_][]const u8{full_path}); | ||
| 307 | defer allocator.free(resolved_path); | ||
| 308 | |||
| 309 | var end_index: usize = resolved_path.len; | ||
| 310 | while (true) { | ||
| 311 | makeDir(resolved_path[0..end_index]) catch |err| switch (err) { | ||
| 312 | error.PathAlreadyExists => { | ||
| 313 | // TODO stat the file and return an error if it's not a directory | ||
| 314 | // this is important because otherwise a dangling symlink | ||
| 315 | // could cause an infinite loop | ||
| 316 | if (end_index == resolved_path.len) return; | ||
| 317 | }, | ||
| 318 | error.FileNotFound => { | ||
| 319 | // march end_index backward until next path component | ||
| 320 | while (true) { | ||
| 321 | end_index -= 1; | ||
| 322 | if (path.isSep(resolved_path[end_index])) break; | ||
| 323 | } | ||
| 324 | continue; | ||
| 325 | }, | ||
| 326 | else => return err, | ||
| 327 | }; | ||
| 328 | if (end_index == resolved_path.len) return; | ||
| 329 | // march end_index forward until next path component | ||
| 330 | while (true) { | ||
| 331 | end_index += 1; | ||
| 332 | if (end_index == resolved_path.len or path.isSep(resolved_path[end_index])) break; | ||
| 333 | } | ||
| 334 | } | ||
| 335 | } | 284 | } |
| 336 | 285 | ||
| 337 | /// Returns `error.DirNotEmpty` if the directory is not empty. | 286 | /// Returns `error.DirNotEmpty` if the directory is not empty. |
| ... | @@ -709,7 +658,6 @@ pub const Dir = struct { | ... | @@ -709,7 +658,6 @@ pub const Dir = struct { |
| 709 | /// Call `File.close` to release the resource. | 658 | /// Call `File.close` to release the resource. |
| 710 | /// Asserts that the path parameter has no null bytes. | 659 | /// Asserts that the path parameter has no null bytes. |
| 711 | pub fn openFile(self: Dir, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File { | 660 | pub fn openFile(self: Dir, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File { |
| 712 | if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0); | ||
| 713 | if (builtin.os.tag == .windows) { | 661 | if (builtin.os.tag == .windows) { |
| 714 | const path_w = try os.windows.sliceToPrefixedFileW(sub_path); | 662 | const path_w = try os.windows.sliceToPrefixedFileW(sub_path); |
| 715 | return self.openFileW(&path_w, flags); | 663 | return self.openFileW(&path_w, flags); |
| ... | @@ -759,7 +707,6 @@ pub const Dir = struct { | ... | @@ -759,7 +707,6 @@ pub const Dir = struct { |
| 759 | /// Call `File.close` on the result when done. | 707 | /// Call `File.close` on the result when done. |
| 760 | /// Asserts that the path parameter has no null bytes. | 708 | /// Asserts that the path parameter has no null bytes. |
| 761 | pub fn createFile(self: Dir, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File { | 709 | pub fn createFile(self: Dir, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File { |
| 762 | if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0); | ||
| 763 | if (builtin.os.tag == .windows) { | 710 | if (builtin.os.tag == .windows) { |
| 764 | const path_w = try os.windows.sliceToPrefixedFileW(sub_path); | 711 | const path_w = try os.windows.sliceToPrefixedFileW(sub_path); |
| 765 | return self.createFileW(&path_w, flags); | 712 | return self.createFileW(&path_w, flags); |
| ... | @@ -882,6 +829,64 @@ pub const Dir = struct { | ... | @@ -882,6 +829,64 @@ pub const Dir = struct { |
| 882 | } | 829 | } |
| 883 | } | 830 | } |
| 884 | 831 | ||
| 832 | pub fn makeDir(self: Dir, sub_path: []const u8) !void { | ||
| 833 | try os.mkdirat(self.fd, sub_path, default_new_dir_mode); | ||
| 834 | } | ||
| 835 | |||
| 836 | pub fn makeDirZ(self: Dir, sub_path: [*:0]const u8) !void { | ||
| 837 | try os.mkdiratC(self.fd, sub_path, default_new_dir_mode); | ||
| 838 | } | ||
| 839 | |||
| 840 | pub fn makeDirW(self: Dir, sub_path: [*:0]const u16) !void { | ||
| 841 | const handle = try os.windows.CreateDirectoryW(self.fd, sub_path, null); | ||
| 842 | os.windows.CloseHandle(handle); | ||
| 843 | } | ||
| 844 | |||
| 845 | /// Calls makeDir recursively to make an entire path. Returns success if the path | ||
| 846 | /// already exists and is a directory. | ||
| 847 | /// This function is not atomic, and if it returns an error, the file system may | ||
| 848 | /// have been modified regardless. | ||
| 849 | pub fn makePath(self: Dir, sub_path: []const u8) !void { | ||
| 850 | var end_index: usize = sub_path.len; | ||
| 851 | while (true) { | ||
| 852 | self.makeDir(sub_path[0..end_index]) catch |err| switch (err) { | ||
| 853 | error.PathAlreadyExists => { | ||
| 854 | // TODO stat the file and return an error if it's not a directory | ||
| 855 | // this is important because otherwise a dangling symlink | ||
| 856 | // could cause an infinite loop | ||
| 857 | if (end_index == sub_path.len) return; | ||
| 858 | }, | ||
| 859 | error.FileNotFound => { | ||
| 860 | if (end_index == 0) return err; | ||
| 861 | // march end_index backward until next path component | ||
| 862 | while (true) { | ||
| 863 | end_index -= 1; | ||
| 864 | if (path.isSep(sub_path[end_index])) break; | ||
| 865 | } | ||
| 866 | continue; | ||
| 867 | }, | ||
| 868 | else => return err, | ||
| 869 | }; | ||
| 870 | if (end_index == sub_path.len) return; | ||
| 871 | // march end_index forward until next path component | ||
| 872 | while (true) { | ||
| 873 | end_index += 1; | ||
| 874 | if (end_index == sub_path.len or path.isSep(sub_path[end_index])) break; | ||
| 875 | } | ||
| 876 | } | ||
| 877 | } | ||
| 878 | |||
| 879 | /// Changes the current working directory to the open directory handle. | ||
| 880 | /// This modifies global state and can have surprising effects in multi- | ||
| 881 | /// threaded applications. Most applications and especially libraries should | ||
| 882 | /// not call this function as a general rule, however it can have use cases | ||
| 883 | /// in, for example, implementing a shell, or child process execution. | ||
| 884 | /// Not all targets support this. For example, WASI does not have the concept | ||
| 885 | /// of a current working directory. | ||
| 886 | pub fn setAsCwd(self: Dir) !void { | ||
| 887 | try os.fchdir(self.fd); | ||
| 888 | } | ||
| 889 | |||
| 885 | /// Deprecated; call `openDirList` directly. | 890 | /// Deprecated; call `openDirList` directly. |
| 886 | pub fn openDir(self: Dir, sub_path: []const u8) OpenError!Dir { | 891 | pub fn openDir(self: Dir, sub_path: []const u8) OpenError!Dir { |
| 887 | return self.openDirList(sub_path); | 892 | return self.openDirList(sub_path); |
| ... | @@ -900,7 +905,6 @@ pub const Dir = struct { | ... | @@ -900,7 +905,6 @@ pub const Dir = struct { |
| 900 | /// | 905 | /// |
| 901 | /// Asserts that the path parameter has no null bytes. | 906 | /// Asserts that the path parameter has no null bytes. |
| 902 | pub fn openDirTraverse(self: Dir, sub_path: []const u8) OpenError!Dir { | 907 | pub fn openDirTraverse(self: Dir, sub_path: []const u8) OpenError!Dir { |
| 903 | if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0); | ||
| 904 | if (builtin.os.tag == .windows) { | 908 | if (builtin.os.tag == .windows) { |
| 905 | const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); | 909 | const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); |
| 906 | return self.openDirTraverseW(&sub_path_w); | 910 | return self.openDirTraverseW(&sub_path_w); |
| ... | @@ -918,7 +922,6 @@ pub const Dir = struct { | ... | @@ -918,7 +922,6 @@ pub const Dir = struct { |
| 918 | /// | 922 | /// |
| 919 | /// Asserts that the path parameter has no null bytes. | 923 | /// Asserts that the path parameter has no null bytes. |
| 920 | pub fn openDirList(self: Dir, sub_path: []const u8) OpenError!Dir { | 924 | pub fn openDirList(self: Dir, sub_path: []const u8) OpenError!Dir { |
| 921 | if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0); | ||
| 922 | if (builtin.os.tag == .windows) { | 925 | if (builtin.os.tag == .windows) { |
| 923 | const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); | 926 | const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); |
| 924 | return self.openDirListW(&sub_path_w); | 927 | return self.openDirListW(&sub_path_w); |
| ... | @@ -1082,7 +1085,6 @@ pub const Dir = struct { | ... | @@ -1082,7 +1085,6 @@ pub const Dir = struct { |
| 1082 | /// To delete a directory recursively, see `deleteTree`. | 1085 | /// To delete a directory recursively, see `deleteTree`. |
| 1083 | /// Asserts that the path parameter has no null bytes. | 1086 | /// Asserts that the path parameter has no null bytes. |
| 1084 | pub fn deleteDir(self: Dir, sub_path: []const u8) DeleteDirError!void { | 1087 | pub fn deleteDir(self: Dir, sub_path: []const u8) DeleteDirError!void { |
| 1085 | if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0); | ||
| 1086 | if (builtin.os.tag == .windows) { | 1088 | if (builtin.os.tag == .windows) { |
| 1087 | const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); | 1089 | const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); |
| 1088 | return self.deleteDirW(&sub_path_w); | 1090 | return self.deleteDirW(&sub_path_w); |
| ... | @@ -1112,7 +1114,6 @@ pub const Dir = struct { | ... | @@ -1112,7 +1114,6 @@ pub const Dir = struct { |
| 1112 | /// The return value is a slice of `buffer`, from index `0`. | 1114 | /// The return value is a slice of `buffer`, from index `0`. |
| 1113 | /// Asserts that the path parameter has no null bytes. | 1115 | /// Asserts that the path parameter has no null bytes. |
| 1114 | pub fn readLink(self: Dir, sub_path: []const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 { | 1116 | pub fn readLink(self: Dir, sub_path: []const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 { |
| 1115 | if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0); | ||
| 1116 | const sub_path_c = try os.toPosixPath(sub_path); | 1117 | const sub_path_c = try os.toPosixPath(sub_path); |
| 1117 | return self.readLinkC(&sub_path_c, buffer); | 1118 | return self.readLinkC(&sub_path_c, buffer); |
| 1118 | } | 1119 | } |
lib/std/fs/file.zig+91| ... | @@ -271,6 +271,8 @@ pub const File = struct { | ... | @@ -271,6 +271,8 @@ pub const File = struct { |
| 271 | /// The `iovecs` parameter is mutable because this function needs to mutate the fields in | 271 | /// The `iovecs` parameter is mutable because this function needs to mutate the fields in |
| 272 | /// order to handle partial reads from the underlying OS layer. | 272 | /// order to handle partial reads from the underlying OS layer. |
| 273 | pub fn readvAll(self: File, iovecs: []os.iovec) ReadError!void { | 273 | pub fn readvAll(self: File, iovecs: []os.iovec) ReadError!void { |
| 274 | if (iovecs.len == 0) return; | ||
| 275 | |||
| 274 | var i: usize = 0; | 276 | var i: usize = 0; |
| 275 | while (true) { | 277 | while (true) { |
| 276 | var amt = try self.readv(iovecs[i..]); | 278 | var amt = try self.readv(iovecs[i..]); |
| ... | @@ -295,6 +297,8 @@ pub const File = struct { | ... | @@ -295,6 +297,8 @@ pub const File = struct { |
| 295 | /// The `iovecs` parameter is mutable because this function needs to mutate the fields in | 297 | /// The `iovecs` parameter is mutable because this function needs to mutate the fields in |
| 296 | /// order to handle partial reads from the underlying OS layer. | 298 | /// order to handle partial reads from the underlying OS layer. |
| 297 | pub fn preadvAll(self: File, iovecs: []const os.iovec, offset: u64) PReadError!void { | 299 | pub fn preadvAll(self: File, iovecs: []const os.iovec, offset: u64) PReadError!void { |
| 300 | if (iovecs.len == 0) return; | ||
| 301 | |||
| 298 | var i: usize = 0; | 302 | var i: usize = 0; |
| 299 | var off: usize = 0; | 303 | var off: usize = 0; |
| 300 | while (true) { | 304 | while (true) { |
| ... | @@ -354,6 +358,8 @@ pub const File = struct { | ... | @@ -354,6 +358,8 @@ pub const File = struct { |
| 354 | /// The `iovecs` parameter is mutable because this function needs to mutate the fields in | 358 | /// The `iovecs` parameter is mutable because this function needs to mutate the fields in |
| 355 | /// order to handle partial writes from the underlying OS layer. | 359 | /// order to handle partial writes from the underlying OS layer. |
| 356 | pub fn writevAll(self: File, iovecs: []os.iovec_const) WriteError!void { | 360 | pub fn writevAll(self: File, iovecs: []os.iovec_const) WriteError!void { |
| 361 | if (iovecs.len == 0) return; | ||
| 362 | |||
| 357 | var i: usize = 0; | 363 | var i: usize = 0; |
| 358 | while (true) { | 364 | while (true) { |
| 359 | var amt = try self.writev(iovecs[i..]); | 365 | var amt = try self.writev(iovecs[i..]); |
| ... | @@ -378,6 +384,8 @@ pub const File = struct { | ... | @@ -378,6 +384,8 @@ pub const File = struct { |
| 378 | /// The `iovecs` parameter is mutable because this function needs to mutate the fields in | 384 | /// The `iovecs` parameter is mutable because this function needs to mutate the fields in |
| 379 | /// order to handle partial writes from the underlying OS layer. | 385 | /// order to handle partial writes from the underlying OS layer. |
| 380 | pub fn pwritevAll(self: File, iovecs: []os.iovec_const, offset: usize) PWriteError!void { | 386 | pub fn pwritevAll(self: File, iovecs: []os.iovec_const, offset: usize) PWriteError!void { |
| 387 | if (iovecs.len == 0) return; | ||
| 388 | |||
| 381 | var i: usize = 0; | 389 | var i: usize = 0; |
| 382 | var off: usize = 0; | 390 | var off: usize = 0; |
| 383 | while (true) { | 391 | while (true) { |
| ... | @@ -393,6 +401,89 @@ pub const File = struct { | ... | @@ -393,6 +401,89 @@ pub const File = struct { |
| 393 | } | 401 | } |
| 394 | } | 402 | } |
| 395 | 403 | ||
| 404 | pub const WriteFileOptions = struct { | ||
| 405 | in_offset: u64 = 0, | ||
| 406 | |||
| 407 | /// `null` means the entire file. `0` means no bytes from the file. | ||
| 408 | /// When this is `null`, trailers must be sent in a separate writev() call | ||
| 409 | /// due to a flaw in the BSD sendfile API. Other operating systems, such as | ||
| 410 | /// Linux, already do this anyway due to API limitations. | ||
| 411 | /// If the size of the source file is known, passing the size here will save one syscall. | ||
| 412 | in_len: ?u64 = null, | ||
| 413 | |||
| 414 | headers_and_trailers: []os.iovec_const = &[0]os.iovec_const{}, | ||
| 415 | |||
| 416 | /// The trailer count is inferred from `headers_and_trailers.len - header_count` | ||
| 417 | header_count: usize = 0, | ||
| 418 | }; | ||
| 419 | |||
| 420 | pub const WriteFileError = os.SendFileError; | ||
| 421 | |||
| 422 | /// TODO integrate with async I/O | ||
| 423 | pub fn writeFileAll(self: File, in_file: File, args: WriteFileOptions) WriteFileError!void { | ||
| 424 | const count = blk: { | ||
| 425 | if (args.in_len) |l| { | ||
| 426 | if (l == 0) { | ||
| 427 | return self.writevAll(args.headers_and_trailers); | ||
| 428 | } else { | ||
| 429 | break :blk l; | ||
| 430 | } | ||
| 431 | } else { | ||
| 432 | break :blk 0; | ||
| 433 | } | ||
| 434 | }; | ||
| 435 | const headers = args.headers_and_trailers[0..args.header_count]; | ||
| 436 | const trailers = args.headers_and_trailers[args.header_count..]; | ||
| 437 | const zero_iovec = &[0]os.iovec_const{}; | ||
| 438 | // When reading the whole file, we cannot put the trailers in the sendfile() syscall, | ||
| 439 | // because we have no way to determine whether a partial write is past the end of the file or not. | ||
| 440 | const trls = if (count == 0) zero_iovec else trailers; | ||
| 441 | const offset = args.in_offset; | ||
| 442 | const out_fd = self.handle; | ||
| 443 | const in_fd = in_file.handle; | ||
| 444 | const flags = 0; | ||
| 445 | var amt: usize = 0; | ||
| 446 | hdrs: { | ||
| 447 | var i: usize = 0; | ||
| 448 | while (i < headers.len) { | ||
| 449 | amt = try os.sendfile(out_fd, in_fd, offset, count, headers[i..], trls, flags); | ||
| 450 | while (amt >= headers[i].iov_len) { | ||
| 451 | amt -= headers[i].iov_len; | ||
| 452 | i += 1; | ||
| 453 | if (i >= headers.len) break :hdrs; | ||
| 454 | } | ||
| 455 | headers[i].iov_base += amt; | ||
| 456 | headers[i].iov_len -= amt; | ||
| 457 | } | ||
| 458 | } | ||
| 459 | if (count == 0) { | ||
| 460 | var off: u64 = amt; | ||
| 461 | while (true) { | ||
| 462 | amt = try os.sendfile(out_fd, in_fd, offset + off, 0, zero_iovec, zero_iovec, flags); | ||
| 463 | if (amt == 0) break; | ||
| 464 | off += amt; | ||
| 465 | } | ||
| 466 | } else { | ||
| 467 | var off: u64 = amt; | ||
| 468 | while (off < count) { | ||
| 469 | amt = try os.sendfile(out_fd, in_fd, offset + off, count - off, zero_iovec, trailers, flags); | ||
| 470 | off += amt; | ||
| 471 | } | ||
| 472 | amt = @intCast(usize, off - count); | ||
| 473 | } | ||
| 474 | var i: usize = 0; | ||
| 475 | while (i < trailers.len) { | ||
| 476 | while (amt >= headers[i].iov_len) { | ||
| 477 | amt -= trailers[i].iov_len; | ||
| 478 | i += 1; | ||
| 479 | if (i >= trailers.len) return; | ||
| 480 | } | ||
| 481 | trailers[i].iov_base += amt; | ||
| 482 | trailers[i].iov_len -= amt; | ||
| 483 | amt = try os.writev(self.handle, trailers[i..]); | ||
| 484 | } | ||
| 485 | } | ||
| 486 | |||
| 396 | pub fn inStream(file: File) InStream { | 487 | pub fn inStream(file: File) InStream { |
| 397 | return InStream{ | 488 | return InStream{ |
| 398 | .file = file, | 489 | .file = file, |
lib/std/fs/watch.zig+2-3| ... | @@ -618,11 +618,10 @@ test "write a file, watch it, write it again" { | ... | @@ -618,11 +618,10 @@ test "write a file, watch it, write it again" { |
| 618 | // TODO re-enable this test | 618 | // TODO re-enable this test |
| 619 | if (true) return error.SkipZigTest; | 619 | if (true) return error.SkipZigTest; |
| 620 | 620 | ||
| 621 | const allocator = std.heap.page_allocator; | 621 | try fs.cwd().makePath(test_tmp_dir); |
| 622 | |||
| 623 | try os.makePath(allocator, test_tmp_dir); | ||
| 624 | defer os.deleteTree(test_tmp_dir) catch {}; | 622 | defer os.deleteTree(test_tmp_dir) catch {}; |
| 625 | 623 | ||
| 624 | const allocator = std.heap.page_allocator; | ||
| 626 | return testFsWatch(&allocator); | 625 | return testFsWatch(&allocator); |
| 627 | } | 626 | } |
| 628 | 627 |
lib/std/os.zig+118-22| ... | @@ -1355,7 +1355,6 @@ pub const UnlinkatError = UnlinkError || error{ | ... | @@ -1355,7 +1355,6 @@ pub const UnlinkatError = UnlinkError || error{ |
| 1355 | /// Delete a file name and possibly the file it refers to, based on an open directory handle. | 1355 | /// Delete a file name and possibly the file it refers to, based on an open directory handle. |
| 1356 | /// Asserts that the path parameter has no null bytes. | 1356 | /// Asserts that the path parameter has no null bytes. |
| 1357 | pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void { | 1357 | pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void { |
| 1358 | if (std.debug.runtime_safety) for (file_path) |byte| assert(byte != 0); | ||
| 1359 | if (builtin.os.tag == .windows) { | 1358 | if (builtin.os.tag == .windows) { |
| 1360 | const file_path_w = try windows.sliceToPrefixedFileW(file_path); | 1359 | const file_path_w = try windows.sliceToPrefixedFileW(file_path); |
| 1361 | return unlinkatW(dirfd, &file_path_w, flags); | 1360 | return unlinkatW(dirfd, &file_path_w, flags); |
| ... | @@ -1540,25 +1539,69 @@ pub const MakeDirError = error{ | ... | @@ -1540,25 +1539,69 @@ pub const MakeDirError = error{ |
| 1540 | ReadOnlyFileSystem, | 1539 | ReadOnlyFileSystem, |
| 1541 | InvalidUtf8, | 1540 | InvalidUtf8, |
| 1542 | BadPathName, | 1541 | BadPathName, |
| 1542 | NoDevice, | ||
| 1543 | } || UnexpectedError; | 1543 | } || UnexpectedError; |
| 1544 | 1544 | ||
| 1545 | pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!void { | ||
| 1546 | if (builtin.os.tag == .windows) { | ||
| 1547 | const sub_dir_path_w = try windows.sliceToPrefixedFileW(sub_dir_path); | ||
| 1548 | return mkdiratW(dir_fd, &sub_dir_path_w, mode); | ||
| 1549 | } else { | ||
| 1550 | const sub_dir_path_c = try toPosixPath(sub_dir_path); | ||
| 1551 | return mkdiratC(dir_fd, &sub_dir_path_c, mode); | ||
| 1552 | } | ||
| 1553 | } | ||
| 1554 | |||
| 1555 | pub fn mkdiratC(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: u32) MakeDirError!void { | ||
| 1556 | if (builtin.os.tag == .windows) { | ||
| 1557 | const sub_dir_path_w = try windows.cStrToPrefixedFileW(sub_dir_path); | ||
| 1558 | return mkdiratW(dir_fd, &sub_dir_path_w, mode); | ||
| 1559 | } | ||
| 1560 | switch (errno(system.mkdirat(dir_fd, sub_dir_path, mode))) { | ||
| 1561 | 0 => return, | ||
| 1562 | EACCES => return error.AccessDenied, | ||
| 1563 | EBADF => unreachable, | ||
| 1564 | EPERM => return error.AccessDenied, | ||
| 1565 | EDQUOT => return error.DiskQuota, | ||
| 1566 | EEXIST => return error.PathAlreadyExists, | ||
| 1567 | EFAULT => unreachable, | ||
| 1568 | ELOOP => return error.SymLinkLoop, | ||
| 1569 | EMLINK => return error.LinkQuotaExceeded, | ||
| 1570 | ENAMETOOLONG => return error.NameTooLong, | ||
| 1571 | ENOENT => return error.FileNotFound, | ||
| 1572 | ENOMEM => return error.SystemResources, | ||
| 1573 | ENOSPC => return error.NoSpaceLeft, | ||
| 1574 | ENOTDIR => return error.NotDir, | ||
| 1575 | EROFS => return error.ReadOnlyFileSystem, | ||
| 1576 | else => |err| return unexpectedErrno(err), | ||
| 1577 | } | ||
| 1578 | } | ||
| 1579 | |||
| 1580 | pub fn mkdiratW(dir_fd: fd_t, sub_path_w: [*:0]const u16, mode: u32) MakeDirError!void { | ||
| 1581 | const sub_dir_handle = try windows.CreateDirectoryW(dir_fd, sub_path_w, null); | ||
| 1582 | windows.CloseHandle(sub_dir_handle); | ||
| 1583 | } | ||
| 1584 | |||
| 1545 | /// Create a directory. | 1585 | /// Create a directory. |
| 1546 | /// `mode` is ignored on Windows. | 1586 | /// `mode` is ignored on Windows. |
| 1547 | pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void { | 1587 | pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void { |
| 1548 | if (builtin.os.tag == .windows) { | 1588 | if (builtin.os.tag == .windows) { |
| 1549 | const dir_path_w = try windows.sliceToPrefixedFileW(dir_path); | 1589 | const sub_dir_handle = try windows.CreateDirectory(null, dir_path, null); |
| 1550 | return windows.CreateDirectoryW(&dir_path_w, null); | 1590 | windows.CloseHandle(sub_dir_handle); |
| 1591 | return; | ||
| 1551 | } else { | 1592 | } else { |
| 1552 | const dir_path_c = try toPosixPath(dir_path); | 1593 | const dir_path_c = try toPosixPath(dir_path); |
| 1553 | return mkdirC(&dir_path_c, mode); | 1594 | return mkdirZ(&dir_path_c, mode); |
| 1554 | } | 1595 | } |
| 1555 | } | 1596 | } |
| 1556 | 1597 | ||
| 1557 | /// Same as `mkdir` but the parameter is a null-terminated UTF8-encoded string. | 1598 | /// Same as `mkdir` but the parameter is a null-terminated UTF8-encoded string. |
| 1558 | pub fn mkdirC(dir_path: [*:0]const u8, mode: u32) MakeDirError!void { | 1599 | pub fn mkdirZ(dir_path: [*:0]const u8, mode: u32) MakeDirError!void { |
| 1559 | if (builtin.os.tag == .windows) { | 1600 | if (builtin.os.tag == .windows) { |
| 1560 | const dir_path_w = try windows.cStrToPrefixedFileW(dir_path); | 1601 | const dir_path_w = try windows.cStrToPrefixedFileW(dir_path); |
| 1561 | return windows.CreateDirectoryW(&dir_path_w, null); | 1602 | const sub_dir_handle = try windows.CreateDirectoryW(null, &dir_path_w, null); |
| 1603 | windows.CloseHandle(sub_dir_handle); | ||
| 1604 | return; | ||
| 1562 | } | 1605 | } |
| 1563 | switch (errno(system.mkdir(dir_path, mode))) { | 1606 | switch (errno(system.mkdir(dir_path, mode))) { |
| 1564 | 0 => return, | 1607 | 0 => return, |
| ... | @@ -1671,6 +1714,26 @@ pub fn chdirC(dir_path: [*:0]const u8) ChangeCurDirError!void { | ... | @@ -1671,6 +1714,26 @@ pub fn chdirC(dir_path: [*:0]const u8) ChangeCurDirError!void { |
| 1671 | } | 1714 | } |
| 1672 | } | 1715 | } |
| 1673 | 1716 | ||
| 1717 | pub const FchdirError = error{ | ||
| 1718 | AccessDenied, | ||
| 1719 | NotDir, | ||
| 1720 | FileSystem, | ||
| 1721 | } || UnexpectedError; | ||
| 1722 | |||
| 1723 | pub fn fchdir(dirfd: fd_t) FchdirError!void { | ||
| 1724 | while (true) { | ||
| 1725 | switch (errno(system.fchdir(dirfd))) { | ||
| 1726 | 0 => return, | ||
| 1727 | EACCES => return error.AccessDenied, | ||
| 1728 | EBADF => unreachable, | ||
| 1729 | ENOTDIR => return error.NotDir, | ||
| 1730 | EINTR => continue, | ||
| 1731 | EIO => return error.FileSystem, | ||
| 1732 | else => |err| return unexpectedErrno(err), | ||
| 1733 | } | ||
| 1734 | } | ||
| 1735 | } | ||
| 1736 | |||
| 1674 | pub const ReadLinkError = error{ | 1737 | pub const ReadLinkError = error{ |
| 1675 | AccessDenied, | 1738 | AccessDenied, |
| 1676 | FileSystem, | 1739 | FileSystem, |
| ... | @@ -2322,6 +2385,29 @@ pub fn fstat(fd: fd_t) FStatError!Stat { | ... | @@ -2322,6 +2385,29 @@ pub fn fstat(fd: fd_t) FStatError!Stat { |
| 2322 | } | 2385 | } |
| 2323 | } | 2386 | } |
| 2324 | 2387 | ||
| 2388 | const FStatAtError = FStatError || error{NameTooLong}; | ||
| 2389 | |||
| 2390 | pub fn fstatat(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError![]Stat { | ||
| 2391 | const pathname_c = try toPosixPath(pathname); | ||
| 2392 | return fstatatC(dirfd, &pathname_c, flags); | ||
| 2393 | } | ||
| 2394 | |||
| 2395 | pub fn fstatatC(dirfd: fd_t, pathname: [*:0]const u8, flags: u32) FStatAtError!Stat { | ||
| 2396 | var stat: Stat = undefined; | ||
| 2397 | switch (errno(system.fstatat(dirfd, pathname, &stat, flags))) { | ||
| 2398 | 0 => return stat, | ||
| 2399 | EINVAL => unreachable, | ||
| 2400 | EBADF => unreachable, // Always a race condition. | ||
| 2401 | ENOMEM => return error.SystemResources, | ||
| 2402 | EACCES => return error.AccessDenied, | ||
| 2403 | EFAULT => unreachable, | ||
| 2404 | ENAMETOOLONG => return error.NameTooLong, | ||
| 2405 | ENOENT => return error.FileNotFound, | ||
| 2406 | ENOTDIR => return error.FileNotFound, | ||
| 2407 | else => |err| return unexpectedErrno(err), | ||
| 2408 | } | ||
| 2409 | } | ||
| 2410 | |||
| 2325 | pub const KQueueError = error{ | 2411 | pub const KQueueError = error{ |
| 2326 | /// The per-process limit on the number of open file descriptors has been reached. | 2412 | /// The per-process limit on the number of open file descriptors has been reached. |
| 2327 | ProcessFdQuotaExceeded, | 2413 | ProcessFdQuotaExceeded, |
| ... | @@ -3169,6 +3255,7 @@ pub fn sched_getaffinity(pid: pid_t) SchedGetAffinityError!cpu_set_t { | ... | @@ -3169,6 +3255,7 @@ pub fn sched_getaffinity(pid: pid_t) SchedGetAffinityError!cpu_set_t { |
| 3169 | /// Used to convert a slice to a null terminated slice on the stack. | 3255 | /// Used to convert a slice to a null terminated slice on the stack. |
| 3170 | /// TODO https://github.com/ziglang/zig/issues/287 | 3256 | /// TODO https://github.com/ziglang/zig/issues/287 |
| 3171 | pub fn toPosixPath(file_path: []const u8) ![PATH_MAX - 1:0]u8 { | 3257 | pub fn toPosixPath(file_path: []const u8) ![PATH_MAX - 1:0]u8 { |
| 3258 | if (std.debug.runtime_safety) assert(std.mem.indexOfScalar(u8, file_path, 0) == null); | ||
| 3172 | var path_with_null: [PATH_MAX - 1:0]u8 = undefined; | 3259 | var path_with_null: [PATH_MAX - 1:0]u8 = undefined; |
| 3173 | // >= rather than > to make room for the null byte | 3260 | // >= rather than > to make room for the null byte |
| 3174 | if (file_path.len >= PATH_MAX) return error.NameTooLong; | 3261 | if (file_path.len >= PATH_MAX) return error.NameTooLong; |
| ... | @@ -3492,12 +3579,12 @@ fn count_iovec_bytes(iovs: []const iovec_const) usize { | ... | @@ -3492,12 +3579,12 @@ fn count_iovec_bytes(iovs: []const iovec_const) usize { |
| 3492 | } | 3579 | } |
| 3493 | 3580 | ||
| 3494 | /// Transfer data between file descriptors, with optional headers and trailers. | 3581 | /// Transfer data between file descriptors, with optional headers and trailers. |
| 3495 | /// Returns the number of bytes written. This will be zero if `in_offset` falls beyond the end of the file. | 3582 | /// Returns the number of bytes written, which can be zero. |
| 3496 | /// | 3583 | /// |
| 3497 | /// The `sendfile` call copies `count` bytes from one file descriptor to another. When possible, | 3584 | /// The `sendfile` call copies `in_len` bytes from one file descriptor to another. When possible, |
| 3498 | /// this is done within the operating system kernel, which can provide better performance | 3585 | /// this is done within the operating system kernel, which can provide better performance |
| 3499 | /// characteristics than transferring data from kernel to user space and back, such as with | 3586 | /// characteristics than transferring data from kernel to user space and back, such as with |
| 3500 | /// `read` and `write` calls. When `count` is `0`, it means to copy until the end of the input file has been | 3587 | /// `read` and `write` calls. When `in_len` is `0`, it means to copy until the end of the input file has been |
| 3501 | /// reached. Note, however, that partial writes are still possible in this case. | 3588 | /// reached. Note, however, that partial writes are still possible in this case. |
| 3502 | /// | 3589 | /// |
| 3503 | /// `in_fd` must be a file descriptor opened for reading, and `out_fd` must be a file descriptor | 3590 | /// `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 { | ... | @@ -3506,7 +3593,8 @@ fn count_iovec_bytes(iovs: []const iovec_const) usize { |
| 3506 | /// atomicity guarantees no longer apply. | 3593 | /// atomicity guarantees no longer apply. |
| 3507 | /// | 3594 | /// |
| 3508 | /// Copying begins reading at `in_offset`. The input file descriptor seek position is ignored and not updated. | 3595 | /// Copying begins reading at `in_offset`. The input file descriptor seek position is ignored and not updated. |
| 3509 | /// If the output file descriptor has a seek position, it is updated as bytes are written. | 3596 | /// If the output file descriptor has a seek position, it is updated as bytes are written. When |
| 3597 | /// `in_offset` is past the end of the input file, it successfully reads 0 bytes. | ||
| 3510 | /// | 3598 | /// |
| 3511 | /// `flags` has different meanings per operating system; refer to the respective man pages. | 3599 | /// `flags` has different meanings per operating system; refer to the respective man pages. |
| 3512 | /// | 3600 | /// |
| ... | @@ -3527,7 +3615,7 @@ pub fn sendfile( | ... | @@ -3527,7 +3615,7 @@ pub fn sendfile( |
| 3527 | out_fd: fd_t, | 3615 | out_fd: fd_t, |
| 3528 | in_fd: fd_t, | 3616 | in_fd: fd_t, |
| 3529 | in_offset: u64, | 3617 | in_offset: u64, |
| 3530 | count: usize, | 3618 | in_len: u64, |
| 3531 | headers: []const iovec_const, | 3619 | headers: []const iovec_const, |
| 3532 | trailers: []const iovec_const, | 3620 | trailers: []const iovec_const, |
| 3533 | flags: u32, | 3621 | flags: u32, |
| ... | @@ -3536,9 +3624,15 @@ pub fn sendfile( | ... | @@ -3536,9 +3624,15 @@ pub fn sendfile( |
| 3536 | var total_written: usize = 0; | 3624 | var total_written: usize = 0; |
| 3537 | 3625 | ||
| 3538 | // Prevents EOVERFLOW. | 3626 | // Prevents EOVERFLOW. |
| 3627 | const size_t = @Type(std.builtin.TypeInfo{ | ||
| 3628 | .Int = .{ | ||
| 3629 | .is_signed = false, | ||
| 3630 | .bits = @typeInfo(usize).Int.bits - 1, | ||
| 3631 | }, | ||
| 3632 | }); | ||
| 3539 | const max_count = switch (std.Target.current.os.tag) { | 3633 | const max_count = switch (std.Target.current.os.tag) { |
| 3540 | .linux => 0x7ffff000, | 3634 | .linux => 0x7ffff000, |
| 3541 | else => math.maxInt(isize), | 3635 | else => math.maxInt(size_t), |
| 3542 | }; | 3636 | }; |
| 3543 | 3637 | ||
| 3544 | switch (std.Target.current.os.tag) { | 3638 | switch (std.Target.current.os.tag) { |
| ... | @@ -3558,7 +3652,7 @@ pub fn sendfile( | ... | @@ -3558,7 +3652,7 @@ pub fn sendfile( |
| 3558 | } | 3652 | } |
| 3559 | 3653 | ||
| 3560 | // Here we match BSD behavior, making a zero count value send as many bytes as possible. | 3654 | // Here we match BSD behavior, making a zero count value send as many bytes as possible. |
| 3561 | const adjusted_count = if (count == 0) max_count else math.min(count, max_count); | 3655 | const adjusted_count = if (in_len == 0) max_count else math.min(in_len, @as(size_t, max_count)); |
| 3562 | 3656 | ||
| 3563 | while (true) { | 3657 | while (true) { |
| 3564 | var offset: off_t = @bitCast(off_t, in_offset); | 3658 | var offset: off_t = @bitCast(off_t, in_offset); |
| ... | @@ -3567,10 +3661,10 @@ pub fn sendfile( | ... | @@ -3567,10 +3661,10 @@ pub fn sendfile( |
| 3567 | 0 => { | 3661 | 0 => { |
| 3568 | const amt = @bitCast(usize, rc); | 3662 | const amt = @bitCast(usize, rc); |
| 3569 | total_written += amt; | 3663 | total_written += amt; |
| 3570 | if (count == 0 and amt == 0) { | 3664 | if (in_len == 0 and amt == 0) { |
| 3571 | // We have detected EOF from `in_fd`. | 3665 | // We have detected EOF from `in_fd`. |
| 3572 | break; | 3666 | break; |
| 3573 | } else if (amt < count) { | 3667 | } else if (amt < in_len) { |
| 3574 | return total_written; | 3668 | return total_written; |
| 3575 | } else { | 3669 | } else { |
| 3576 | break; | 3670 | break; |
| ... | @@ -3636,7 +3730,7 @@ pub fn sendfile( | ... | @@ -3636,7 +3730,7 @@ pub fn sendfile( |
| 3636 | hdtr = &hdtr_data; | 3730 | hdtr = &hdtr_data; |
| 3637 | } | 3731 | } |
| 3638 | 3732 | ||
| 3639 | const adjusted_count = math.min(count, max_count); | 3733 | const adjusted_count = math.min(in_len, max_count); |
| 3640 | 3734 | ||
| 3641 | while (true) { | 3735 | while (true) { |
| 3642 | var sbytes: off_t = undefined; | 3736 | var sbytes: off_t = undefined; |
| ... | @@ -3714,7 +3808,7 @@ pub fn sendfile( | ... | @@ -3714,7 +3808,7 @@ pub fn sendfile( |
| 3714 | hdtr = &hdtr_data; | 3808 | hdtr = &hdtr_data; |
| 3715 | } | 3809 | } |
| 3716 | 3810 | ||
| 3717 | const adjusted_count = math.min(count, @as(u63, max_count)); | 3811 | const adjusted_count = math.min(in_len, @as(u63, max_count)); |
| 3718 | 3812 | ||
| 3719 | while (true) { | 3813 | while (true) { |
| 3720 | var sbytes: off_t = adjusted_count; | 3814 | var sbytes: off_t = adjusted_count; |
| ... | @@ -3724,12 +3818,14 @@ pub fn sendfile( | ... | @@ -3724,12 +3818,14 @@ pub fn sendfile( |
| 3724 | switch (err) { | 3818 | switch (err) { |
| 3725 | 0 => return amt, | 3819 | 0 => return amt, |
| 3726 | 3820 | ||
| 3727 | EBADF => unreachable, // Always a race condition. | ||
| 3728 | EFAULT => unreachable, // Segmentation fault. | 3821 | EFAULT => unreachable, // Segmentation fault. |
| 3729 | EINVAL => unreachable, | 3822 | EINVAL => unreachable, |
| 3730 | ENOTCONN => unreachable, // `out_fd` is an unconnected socket. | 3823 | ENOTCONN => unreachable, // `out_fd` is an unconnected socket. |
| 3731 | 3824 | ||
| 3732 | ENOTSUP, ENOTSOCK, ENOSYS => break :sf, | 3825 | // On macOS version 10.14.6, I observed Darwin return EBADF when |
| 3826 | // using sendfile on a valid open file descriptor of a file | ||
| 3827 | // system file. | ||
| 3828 | ENOTSUP, ENOTSOCK, ENOSYS, EBADF => break :sf, | ||
| 3733 | 3829 | ||
| 3734 | EINTR => if (amt != 0) return amt else continue, | 3830 | EINTR => if (amt != 0) return amt else continue, |
| 3735 | 3831 | ||
| ... | @@ -3768,10 +3864,10 @@ pub fn sendfile( | ... | @@ -3768,10 +3864,10 @@ pub fn sendfile( |
| 3768 | rw: { | 3864 | rw: { |
| 3769 | var buf: [8 * 4096]u8 = undefined; | 3865 | var buf: [8 * 4096]u8 = undefined; |
| 3770 | // Here we match BSD behavior, making a zero count value send as many bytes as possible. | 3866 | // Here we match BSD behavior, making a zero count value send as many bytes as possible. |
| 3771 | const adjusted_count = if (count == 0) buf.len else math.min(buf.len, count); | 3867 | const adjusted_count = if (in_len == 0) buf.len else math.min(buf.len, in_len); |
| 3772 | const amt_read = try pread(in_fd, buf[0..adjusted_count], in_offset); | 3868 | const amt_read = try pread(in_fd, buf[0..adjusted_count], in_offset); |
| 3773 | if (amt_read == 0) { | 3869 | if (amt_read == 0) { |
| 3774 | if (count == 0) { | 3870 | if (in_len == 0) { |
| 3775 | // We have detected EOF from `in_fd`. | 3871 | // We have detected EOF from `in_fd`. |
| 3776 | break :rw; | 3872 | break :rw; |
| 3777 | } else { | 3873 | } else { |
| ... | @@ -3780,7 +3876,7 @@ pub fn sendfile( | ... | @@ -3780,7 +3876,7 @@ pub fn sendfile( |
| 3780 | } | 3876 | } |
| 3781 | const amt_written = try write(out_fd, buf[0..amt_read]); | 3877 | const amt_written = try write(out_fd, buf[0..amt_read]); |
| 3782 | total_written += amt_written; | 3878 | total_written += amt_written; |
| 3783 | if (amt_written < count or count == 0) return total_written; | 3879 | if (amt_written < in_len or in_len == 0) return total_written; |
| 3784 | } | 3880 | } |
| 3785 | 3881 | ||
| 3786 | if (trailers.len != 0) { | 3882 | if (trailers.len != 0) { |
lib/std/os/linux.zig+4| ... | @@ -76,6 +76,10 @@ pub fn chdir(path: [*:0]const u8) usize { | ... | @@ -76,6 +76,10 @@ pub fn chdir(path: [*:0]const u8) usize { |
| 76 | return syscall1(SYS_chdir, @ptrToInt(path)); | 76 | return syscall1(SYS_chdir, @ptrToInt(path)); |
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | pub fn fchdir(fd: fd_t) usize { | ||
| 80 | return syscall1(SYS_fchdir, @bitCast(usize, @as(isize, fd))); | ||
| 81 | } | ||
| 82 | |||
| 79 | pub fn chroot(path: [*:0]const u8) usize { | 83 | pub fn chroot(path: [*:0]const u8) usize { |
| 80 | return syscall1(SYS_chroot, @ptrToInt(path)); | 84 | return syscall1(SYS_chroot, @ptrToInt(path)); |
| 81 | } | 85 | } |
lib/std/os/test.zig+12-58| ... | @@ -16,7 +16,7 @@ const AtomicRmwOp = builtin.AtomicRmwOp; | ... | @@ -16,7 +16,7 @@ const AtomicRmwOp = builtin.AtomicRmwOp; |
| 16 | const AtomicOrder = builtin.AtomicOrder; | 16 | const AtomicOrder = builtin.AtomicOrder; |
| 17 | 17 | ||
| 18 | test "makePath, put some files in it, deleteTree" { | 18 | test "makePath, put some files in it, deleteTree" { |
| 19 | try fs.makePath(a, "os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c"); | 19 | try fs.cwd().makePath("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c"); |
| 20 | try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c" ++ fs.path.sep_str ++ "file.txt", "nonsense"); | 20 | try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c" ++ fs.path.sep_str ++ "file.txt", "nonsense"); |
| 21 | try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "file2.txt", "blah"); | 21 | try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "file2.txt", "blah"); |
| 22 | try fs.deleteTree("os_test_tmp"); | 22 | try fs.deleteTree("os_test_tmp"); |
| ... | @@ -28,7 +28,7 @@ test "makePath, put some files in it, deleteTree" { | ... | @@ -28,7 +28,7 @@ test "makePath, put some files in it, deleteTree" { |
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | test "access file" { | 30 | test "access file" { |
| 31 | try fs.makePath(a, "os_test_tmp"); | 31 | try fs.cwd().makePath("os_test_tmp"); |
| 32 | if (fs.cwd().access("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", .{})) |ok| { | 32 | if (fs.cwd().access("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", .{})) |ok| { |
| 33 | @panic("expected error"); | 33 | @panic("expected error"); |
| 34 | } else |err| { | 34 | } else |err| { |
| ... | @@ -45,7 +45,7 @@ fn testThreadIdFn(thread_id: *Thread.Id) void { | ... | @@ -45,7 +45,7 @@ fn testThreadIdFn(thread_id: *Thread.Id) void { |
| 45 | } | 45 | } |
| 46 | 46 | ||
| 47 | test "sendfile" { | 47 | test "sendfile" { |
| 48 | try fs.makePath(a, "os_test_tmp"); | 48 | try fs.cwd().makePath("os_test_tmp"); |
| 49 | defer fs.deleteTree("os_test_tmp") catch {}; | 49 | defer fs.deleteTree("os_test_tmp") catch {}; |
| 50 | 50 | ||
| 51 | var dir = try fs.cwd().openDirList("os_test_tmp"); | 51 | var dir = try fs.cwd().openDirList("os_test_tmp"); |
| ... | @@ -74,7 +74,9 @@ test "sendfile" { | ... | @@ -74,7 +74,9 @@ test "sendfile" { |
| 74 | 74 | ||
| 75 | const header1 = "header1\n"; | 75 | const header1 = "header1\n"; |
| 76 | const header2 = "second header\n"; | 76 | const header2 = "second header\n"; |
| 77 | var headers = [_]os.iovec_const{ | 77 | const trailer1 = "trailer1\n"; |
| 78 | const trailer2 = "second trailer\n"; | ||
| 79 | var hdtr = [_]os.iovec_const{ | ||
| 78 | .{ | 80 | .{ |
| 79 | .iov_base = header1, | 81 | .iov_base = header1, |
| 80 | .iov_len = header1.len, | 82 | .iov_len = header1.len, |
| ... | @@ -83,11 +85,6 @@ test "sendfile" { | ... | @@ -83,11 +85,6 @@ test "sendfile" { |
| 83 | .iov_base = header2, | 85 | .iov_base = header2, |
| 84 | .iov_len = header2.len, | 86 | .iov_len = header2.len, |
| 85 | }, | 87 | }, |
| 86 | }; | ||
| 87 | |||
| 88 | const trailer1 = "trailer1\n"; | ||
| 89 | const trailer2 = "second trailer\n"; | ||
| 90 | var trailers = [_]os.iovec_const{ | ||
| 91 | .{ | 88 | .{ |
| 92 | .iov_base = trailer1, | 89 | .iov_base = trailer1, |
| 93 | .iov_len = trailer1.len, | 90 | .iov_len = trailer1.len, |
| ... | @@ -99,59 +96,16 @@ test "sendfile" { | ... | @@ -99,59 +96,16 @@ test "sendfile" { |
| 99 | }; | 96 | }; |
| 100 | 97 | ||
| 101 | var written_buf: [header1.len + header2.len + 10 + trailer1.len + trailer2.len]u8 = undefined; | 98 | var written_buf: [header1.len + header2.len + 10 + trailer1.len + trailer2.len]u8 = undefined; |
| 102 | try sendfileAll(dest_file.handle, src_file.handle, 1, 10, &headers, &trailers, 0); | 99 | try dest_file.writeFileAll(src_file, .{ |
| 103 | 100 | .in_offset = 1, | |
| 101 | .in_len = 10, | ||
| 102 | .headers_and_trailers = &hdtr, | ||
| 103 | .header_count = 2, | ||
| 104 | }); | ||
| 104 | try dest_file.preadAll(&written_buf, 0); | 105 | try dest_file.preadAll(&written_buf, 0); |
| 105 | expect(mem.eql(u8, &written_buf, "header1\nsecond header\nine1\nsecontrailer1\nsecond trailer\n")); | 106 | expect(mem.eql(u8, &written_buf, "header1\nsecond header\nine1\nsecontrailer1\nsecond trailer\n")); |
| 106 | } | 107 | } |
| 107 | 108 | ||
| 108 | fn sendfileAll( | ||
| 109 | out_fd: os.fd_t, | ||
| 110 | in_fd: os.fd_t, | ||
| 111 | offset: u64, | ||
| 112 | count: usize, | ||
| 113 | headers: []os.iovec_const, | ||
| 114 | trailers: []os.iovec_const, | ||
| 115 | flags: u32, | ||
| 116 | ) os.SendFileError!void { | ||
| 117 | var amt: usize = undefined; | ||
| 118 | hdrs: { | ||
| 119 | var i: usize = 0; | ||
| 120 | while (i < headers.len) { | ||
| 121 | amt = try os.sendfile(out_fd, in_fd, offset, count, headers[i..], trailers, flags); | ||
| 122 | while (amt >= headers[i].iov_len) { | ||
| 123 | amt -= headers[i].iov_len; | ||
| 124 | i += 1; | ||
| 125 | if (i >= headers.len) break :hdrs; | ||
| 126 | } | ||
| 127 | headers[i].iov_base += amt; | ||
| 128 | headers[i].iov_len -= amt; | ||
| 129 | } | ||
| 130 | } | ||
| 131 | var off = amt; | ||
| 132 | while (off < count) { | ||
| 133 | amt = try os.sendfile(out_fd, in_fd, offset + off, count - off, &[0]os.iovec_const{}, trailers, flags); | ||
| 134 | off += amt; | ||
| 135 | } | ||
| 136 | amt = off - count; | ||
| 137 | var i: usize = 0; | ||
| 138 | while (i < trailers.len) { | ||
| 139 | while (amt >= headers[i].iov_len) { | ||
| 140 | amt -= trailers[i].iov_len; | ||
| 141 | i += 1; | ||
| 142 | if (i >= trailers.len) return; | ||
| 143 | } | ||
| 144 | trailers[i].iov_base += amt; | ||
| 145 | trailers[i].iov_len -= amt; | ||
| 146 | if (std.Target.current.os.tag == .windows) { | ||
| 147 | amt = try os.writev(out_fd, trailers[i..]); | ||
| 148 | } else { | ||
| 149 | // Here we must use send because it's the only way to give the flags. | ||
| 150 | amt = try os.send(out_fd, trailers[i].iov_base[0..trailers[i].iov_len], flags); | ||
| 151 | } | ||
| 152 | } | ||
| 153 | } | ||
| 154 | |||
| 155 | test "std.Thread.getCurrentId" { | 109 | test "std.Thread.getCurrentId" { |
| 156 | if (builtin.single_threaded) return error.SkipZigTest; | 110 | if (builtin.single_threaded) return error.SkipZigTest; |
| 157 | 111 |
lib/std/os/windows.zig+61-10| ... | @@ -337,7 +337,7 @@ pub fn GetQueuedCompletionStatus( | ... | @@ -337,7 +337,7 @@ pub fn GetQueuedCompletionStatus( |
| 337 | } | 337 | } |
| 338 | 338 | ||
| 339 | pub fn CloseHandle(hObject: HANDLE) void { | 339 | pub fn CloseHandle(hObject: HANDLE) void { |
| 340 | assert(kernel32.CloseHandle(hObject) != 0); | 340 | assert(ntdll.NtClose(hObject) == .SUCCESS); |
| 341 | } | 341 | } |
| 342 | 342 | ||
| 343 | pub fn FindClose(hFindFile: HANDLE) void { | 343 | 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 | ... | @@ -586,23 +586,74 @@ pub fn MoveFileExW(old_path: [*:0]const u16, new_path: [*:0]const u16, flags: DW |
| 586 | } | 586 | } |
| 587 | 587 | ||
| 588 | pub const CreateDirectoryError = error{ | 588 | pub const CreateDirectoryError = error{ |
| 589 | NameTooLong, | ||
| 589 | PathAlreadyExists, | 590 | PathAlreadyExists, |
| 590 | FileNotFound, | 591 | FileNotFound, |
| 592 | NoDevice, | ||
| 593 | AccessDenied, | ||
| 591 | Unexpected, | 594 | Unexpected, |
| 592 | }; | 595 | }; |
| 593 | 596 | ||
| 594 | pub fn CreateDirectory(pathname: []const u8, attrs: ?*SECURITY_ATTRIBUTES) CreateDirectoryError!void { | 597 | /// Returns an open directory handle which the caller is responsible for closing with `CloseHandle`. |
| 598 | pub fn CreateDirectory(dir: ?HANDLE, pathname: []const u8, sa: ?*SECURITY_ATTRIBUTES) CreateDirectoryError!HANDLE { | ||
| 595 | const pathname_w = try sliceToPrefixedFileW(pathname); | 599 | const pathname_w = try sliceToPrefixedFileW(pathname); |
| 596 | return CreateDirectoryW(&pathname_w, attrs); | 600 | return CreateDirectoryW(dir, &pathname_w, sa); |
| 597 | } | 601 | } |
| 598 | 602 | ||
| 599 | pub fn CreateDirectoryW(pathname: [*:0]const u16, attrs: ?*SECURITY_ATTRIBUTES) CreateDirectoryError!void { | 603 | /// Same as `CreateDirectory` except takes a WTF-16 encoded path. |
| 600 | if (kernel32.CreateDirectoryW(pathname, attrs) == 0) { | 604 | pub fn CreateDirectoryW( |
| 601 | switch (kernel32.GetLastError()) { | 605 | dir: ?HANDLE, |
| 602 | .ALREADY_EXISTS => return error.PathAlreadyExists, | 606 | sub_path_w: [*:0]const u16, |
| 603 | .PATH_NOT_FOUND => return error.FileNotFound, | 607 | sa: ?*SECURITY_ATTRIBUTES, |
| 604 | else => |err| return unexpectedError(err), | 608 | ) CreateDirectoryError!HANDLE { |
| 605 | } | 609 | const path_len_bytes = math.cast(u16, mem.toSliceConst(u16, sub_path_w).len * 2) catch |err| switch (err) { |
| 610 | error.Overflow => return error.NameTooLong, | ||
| 611 | }; | ||
| 612 | var nt_name = UNICODE_STRING{ | ||
| 613 | .Length = path_len_bytes, | ||
| 614 | .MaximumLength = path_len_bytes, | ||
| 615 | .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)), | ||
| 616 | }; | ||
| 617 | |||
| 618 | if (sub_path_w[0] == '.' and sub_path_w[1] == 0) { | ||
| 619 | // Windows does not recognize this, but it does work with empty string. | ||
| 620 | nt_name.Length = 0; | ||
| 621 | } | ||
| 622 | |||
| 623 | var attr = OBJECT_ATTRIBUTES{ | ||
| 624 | .Length = @sizeOf(OBJECT_ATTRIBUTES), | ||
| 625 | .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sub_path_w)) null else dir, | ||
| 626 | .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here. | ||
| 627 | .ObjectName = &nt_name, | ||
| 628 | .SecurityDescriptor = if (sa) |ptr| ptr.lpSecurityDescriptor else null, | ||
| 629 | .SecurityQualityOfService = null, | ||
| 630 | }; | ||
| 631 | var io: IO_STATUS_BLOCK = undefined; | ||
| 632 | var result_handle: HANDLE = undefined; | ||
| 633 | const rc = ntdll.NtCreateFile( | ||
| 634 | &result_handle, | ||
| 635 | GENERIC_READ | SYNCHRONIZE, | ||
| 636 | &attr, | ||
| 637 | &io, | ||
| 638 | null, | ||
| 639 | FILE_ATTRIBUTE_NORMAL, | ||
| 640 | FILE_SHARE_READ, | ||
| 641 | FILE_CREATE, | ||
| 642 | FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, | ||
| 643 | null, | ||
| 644 | 0, | ||
| 645 | ); | ||
| 646 | switch (rc) { | ||
| 647 | .SUCCESS => return result_handle, | ||
| 648 | .OBJECT_NAME_INVALID => unreachable, | ||
| 649 | .OBJECT_NAME_NOT_FOUND => return error.FileNotFound, | ||
| 650 | .OBJECT_PATH_NOT_FOUND => return error.FileNotFound, | ||
| 651 | .NO_MEDIA_IN_DEVICE => return error.NoDevice, | ||
| 652 | .INVALID_PARAMETER => unreachable, | ||
| 653 | .ACCESS_DENIED => return error.AccessDenied, | ||
| 654 | .OBJECT_PATH_SYNTAX_BAD => unreachable, | ||
| 655 | .OBJECT_NAME_COLLISION => return error.PathAlreadyExists, | ||
| 656 | else => return unexpectedStatus(rc), | ||
| 606 | } | 657 | } |
| 607 | } | 658 | } |
| 608 | 659 |
src-self-hosted/compilation.zig+1-1| ... | @@ -1179,7 +1179,7 @@ pub const Compilation = struct { | ... | @@ -1179,7 +1179,7 @@ pub const Compilation = struct { |
| 1179 | defer self.gpa().free(zig_dir_path); | 1179 | defer self.gpa().free(zig_dir_path); |
| 1180 | 1180 | ||
| 1181 | const tmp_dir = try fs.path.join(self.arena(), &[_][]const u8{ zig_dir_path, comp_dir_name[0..] }); | 1181 | const tmp_dir = try fs.path.join(self.arena(), &[_][]const u8{ zig_dir_path, comp_dir_name[0..] }); |
| 1182 | try fs.makePath(self.gpa(), tmp_dir); | 1182 | try fs.cwd().makePath(tmp_dir); |
| 1183 | return tmp_dir; | 1183 | return tmp_dir; |
| 1184 | } | 1184 | } |
| 1185 | 1185 |
src-self-hosted/test.zig+3-3| ... | @@ -56,7 +56,7 @@ pub const TestContext = struct { | ... | @@ -56,7 +56,7 @@ pub const TestContext = struct { |
| 56 | self.zig_lib_dir = try introspect.resolveZigLibDir(allocator); | 56 | self.zig_lib_dir = try introspect.resolveZigLibDir(allocator); |
| 57 | errdefer allocator.free(self.zig_lib_dir); | 57 | errdefer allocator.free(self.zig_lib_dir); |
| 58 | 58 | ||
| 59 | try std.fs.makePath(allocator, tmp_dir_name); | 59 | try std.fs.cwd().makePath(tmp_dir_name); |
| 60 | errdefer std.fs.deleteTree(tmp_dir_name) catch {}; | 60 | errdefer std.fs.deleteTree(tmp_dir_name) catch {}; |
| 61 | } | 61 | } |
| 62 | 62 | ||
| ... | @@ -85,7 +85,7 @@ pub const TestContext = struct { | ... | @@ -85,7 +85,7 @@ pub const TestContext = struct { |
| 85 | const file1_path = try std.fs.path.join(allocator, [_][]const u8{ tmp_dir_name, file_index, file1 }); | 85 | const file1_path = try std.fs.path.join(allocator, [_][]const u8{ tmp_dir_name, file_index, file1 }); |
| 86 | 86 | ||
| 87 | if (std.fs.path.dirname(file1_path)) |dirname| { | 87 | if (std.fs.path.dirname(file1_path)) |dirname| { |
| 88 | try std.fs.makePath(allocator, dirname); | 88 | try std.fs.cwd().makePath(dirname); |
| 89 | } | 89 | } |
| 90 | 90 | ||
| 91 | // TODO async I/O | 91 | // TODO async I/O |
| ... | @@ -119,7 +119,7 @@ pub const TestContext = struct { | ... | @@ -119,7 +119,7 @@ pub const TestContext = struct { |
| 119 | 119 | ||
| 120 | const output_file = try std.fmt.allocPrint(allocator, "{}-out{}", .{ file1_path, (Target{ .Native = {} }).exeFileExt() }); | 120 | const output_file = try std.fmt.allocPrint(allocator, "{}-out{}", .{ file1_path, (Target{ .Native = {} }).exeFileExt() }); |
| 121 | if (std.fs.path.dirname(file1_path)) |dirname| { | 121 | if (std.fs.path.dirname(file1_path)) |dirname| { |
| 122 | try std.fs.makePath(allocator, dirname); | 122 | try std.fs.cwd().makePath(dirname); |
| 123 | } | 123 | } |
| 124 | 124 | ||
| 125 | // TODO async I/O | 125 | // TODO async I/O |
test/cli.zig+1-1| ... | @@ -37,7 +37,7 @@ pub fn main() !void { | ... | @@ -37,7 +37,7 @@ pub fn main() !void { |
| 37 | }; | 37 | }; |
| 38 | for (test_fns) |testFn| { | 38 | for (test_fns) |testFn| { |
| 39 | try fs.deleteTree(dir_path); | 39 | try fs.deleteTree(dir_path); |
| 40 | try fs.makeDir(dir_path); | 40 | try fs.cwd().makeDir(dir_path); |
| 41 | try testFn(zig_exe, dir_path); | 41 | try testFn(zig_exe, dir_path); |
| 42 | } | 42 | } |
| 43 | } | 43 | } |