From d7384f36121d52191097af50d6dc12c0eb08fd75 Mon Sep 17 00:00:00 2001 From: Constantin Konstantinidis Date: Sun, 16 Aug 2020 13:48:09 +0200 Subject: [PATCH] os: implement File.Chmod on Windows Fixes: #39606 Change-Id: I4def67ef18bd3ff866b140f6e76cdabe5d51a1c5 Reviewed-on: https://go-review.googlesource.com/c/go/+/250077 Run-TryBot: Alex Brainman TryBot-Result: Gobot Gobot Reviewed-by: Alex Brainman --- src/internal/poll/fd_posix.go | 11 -------- src/internal/poll/fd_unix.go | 11 ++++++++ src/internal/poll/fd_windows.go | 27 +++++++++++++++++++ .../syscall/windows/syscall_windows.go | 9 +++++++ .../syscall/windows/zsyscall_windows.go | 13 +++++++++ src/os/os_test.go | 27 +++++++++++-------- 6 files changed, 76 insertions(+), 22 deletions(-) diff --git a/src/internal/poll/fd_posix.go b/src/internal/poll/fd_posix.go index e5fb05c9c2..4edfa953a4 100644 --- a/src/internal/poll/fd_posix.go +++ b/src/internal/poll/fd_posix.go @@ -29,17 +29,6 @@ func (fd *FD) Shutdown(how int) error { return syscall.Shutdown(fd.Sysfd, how) } -// Fchmod wraps syscall.Fchmod. -func (fd *FD) Fchmod(mode uint32) error { - if err := fd.incref(); err != nil { - return err - } - defer fd.decref() - return ignoringEINTR(func() error { - return syscall.Fchmod(fd.Sysfd, mode) - }) -} - // Fchown wraps syscall.Fchown. func (fd *FD) Fchown(uid, gid int) error { if err := fd.incref(); err != nil { diff --git a/src/internal/poll/fd_unix.go b/src/internal/poll/fd_unix.go index 1d5101eac3..f6f6c52f31 100644 --- a/src/internal/poll/fd_unix.go +++ b/src/internal/poll/fd_unix.go @@ -437,6 +437,17 @@ func (fd *FD) ReadDirent(buf []byte) (int, error) { } } +// Fchmod wraps syscall.Fchmod. +func (fd *FD) Fchmod(mode uint32) error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return ignoringEINTR(func() error { + return syscall.Fchmod(fd.Sysfd, mode) + }) +} + // Fchdir wraps syscall.Fchdir. func (fd *FD) Fchdir() error { if err := fd.incref(); err != nil { diff --git a/src/internal/poll/fd_windows.go b/src/internal/poll/fd_windows.go index e1ef6199b3..d8c834f929 100644 --- a/src/internal/poll/fd_windows.go +++ b/src/internal/poll/fd_windows.go @@ -886,6 +886,33 @@ func (fd *FD) FindNextFile(data *syscall.Win32finddata) error { return syscall.FindNextFile(fd.Sysfd, data) } +// Fchmod updates syscall.ByHandleFileInformation.Fileattributes when needed. +func (fd *FD) Fchmod(mode uint32) error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + + var d syscall.ByHandleFileInformation + if err := syscall.GetFileInformationByHandle(fd.Sysfd, &d); err != nil { + return err + } + attrs := d.FileAttributes + if mode&syscall.S_IWRITE != 0 { + attrs &^= syscall.FILE_ATTRIBUTE_READONLY + } else { + attrs |= syscall.FILE_ATTRIBUTE_READONLY + } + if attrs == d.FileAttributes { + return nil + } + + var du windows.FILE_BASIC_INFO + du.FileAttributes = attrs + l := uint32(unsafe.Sizeof(d)) + return windows.SetFileInformationByHandle(fd.Sysfd, windows.FileBasicInfo, uintptr(unsafe.Pointer(&du)), l) +} + // Fchdir wraps syscall.Fchdir. func (fd *FD) Fchdir() error { if err := fd.incref(); err != nil { diff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go index edf0b5a40b..1f40c11820 100644 --- a/src/internal/syscall/windows/syscall_windows.go +++ b/src/internal/syscall/windows/syscall_windows.go @@ -131,6 +131,14 @@ type IpAdapterAddresses struct { /* more fields might be present here. */ } +type FILE_BASIC_INFO struct { + CreationTime syscall.Filetime + LastAccessTime syscall.Filetime + LastWriteTime syscall.Filetime + ChangedTime syscall.Filetime + FileAttributes uint32 +} + const ( IfOperStatusUp = 1 IfOperStatusDown = 2 @@ -145,6 +153,7 @@ const ( //sys GetComputerNameEx(nameformat uint32, buf *uint16, n *uint32) (err error) = GetComputerNameExW //sys MoveFileEx(from *uint16, to *uint16, flags uint32) (err error) = MoveFileExW //sys GetModuleFileName(module syscall.Handle, fn *uint16, len uint32) (n uint32, err error) = kernel32.GetModuleFileNameW +//sys SetFileInformationByHandle(handle syscall.Handle, fileInformationClass uint32, buf uintptr, bufsize uint32) (err error) = kernel32.SetFileInformationByHandle const ( WSA_FLAG_OVERLAPPED = 0x01 diff --git a/src/internal/syscall/windows/zsyscall_windows.go b/src/internal/syscall/windows/zsyscall_windows.go index ca5b4e6f16..0840dc283a 100644 --- a/src/internal/syscall/windows/zsyscall_windows.go +++ b/src/internal/syscall/windows/zsyscall_windows.go @@ -71,6 +71,7 @@ var ( procNetUserGetLocalGroups = modnetapi32.NewProc("NetUserGetLocalGroups") procGetProcessMemoryInfo = modpsapi.NewProc("GetProcessMemoryInfo") procGetFileInformationByHandleEx = modkernel32.NewProc("GetFileInformationByHandleEx") + procSetFileInformationByHandle = modkernel32.NewProc("SetFileInformationByHandle") ) func GetAdaptersAddresses(family uint32, flags uint32, reserved uintptr, adapterAddresses *IpAdapterAddresses, sizePointer *uint32) (errcode error) { @@ -81,6 +82,18 @@ func GetAdaptersAddresses(family uint32, flags uint32, reserved uintptr, adapter return } +func SetFileInformationByHandle(handle syscall.Handle, fileInformationClass uint32, buf uintptr, bufsize uint32) (err error) { + r1, _, e1 := syscall.Syscall6(procSetFileInformationByHandle.Addr(), 4, uintptr(handle), uintptr(fileInformationClass), uintptr(buf), uintptr(bufsize), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + func GetComputerNameEx(nameformat uint32, buf *uint16, n *uint32) (err error) { r1, _, e1 := syscall.Syscall(procGetComputerNameExW.Addr(), 3, uintptr(nameformat), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(n))) if r1 == 0 { diff --git a/src/os/os_test.go b/src/os/os_test.go index 520916d880..3359301316 100644 --- a/src/os/os_test.go +++ b/src/os/os_test.go @@ -1099,29 +1099,34 @@ func checkMode(t *testing.T, path string, mode FileMode) { if err != nil { t.Fatalf("Stat %q (looking for mode %#o): %s", path, mode, err) } - if dir.Mode()&0777 != mode { + if dir.Mode()&ModePerm != mode { t.Errorf("Stat %q: mode %#o want %#o", path, dir.Mode(), mode) } } func TestChmod(t *testing.T) { - // Chmod is not supported under windows. - if runtime.GOOS == "windows" { - return - } f := newFile("TestChmod", t) defer Remove(f.Name()) defer f.Close() + // Creation mode is read write - if err := Chmod(f.Name(), 0456); err != nil { - t.Fatalf("chmod %s 0456: %s", f.Name(), err) + fm := FileMode(0456) + if runtime.GOOS == "windows" { + fm = FileMode(0444) // read-only file } - checkMode(t, f.Name(), 0456) + if err := Chmod(f.Name(), fm); err != nil { + t.Fatalf("chmod %s %#o: %s", f.Name(), fm, err) + } + checkMode(t, f.Name(), fm) - if err := f.Chmod(0123); err != nil { - t.Fatalf("chmod %s 0123: %s", f.Name(), err) + fm = FileMode(0123) + if runtime.GOOS == "windows" { + fm = FileMode(0666) // read-write file + } + if err := f.Chmod(fm); err != nil { + t.Fatalf("chmod %s %#o: %s", f.Name(), fm, err) } - checkMode(t, f.Name(), 0123) + checkMode(t, f.Name(), fm) } func checkSize(t *testing.T, f *File, size int64) { -- 2.48.1