]> Cypherpunks repositories - gostls13.git/commitdiff
[release-branch.go1.24] syscall: don't truncate newly created files on Windows
authorqmuntal <quimmuntal@gmail.com>
Tue, 18 Feb 2025 10:55:07 +0000 (11:55 +0100)
committerGopher Robot <gobot@golang.org>
Wed, 19 Feb 2025 18:46:24 +0000 (10:46 -0800)
There is no need for syscall.OpenFile to truncate newly created files.
Some special Windows files, like the NUL device, can't be
truncated, so we should avoid truncating unless it is really necessary.

For #71752
Fixes #71836

Change-Id: I8238048594f706f6a5281053d55cfe3dc898828d
Reviewed-on: https://go-review.googlesource.com/c/go/+/650276
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Damien Neil <dneil@google.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
(cherry picked from commit 4267fd389e941cf197cc3890cc42e474866c0d30)
Reviewed-on: https://go-review.googlesource.com/c/go/+/650597
Reviewed-by: Ian Lance Taylor <iant@google.com>
Auto-Submit: Ian Lance Taylor <iant@google.com>
Commit-Queue: Ian Lance Taylor <iant@google.com>
Reviewed-by: Quim Muntal <quimmuntal@gmail.com>
src/os/os_test.go
src/syscall/syscall_windows.go
src/syscall/zsyscall_windows.go

index 1e2db94dea28abf1354f6a0c7607a36033b104d2..4ddbe6022bfe14884e772040a8b953ff5ce39afc 100644 (file)
@@ -3848,3 +3848,14 @@ func TestRemoveReadOnlyFile(t *testing.T) {
                }
        })
 }
+
+func TestOpenFileDevNull(t *testing.T) {
+       // See https://go.dev/issue/71752.
+       t.Parallel()
+
+       f, err := OpenFile(DevNull, O_WRONLY|O_CREATE|O_TRUNC, 0o644)
+       if err != nil {
+               t.Fatalf("OpenFile(DevNull): %v", err)
+       }
+       f.Close()
+}
index 05c29c7b2060ad5813d8cabd0d09dcdd0d4232f3..344f6c325cd8fb54720ce3046c90a647067e76e2 100644 (file)
@@ -235,7 +235,7 @@ func NewCallbackCDecl(fn any) uintptr {
 //sys  GetVersion() (ver uint32, err error)
 //sys  formatMessage(flags uint32, msgsrc uintptr, msgid uint32, langid uint32, buf []uint16, args *byte) (n uint32, err error) = FormatMessageW
 //sys  ExitProcess(exitcode uint32)
-//sys  CreateFile(name *uint16, access uint32, mode uint32, sa *SecurityAttributes, createmode uint32, attrs uint32, templatefile int32) (handle Handle, err error) [failretval==InvalidHandle] = CreateFileW
+//sys  createFile(name *uint16, access uint32, mode uint32, sa *SecurityAttributes, createmode uint32, attrs uint32, templatefile int32) (handle Handle, err error) [failretval == InvalidHandle || e1 == ERROR_ALREADY_EXISTS ] = CreateFileW
 //sys  readFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) (err error) = ReadFile
 //sys  writeFile(handle Handle, buf []byte, done *uint32, overlapped *Overlapped) (err error) = WriteFile
 //sys  SetFilePointer(handle Handle, lowoffset int32, highoffsetptr *int32, whence uint32) (newlowoffset uint32, err error) [failretval==0xffffffff]
@@ -404,8 +404,8 @@ func Open(name string, flag int, perm uint32) (fd Handle, err error) {
                const _FILE_FLAG_WRITE_THROUGH = 0x80000000
                attrs |= _FILE_FLAG_WRITE_THROUGH
        }
-       h, err := CreateFile(namep, access, sharemode, sa, createmode, attrs, 0)
-       if err != nil {
+       h, err := createFile(namep, access, sharemode, sa, createmode, attrs, 0)
+       if h == InvalidHandle {
                if err == ERROR_ACCESS_DENIED && (flag&O_WRONLY != 0 || flag&O_RDWR != 0) {
                        // We should return EISDIR when we are trying to open a directory with write access.
                        fa, e1 := GetFileAttributes(namep)
@@ -413,9 +413,11 @@ func Open(name string, flag int, perm uint32) (fd Handle, err error) {
                                err = EISDIR
                        }
                }
-               return InvalidHandle, err
+               return h, err
        }
-       if flag&O_TRUNC == O_TRUNC {
+       // Ignore O_TRUNC if the file has just been created.
+       if flag&O_TRUNC == O_TRUNC &&
+               (createmode == OPEN_EXISTING || (createmode == OPEN_ALWAYS && err == ERROR_ALREADY_EXISTS)) {
                err = Ftruncate(h, 0)
                if err != nil {
                        CloseHandle(h)
@@ -1454,3 +1456,13 @@ func GetStartupInfo(startupInfo *StartupInfo) error {
        getStartupInfo(startupInfo)
        return nil
 }
+
+func CreateFile(name *uint16, access uint32, mode uint32, sa *SecurityAttributes, createmode uint32, attrs uint32, templatefile int32) (handle Handle, err error) {
+       handle, err = createFile(name, access, mode, sa, createmode, attrs, templatefile)
+       if handle != InvalidHandle {
+               // CreateFileW can return ERROR_ALREADY_EXISTS with a valid handle.
+               // We only want to return an error if the handle is invalid.
+               err = nil
+       }
+       return handle, err
+}
index c0585a6df2eb118e83b21fda2f613e3c2a09cfd1..a58de3412ca55bcf48954a4b0d7a23e3d99ed636 100644 (file)
@@ -502,10 +502,10 @@ func CreateFileMapping(fhandle Handle, sa *SecurityAttributes, prot uint32, maxS
        return
 }
 
-func CreateFile(name *uint16, access uint32, mode uint32, sa *SecurityAttributes, createmode uint32, attrs uint32, templatefile int32) (handle Handle, err error) {
+func createFile(name *uint16, access uint32, mode uint32, sa *SecurityAttributes, createmode uint32, attrs uint32, templatefile int32) (handle Handle, err error) {
        r0, _, e1 := Syscall9(procCreateFileW.Addr(), 7, uintptr(unsafe.Pointer(name)), uintptr(access), uintptr(mode), uintptr(unsafe.Pointer(sa)), uintptr(createmode), uintptr(attrs), uintptr(templatefile), 0, 0)
        handle = Handle(r0)
-       if handle == InvalidHandle {
+       if handle == InvalidHandle || e1 == ERROR_ALREADY_EXISTS {
                err = errnoErr(e1)
        }
        return