authorgravatar for thatlemon@gmail.comLemonBoy <thatlemon@gmail.com> 2020-09-01 18:48:43+02:00
committergravatar for thatlemon@gmail.comLemonBoy <thatlemon@gmail.com> 2020-09-02 11:11:57+02:00
log73a8c9beaa63c48b6ddaeec8d2a67b239f0dec92
tree38e42ddabbcb8c6306d7f225154d26d3db1f58d9
parent26140678a5c72604f2baac3cb9d1e5f7b37b6b8d

std: Don't trust stat() size in readAllAlloc fns

Some files such as the ones in /proc report a st_size of zero, try to read the file anyway if we hit that case.

5 files changed, 28 insertions(+), 17 deletions(-)

lib/std/fs.zig+1-3
...@@ -1454,9 +1454,7 @@ pub const Dir = struct {...@@ -1454,9 +1454,7 @@ pub const Dir = struct {
1454 var file = try self.openFile(file_path, .{});1454 var file = try self.openFile(file_path, .{});
1455 defer file.close();1455 defer file.close();
14561456
1457 const stat_size = try file.getEndPos();1457 return file.readAllAllocOptions(allocator, max_bytes, alignment, optional_sentinel);
1458
1459 return file.readAllAllocOptions(allocator, stat_size, max_bytes, alignment, optional_sentinel);
1460 }1458 }
14611459
1462 pub const DeleteTreeError = error{1460 pub const DeleteTreeError = error{
lib/std/fs/file.zig+21-7
...@@ -365,8 +365,8 @@ pub const File = struct {...@@ -365,8 +365,8 @@ pub const File = struct {
365365
366 /// On success, caller owns returned buffer.366 /// On success, caller owns returned buffer.
367 /// If the file is larger than `max_bytes`, returns `error.FileTooBig`.367 /// If the file is larger than `max_bytes`, returns `error.FileTooBig`.
368 pub fn readAllAlloc(self: File, allocator: *mem.Allocator, stat_size: u64, max_bytes: usize) ![]u8 {368 pub fn readAllAlloc(self: File, allocator: *mem.Allocator, max_bytes: usize) ![]u8 {
369 return self.readAllAllocOptions(allocator, stat_size, max_bytes, @alignOf(u8), null);369 return self.readAllAllocOptions(allocator, max_bytes, @alignOf(u8), null);
370 }370 }
371371
372 /// On success, caller owns returned buffer.372 /// On success, caller owns returned buffer.
...@@ -375,19 +375,33 @@ pub const File = struct {...@@ -375,19 +375,33 @@ pub const File = struct {
375 pub fn readAllAllocOptions(375 pub fn readAllAllocOptions(
376 self: File,376 self: File,
377 allocator: *mem.Allocator,377 allocator: *mem.Allocator,
378 stat_size: u64,
379 max_bytes: usize,378 max_bytes: usize,
380 comptime alignment: u29,379 comptime alignment: u29,
381 comptime optional_sentinel: ?u8,380 comptime optional_sentinel: ?u8,
382 ) !(if (optional_sentinel) |s| [:s]align(alignment) u8 else []align(alignment) u8) {381 ) !(if (optional_sentinel) |s| [:s]align(alignment) u8 else []align(alignment) u8) {
382 const stat_size = try self.getEndPos();
383 const size = math.cast(usize, stat_size) catch math.maxInt(usize);383 const size = math.cast(usize, stat_size) catch math.maxInt(usize);
384 if (size > max_bytes) return error.FileTooBig;384 if (size > max_bytes) return error.FileTooBig;
385385
386 const buf = try allocator.allocWithOptions(u8, size, alignment, optional_sentinel);386 // The file size returned by stat is used as hint to set the buffer
387 errdefer allocator.free(buf);387 // size. If the reported size is zero, as it happens on Linux for files
388 // in /proc, a small buffer is allocated instead.
389 const initial_cap = (if (size > 0) size else 1024) + @boolToInt(optional_sentinel != null);
390 var array_list = try std.ArrayListAligned(u8, alignment).initCapacity(allocator, initial_cap);
391 defer array_list.deinit();
388392
389 try self.reader().readNoEof(buf);393 self.reader().readAllArrayList(&array_list, max_bytes) catch |err| switch (err) {
390 return buf;394 error.StreamTooLong => return error.FileTooBig,
395 else => |e| return e,
396 };
397
398 if (optional_sentinel) |sentinel| {
399 try array_list.append(sentinel);
400 const buf = array_list.toOwnedSlice();
401 return buf[0 .. buf.len - 1 :sentinel];
402 } else {
403 return array_list.toOwnedSlice();
404 }
391 }405 }
392406
393 pub const ReadError = os.ReadError;407 pub const ReadError = os.ReadError;
lib/std/fs/test.zig+4-5
...@@ -188,30 +188,29 @@ test "readAllAlloc" {...@@ -188,30 +188,29 @@ test "readAllAlloc" {
188 var file = try tmp_dir.dir.createFile("test_file", .{ .read = true });188 var file = try tmp_dir.dir.createFile("test_file", .{ .read = true });
189 defer file.close();189 defer file.close();
190190
191 const buf1 = try file.readAllAlloc(testing.allocator, 0, 1024);191 const buf1 = try file.readAllAlloc(testing.allocator, 1024);
192 defer testing.allocator.free(buf1);192 defer testing.allocator.free(buf1);
193 testing.expect(buf1.len == 0);193 testing.expect(buf1.len == 0);
194194
195 const write_buf: []const u8 = "this is a test.\nthis is a test.\nthis is a test.\nthis is a test.\n";195 const write_buf: []const u8 = "this is a test.\nthis is a test.\nthis is a test.\nthis is a test.\n";
196 try file.writeAll(write_buf);196 try file.writeAll(write_buf);
197 try file.seekTo(0);197 try file.seekTo(0);
198 const file_size = try file.getEndPos();
199198
200 // max_bytes > file_size199 // max_bytes > file_size
201 const buf2 = try file.readAllAlloc(testing.allocator, file_size, 1024);200 const buf2 = try file.readAllAlloc(testing.allocator, 1024);
202 defer testing.allocator.free(buf2);201 defer testing.allocator.free(buf2);
203 testing.expectEqual(write_buf.len, buf2.len);202 testing.expectEqual(write_buf.len, buf2.len);
204 testing.expect(std.mem.eql(u8, write_buf, buf2));203 testing.expect(std.mem.eql(u8, write_buf, buf2));
205 try file.seekTo(0);204 try file.seekTo(0);
206205
207 // max_bytes == file_size206 // max_bytes == file_size
208 const buf3 = try file.readAllAlloc(testing.allocator, file_size, write_buf.len);207 const buf3 = try file.readAllAlloc(testing.allocator, write_buf.len);
209 defer testing.allocator.free(buf3);208 defer testing.allocator.free(buf3);
210 testing.expectEqual(write_buf.len, buf3.len);209 testing.expectEqual(write_buf.len, buf3.len);
211 testing.expect(std.mem.eql(u8, write_buf, buf3));210 testing.expect(std.mem.eql(u8, write_buf, buf3));
212211
213 // max_bytes < file_size212 // max_bytes < file_size
214 testing.expectError(error.FileTooBig, file.readAllAlloc(testing.allocator, file_size, write_buf.len - 1));213 testing.expectError(error.FileTooBig, file.readAllAlloc(testing.allocator, write_buf.len - 1));
215}214}
216215
217test "directory operations on files" {216test "directory operations on files" {
src-self-hosted/main.zig+2-1
...@@ -742,6 +742,7 @@ const FmtError = error{...@@ -742,6 +742,7 @@ const FmtError = error{
742 LinkQuotaExceeded,742 LinkQuotaExceeded,
743 FileBusy,743 FileBusy,
744 EndOfStream,744 EndOfStream,
745 Unseekable,
745 NotOpenForWriting,746 NotOpenForWriting,
746} || fs.File.OpenError;747} || fs.File.OpenError;
747748
...@@ -805,7 +806,7 @@ fn fmtPathFile(...@@ -805,7 +806,7 @@ fn fmtPathFile(
805 if (stat.kind == .Directory)806 if (stat.kind == .Directory)
806 return error.IsDir;807 return error.IsDir;
807808
808 const source_code = source_file.readAllAlloc(fmt.gpa, stat.size, max_src_size) catch |err| switch (err) {809 const source_code = source_file.readAllAlloc(fmt.gpa, max_src_size) catch |err| switch (err) {
809 error.ConnectionResetByPeer => unreachable,810 error.ConnectionResetByPeer => unreachable,
810 error.ConnectionTimedOut => unreachable,811 error.ConnectionTimedOut => unreachable,
811 error.NotOpenForReading => unreachable,812 error.NotOpenForReading => unreachable,
src-self-hosted/stage2.zig-1
...@@ -615,7 +615,6 @@ export fn stage2_libc_parse(stage1_libc: *Stage2LibCInstallation, libc_file_z: [...@@ -615,7 +615,6 @@ export fn stage2_libc_parse(stage1_libc: *Stage2LibCInstallation, libc_file_z: [
615 error.NotOpenForWriting => unreachable,615 error.NotOpenForWriting => unreachable,
616 error.NotOpenForReading => unreachable,616 error.NotOpenForReading => unreachable,
617 error.Unexpected => return .Unexpected,617 error.Unexpected => return .Unexpected,
618 error.EndOfStream => return .EndOfFile,
619 error.IsDir => return .IsDir,618 error.IsDir => return .IsDir,
620 error.ConnectionResetByPeer => unreachable,619 error.ConnectionResetByPeer => unreachable,
621 error.ConnectionTimedOut => unreachable,620 error.ConnectionTimedOut => unreachable,