authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-03-11 13:06:30-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-03-11 13:06:30-04:00
logd96b6c0d9f7a4fc6e7e28dd92d18e548f00885de
tree1c2774d9d9ffe279fa1ae330eec529dbdc9cb994
parented13cffca4c42718cc7239faf7aab642ac67ef77
signaturelock-open Commit is signed but in an unrecognized format.

fix footguns in File readAll functions


4 files changed, 75 insertions(+), 37 deletions(-)

lib/std/fs.zig+12-21
...@@ -96,6 +96,7 @@ pub fn updateFile(source_path: []const u8, dest_path: []const u8) !PrevStatus {...@@ -96,6 +96,7 @@ pub fn updateFile(source_path: []const u8, dest_path: []const u8) !PrevStatus {
96/// atime, and mode of the source file so that the next call to `updateFile` will not need a copy.96/// atime, and mode of the source file so that the next call to `updateFile` will not need a copy.
97/// Returns the previous status of the file before updating.97/// Returns the previous status of the file before updating.
98/// If any of the directories do not exist for dest_path, they are created.98/// If any of the directories do not exist for dest_path, they are created.
99/// TODO rework this to integrate with Dir
99pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?File.Mode) !PrevStatus {100pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?File.Mode) !PrevStatus {
100 const my_cwd = cwd();101 const my_cwd = cwd();
101102
...@@ -141,29 +142,25 @@ pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?Fil...@@ -141,29 +142,25 @@ pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?Fil
141/// there is a possibility of power loss or application termination leaving temporary files present142/// there is a possibility of power loss or application termination leaving temporary files present
142/// in the same directory as dest_path.143/// in the same directory as dest_path.
143/// Destination file will have the same mode as the source file.144/// Destination file will have the same mode as the source file.
145/// TODO rework this to integrate with Dir
144pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void {146pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void {
145 var in_file = try cwd().openFile(source_path, .{});147 var in_file = try cwd().openFile(source_path, .{});
146 defer in_file.close();148 defer in_file.close();
147149
148 const mode = try in_file.mode();150 const stat = try in_file.stat();
149 const in_stream = &in_file.inStream().stream;
150151
151 var atomic_file = try AtomicFile.init(dest_path, mode);152 var atomic_file = try AtomicFile.init(dest_path, stat.mode);
152 defer atomic_file.deinit();153 defer atomic_file.deinit();
153154
154 var buf: [mem.page_size]u8 = undefined;155 try atomic_file.file.writeFileAll(in_file, .{ .in_len = stat.size });
155 while (true) {156 return atomic_file.finish();
156 const amt = try in_stream.readFull(buf[0..]);
157 try atomic_file.file.write(buf[0..amt]);
158 if (amt != buf.len) {
159 return atomic_file.finish();
160 }
161 }
162}157}
163158
164/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is159/// Guaranteed to be atomic.
165/// merged and readily available,160/// On Linux, until https://patchwork.kernel.org/patch/9636735/ is merged and readily available,
166/// there is a possibility of power loss or application termination leaving temporary files present161/// there is a possibility of power loss or application termination leaving temporary files present
162/// in the same directory as dest_path.
163/// TODO rework this to integrate with Dir
167pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void {164pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void {
168 var in_file = try cwd().openFile(source_path, .{});165 var in_file = try cwd().openFile(source_path, .{});
169 defer in_file.close();166 defer in_file.close();
...@@ -171,14 +168,8 @@ pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.M...@@ -171,14 +168,8 @@ pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.M
171 var atomic_file = try AtomicFile.init(dest_path, mode);168 var atomic_file = try AtomicFile.init(dest_path, mode);
172 defer atomic_file.deinit();169 defer atomic_file.deinit();
173170
174 var buf: [mem.page_size * 6]u8 = undefined;171 try atomic_file.file.writeFileAll(in_file, .{});
175 while (true) {172 return atomic_file.finish();
176 const amt = try in_file.read(buf[0..]);
177 try atomic_file.file.write(buf[0..amt]);
178 if (amt != buf.len) {
179 return atomic_file.finish();
180 }
181 }
182}173}
183174
184/// TODO update this API to avoid a getrandom syscall for every operation. It175/// TODO update this API to avoid a getrandom syscall for every operation. It
lib/std/fs/file.zig+33-9
...@@ -250,11 +250,16 @@ pub const File = struct {...@@ -250,11 +250,16 @@ pub const File = struct {
250 }250 }
251 }251 }
252252
253 pub fn readAll(self: File, buffer: []u8) ReadError!void {253 /// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it
254 /// means the file reached the end. Reaching the end of a file is not an error condition.
255 pub fn readAll(self: File, buffer: []u8) ReadError!usize {
254 var index: usize = 0;256 var index: usize = 0;
255 while (index < buffer.len) {257 while (index != buffer.len) {
256 index += try self.read(buffer[index..]);258 const amt = try self.read(buffer[index..]);
259 if (amt == 0) break;
260 index += amt;
257 }261 }
262 return index;
258 }263 }
259264
260 pub fn pread(self: File, buffer: []u8, offset: u64) PReadError!usize {265 pub fn pread(self: File, buffer: []u8, offset: u64) PReadError!usize {
...@@ -265,11 +270,16 @@ pub const File = struct {...@@ -265,11 +270,16 @@ pub const File = struct {
265 }270 }
266 }271 }
267272
268 pub fn preadAll(self: File, buffer: []u8, offset: u64) PReadError!void {273 /// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it
274 /// means the file reached the end. Reaching the end of a file is not an error condition.
275 pub fn preadAll(self: File, buffer: []u8, offset: u64) PReadError!usize {
269 var index: usize = 0;276 var index: usize = 0;
270 while (index < buffer.len) {277 while (index != buffer.len) {
271 index += try self.pread(buffer[index..], offset + index);278 const amt = try self.pread(buffer[index..], offset + index);
279 if (amt == 0) break;
280 index += amt;
272 }281 }
282 return index;
273 }283 }
274284
275 pub fn readv(self: File, iovecs: []const os.iovec) ReadError!usize {285 pub fn readv(self: File, iovecs: []const os.iovec) ReadError!usize {
...@@ -280,19 +290,27 @@ pub const File = struct {...@@ -280,19 +290,27 @@ pub const File = struct {
280 }290 }
281 }291 }
282292
293 /// Returns the number of bytes read. If the number read is smaller than the total bytes
294 /// from all the buffers, it means the file reached the end. Reaching the end of a file
295 /// is not an error condition.
283 /// The `iovecs` parameter is mutable because this function needs to mutate the fields in296 /// The `iovecs` parameter is mutable because this function needs to mutate the fields in
284 /// order to handle partial reads from the underlying OS layer.297 /// order to handle partial reads from the underlying OS layer.
285 pub fn readvAll(self: File, iovecs: []os.iovec) ReadError!void {298 pub fn readvAll(self: File, iovecs: []os.iovec) ReadError!usize {
286 if (iovecs.len == 0) return;299 if (iovecs.len == 0) return;
287300
288 var i: usize = 0;301 var i: usize = 0;
302 var off: usize = 0;
289 while (true) {303 while (true) {
290 var amt = try self.readv(iovecs[i..]);304 var amt = try self.readv(iovecs[i..]);
305 var eof = amt == 0;
306 off += amt;
291 while (amt >= iovecs[i].iov_len) {307 while (amt >= iovecs[i].iov_len) {
292 amt -= iovecs[i].iov_len;308 amt -= iovecs[i].iov_len;
293 i += 1;309 i += 1;
294 if (i >= iovecs.len) return;310 if (i >= iovecs.len) return off;
311 eof = false;
295 }312 }
313 if (eof) return off;
296 iovecs[i].iov_base += amt;314 iovecs[i].iov_base += amt;
297 iovecs[i].iov_len -= amt;315 iovecs[i].iov_len -= amt;
298 }316 }
...@@ -306,6 +324,9 @@ pub const File = struct {...@@ -306,6 +324,9 @@ pub const File = struct {
306 }324 }
307 }325 }
308326
327 /// Returns the number of bytes read. If the number read is smaller than the total bytes
328 /// from all the buffers, it means the file reached the end. Reaching the end of a file
329 /// is not an error condition.
309 /// The `iovecs` parameter is mutable because this function needs to mutate the fields in330 /// The `iovecs` parameter is mutable because this function needs to mutate the fields in
310 /// order to handle partial reads from the underlying OS layer.331 /// order to handle partial reads from the underlying OS layer.
311 pub fn preadvAll(self: File, iovecs: []const os.iovec, offset: u64) PReadError!void {332 pub fn preadvAll(self: File, iovecs: []const os.iovec, offset: u64) PReadError!void {
...@@ -315,12 +336,15 @@ pub const File = struct {...@@ -315,12 +336,15 @@ pub const File = struct {
315 var off: usize = 0;336 var off: usize = 0;
316 while (true) {337 while (true) {
317 var amt = try self.preadv(iovecs[i..], offset + off);338 var amt = try self.preadv(iovecs[i..], offset + off);
339 var eof = amt == 0;
318 off += amt;340 off += amt;
319 while (amt >= iovecs[i].iov_len) {341 while (amt >= iovecs[i].iov_len) {
320 amt -= iovecs[i].iov_len;342 amt -= iovecs[i].iov_len;
321 i += 1;343 i += 1;
322 if (i >= iovecs.len) return;344 if (i >= iovecs.len) return off;
345 eof = false;
323 }346 }
347 if (eof) return off;
324 iovecs[i].iov_base += amt;348 iovecs[i].iov_base += amt;
325 iovecs[i].iov_len -= amt;349 iovecs[i].iov_len -= amt;
326 }350 }
lib/std/io/in_stream.zig+1-4
...@@ -28,10 +28,7 @@ pub fn InStream(...@@ -28,10 +28,7 @@ pub fn InStream(
28 return readFn(self.context, buffer);28 return readFn(self.context, buffer);
29 }29 }
3030
31 /// Deprecated: use `readAll`.31 /// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it
32 pub const readFull = readAll;
33
34 /// Returns the number of bytes read. If the number read is smaller than buf.len, it
35 /// means the stream reached the end. Reaching the end of a stream is not an error32 /// means the stream reached the end. Reaching the end of a stream is not an error
36 /// condition.33 /// condition.
37 pub fn readAll(self: Self, buffer: []u8) Error!usize {34 pub fn readAll(self: Self, buffer: []u8) Error!usize {
lib/std/os/test.zig+29-3
...@@ -95,15 +95,41 @@ test "sendfile" {...@@ -95,15 +95,41 @@ test "sendfile" {
95 },95 },
96 };96 };
9797
98 var written_buf: [header1.len + header2.len + 10 + trailer1.len + trailer2.len]u8 = undefined;98 var written_buf: [100]u8 = undefined;
99 try dest_file.writeFileAll(src_file, .{99 try dest_file.writeFileAll(src_file, .{
100 .in_offset = 1,100 .in_offset = 1,
101 .in_len = 10,101 .in_len = 10,
102 .headers_and_trailers = &hdtr,102 .headers_and_trailers = &hdtr,
103 .header_count = 2,103 .header_count = 2,
104 });104 });
105 try dest_file.preadAll(&written_buf, 0);105 const amt = try dest_file.preadAll(&written_buf, 0);
106 expect(mem.eql(u8, &written_buf, "header1\nsecond header\nine1\nsecontrailer1\nsecond trailer\n"));106 expect(mem.eql(u8, written_buf[0..amt], "header1\nsecond header\nine1\nsecontrailer1\nsecond trailer\n"));
107}
108
109test "fs.copyFile" {
110 const data = "u6wj+JmdF3qHsFPE BUlH2g4gJCmEz0PP";
111 const src_file = "tmp_test_copy_file.txt";
112 const dest_file = "tmp_test_copy_file2.txt";
113 const dest_file2 = "tmp_test_copy_file3.txt";
114
115 try fs.cwd().writeFile(src_file, data);
116 defer fs.cwd().deleteFile(src_file) catch {};
117
118 try fs.copyFile(src_file, dest_file);
119 defer fs.cwd().deleteFile(dest_file) catch {};
120
121 try fs.copyFileMode(src_file, dest_file2, File.default_mode);
122 defer fs.cwd().deleteFile(dest_file2) catch {};
123
124 try expectFileContents(dest_file, data);
125 try expectFileContents(dest_file2, data);
126}
127
128fn expectFileContents(file_path: []const u8, data: []const u8) !void {
129 const contents = try fs.cwd().readFileAlloc(testing.allocator, file_path, 1000);
130 defer testing.allocator.free(contents);
131
132 testing.expectEqualSlices(u8, data, contents);
107}133}
108134
109test "std.Thread.getCurrentId" {135test "std.Thread.getCurrentId" {