authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-11-30 18:37:00-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-11-30 18:37:00-05:00
log11b8d3ce9d7bc8e3ab16813fd5415c9d45dbb232
tree879d454b8c77d8a9dfeb4044ab55f66e513e7810
parent85e1e3b95f1f1699a842a5e889d8987692a829a4
parent2e7350140d4fbf9335e48693ccc4cba77d229c84
signaturelock-open Commit is signed but in an unrecognized format.

Merge branch 'std.fs.File'


15 files changed, 368 insertions(+), 166 deletions(-)

lib/std/build.zig+1-1
...@@ -2416,7 +2416,7 @@ fn findVcpkgRoot(allocator: *Allocator) !?[]const u8 {...@@ -2416,7 +2416,7 @@ fn findVcpkgRoot(allocator: *Allocator) !?[]const u8 {
2416 const path_file = try fs.path.join(allocator, [_][]const u8{ appdata_path, "vcpkg.path.txt" });2416 const path_file = try fs.path.join(allocator, [_][]const u8{ appdata_path, "vcpkg.path.txt" });
2417 defer allocator.free(path_file);2417 defer allocator.free(path_file);
24182418
2419 const file = fs.File.openRead(path_file) catch return null;2419 const file = fs.cwd().openFile(path_file, .{}) catch return null;
2420 defer file.close();2420 defer file.close();
24212421
2422 const size = @intCast(usize, try file.getEndPos());2422 const size = @intCast(usize, try file.getEndPos());
lib/std/debug.zig+2-2
...@@ -1131,7 +1131,7 @@ fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo {...@@ -1131,7 +1131,7 @@ fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo {
1131}1131}
11321132
1133fn printLineFromFileAnyOs(out_stream: var, line_info: LineInfo) !void {1133fn printLineFromFileAnyOs(out_stream: var, line_info: LineInfo) !void {
1134 var f = try File.openRead(line_info.file_name);1134 var f = try fs.cwd().openFile(line_info.file_name, .{});
1135 defer f.close();1135 defer f.close();
1136 // TODO fstat and make sure that the file has the correct size1136 // TODO fstat and make sure that the file has the correct size
11371137
...@@ -2089,7 +2089,7 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u...@@ -2089,7 +2089,7 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u
2089 const ofile_path = mem.toSliceConst(u8, @ptrCast([*:0]const u8, di.strings.ptr + ofile.n_strx));2089 const ofile_path = mem.toSliceConst(u8, @ptrCast([*:0]const u8, di.strings.ptr + ofile.n_strx));
20902090
2091 gop.kv.value = MachOFile{2091 gop.kv.value = MachOFile{
2092 .bytes = try std.fs.Dir.cwd().readFileAllocAligned(2092 .bytes = try std.fs.cwd().readFileAllocAligned(
2093 di.ofiles.allocator,2093 di.ofiles.allocator,
2094 ofile_path,2094 ofile_path,
2095 maxInt(usize),2095 maxInt(usize),
lib/std/fs.zig+229-49
...@@ -13,8 +13,6 @@ pub const File = @import("fs/file.zig").File;...@@ -13,8 +13,6 @@ pub const File = @import("fs/file.zig").File;
1313
14pub const symLink = os.symlink;14pub const symLink = os.symlink;
15pub const symLinkC = os.symlinkC;15pub const symLinkC = os.symlinkC;
16pub const deleteFile = os.unlink;
17pub const deleteFileC = os.unlinkC;
18pub const rename = os.rename;16pub const rename = os.rename;
19pub const renameC = os.renameC;17pub const renameC = os.renameC;
20pub const renameW = os.renameW;18pub const renameW = os.renameW;
...@@ -88,13 +86,15 @@ pub fn updateFile(source_path: []const u8, dest_path: []const u8) !PrevStatus {...@@ -88,13 +86,15 @@ pub fn updateFile(source_path: []const u8, dest_path: []const u8) !PrevStatus {
88/// If any of the directories do not exist for dest_path, they are created.86/// If any of the directories do not exist for dest_path, they are created.
89/// TODO https://github.com/ziglang/zig/issues/288587/// TODO https://github.com/ziglang/zig/issues/2885
90pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?File.Mode) !PrevStatus {88pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?File.Mode) !PrevStatus {
91 var src_file = try File.openRead(source_path);89 const my_cwd = cwd();
90
91 var src_file = try my_cwd.openFile(source_path, .{});
92 defer src_file.close();92 defer src_file.close();
9393
94 const src_stat = try src_file.stat();94 const src_stat = try src_file.stat();
95 check_dest_stat: {95 check_dest_stat: {
96 const dest_stat = blk: {96 const dest_stat = blk: {
97 var dest_file = File.openRead(dest_path) catch |err| switch (err) {97 var dest_file = my_cwd.openFile(dest_path, .{}) catch |err| switch (err) {
98 error.FileNotFound => break :check_dest_stat,98 error.FileNotFound => break :check_dest_stat,
99 else => |e| return e,99 else => |e| return e,
100 };100 };
...@@ -157,7 +157,7 @@ pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?Fil...@@ -157,7 +157,7 @@ pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?Fil
157/// in the same directory as dest_path.157/// in the same directory as dest_path.
158/// Destination file will have the same mode as the source file.158/// Destination file will have the same mode as the source file.
159pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void {159pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void {
160 var in_file = try File.openRead(source_path);160 var in_file = try cwd().openFile(source_path, .{});
161 defer in_file.close();161 defer in_file.close();
162162
163 const mode = try in_file.mode();163 const mode = try in_file.mode();
...@@ -180,7 +180,7 @@ pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void {...@@ -180,7 +180,7 @@ pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void {
180/// merged and readily available,180/// merged and readily available,
181/// there is a possibility of power loss or application termination leaving temporary files present181/// there is a possibility of power loss or application termination leaving temporary files present
182pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void {182pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void {
183 var in_file = try File.openRead(source_path);183 var in_file = try cwd().openFile(source_path, .{});
184 defer in_file.close();184 defer in_file.close();
185185
186 var atomic_file = try AtomicFile.init(dest_path, mode);186 var atomic_file = try AtomicFile.init(dest_path, mode);
...@@ -206,8 +206,6 @@ pub const AtomicFile = struct {...@@ -206,8 +206,6 @@ pub const AtomicFile = struct {
206206
207 /// dest_path must remain valid for the lifetime of AtomicFile207 /// dest_path must remain valid for the lifetime of AtomicFile
208 /// call finish to atomically replace dest_path with contents208 /// call finish to atomically replace dest_path with contents
209 /// TODO once we have null terminated pointers, use the
210 /// openWriteNoClobberN function
211 pub fn init(dest_path: []const u8, mode: File.Mode) InitError!AtomicFile {209 pub fn init(dest_path: []const u8, mode: File.Mode) InitError!AtomicFile {
212 const dirname = path.dirname(dest_path);210 const dirname = path.dirname(dest_path);
213 var rand_buf: [12]u8 = undefined;211 var rand_buf: [12]u8 = undefined;
...@@ -224,15 +222,19 @@ pub const AtomicFile = struct {...@@ -224,15 +222,19 @@ pub const AtomicFile = struct {
224222
225 tmp_path_buf[tmp_path_len] = 0;223 tmp_path_buf[tmp_path_len] = 0;
226224
225 const my_cwd = cwd();
226
227 while (true) {227 while (true) {
228 try crypto.randomBytes(rand_buf[0..]);228 try crypto.randomBytes(rand_buf[0..]);
229 b64_fs_encoder.encode(tmp_path_buf[dirname_component_len..tmp_path_len], rand_buf);229 b64_fs_encoder.encode(tmp_path_buf[dirname_component_len..tmp_path_len], rand_buf);
230230
231 const file = File.openWriteNoClobberC(@ptrCast([*:0]u8, &tmp_path_buf), mode) catch |err| switch (err) {231 // TODO https://github.com/ziglang/zig/issues/3770 to clean up this @ptrCast
232 const file = my_cwd.createFileC(
233 @ptrCast([*:0]u8, &tmp_path_buf),
234 .{ .mode = mode, .exclusive = true },
235 ) catch |err| switch (err) {
232 error.PathAlreadyExists => continue,236 error.PathAlreadyExists => continue,
233 // TODO zig should figure out that this error set does not include PathAlreadyExists since237 else => |e| return e,
234 // it is handled in the above switch
235 else => return err,
236 };238 };
237239
238 return AtomicFile{240 return AtomicFile{
...@@ -248,7 +250,7 @@ pub const AtomicFile = struct {...@@ -248,7 +250,7 @@ pub const AtomicFile = struct {
248 pub fn deinit(self: *AtomicFile) void {250 pub fn deinit(self: *AtomicFile) void {
249 if (!self.finished) {251 if (!self.finished) {
250 self.file.close();252 self.file.close();
251 deleteFileC(@ptrCast([*:0]u8, &self.tmp_path_buf)) catch {};253 cwd().deleteFileC(@ptrCast([*:0]u8, &self.tmp_path_buf)) catch {};
252 self.finished = true;254 self.finished = true;
253 }255 }
254 }256 }
...@@ -350,12 +352,12 @@ pub fn deleteTree(full_path: []const u8) !void {...@@ -350,12 +352,12 @@ pub fn deleteTree(full_path: []const u8) !void {
350 CannotDeleteRootDirectory,352 CannotDeleteRootDirectory,
351 }.CannotDeleteRootDirectory;353 }.CannotDeleteRootDirectory;
352354
353 var dir = try Dir.cwd().openDirList(dirname);355 var dir = try cwd().openDirList(dirname);
354 defer dir.close();356 defer dir.close();
355357
356 return dir.deleteTree(path.basename(full_path));358 return dir.deleteTree(path.basename(full_path));
357 } else {359 } else {
358 return Dir.cwd().deleteTree(full_path);360 return cwd().deleteTree(full_path);
359 }361 }
360}362}
361363
...@@ -657,17 +659,6 @@ pub const Dir = struct {...@@ -657,17 +659,6 @@ pub const Dir = struct {
657 }659 }
658 }660 }
659661
660 /// Returns an handle to the current working directory that is open for traversal.
661 /// Closing the returned `Dir` is checked illegal behavior. Iterating over the result is illegal behavior.
662 /// On POSIX targets, this function is comptime-callable.
663 pub fn cwd() Dir {
664 if (builtin.os == .windows) {
665 return Dir{ .fd = os.windows.peb().ProcessParameters.CurrentDirectory.Handle };
666 } else {
667 return Dir{ .fd = os.AT_FDCWD };
668 }
669 }
670
671 pub const OpenError = error{662 pub const OpenError = error{
672 FileNotFound,663 FileNotFound,
673 NotDir,664 NotDir,
...@@ -683,12 +674,12 @@ pub const Dir = struct {...@@ -683,12 +674,12 @@ pub const Dir = struct {
683 DeviceBusy,674 DeviceBusy,
684 } || os.UnexpectedError;675 } || os.UnexpectedError;
685676
686 /// Deprecated; call `Dir.cwd().openDirList` directly.677 /// Deprecated; call `cwd().openDirList` directly.
687 pub fn open(dir_path: []const u8) OpenError!Dir {678 pub fn open(dir_path: []const u8) OpenError!Dir {
688 return cwd().openDirList(dir_path);679 return cwd().openDirList(dir_path);
689 }680 }
690681
691 /// Deprecated; call `Dir.cwd().openDirListC` directly.682 /// Deprecated; call `cwd().openDirListC` directly.
692 pub fn openC(dir_path_c: [*:0]const u8) OpenError!Dir {683 pub fn openC(dir_path_c: [*:0]const u8) OpenError!Dir {
693 return cwd().openDirListC(dir_path_c);684 return cwd().openDirListC(dir_path_c);
694 }685 }
...@@ -698,29 +689,110 @@ pub const Dir = struct {...@@ -698,29 +689,110 @@ pub const Dir = struct {
698 self.* = undefined;689 self.* = undefined;
699 }690 }
700691
701 /// Call `File.close` on the result when done.692 /// Opens a file for reading or writing, without attempting to create a new file.
702 pub fn openRead(self: Dir, sub_path: []const u8) File.OpenError!File {693 /// Call `File.close` to release the resource.
694 /// Asserts that the path parameter has no null bytes.
695 pub fn openFile(self: Dir, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File {
696 if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0);
703 if (builtin.os == .windows) {697 if (builtin.os == .windows) {
704 const path_w = try os.windows.sliceToPrefixedFileW(sub_path);698 const path_w = try os.windows.sliceToPrefixedFileW(sub_path);
705 return self.openReadW(&path_w);699 return self.openFileW(&path_w, flags);
706 }700 }
707 const path_c = try os.toPosixPath(sub_path);701 const path_c = try os.toPosixPath(sub_path);
708 return self.openReadC(&path_c);702 return self.openFileC(&path_c, flags);
709 }703 }
710704
711 /// Call `File.close` on the result when done.705 /// Same as `openFile` but the path parameter is null-terminated.
712 pub fn openReadC(self: Dir, sub_path: [*:0]const u8) File.OpenError!File {706 pub fn openFileC(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) File.OpenError!File {
713 if (builtin.os == .windows) {707 if (builtin.os == .windows) {
714 const path_w = try os.windows.cStrToPrefixedFileW(sub_path);708 const path_w = try os.windows.cStrToPrefixedFileW(sub_path);
715 return self.openReadW(&path_w);709 return self.openFileW(&path_w, flags);
716 }710 }
717 const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0;711 const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0;
718 const flags = O_LARGEFILE | os.O_RDONLY | os.O_CLOEXEC;712 const os_flags = O_LARGEFILE | os.O_CLOEXEC | if (flags.write and flags.read)
719 const fd = try os.openatC(self.fd, sub_path, flags, 0);713 @as(u32, os.O_RDWR)
720 return File.openHandle(fd);714 else if (flags.write)
715 @as(u32, os.O_WRONLY)
716 else
717 @as(u32, os.O_RDONLY);
718 const fd = try os.openatC(self.fd, sub_path, os_flags, 0);
719 return File{ .handle = fd };
720 }
721
722 /// Same as `openFile` but Windows-only and the path parameter is
723 /// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded.
724 pub fn openFileW(self: Dir, sub_path_w: [*:0]const u16, flags: File.OpenFlags) File.OpenError!File {
725 const w = os.windows;
726 const access_mask = w.SYNCHRONIZE |
727 (if (flags.read) @as(u32, w.GENERIC_READ) else 0) |
728 (if (flags.write) @as(u32, w.GENERIC_WRITE) else 0);
729 return self.openFileWindows(sub_path_w, access_mask, w.FILE_OPEN);
721 }730 }
722731
723 pub fn openReadW(self: Dir, sub_path_w: [*:0]const u16) File.OpenError!File {732 /// Creates, opens, or overwrites a file with write access.
733 /// Call `File.close` on the result when done.
734 /// Asserts that the path parameter has no null bytes.
735 pub fn createFile(self: Dir, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File {
736 if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0);
737 if (builtin.os == .windows) {
738 const path_w = try os.windows.sliceToPrefixedFileW(sub_path);
739 return self.createFileW(&path_w, flags);
740 }
741 const path_c = try os.toPosixPath(sub_path);
742 return self.createFileC(&path_c, flags);
743 }
744
745 /// Same as `createFile` but the path parameter is null-terminated.
746 pub fn createFileC(self: Dir, sub_path_c: [*:0]const u8, flags: File.CreateFlags) File.OpenError!File {
747 if (builtin.os == .windows) {
748 const path_w = try os.windows.cStrToPrefixedFileW(sub_path_c);
749 return self.createFileW(&path_w, flags);
750 }
751 const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0;
752 const os_flags = O_LARGEFILE | os.O_CREAT | os.O_CLOEXEC |
753 (if (flags.truncate) @as(u32, os.O_TRUNC) else 0) |
754 (if (flags.read) @as(u32, os.O_RDWR) else os.O_WRONLY) |
755 (if (flags.exclusive) @as(u32, os.O_EXCL) else 0);
756 const fd = try os.openatC(self.fd, sub_path_c, os_flags, flags.mode);
757 return File{ .handle = fd };
758 }
759
760 /// Same as `createFile` but Windows-only and the path parameter is
761 /// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded.
762 pub fn createFileW(self: Dir, sub_path_w: [*:0]const u16, flags: File.CreateFlags) File.OpenError!File {
763 const w = os.windows;
764 const access_mask = w.SYNCHRONIZE | w.GENERIC_WRITE |
765 (if (flags.read) @as(u32, w.GENERIC_READ) else 0);
766 const creation = if (flags.exclusive)
767 @as(u32, w.FILE_CREATE)
768 else if (flags.truncate)
769 @as(u32, w.FILE_OVERWRITE_IF)
770 else
771 @as(u32, w.FILE_OPEN_IF);
772 return self.openFileWindows(sub_path_w, access_mask, creation);
773 }
774
775 /// Deprecated; call `openFile` directly.
776 pub fn openRead(self: Dir, sub_path: []const u8) File.OpenError!File {
777 return self.openFile(sub_path, .{});
778 }
779
780 /// Deprecated; call `openFileC` directly.
781 pub fn openReadC(self: Dir, sub_path: [*:0]const u8) File.OpenError!File {
782 return self.openFileC(sub_path, .{});
783 }
784
785 /// Deprecated; call `openFileW` directly.
786 pub fn openReadW(self: Dir, sub_path: [*:0]const u16) File.OpenError!File {
787 return self.openFileW(sub_path, .{});
788 }
789
790 pub fn openFileWindows(
791 self: Dir,
792 sub_path_w: [*:0]const u16,
793 access_mask: os.windows.ACCESS_MASK,
794 creation: os.windows.ULONG,
795 ) File.OpenError!File {
724 const w = os.windows;796 const w = os.windows;
725797
726 var result = File{ .handle = undefined };798 var result = File{ .handle = undefined };
...@@ -750,13 +822,13 @@ pub const Dir = struct {...@@ -750,13 +822,13 @@ pub const Dir = struct {
750 var io: w.IO_STATUS_BLOCK = undefined;822 var io: w.IO_STATUS_BLOCK = undefined;
751 const rc = w.ntdll.NtCreateFile(823 const rc = w.ntdll.NtCreateFile(
752 &result.handle,824 &result.handle,
753 w.GENERIC_READ | w.SYNCHRONIZE,825 access_mask,
754 &attr,826 &attr,
755 &io,827 &io,
756 null,828 null,
757 w.FILE_ATTRIBUTE_NORMAL,829 w.FILE_ATTRIBUTE_NORMAL,
758 w.FILE_SHARE_READ,830 w.FILE_SHARE_WRITE | w.FILE_SHARE_READ | w.FILE_SHARE_DELETE,
759 w.FILE_OPEN,831 creation,
760 w.FILE_NON_DIRECTORY_FILE | w.FILE_SYNCHRONOUS_IO_NONALERT,832 w.FILE_NON_DIRECTORY_FILE | w.FILE_SYNCHRONOUS_IO_NONALERT,
761 null,833 null,
762 0,834 0,
...@@ -771,6 +843,7 @@ pub const Dir = struct {...@@ -771,6 +843,7 @@ pub const Dir = struct {
771 w.STATUS.ACCESS_DENIED => return error.AccessDenied,843 w.STATUS.ACCESS_DENIED => return error.AccessDenied,
772 w.STATUS.PIPE_BUSY => return error.PipeBusy,844 w.STATUS.PIPE_BUSY => return error.PipeBusy,
773 w.STATUS.OBJECT_PATH_SYNTAX_BAD => unreachable,845 w.STATUS.OBJECT_PATH_SYNTAX_BAD => unreachable,
846 w.STATUS.OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
774 else => return w.unexpectedStatus(rc),847 else => return w.unexpectedStatus(rc),
775 }848 }
776 }849 }
...@@ -790,7 +863,10 @@ pub const Dir = struct {...@@ -790,7 +863,10 @@ pub const Dir = struct {
790 /// list the contents of a directory, open it with `openDirList`.863 /// list the contents of a directory, open it with `openDirList`.
791 ///864 ///
792 /// Call `close` on the result when done.865 /// Call `close` on the result when done.
866 ///
867 /// Asserts that the path parameter has no null bytes.
793 pub fn openDirTraverse(self: Dir, sub_path: []const u8) OpenError!Dir {868 pub fn openDirTraverse(self: Dir, sub_path: []const u8) OpenError!Dir {
869 if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0);
794 if (builtin.os == .windows) {870 if (builtin.os == .windows) {
795 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);871 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
796 return self.openDirTraverseW(&sub_path_w);872 return self.openDirTraverseW(&sub_path_w);
...@@ -805,7 +881,10 @@ pub const Dir = struct {...@@ -805,7 +881,10 @@ pub const Dir = struct {
805 /// same and may be more efficient.881 /// same and may be more efficient.
806 ///882 ///
807 /// Call `close` on the result when done.883 /// Call `close` on the result when done.
884 ///
885 /// Asserts that the path parameter has no null bytes.
808 pub fn openDirList(self: Dir, sub_path: []const u8) OpenError!Dir {886 pub fn openDirList(self: Dir, sub_path: []const u8) OpenError!Dir {
887 if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0);
809 if (builtin.os == .windows) {888 if (builtin.os == .windows) {
810 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);889 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
811 return self.openDirListW(&sub_path_w);890 return self.openDirListW(&sub_path_w);
...@@ -920,9 +999,12 @@ pub const Dir = struct {...@@ -920,9 +999,12 @@ pub const Dir = struct {
920 pub const DeleteFileError = os.UnlinkError;999 pub const DeleteFileError = os.UnlinkError;
9211000
922 /// Delete a file name and possibly the file it refers to, based on an open directory handle.1001 /// Delete a file name and possibly the file it refers to, based on an open directory handle.
1002 /// Asserts that the path parameter has no null bytes.
923 pub fn deleteFile(self: Dir, sub_path: []const u8) DeleteFileError!void {1003 pub fn deleteFile(self: Dir, sub_path: []const u8) DeleteFileError!void {
924 const sub_path_c = try os.toPosixPath(sub_path);1004 os.unlinkat(self.fd, sub_path, 0) catch |err| switch (err) {
925 return self.deleteFileC(&sub_path_c);1005 error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR
1006 else => |e| return e,
1007 };
926 }1008 }
9271009
928 /// Same as `deleteFile` except the parameter is null-terminated.1010 /// Same as `deleteFile` except the parameter is null-terminated.
...@@ -933,6 +1015,14 @@ pub const Dir = struct {...@@ -933,6 +1015,14 @@ pub const Dir = struct {
933 };1015 };
934 }1016 }
9351017
1018 /// Same as `deleteFile` except the parameter is WTF-16 encoded.
1019 pub fn deleteFileW(self: Dir, sub_path_w: [*:0]const u16) DeleteFileError!void {
1020 os.unlinkatW(self.fd, sub_path_w, 0) catch |err| switch (err) {
1021 error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR
1022 else => |e| return e,
1023 };
1024 }
1025
936 pub const DeleteDirError = error{1026 pub const DeleteDirError = error{
937 DirNotEmpty,1027 DirNotEmpty,
938 FileNotFound,1028 FileNotFound,
...@@ -951,7 +1041,9 @@ pub const Dir = struct {...@@ -951,7 +1041,9 @@ pub const Dir = struct {
9511041
952 /// Returns `error.DirNotEmpty` if the directory is not empty.1042 /// Returns `error.DirNotEmpty` if the directory is not empty.
953 /// To delete a directory recursively, see `deleteTree`.1043 /// To delete a directory recursively, see `deleteTree`.
1044 /// Asserts that the path parameter has no null bytes.
954 pub fn deleteDir(self: Dir, sub_path: []const u8) DeleteDirError!void {1045 pub fn deleteDir(self: Dir, sub_path: []const u8) DeleteDirError!void {
1046 if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0);
955 if (builtin.os == .windows) {1047 if (builtin.os == .windows) {
956 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);1048 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
957 return self.deleteDirW(&sub_path_w);1049 return self.deleteDirW(&sub_path_w);
...@@ -979,7 +1071,9 @@ pub const Dir = struct {...@@ -979,7 +1071,9 @@ pub const Dir = struct {
9791071
980 /// Read value of a symbolic link.1072 /// Read value of a symbolic link.
981 /// The return value is a slice of `buffer`, from index `0`.1073 /// The return value is a slice of `buffer`, from index `0`.
1074 /// Asserts that the path parameter has no null bytes.
982 pub fn readLink(self: Dir, sub_path: []const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {1075 pub fn readLink(self: Dir, sub_path: []const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
1076 if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0);
983 const sub_path_c = try os.toPosixPath(sub_path);1077 const sub_path_c = try os.toPosixPath(sub_path);
984 return self.readLinkC(&sub_path_c, buffer);1078 return self.readLinkC(&sub_path_c, buffer);
985 }1079 }
...@@ -1190,8 +1284,94 @@ pub const Dir = struct {...@@ -1190,8 +1284,94 @@ pub const Dir = struct {
1190 }1284 }
1191 }1285 }
1192 }1286 }
1287
1288 /// Writes content to the file system, creating a new file if it does not exist, truncating
1289 /// if it already exists.
1290 pub fn writeFile(self: Dir, sub_path: []const u8, data: []const u8) !void {
1291 var file = try self.createFile(sub_path, .{});
1292 defer file.close();
1293 try file.write(data);
1294 }
1193};1295};
11941296
1297/// Returns an handle to the current working directory that is open for traversal.
1298/// Closing the returned `Dir` is checked illegal behavior. Iterating over the result is illegal behavior.
1299/// On POSIX targets, this function is comptime-callable.
1300pub fn cwd() Dir {
1301 if (builtin.os == .windows) {
1302 return Dir{ .fd = os.windows.peb().ProcessParameters.CurrentDirectory.Handle };
1303 } else {
1304 return Dir{ .fd = os.AT_FDCWD };
1305 }
1306}
1307
1308/// Opens a file for reading or writing, without attempting to create a new file, based on an absolute path.
1309/// Call `File.close` to release the resource.
1310/// Asserts that the path is absolute. See `Dir.openFile` for a function that
1311/// operates on both absolute and relative paths.
1312/// Asserts that the path parameter has no null bytes. See `openFileAbsoluteC` for a function
1313/// that accepts a null-terminated path.
1314pub fn openFileAbsolute(absolute_path: []const u8, flags: File.OpenFlags) File.OpenError!File {
1315 assert(path.isAbsolute(absolute_path));
1316 return cwd().openFile(absolute_path, flags);
1317}
1318
1319/// Same as `openFileAbsolute` but the path parameter is null-terminated.
1320pub fn openFileAbsoluteC(absolute_path_c: [*:0]const u8, flags: File.OpenFlags) File.OpenError!File {
1321 assert(path.isAbsoluteC(absolute_path_c));
1322 return cwd().openFileC(absolute_path_c, flags);
1323}
1324
1325/// Same as `openFileAbsolute` but the path parameter is WTF-16 encoded.
1326pub fn openFileAbsoluteW(absolute_path_w: [*:0]const u16, flags: File.OpenFlags) File.OpenError!File {
1327 assert(path.isAbsoluteW(absolute_path_w));
1328 return cwd().openFileW(absolute_path_w, flags);
1329}
1330
1331/// Creates, opens, or overwrites a file with write access, based on an absolute path.
1332/// Call `File.close` to release the resource.
1333/// Asserts that the path is absolute. See `Dir.createFile` for a function that
1334/// operates on both absolute and relative paths.
1335/// Asserts that the path parameter has no null bytes. See `createFileAbsoluteC` for a function
1336/// that accepts a null-terminated path.
1337pub fn createFileAbsolute(absolute_path: []const u8, flags: File.CreateFlags) File.OpenError!File {
1338 assert(path.isAbsolute(absolute_path));
1339 return cwd().createFile(absolute_path, flags);
1340}
1341
1342/// Same as `createFileAbsolute` but the path parameter is null-terminated.
1343pub fn createFileAbsoluteC(absolute_path_c: [*:0]const u8, flags: File.CreateFlags) File.OpenError!File {
1344 assert(path.isAbsoluteC(absolute_path_c));
1345 return cwd().createFileC(absolute_path_c, flags);
1346}
1347
1348/// Same as `createFileAbsolute` but the path parameter is WTF-16 encoded.
1349pub fn createFileAbsoluteW(absolute_path_w: [*:0]const u16, flags: File.CreateFlags) File.OpenError!File {
1350 assert(path.isAbsoluteW(absolute_path_w));
1351 return cwd().createFileW(absolute_path_w, flags);
1352}
1353
1354/// Delete a file name and possibly the file it refers to, based on an absolute path.
1355/// Asserts that the path is absolute. See `Dir.deleteFile` for a function that
1356/// operates on both absolute and relative paths.
1357/// Asserts that the path parameter has no null bytes.
1358pub fn deleteFileAbsolute(absolute_path: []const u8) DeleteFileError!void {
1359 assert(path.isAbsolute(absolute_path));
1360 return cwd().deleteFile(absolute_path);
1361}
1362
1363/// Same as `deleteFileAbsolute` except the parameter is null-terminated.
1364pub fn deleteFileAbsoluteC(absolute_path_c: [*:0]const u8) DeleteFileError!void {
1365 assert(path.isAbsoluteC(absolute_path_c));
1366 return cwd().deleteFileC(absolute_path_c);
1367}
1368
1369/// Same as `deleteFileAbsolute` except the parameter is WTF-16 encoded.
1370pub fn deleteFileAbsoluteW(absolute_path_w: [*:0]const u16) DeleteFileError!void {
1371 assert(path.isAbsoluteW(absolute_path_w));
1372 return cwd().deleteFileW(absolute_path_w);
1373}
1374
1195pub const Walker = struct {1375pub const Walker = struct {
1196 stack: std.ArrayList(StackItem),1376 stack: std.ArrayList(StackItem),
1197 name_buffer: std.Buffer,1377 name_buffer: std.Buffer,
...@@ -1264,7 +1444,7 @@ pub const Walker = struct {...@@ -1264,7 +1444,7 @@ pub const Walker = struct {
1264pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {1444pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {
1265 assert(!mem.endsWith(u8, dir_path, path.sep_str));1445 assert(!mem.endsWith(u8, dir_path, path.sep_str));
12661446
1267 var dir = try Dir.cwd().openDirList(dir_path);1447 var dir = try cwd().openDirList(dir_path);
1268 errdefer dir.close();1448 errdefer dir.close();
12691449
1270 var name_buffer = try std.Buffer.init(allocator, dir_path);1450 var name_buffer = try std.Buffer.init(allocator, dir_path);
...@@ -1298,18 +1478,18 @@ pub const OpenSelfExeError = os.OpenError || os.windows.CreateFileError || SelfE...@@ -1298,18 +1478,18 @@ pub const OpenSelfExeError = os.OpenError || os.windows.CreateFileError || SelfE
12981478
1299pub fn openSelfExe() OpenSelfExeError!File {1479pub fn openSelfExe() OpenSelfExeError!File {
1300 if (builtin.os == .linux) {1480 if (builtin.os == .linux) {
1301 return File.openReadC("/proc/self/exe");1481 return openFileAbsoluteC("/proc/self/exe", .{});
1302 }1482 }
1303 if (builtin.os == .windows) {1483 if (builtin.os == .windows) {
1304 const wide_slice = selfExePathW();1484 const wide_slice = selfExePathW();
1305 const prefixed_path_w = try os.windows.wToPrefixedFileW(wide_slice);1485 const prefixed_path_w = try os.windows.wToPrefixedFileW(wide_slice);
1306 return Dir.cwd().openReadW(&prefixed_path_w);1486 return cwd().openReadW(&prefixed_path_w);
1307 }1487 }
1308 var buf: [MAX_PATH_BYTES]u8 = undefined;1488 var buf: [MAX_PATH_BYTES]u8 = undefined;
1309 const self_exe_path = try selfExePath(&buf);1489 const self_exe_path = try selfExePath(&buf);
1310 buf[self_exe_path.len] = 0;1490 buf[self_exe_path.len] = 0;
1311 // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/37311491 // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770
1312 return File.openReadC(@ptrCast([*:0]u8, self_exe_path.ptr));1492 return openFileAbsoluteC(@ptrCast([*:0]u8, self_exe_path.ptr), .{});
1313}1493}
13141494
1315test "openSelfExe" {1495test "openSelfExe" {
lib/std/fs/file.zig+56-73
...@@ -25,105 +25,87 @@ pub const File = struct {...@@ -25,105 +25,87 @@ pub const File = struct {
2525
26 pub const OpenError = windows.CreateFileError || os.OpenError;26 pub const OpenError = windows.CreateFileError || os.OpenError;
2727
28 /// Deprecated; call `std.fs.Dir.openRead` directly.28 /// TODO https://github.com/ziglang/zig/issues/3802
29 pub const OpenFlags = struct {
30 read: bool = true,
31 write: bool = false,
32 };
33
34 /// TODO https://github.com/ziglang/zig/issues/3802
35 pub const CreateFlags = struct {
36 /// Whether the file will be created with read access.
37 read: bool = false,
38
39 /// If the file already exists, and is a regular file, and the access
40 /// mode allows writing, it will be truncated to length 0.
41 truncate: bool = true,
42
43 /// Ensures that this open call creates the file, otherwise causes
44 /// `error.FileAlreadyExists` to be returned.
45 exclusive: bool = false,
46
47 /// For POSIX systems this is the file system mode the file will
48 /// be created with.
49 mode: Mode = default_mode,
50 };
51
52 /// Deprecated; call `std.fs.Dir.openFile` directly.
29 pub fn openRead(path: []const u8) OpenError!File {53 pub fn openRead(path: []const u8) OpenError!File {
30 return std.fs.Dir.cwd().openRead(path);54 return std.fs.cwd().openFile(path, .{});
31 }55 }
3256
33 /// Deprecated; call `std.fs.Dir.openReadC` directly.57 /// Deprecated; call `std.fs.Dir.openFileC` directly.
34 pub fn openReadC(path_c: [*:0]const u8) OpenError!File {58 pub fn openReadC(path_c: [*:0]const u8) OpenError!File {
35 return std.fs.Dir.cwd().openReadC(path_c);59 return std.fs.cwd().openFileC(path_c, .{});
36 }60 }
3761
38 /// Deprecated; call `std.fs.Dir.openReadW` directly.62 /// Deprecated; call `std.fs.Dir.openFileW` directly.
39 pub fn openReadW(path_w: [*]const u16) OpenError!File {63 pub fn openReadW(path_w: [*]const u16) OpenError!File {
40 return std.fs.Dir.cwd().openReadW(path_w);64 return std.fs.cwd().openFileW(path_w, .{});
41 }65 }
4266
43 /// Calls `openWriteMode` with `default_mode` for the mode.67 /// Deprecated; call `std.fs.Dir.createFile` directly.
44 /// TODO: deprecate this and move it to `std.fs.Dir`.
45 pub fn openWrite(path: []const u8) OpenError!File {68 pub fn openWrite(path: []const u8) OpenError!File {
46 return openWriteMode(path, default_mode);69 return std.fs.cwd().createFile(path, .{});
47 }70 }
4871
49 /// If the path does not exist it will be created.72 /// Deprecated; call `std.fs.Dir.createFile` directly.
50 /// If a file already exists in the destination it will be truncated.
51 /// Call close to clean up.
52 /// TODO: deprecate this and move it to `std.fs.Dir`.
53 pub fn openWriteMode(path: []const u8, file_mode: Mode) OpenError!File {73 pub fn openWriteMode(path: []const u8, file_mode: Mode) OpenError!File {
54 if (builtin.os == .windows) {74 return std.fs.cwd().createFile(path, .{ .mode = file_mode });
55 const path_w = try windows.sliceToPrefixedFileW(path);
56 return openWriteModeW(&path_w, file_mode);
57 }
58 const path_c = try os.toPosixPath(path);
59 return openWriteModeC(&path_c, file_mode);
60 }75 }
6176
62 /// Same as `openWriteMode` except `path` is null-terminated.77 /// Deprecated; call `std.fs.Dir.createFileC` directly.
63 /// TODO: deprecate this and move it to `std.fs.Dir`.78 pub fn openWriteModeC(path_c: [*:0]const u8, file_mode: Mode) OpenError!File {
64 pub fn openWriteModeC(path: [*:0]const u8, file_mode: Mode) OpenError!File {79 return std.fs.cwd().createFileC(path_c, .{ .mode = file_mode });
65 if (builtin.os == .windows) {
66 const path_w = try windows.cStrToPrefixedFileW(path);
67 return openWriteModeW(&path_w, file_mode);
68 }
69 const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0;
70 const flags = O_LARGEFILE | os.O_WRONLY | os.O_CREAT | os.O_CLOEXEC | os.O_TRUNC;
71 const fd = try os.openC(path, flags, file_mode);
72 return openHandle(fd);
73 }80 }
7481
75 /// Same as `openWriteMode` except `path` is null-terminated and UTF16LE encoded82 /// Deprecated; call `std.fs.Dir.createFileW` directly.
76 /// TODO: deprecate this and move it to `std.fs.Dir`.
77 pub fn openWriteModeW(path_w: [*:0]const u16, file_mode: Mode) OpenError!File {83 pub fn openWriteModeW(path_w: [*:0]const u16, file_mode: Mode) OpenError!File {
78 const handle = try windows.CreateFileW(84 return std.fs.cwd().createFileW(path_w, .{ .mode = file_mode });
79 path_w,
80 windows.GENERIC_WRITE,
81 windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
82 null,
83 windows.CREATE_ALWAYS,
84 windows.FILE_ATTRIBUTE_NORMAL,
85 null,
86 );
87 return openHandle(handle);
88 }85 }
8986
90 /// If the path does not exist it will be created.87 /// Deprecated; call `std.fs.Dir.createFile` directly.
91 /// If a file already exists in the destination this returns OpenError.PathAlreadyExists
92 /// Call close to clean up.
93 /// TODO: deprecate this and move it to `std.fs.Dir`.
94 pub fn openWriteNoClobber(path: []const u8, file_mode: Mode) OpenError!File {88 pub fn openWriteNoClobber(path: []const u8, file_mode: Mode) OpenError!File {
95 if (builtin.os == .windows) {89 return std.fs.cwd().createFile(path, .{
96 const path_w = try windows.sliceToPrefixedFileW(path);90 .mode = file_mode,
97 return openWriteNoClobberW(&path_w, file_mode);91 .exclusive = true,
98 }92 });
99 const path_c = try os.toPosixPath(path);
100 return openWriteNoClobberC(&path_c, file_mode);
101 }93 }
10294
103 /// TODO: deprecate this and move it to `std.fs.Dir`.95 /// Deprecated; call `std.fs.Dir.createFileC` directly.
104 pub fn openWriteNoClobberC(path: [*:0]const u8, file_mode: Mode) OpenError!File {96 pub fn openWriteNoClobberC(path_c: [*:0]const u8, file_mode: Mode) OpenError!File {
105 if (builtin.os == .windows) {97 return std.fs.cwd().createFileC(path_c, .{
106 const path_w = try windows.cStrToPrefixedFileW(path);98 .mode = file_mode,
107 return openWriteNoClobberW(&path_w, file_mode);99 .exclusive = true,
108 }100 });
109 const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0;
110 const flags = O_LARGEFILE | os.O_WRONLY | os.O_CREAT | os.O_CLOEXEC | os.O_EXCL;
111 const fd = try os.openC(path, flags, file_mode);
112 return openHandle(fd);
113 }101 }
114102
115 /// TODO: deprecate this and move it to `std.fs.Dir`.103 /// Deprecated; call `std.fs.Dir.createFileW` directly.
116 pub fn openWriteNoClobberW(path_w: [*:0]const u16, file_mode: Mode) OpenError!File {104 pub fn openWriteNoClobberW(path_w: [*:0]const u16, file_mode: Mode) OpenError!File {
117 const handle = try windows.CreateFileW(105 return std.fs.cwd().createFileW(path_w, .{
118 path_w,106 .mode = file_mode,
119 windows.GENERIC_WRITE,107 .exclusive = true,
120 windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,108 });
121 null,
122 windows.CREATE_NEW,
123 windows.FILE_ATTRIBUTE_NORMAL,
124 null,
125 );
126 return openHandle(handle);
127 }109 }
128110
129 pub fn openHandle(handle: os.fd_t) File {111 pub fn openHandle(handle: os.fd_t) File {
...@@ -246,6 +228,7 @@ pub const File = struct {...@@ -246,6 +228,7 @@ pub const File = struct {
246 windows.STATUS.SUCCESS => {},228 windows.STATUS.SUCCESS => {},
247 windows.STATUS.BUFFER_OVERFLOW => {},229 windows.STATUS.BUFFER_OVERFLOW => {},
248 windows.STATUS.INVALID_PARAMETER => unreachable,230 windows.STATUS.INVALID_PARAMETER => unreachable,
231 windows.STATUS.ACCESS_DENIED => return error.AccessDenied,
249 else => return windows.unexpectedStatus(rc),232 else => return windows.unexpectedStatus(rc),
250 }233 }
251 return Stat{234 return Stat{
lib/std/fs/path.zig+32-1
...@@ -128,6 +128,14 @@ test "join" {...@@ -128,6 +128,14 @@ test "join" {
128 testJoinPosix([_][]const u8{ "a/", "/c" }, "a/c");128 testJoinPosix([_][]const u8{ "a/", "/c" }, "a/c");
129}129}
130130
131pub fn isAbsoluteC(path_c: [*:0]const u8) bool {
132 if (builtin.os == .windows) {
133 return isAbsoluteWindowsC(path_c);
134 } else {
135 return isAbsolutePosixC(path_c);
136 }
137}
138
131pub fn isAbsolute(path: []const u8) bool {139pub fn isAbsolute(path: []const u8) bool {
132 if (builtin.os == .windows) {140 if (builtin.os == .windows) {
133 return isAbsoluteWindows(path);141 return isAbsoluteWindows(path);
...@@ -136,7 +144,7 @@ pub fn isAbsolute(path: []const u8) bool {...@@ -136,7 +144,7 @@ pub fn isAbsolute(path: []const u8) bool {
136 }144 }
137}145}
138146
139pub fn isAbsoluteW(path_w: [*]const u16) bool {147pub fn isAbsoluteW(path_w: [*:0]const u16) bool {
140 if (path_w[0] == '/')148 if (path_w[0] == '/')
141 return true;149 return true;
142150
...@@ -174,10 +182,33 @@ pub fn isAbsoluteWindows(path: []const u8) bool {...@@ -174,10 +182,33 @@ pub fn isAbsoluteWindows(path: []const u8) bool {
174 return false;182 return false;
175}183}
176184
185pub fn isAbsoluteWindowsC(path_c: [*:0]const u8) bool {
186 if (path_c[0] == '/')
187 return true;
188
189 if (path_c[0] == '\\') {
190 return true;
191 }
192 if (path_c[0] == 0 or path_c[1] == 0 or path_c[2] == 0) {
193 return false;
194 }
195 if (path_c[1] == ':') {
196 if (path_c[2] == '/')
197 return true;
198 if (path_c[2] == '\\')
199 return true;
200 }
201 return false;
202}
203
177pub fn isAbsolutePosix(path: []const u8) bool {204pub fn isAbsolutePosix(path: []const u8) bool {
178 return path[0] == sep_posix;205 return path[0] == sep_posix;
179}206}
180207
208pub fn isAbsolutePosixC(path_c: [*:0]const u8) bool {
209 return path_c[0] == sep_posix;
210}
211
181test "isAbsoluteWindows" {212test "isAbsoluteWindows" {
182 testIsAbsoluteWindows("/", true);213 testIsAbsoluteWindows("/", true);
183 testIsAbsoluteWindows("//", true);214 testIsAbsoluteWindows("//", true);
lib/std/io.zig+4-7
...@@ -61,17 +61,14 @@ pub const COutStream = @import("io/c_out_stream.zig").COutStream;...@@ -61,17 +61,14 @@ pub const COutStream = @import("io/c_out_stream.zig").COutStream;
61pub const InStream = @import("io/in_stream.zig").InStream;61pub const InStream = @import("io/in_stream.zig").InStream;
62pub const OutStream = @import("io/out_stream.zig").OutStream;62pub const OutStream = @import("io/out_stream.zig").OutStream;
6363
64/// TODO move this to `std.fs` and add a version to `std.fs.Dir`.64/// Deprecated; use `std.fs.Dir.writeFile`.
65pub fn writeFile(path: []const u8, data: []const u8) !void {65pub fn writeFile(path: []const u8, data: []const u8) !void {
66 var file = try File.openWrite(path);66 return fs.cwd().writeFile(path, data);
67 defer file.close();
68 try file.write(data);
69}67}
7068
71/// On success, caller owns returned buffer.69/// Deprecated; use `std.fs.Dir.readFileAlloc`.
72/// This function is deprecated; use `std.fs.Dir.readFileAlloc`.
73pub fn readFileAlloc(allocator: *mem.Allocator, path: []const u8) ![]u8 {70pub fn readFileAlloc(allocator: *mem.Allocator, path: []const u8) ![]u8 {
74 return fs.Dir.cwd().readFileAlloc(allocator, path, math.maxInt(usize));71 return fs.cwd().readFileAlloc(allocator, path, math.maxInt(usize));
75}72}
7673
77pub fn BufferedInStream(comptime Error: type) type {74pub fn BufferedInStream(comptime Error: type) type {
lib/std/io/test.zig+15-13
...@@ -14,12 +14,14 @@ test "write a file, read it, then delete it" {...@@ -14,12 +14,14 @@ test "write a file, read it, then delete it" {
14 var raw_bytes: [200 * 1024]u8 = undefined;14 var raw_bytes: [200 * 1024]u8 = undefined;
15 var allocator = &std.heap.FixedBufferAllocator.init(raw_bytes[0..]).allocator;15 var allocator = &std.heap.FixedBufferAllocator.init(raw_bytes[0..]).allocator;
1616
17 const cwd = fs.cwd();
18
17 var data: [1024]u8 = undefined;19 var data: [1024]u8 = undefined;
18 var prng = DefaultPrng.init(1234);20 var prng = DefaultPrng.init(1234);
19 prng.random.bytes(data[0..]);21 prng.random.bytes(data[0..]);
20 const tmp_file_name = "temp_test_file.txt";22 const tmp_file_name = "temp_test_file.txt";
21 {23 {
22 var file = try File.openWrite(tmp_file_name);24 var file = try cwd.createFile(tmp_file_name, .{});
23 defer file.close();25 defer file.close();
2426
25 var file_out_stream = file.outStream();27 var file_out_stream = file.outStream();
...@@ -32,8 +34,8 @@ test "write a file, read it, then delete it" {...@@ -32,8 +34,8 @@ test "write a file, read it, then delete it" {
32 }34 }
3335
34 {36 {
35 // make sure openWriteNoClobber doesn't harm the file37 // Make sure the exclusive flag is honored.
36 if (File.openWriteNoClobber(tmp_file_name, File.default_mode)) |file| {38 if (cwd.createFile(tmp_file_name, .{ .exclusive = true })) |file| {
37 unreachable;39 unreachable;
38 } else |err| {40 } else |err| {
39 std.debug.assert(err == File.OpenError.PathAlreadyExists);41 std.debug.assert(err == File.OpenError.PathAlreadyExists);
...@@ -41,7 +43,7 @@ test "write a file, read it, then delete it" {...@@ -41,7 +43,7 @@ test "write a file, read it, then delete it" {
41 }43 }
4244
43 {45 {
44 var file = try File.openRead(tmp_file_name);46 var file = try cwd.openFile(tmp_file_name, .{});
45 defer file.close();47 defer file.close();
4648
47 const file_size = try file.getEndPos();49 const file_size = try file.getEndPos();
...@@ -58,7 +60,7 @@ test "write a file, read it, then delete it" {...@@ -58,7 +60,7 @@ test "write a file, read it, then delete it" {
58 expect(mem.eql(u8, contents["begin".len .. contents.len - "end".len], data));60 expect(mem.eql(u8, contents["begin".len .. contents.len - "end".len], data));
59 expect(mem.eql(u8, contents[contents.len - "end".len ..], "end"));61 expect(mem.eql(u8, contents[contents.len - "end".len ..], "end"));
60 }62 }
61 try fs.deleteFile(tmp_file_name);63 try cwd.deleteFile(tmp_file_name);
62}64}
6365
64test "BufferOutStream" {66test "BufferOutStream" {
...@@ -274,7 +276,7 @@ test "BitOutStream" {...@@ -274,7 +276,7 @@ test "BitOutStream" {
274test "BitStreams with File Stream" {276test "BitStreams with File Stream" {
275 const tmp_file_name = "temp_test_file.txt";277 const tmp_file_name = "temp_test_file.txt";
276 {278 {
277 var file = try File.openWrite(tmp_file_name);279 var file = try fs.cwd().createFile(tmp_file_name, .{});
278 defer file.close();280 defer file.close();
279281
280 var file_out = file.outStream();282 var file_out = file.outStream();
...@@ -291,7 +293,7 @@ test "BitStreams with File Stream" {...@@ -291,7 +293,7 @@ test "BitStreams with File Stream" {
291 try bit_stream.flushBits();293 try bit_stream.flushBits();
292 }294 }
293 {295 {
294 var file = try File.openRead(tmp_file_name);296 var file = try fs.cwd().openFile(tmp_file_name, .{});
295 defer file.close();297 defer file.close();
296298
297 var file_in = file.inStream();299 var file_in = file.inStream();
...@@ -316,7 +318,7 @@ test "BitStreams with File Stream" {...@@ -316,7 +318,7 @@ test "BitStreams with File Stream" {
316318
317 expectError(error.EndOfStream, bit_stream.readBitsNoEof(u1, 1));319 expectError(error.EndOfStream, bit_stream.readBitsNoEof(u1, 1));
318 }320 }
319 try fs.deleteFile(tmp_file_name);321 try fs.cwd().deleteFile(tmp_file_name);
320}322}
321323
322fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime packing: io.Packing) !void {324fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime packing: io.Packing) !void {
...@@ -599,7 +601,7 @@ test "c out stream" {...@@ -599,7 +601,7 @@ test "c out stream" {
599 const out_file = std.c.fopen(filename, "w") orelse return error.UnableToOpenTestFile;601 const out_file = std.c.fopen(filename, "w") orelse return error.UnableToOpenTestFile;
600 defer {602 defer {
601 _ = std.c.fclose(out_file);603 _ = std.c.fclose(out_file);
602 fs.deleteFileC(filename) catch {};604 fs.cwd().deleteFileC(filename) catch {};
603 }605 }
604606
605 const out_stream = &io.COutStream.init(out_file).stream;607 const out_stream = &io.COutStream.init(out_file).stream;
...@@ -608,10 +610,10 @@ test "c out stream" {...@@ -608,10 +610,10 @@ test "c out stream" {
608610
609test "File seek ops" {611test "File seek ops" {
610 const tmp_file_name = "temp_test_file.txt";612 const tmp_file_name = "temp_test_file.txt";
611 var file = try File.openWrite(tmp_file_name);613 var file = try fs.cwd().createFile(tmp_file_name, .{});
612 defer {614 defer {
613 file.close();615 file.close();
614 fs.deleteFile(tmp_file_name) catch {};616 fs.cwd().deleteFile(tmp_file_name) catch {};
615 }617 }
616618
617 try file.write([_]u8{0x55} ** 8192);619 try file.write([_]u8{0x55} ** 8192);
...@@ -632,10 +634,10 @@ test "File seek ops" {...@@ -632,10 +634,10 @@ test "File seek ops" {
632634
633test "updateTimes" {635test "updateTimes" {
634 const tmp_file_name = "just_a_temporary_file.txt";636 const tmp_file_name = "just_a_temporary_file.txt";
635 var file = try File.openWrite(tmp_file_name);637 var file = try fs.cwd().createFile(tmp_file_name, .{ .read = true });
636 defer {638 defer {
637 file.close();639 file.close();
638 std.fs.deleteFile(tmp_file_name) catch {};640 std.fs.cwd().deleteFile(tmp_file_name) catch {};
639 }641 }
640 var stat_old = try file.stat();642 var stat_old = try file.stat();
641 // Set atime and mtime to 5s before643 // Set atime and mtime to 5s before
lib/std/net.zig+2-2
...@@ -812,7 +812,7 @@ fn linuxLookupNameFromHosts(...@@ -812,7 +812,7 @@ fn linuxLookupNameFromHosts(
812 family: os.sa_family_t,812 family: os.sa_family_t,
813 port: u16,813 port: u16,
814) !void {814) !void {
815 const file = fs.File.openReadC("/etc/hosts") catch |err| switch (err) {815 const file = fs.openFileAbsoluteC("/etc/hosts", .{}) catch |err| switch (err) {
816 error.FileNotFound,816 error.FileNotFound,
817 error.NotDir,817 error.NotDir,
818 error.AccessDenied,818 error.AccessDenied,
...@@ -1006,7 +1006,7 @@ fn getResolvConf(allocator: *mem.Allocator, rc: *ResolvConf) !void {...@@ -1006,7 +1006,7 @@ fn getResolvConf(allocator: *mem.Allocator, rc: *ResolvConf) !void {
1006 };1006 };
1007 errdefer rc.deinit();1007 errdefer rc.deinit();
10081008
1009 const file = fs.File.openReadC("/etc/resolv.conf") catch |err| switch (err) {1009 const file = fs.openFileAbsoluteC("/etc/resolv.conf", .{}) catch |err| switch (err) {
1010 error.FileNotFound,1010 error.FileNotFound,
1011 error.NotDir,1011 error.NotDir,
1012 error.AccessDenied,1012 error.AccessDenied,
lib/std/os.zig+13-6
...@@ -798,7 +798,7 @@ pub fn execvpeC(file: [*:0]const u8, child_argv: [*:null]const ?[*:0]const u8, e...@@ -798,7 +798,7 @@ pub fn execvpeC(file: [*:0]const u8, child_argv: [*:null]const ?[*:0]const u8, e
798 path_buf[search_path.len] = '/';798 path_buf[search_path.len] = '/';
799 mem.copy(u8, path_buf[search_path.len + 1 ..], file_slice);799 mem.copy(u8, path_buf[search_path.len + 1 ..], file_slice);
800 path_buf[search_path.len + file_slice.len + 1] = 0;800 path_buf[search_path.len + file_slice.len + 1] = 0;
801 // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3731801 // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770
802 err = execveC(@ptrCast([*:0]u8, &path_buf), child_argv, envp);802 err = execveC(@ptrCast([*:0]u8, &path_buf), child_argv, envp);
803 switch (err) {803 switch (err) {
804 error.AccessDenied => seen_eacces = true,804 error.AccessDenied => seen_eacces = true,
...@@ -834,7 +834,7 @@ pub fn execvpe(...@@ -834,7 +834,7 @@ pub fn execvpe(
834 @memcpy(arg_buf.ptr, arg.ptr, arg.len);834 @memcpy(arg_buf.ptr, arg.ptr, arg.len);
835 arg_buf[arg.len] = 0;835 arg_buf[arg.len] = 0;
836836
837 // TODO avoid @ptrCast using slice syntax with https://github.com/ziglang/zig/issues/3731837 // TODO avoid @ptrCast using slice syntax with https://github.com/ziglang/zig/issues/3770
838 argv_buf[i] = @ptrCast([*:0]u8, arg_buf.ptr);838 argv_buf[i] = @ptrCast([*:0]u8, arg_buf.ptr);
839 }839 }
840 argv_buf[argv_slice.len] = null;840 argv_buf[argv_slice.len] = null;
...@@ -842,7 +842,7 @@ pub fn execvpe(...@@ -842,7 +842,7 @@ pub fn execvpe(
842 const envp_buf = try createNullDelimitedEnvMap(allocator, env_map);842 const envp_buf = try createNullDelimitedEnvMap(allocator, env_map);
843 defer freeNullDelimitedEnvMap(allocator, envp_buf);843 defer freeNullDelimitedEnvMap(allocator, envp_buf);
844844
845 // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3731845 // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770
846 const argv_ptr = @ptrCast([*:null]?[*:0]u8, argv_buf.ptr);846 const argv_ptr = @ptrCast([*:null]?[*:0]u8, argv_buf.ptr);
847847
848 return execvpeC(argv_buf.ptr[0].?, argv_ptr, envp_buf.ptr);848 return execvpeC(argv_buf.ptr[0].?, argv_ptr, envp_buf.ptr);
...@@ -863,12 +863,12 @@ pub fn createNullDelimitedEnvMap(allocator: *mem.Allocator, env_map: *const std....@@ -863,12 +863,12 @@ pub fn createNullDelimitedEnvMap(allocator: *mem.Allocator, env_map: *const std.
863 @memcpy(env_buf.ptr + pair.key.len + 1, pair.value.ptr, pair.value.len);863 @memcpy(env_buf.ptr + pair.key.len + 1, pair.value.ptr, pair.value.len);
864 env_buf[env_buf.len - 1] = 0;864 env_buf[env_buf.len - 1] = 0;
865865
866 // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3731866 // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770
867 envp_buf[i] = @ptrCast([*:0]u8, env_buf.ptr);867 envp_buf[i] = @ptrCast([*:0]u8, env_buf.ptr);
868 }868 }
869 assert(i == envp_count);869 assert(i == envp_count);
870 }870 }
871 // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3731871 // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770
872 assert(envp_buf[envp_count] == null);872 assert(envp_buf[envp_count] == null);
873 return @ptrCast([*:null]?[*:0]u8, envp_buf.ptr)[0..envp_count];873 return @ptrCast([*:null]?[*:0]u8, envp_buf.ptr)[0..envp_count];
874}874}
...@@ -1087,7 +1087,9 @@ pub const UnlinkatError = UnlinkError || error{...@@ -1087,7 +1087,9 @@ pub const UnlinkatError = UnlinkError || error{
1087};1087};
10881088
1089/// Delete a file name and possibly the file it refers to, based on an open directory handle.1089/// Delete a file name and possibly the file it refers to, based on an open directory handle.
1090/// Asserts that the path parameter has no null bytes.
1090pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void {1091pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void {
1092 if (std.debug.runtime_safety) for (file_path) |byte| assert(byte != 0);
1091 if (builtin.os == .windows) {1093 if (builtin.os == .windows) {
1092 const file_path_w = try windows.sliceToPrefixedFileW(file_path);1094 const file_path_w = try windows.sliceToPrefixedFileW(file_path);
1093 return unlinkatW(dirfd, &file_path_w, flags);1095 return unlinkatW(dirfd, &file_path_w, flags);
...@@ -2026,7 +2028,10 @@ pub fn waitpid(pid: i32, flags: u32) u32 {...@@ -2026,7 +2028,10 @@ pub fn waitpid(pid: i32, flags: u32) u32 {
2026 }2028 }
2027}2029}
20282030
2029pub const FStatError = error{SystemResources} || UnexpectedError;2031pub const FStatError = error{
2032 SystemResources,
2033 AccessDenied,
2034} || UnexpectedError;
20302035
2031pub fn fstat(fd: fd_t) FStatError!Stat {2036pub fn fstat(fd: fd_t) FStatError!Stat {
2032 var stat: Stat = undefined;2037 var stat: Stat = undefined;
...@@ -2036,6 +2041,7 @@ pub fn fstat(fd: fd_t) FStatError!Stat {...@@ -2036,6 +2041,7 @@ pub fn fstat(fd: fd_t) FStatError!Stat {
2036 EINVAL => unreachable,2041 EINVAL => unreachable,
2037 EBADF => unreachable, // Always a race condition.2042 EBADF => unreachable, // Always a race condition.
2038 ENOMEM => return error.SystemResources,2043 ENOMEM => return error.SystemResources,
2044 EACCES => return error.AccessDenied,
2039 else => |err| return unexpectedErrno(err),2045 else => |err| return unexpectedErrno(err),
2040 }2046 }
2041 }2047 }
...@@ -2045,6 +2051,7 @@ pub fn fstat(fd: fd_t) FStatError!Stat {...@@ -2045,6 +2051,7 @@ pub fn fstat(fd: fd_t) FStatError!Stat {
2045 EINVAL => unreachable,2051 EINVAL => unreachable,
2046 EBADF => unreachable, // Always a race condition.2052 EBADF => unreachable, // Always a race condition.
2047 ENOMEM => return error.SystemResources,2053 ENOMEM => return error.SystemResources,
2054 EACCES => return error.AccessDenied,
2048 else => |err| return unexpectedErrno(err),2055 else => |err| return unexpectedErrno(err),
2049 }2056 }
2050}2057}
lib/std/os/linux/test.zig+3-4
...@@ -4,6 +4,7 @@ const linux = std.os.linux;...@@ -4,6 +4,7 @@ const linux = std.os.linux;
4const mem = std.mem;4const mem = std.mem;
5const elf = std.elf;5const elf = std.elf;
6const expect = std.testing.expect;6const expect = std.testing.expect;
7const fs = std.fs;
78
8test "getpid" {9test "getpid" {
9 expect(linux.getpid() != 0);10 expect(linux.getpid() != 0);
...@@ -45,14 +46,12 @@ test "timer" {...@@ -45,14 +46,12 @@ test "timer" {
45 err = linux.epoll_wait(@intCast(i32, epoll_fd), @ptrCast([*]linux.epoll_event, &events), 8, -1);46 err = linux.epoll_wait(@intCast(i32, epoll_fd), @ptrCast([*]linux.epoll_event, &events), 8, -1);
46}47}
4748
48const File = std.fs.File;
49
50test "statx" {49test "statx" {
51 const tmp_file_name = "just_a_temporary_file.txt";50 const tmp_file_name = "just_a_temporary_file.txt";
52 var file = try File.openWrite(tmp_file_name);51 var file = try fs.cwd().createFile(tmp_file_name, .{});
53 defer {52 defer {
54 file.close();53 file.close();
55 std.fs.deleteFile(tmp_file_name) catch {};54 fs.cwd().deleteFile(tmp_file_name) catch {};
56 }55 }
5756
58 var statx_buf: linux.Statx = undefined;57 var statx_buf: linux.Statx = undefined;
lib/std/os/test.zig+2-2
...@@ -20,7 +20,7 @@ test "makePath, put some files in it, deleteTree" {...@@ -20,7 +20,7 @@ test "makePath, put some files in it, deleteTree" {
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");
23 if (fs.Dir.cwd().openDirTraverse("os_test_tmp")) |dir| {23 if (fs.cwd().openDirTraverse("os_test_tmp")) |dir| {
24 @panic("expected error");24 @panic("expected error");
25 } else |err| {25 } else |err| {
26 expect(err == error.FileNotFound);26 expect(err == error.FileNotFound);
...@@ -111,7 +111,7 @@ test "AtomicFile" {...@@ -111,7 +111,7 @@ test "AtomicFile" {
111 const content = try io.readFileAlloc(allocator, test_out_file);111 const content = try io.readFileAlloc(allocator, test_out_file);
112 expect(mem.eql(u8, content, test_content));112 expect(mem.eql(u8, content, test_content));
113113
114 try fs.deleteFile(test_out_file);114 try fs.cwd().deleteFile(test_out_file);
115}115}
116116
117test "thread local storage" {117test "thread local storage" {
lib/std/pdb.zig+2-1
...@@ -6,6 +6,7 @@ const mem = std.mem;...@@ -6,6 +6,7 @@ const mem = std.mem;
6const os = std.os;6const os = std.os;
7const warn = std.debug.warn;7const warn = std.debug.warn;
8const coff = std.coff;8const coff = std.coff;
9const fs = std.fs;
9const File = std.fs.File;10const File = std.fs.File;
1011
11const ArrayList = std.ArrayList;12const ArrayList = std.ArrayList;
...@@ -469,7 +470,7 @@ pub const Pdb = struct {...@@ -469,7 +470,7 @@ pub const Pdb = struct {
469 msf: Msf,470 msf: Msf,
470471
471 pub fn openFile(self: *Pdb, coff_ptr: *coff.Coff, file_name: []u8) !void {472 pub fn openFile(self: *Pdb, coff_ptr: *coff.Coff, file_name: []u8) !void {
472 self.in_file = try File.openRead(file_name);473 self.in_file = try fs.cwd().openFile(file_name, .{});
473 self.allocator = coff_ptr.allocator;474 self.allocator = coff_ptr.allocator;
474 self.coff = coff_ptr;475 self.coff = coff_ptr;
475476
src-self-hosted/main.zig+1-1
...@@ -702,7 +702,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro...@@ -702,7 +702,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro
702 max_src_size,702 max_src_size,
703 ) catch |err| switch (err) {703 ) catch |err| switch (err) {
704 error.IsDir, error.AccessDenied => {704 error.IsDir, error.AccessDenied => {
705 var dir = try fs.Dir.cwd().openDirList(file_path);705 var dir = try fs.cwd().openDirList(file_path);
706 defer dir.close();706 defer dir.close();
707707
708 var group = event.Group(FmtError!void).init(fmt.allocator);708 var group = event.Group(FmtError!void).init(fmt.allocator);
src-self-hosted/stage1.zig+1-1
...@@ -279,7 +279,7 @@ fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtError!void...@@ -279,7 +279,7 @@ fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtError!void
279 const source_code = io.readFileAlloc(fmt.allocator, file_path) catch |err| switch (err) {279 const source_code = io.readFileAlloc(fmt.allocator, file_path) catch |err| switch (err) {
280 error.IsDir, error.AccessDenied => {280 error.IsDir, error.AccessDenied => {
281 // TODO make event based (and dir.next())281 // TODO make event based (and dir.next())
282 var dir = try fs.Dir.cwd().openDirList(file_path);282 var dir = try fs.cwd().openDirList(file_path);
283 defer dir.close();283 defer dir.close();
284284
285 var dir_it = dir.iterate();285 var dir_it = dir.iterate();
test/standalone/cat/main.zig+5-3
...@@ -1,7 +1,7 @@...@@ -1,7 +1,7 @@
1const std = @import("std");1const std = @import("std");
2const io = std.io;2const io = std.io;
3const process = std.process;3const process = std.process;
4const File = std.fs.File;4const fs = std.fs;
5const mem = std.mem;5const mem = std.mem;
6const warn = std.debug.warn;6const warn = std.debug.warn;
7const allocator = std.debug.global_allocator;7const allocator = std.debug.global_allocator;
...@@ -12,6 +12,8 @@ pub fn main() !void {...@@ -12,6 +12,8 @@ pub fn main() !void {
12 var catted_anything = false;12 var catted_anything = false;
13 const stdout_file = io.getStdOut();13 const stdout_file = io.getStdOut();
1414
15 const cwd = fs.cwd();
16
15 while (args_it.next(allocator)) |arg_or_err| {17 while (args_it.next(allocator)) |arg_or_err| {
16 const arg = try unwrapArg(arg_or_err);18 const arg = try unwrapArg(arg_or_err);
17 if (mem.eql(u8, arg, "-")) {19 if (mem.eql(u8, arg, "-")) {
...@@ -20,7 +22,7 @@ pub fn main() !void {...@@ -20,7 +22,7 @@ pub fn main() !void {
20 } else if (arg[0] == '-') {22 } else if (arg[0] == '-') {
21 return usage(exe);23 return usage(exe);
22 } else {24 } else {
23 const file = File.openRead(arg) catch |err| {25 const file = cwd.openFile(arg, .{}) catch |err| {
24 warn("Unable to open file: {}\n", @errorName(err));26 warn("Unable to open file: {}\n", @errorName(err));
25 return err;27 return err;
26 };28 };
...@@ -40,7 +42,7 @@ fn usage(exe: []const u8) !void {...@@ -40,7 +42,7 @@ fn usage(exe: []const u8) !void {
40 return error.Invalid;42 return error.Invalid;
41}43}
4244
43fn cat_file(stdout: File, file: File) !void {45fn cat_file(stdout: fs.File, file: fs.File) !void {
44 var buf: [1024 * 4]u8 = undefined;46 var buf: [1024 * 4]u8 = undefined;
4547
46 while (true) {48 while (true) {