Commit de53537f10

LemonBoy <thatlemon@gmail.com>
2020-03-12 22:46:12
Add NtDll-based ftruncate implementation
1 parent bd0b514
Changed files (5)
lib/std/fs/file.zig
@@ -102,7 +102,7 @@ pub const File = struct {
     pub const SetEndPosError = os.TruncateError;
 
     /// Shrinks or expands the file.
-    /// The file offset after this call is undefined.
+    /// The file offset after this call is left unchanged.
     pub fn setEndPos(self: File, length: u64) SetEndPosError!void {
         try os.ftruncate(self.handle, length);
     }
lib/std/io/test.zig
@@ -1,5 +1,5 @@
-const builtin = @import("builtin");
-const std = @import("../std.zig");
+const std = @import("std");
+const builtin = std.builtin;
 const io = std.io;
 const meta = std.meta;
 const trait = std.trait;
@@ -133,13 +133,19 @@ test "setEndPos" {
         fs.cwd().deleteFile(tmp_file_name) catch {};
     }
 
+    // Verify that the file size changes and the file offset is not moved
     std.testing.expect((try file.getEndPos()) == 0);
+    std.testing.expect((try file.getPos()) == 0);
     try file.setEndPos(8192);
     std.testing.expect((try file.getEndPos()) == 8192);
+    std.testing.expect((try file.getPos()) == 0);
+    try file.seekTo(100);
     try file.setEndPos(4096);
     std.testing.expect((try file.getEndPos()) == 4096);
+    std.testing.expect((try file.getPos()) == 100);
     try file.setEndPos(0);
     std.testing.expect((try file.getEndPos()) == 0);
+    std.testing.expect((try file.getPos()) == 100);
 }
 
 test "updateTimes" {
lib/std/os/windows/bits.zig
@@ -225,6 +225,10 @@ pub const FILE_POSITION_INFORMATION = extern struct {
     CurrentByteOffset: LARGE_INTEGER,
 };
 
+pub const FILE_END_OF_FILE_INFORMATION = extern struct {
+    EndOfFile: LARGE_INTEGER,
+};
+
 pub const FILE_MODE_INFORMATION = extern struct {
     Mode: ULONG,
 };
lib/std/os/windows/ntdll.zig
@@ -16,6 +16,13 @@ pub extern "NtDll" fn NtQueryInformationFile(
     Length: ULONG,
     FileInformationClass: FILE_INFORMATION_CLASS,
 ) callconv(.Stdcall) NTSTATUS;
+pub extern "NtDll" fn NtSetInformationFile(
+    FileHandle: HANDLE,
+    IoStatusBlock: *IO_STATUS_BLOCK,
+    FileInformation: PVOID,
+    Length: ULONG,
+    FileInformationClass: FILE_INFORMATION_CLASS,
+) callconv(.Stdcall) NTSTATUS;
 
 pub extern "NtDll" fn NtQueryAttributesFile(
     ObjectAttributes: *OBJECT_ATTRIBUTES,
lib/std/os.zig
@@ -447,10 +447,25 @@ pub const TruncateError = error{
 
 pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {
     if (std.Target.current.os.tag == .windows) {
-        try windows.SetFilePointerEx_BEGIN(fd, length);
+        var io_status_block: windows.IO_STATUS_BLOCK = undefined;
+        var eof_info = windows.FILE_END_OF_FILE_INFORMATION{
+            .EndOfFile = @bitCast(windows.LARGE_INTEGER, length),
+        };
+
+        const rc = windows.ntdll.NtSetInformationFile(
+            fd,
+            &io_status_block,
+            &eof_info,
+            @sizeOf(windows.FILE_END_OF_FILE_INFORMATION),
+            .FileEndOfFileInformation,
+        );
 
-        if (windows.kernel32.SetEndOfFile(fd) == 0)
-            return TruncateError.Unexpected;
+        switch (rc) {
+            .SUCCESS => {},
+            .INVALID_HANDLE => unreachable, // Handle not open for writing
+            .ACCESS_DENIED => return error.CannotTruncate,
+            else => return windows.unexpectedStatus(rc),
+        }
 
         return;
     }
@@ -471,7 +486,8 @@ pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {
             EIO => return error.InputOutput,
             EPERM => return error.CannotTruncate,
             ETXTBSY => return error.FileBusy,
-            EBADF, EINVAL => unreachable,
+            EBADF => unreachable, // Handle not open for writing
+            EINVAL => unreachable, // Handle not open for writing
             else => |err| return unexpectedErrno(err),
         }
     }