fix footguns in File readAll functions
parent
ed13cffca4
commit
d96b6c0d9f
|
@ -96,6 +96,7 @@ pub fn updateFile(source_path: []const u8, dest_path: []const u8) !PrevStatus {
|
|||
/// atime, and mode of the source file so that the next call to `updateFile` will not need a copy.
|
||||
/// Returns the previous status of the file before updating.
|
||||
/// If any of the directories do not exist for dest_path, they are created.
|
||||
/// TODO rework this to integrate with Dir
|
||||
pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?File.Mode) !PrevStatus {
|
||||
const my_cwd = cwd();
|
||||
|
||||
|
@ -141,29 +142,25 @@ pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?Fil
|
|||
/// there is a possibility of power loss or application termination leaving temporary files present
|
||||
/// in the same directory as dest_path.
|
||||
/// Destination file will have the same mode as the source file.
|
||||
/// TODO rework this to integrate with Dir
|
||||
pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void {
|
||||
var in_file = try cwd().openFile(source_path, .{});
|
||||
defer in_file.close();
|
||||
|
||||
const mode = try in_file.mode();
|
||||
const in_stream = &in_file.inStream().stream;
|
||||
const stat = try in_file.stat();
|
||||
|
||||
var atomic_file = try AtomicFile.init(dest_path, mode);
|
||||
var atomic_file = try AtomicFile.init(dest_path, stat.mode);
|
||||
defer atomic_file.deinit();
|
||||
|
||||
var buf: [mem.page_size]u8 = undefined;
|
||||
while (true) {
|
||||
const amt = try in_stream.readFull(buf[0..]);
|
||||
try atomic_file.file.write(buf[0..amt]);
|
||||
if (amt != buf.len) {
|
||||
return atomic_file.finish();
|
||||
}
|
||||
}
|
||||
try atomic_file.file.writeFileAll(in_file, .{ .in_len = stat.size });
|
||||
return atomic_file.finish();
|
||||
}
|
||||
|
||||
/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is
|
||||
/// merged and readily available,
|
||||
/// Guaranteed to be atomic.
|
||||
/// On Linux, until https://patchwork.kernel.org/patch/9636735/ is merged and readily available,
|
||||
/// there is a possibility of power loss or application termination leaving temporary files present
|
||||
/// in the same directory as dest_path.
|
||||
/// TODO rework this to integrate with Dir
|
||||
pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void {
|
||||
var in_file = try cwd().openFile(source_path, .{});
|
||||
defer in_file.close();
|
||||
|
@ -171,14 +168,8 @@ pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.M
|
|||
var atomic_file = try AtomicFile.init(dest_path, mode);
|
||||
defer atomic_file.deinit();
|
||||
|
||||
var buf: [mem.page_size * 6]u8 = undefined;
|
||||
while (true) {
|
||||
const amt = try in_file.read(buf[0..]);
|
||||
try atomic_file.file.write(buf[0..amt]);
|
||||
if (amt != buf.len) {
|
||||
return atomic_file.finish();
|
||||
}
|
||||
}
|
||||
try atomic_file.file.writeFileAll(in_file, .{});
|
||||
return atomic_file.finish();
|
||||
}
|
||||
|
||||
/// TODO update this API to avoid a getrandom syscall for every operation. It
|
||||
|
|
|
@ -250,11 +250,16 @@ pub const File = struct {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn readAll(self: File, buffer: []u8) ReadError!void {
|
||||
/// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it
|
||||
/// means the file reached the end. Reaching the end of a file is not an error condition.
|
||||
pub fn readAll(self: File, buffer: []u8) ReadError!usize {
|
||||
var index: usize = 0;
|
||||
while (index < buffer.len) {
|
||||
index += try self.read(buffer[index..]);
|
||||
while (index != buffer.len) {
|
||||
const amt = try self.read(buffer[index..]);
|
||||
if (amt == 0) break;
|
||||
index += amt;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
pub fn pread(self: File, buffer: []u8, offset: u64) PReadError!usize {
|
||||
|
@ -265,11 +270,16 @@ pub const File = struct {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn preadAll(self: File, buffer: []u8, offset: u64) PReadError!void {
|
||||
/// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it
|
||||
/// means the file reached the end. Reaching the end of a file is not an error condition.
|
||||
pub fn preadAll(self: File, buffer: []u8, offset: u64) PReadError!usize {
|
||||
var index: usize = 0;
|
||||
while (index < buffer.len) {
|
||||
index += try self.pread(buffer[index..], offset + index);
|
||||
while (index != buffer.len) {
|
||||
const amt = try self.pread(buffer[index..], offset + index);
|
||||
if (amt == 0) break;
|
||||
index += amt;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
pub fn readv(self: File, iovecs: []const os.iovec) ReadError!usize {
|
||||
|
@ -280,19 +290,27 @@ pub const File = struct {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the number of bytes read. If the number read is smaller than the total bytes
|
||||
/// from all the buffers, it means the file reached the end. Reaching the end of a file
|
||||
/// is not an error condition.
|
||||
/// The `iovecs` parameter is mutable because this function needs to mutate the fields in
|
||||
/// order to handle partial reads from the underlying OS layer.
|
||||
pub fn readvAll(self: File, iovecs: []os.iovec) ReadError!void {
|
||||
pub fn readvAll(self: File, iovecs: []os.iovec) ReadError!usize {
|
||||
if (iovecs.len == 0) return;
|
||||
|
||||
var i: usize = 0;
|
||||
var off: usize = 0;
|
||||
while (true) {
|
||||
var amt = try self.readv(iovecs[i..]);
|
||||
var eof = amt == 0;
|
||||
off += amt;
|
||||
while (amt >= iovecs[i].iov_len) {
|
||||
amt -= iovecs[i].iov_len;
|
||||
i += 1;
|
||||
if (i >= iovecs.len) return;
|
||||
if (i >= iovecs.len) return off;
|
||||
eof = false;
|
||||
}
|
||||
if (eof) return off;
|
||||
iovecs[i].iov_base += amt;
|
||||
iovecs[i].iov_len -= amt;
|
||||
}
|
||||
|
@ -306,6 +324,9 @@ pub const File = struct {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the number of bytes read. If the number read is smaller than the total bytes
|
||||
/// from all the buffers, it means the file reached the end. Reaching the end of a file
|
||||
/// is not an error condition.
|
||||
/// The `iovecs` parameter is mutable because this function needs to mutate the fields in
|
||||
/// order to handle partial reads from the underlying OS layer.
|
||||
pub fn preadvAll(self: File, iovecs: []const os.iovec, offset: u64) PReadError!void {
|
||||
|
@ -315,12 +336,15 @@ pub const File = struct {
|
|||
var off: usize = 0;
|
||||
while (true) {
|
||||
var amt = try self.preadv(iovecs[i..], offset + off);
|
||||
var eof = amt == 0;
|
||||
off += amt;
|
||||
while (amt >= iovecs[i].iov_len) {
|
||||
amt -= iovecs[i].iov_len;
|
||||
i += 1;
|
||||
if (i >= iovecs.len) return;
|
||||
if (i >= iovecs.len) return off;
|
||||
eof = false;
|
||||
}
|
||||
if (eof) return off;
|
||||
iovecs[i].iov_base += amt;
|
||||
iovecs[i].iov_len -= amt;
|
||||
}
|
||||
|
|
|
@ -28,10 +28,7 @@ pub fn InStream(
|
|||
return readFn(self.context, buffer);
|
||||
}
|
||||
|
||||
/// Deprecated: use `readAll`.
|
||||
pub const readFull = readAll;
|
||||
|
||||
/// Returns the number of bytes read. If the number read is smaller than buf.len, it
|
||||
/// Returns the number of bytes read. If the number read is smaller than `buffer.len`, it
|
||||
/// means the stream reached the end. Reaching the end of a stream is not an error
|
||||
/// condition.
|
||||
pub fn readAll(self: Self, buffer: []u8) Error!usize {
|
||||
|
|
|
@ -95,15 +95,41 @@ test "sendfile" {
|
|||
},
|
||||
};
|
||||
|
||||
var written_buf: [header1.len + header2.len + 10 + trailer1.len + trailer2.len]u8 = undefined;
|
||||
var written_buf: [100]u8 = undefined;
|
||||
try dest_file.writeFileAll(src_file, .{
|
||||
.in_offset = 1,
|
||||
.in_len = 10,
|
||||
.headers_and_trailers = &hdtr,
|
||||
.header_count = 2,
|
||||
});
|
||||
try dest_file.preadAll(&written_buf, 0);
|
||||
expect(mem.eql(u8, &written_buf, "header1\nsecond header\nine1\nsecontrailer1\nsecond trailer\n"));
|
||||
const amt = try dest_file.preadAll(&written_buf, 0);
|
||||
expect(mem.eql(u8, written_buf[0..amt], "header1\nsecond header\nine1\nsecontrailer1\nsecond trailer\n"));
|
||||
}
|
||||
|
||||
test "fs.copyFile" {
|
||||
const data = "u6wj+JmdF3qHsFPE BUlH2g4gJCmEz0PP";
|
||||
const src_file = "tmp_test_copy_file.txt";
|
||||
const dest_file = "tmp_test_copy_file2.txt";
|
||||
const dest_file2 = "tmp_test_copy_file3.txt";
|
||||
|
||||
try fs.cwd().writeFile(src_file, data);
|
||||
defer fs.cwd().deleteFile(src_file) catch {};
|
||||
|
||||
try fs.copyFile(src_file, dest_file);
|
||||
defer fs.cwd().deleteFile(dest_file) catch {};
|
||||
|
||||
try fs.copyFileMode(src_file, dest_file2, File.default_mode);
|
||||
defer fs.cwd().deleteFile(dest_file2) catch {};
|
||||
|
||||
try expectFileContents(dest_file, data);
|
||||
try expectFileContents(dest_file2, data);
|
||||
}
|
||||
|
||||
fn expectFileContents(file_path: []const u8, data: []const u8) !void {
|
||||
const contents = try fs.cwd().readFileAlloc(testing.allocator, file_path, 1000);
|
||||
defer testing.allocator.free(contents);
|
||||
|
||||
testing.expectEqualSlices(u8, data, contents);
|
||||
}
|
||||
|
||||
test "std.Thread.getCurrentId" {
|
||||
|
|
Loading…
Reference in New Issue