mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 05:44:20 +00:00
lib/std/posix: test ftruncate via std.fs.File.setEndPos()
Add a test for std.fs.File's `setEndPos` (which is a simple wrapper around `std.posix.ftruncate`) to exercise some success and failure paths. Explicitly check that the `ftruncate` length isn't negative when interpreted as a signed value. This avoids having to decode overloaded `EINVAL` errors. Add errno handling to Windows path to map INVALID_PARAMETER to FileTooBig. Fixes #22960
This commit is contained in:
parent
1408288b95
commit
2210c4c360
2 changed files with 63 additions and 4 deletions
|
|
@ -1391,6 +1391,60 @@ test "pwritev, preadv" {
|
||||||
try testing.expectEqualStrings(&buf2, "line1\n");
|
try testing.expectEqualStrings(&buf2, "line1\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "setEndPos" {
|
||||||
|
// https://github.com/ziglang/zig/issues/20747 (open fd does not have write permission)
|
||||||
|
if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
|
||||||
|
|
||||||
|
var tmp = tmpDir(.{});
|
||||||
|
defer tmp.cleanup();
|
||||||
|
|
||||||
|
const file_name = "afile.txt";
|
||||||
|
try tmp.dir.writeFile(.{ .sub_path = file_name, .data = "ninebytes" });
|
||||||
|
const f = try tmp.dir.openFile(file_name, .{ .mode = .read_write });
|
||||||
|
defer f.close();
|
||||||
|
|
||||||
|
const initial_size = try f.getEndPos();
|
||||||
|
var buffer: [32]u8 = undefined;
|
||||||
|
|
||||||
|
{
|
||||||
|
try f.setEndPos(initial_size);
|
||||||
|
try testing.expectEqual(initial_size, try f.getEndPos());
|
||||||
|
try testing.expectEqual(initial_size, try f.preadAll(&buffer, 0));
|
||||||
|
try testing.expectEqualStrings("ninebytes", buffer[0..@intCast(initial_size)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const larger = initial_size + 4;
|
||||||
|
try f.setEndPos(larger);
|
||||||
|
try testing.expectEqual(larger, try f.getEndPos());
|
||||||
|
try testing.expectEqual(larger, try f.preadAll(&buffer, 0));
|
||||||
|
try testing.expectEqualStrings("ninebytes\x00\x00\x00\x00", buffer[0..@intCast(larger)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const smaller = initial_size - 5;
|
||||||
|
try f.setEndPos(smaller);
|
||||||
|
try testing.expectEqual(smaller, try f.getEndPos());
|
||||||
|
try testing.expectEqual(smaller, try f.preadAll(&buffer, 0));
|
||||||
|
try testing.expectEqualStrings("nine", buffer[0..@intCast(smaller)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
try f.setEndPos(0);
|
||||||
|
try testing.expectEqual(0, try f.getEndPos());
|
||||||
|
try testing.expectEqual(0, try f.preadAll(&buffer, 0));
|
||||||
|
|
||||||
|
// Invalid file length should error gracefully. Actual limit is host
|
||||||
|
// and file-system dependent, but 1PB should fail most everywhere.
|
||||||
|
// Except MacOS APFS limit is 8 exabytes.
|
||||||
|
f.setEndPos(0x4_0000_0000_0000) catch |err| if (err != error.FileTooBig) {
|
||||||
|
return err;
|
||||||
|
};
|
||||||
|
|
||||||
|
try testing.expectError(error.FileTooBig, f.setEndPos(std.math.maxInt(u63))); // Maximum signed value
|
||||||
|
|
||||||
|
try testing.expectError(error.FileTooBig, f.setEndPos(std.math.maxInt(u64)));
|
||||||
|
}
|
||||||
|
|
||||||
test "access file" {
|
test "access file" {
|
||||||
try testWithAllSupportedPathTypes(struct {
|
try testWithAllSupportedPathTypes(struct {
|
||||||
fn impl(ctx: *TestContext) !void {
|
fn impl(ctx: *TestContext) !void {
|
||||||
|
|
|
||||||
|
|
@ -1037,11 +1037,15 @@ pub const TruncateError = error{
|
||||||
PermissionDenied,
|
PermissionDenied,
|
||||||
} || UnexpectedError;
|
} || UnexpectedError;
|
||||||
|
|
||||||
|
/// Length must be positive when treated as an i64.
|
||||||
pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {
|
pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {
|
||||||
|
const signed_len: i64 = @bitCast(length);
|
||||||
|
if (signed_len < 0) return error.FileTooBig; // avoid ambiguous EINVAL errors
|
||||||
|
|
||||||
if (native_os == .windows) {
|
if (native_os == .windows) {
|
||||||
var io_status_block: windows.IO_STATUS_BLOCK = undefined;
|
var io_status_block: windows.IO_STATUS_BLOCK = undefined;
|
||||||
var eof_info = windows.FILE_END_OF_FILE_INFORMATION{
|
var eof_info = windows.FILE_END_OF_FILE_INFORMATION{
|
||||||
.EndOfFile = @bitCast(length),
|
.EndOfFile = signed_len,
|
||||||
};
|
};
|
||||||
|
|
||||||
const rc = windows.ntdll.NtSetInformationFile(
|
const rc = windows.ntdll.NtSetInformationFile(
|
||||||
|
|
@ -1057,6 +1061,7 @@ pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {
|
||||||
.INVALID_HANDLE => unreachable, // Handle not open for writing
|
.INVALID_HANDLE => unreachable, // Handle not open for writing
|
||||||
.ACCESS_DENIED => return error.AccessDenied,
|
.ACCESS_DENIED => return error.AccessDenied,
|
||||||
.USER_MAPPED_FILE => return error.AccessDenied,
|
.USER_MAPPED_FILE => return error.AccessDenied,
|
||||||
|
.INVALID_PARAMETER => return error.FileTooBig,
|
||||||
else => return windows.unexpectedStatus(rc),
|
else => return windows.unexpectedStatus(rc),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1069,7 +1074,7 @@ pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {
|
||||||
.PERM => return error.PermissionDenied,
|
.PERM => return error.PermissionDenied,
|
||||||
.TXTBSY => return error.FileBusy,
|
.TXTBSY => return error.FileBusy,
|
||||||
.BADF => unreachable, // Handle not open for writing
|
.BADF => unreachable, // Handle not open for writing
|
||||||
.INVAL => unreachable, // Handle not open for writing
|
.INVAL => unreachable, // Handle not open for writing, negative length, or non-resizable handle
|
||||||
.NOTCAPABLE => return error.AccessDenied,
|
.NOTCAPABLE => return error.AccessDenied,
|
||||||
else => |err| return unexpectedErrno(err),
|
else => |err| return unexpectedErrno(err),
|
||||||
}
|
}
|
||||||
|
|
@ -1077,7 +1082,7 @@ pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {
|
||||||
|
|
||||||
const ftruncate_sym = if (lfs64_abi) system.ftruncate64 else system.ftruncate;
|
const ftruncate_sym = if (lfs64_abi) system.ftruncate64 else system.ftruncate;
|
||||||
while (true) {
|
while (true) {
|
||||||
switch (errno(ftruncate_sym(fd, @bitCast(length)))) {
|
switch (errno(ftruncate_sym(fd, signed_len))) {
|
||||||
.SUCCESS => return,
|
.SUCCESS => return,
|
||||||
.INTR => continue,
|
.INTR => continue,
|
||||||
.FBIG => return error.FileTooBig,
|
.FBIG => return error.FileTooBig,
|
||||||
|
|
@ -1085,7 +1090,7 @@ pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {
|
||||||
.PERM => return error.PermissionDenied,
|
.PERM => return error.PermissionDenied,
|
||||||
.TXTBSY => return error.FileBusy,
|
.TXTBSY => return error.FileBusy,
|
||||||
.BADF => unreachable, // Handle not open for writing
|
.BADF => unreachable, // Handle not open for writing
|
||||||
.INVAL => unreachable, // Handle not open for writing
|
.INVAL => unreachable, // Handle not open for writing, negative length, or non-resizable handle
|
||||||
else => |err| return unexpectedErrno(err),
|
else => |err| return unexpectedErrno(err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue