]> Cypherpunks repositories - gostls13.git/commitdiff
os: implement File.Chmod on Windows
authorConstantin Konstantinidis <constantinkonstantinidis@gmail.com>
Sun, 16 Aug 2020 11:48:09 +0000 (13:48 +0200)
committerAlex Brainman <alex.brainman@gmail.com>
Fri, 11 Sep 2020 22:10:02 +0000 (22:10 +0000)
Fixes: #39606
Change-Id: I4def67ef18bd3ff866b140f6e76cdabe5d51a1c5
Reviewed-on: https://go-review.googlesource.com/c/go/+/250077
Run-TryBot: Alex Brainman <alex.brainman@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
src/internal/poll/fd_posix.go
src/internal/poll/fd_unix.go
src/internal/poll/fd_windows.go
src/internal/syscall/windows/syscall_windows.go
src/internal/syscall/windows/zsyscall_windows.go
src/os/os_test.go

index e5fb05c9c22f2bd95a6399902c5525f59d7157af..4edfa953a4b3d10b52c1c09864b679538a4a3b8a 100644 (file)
@@ -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 {
index 1d5101eac3e84ae06c32e69d948bc3564e54d610..f6f6c52f31883edb52f6397b7b84a23e2f84c2a7 100644 (file)
@@ -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 {
index e1ef6199b37a44a1d9ba581a15d60463bcba4a2d..d8c834f92993fdfd070a92d497c1a5e44f5950a9 100644 (file)
@@ -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 {
index edf0b5a40b8a28fb03976114a864637a85112333..1f40c11820519114c3b56ef6c10a84acf0940eff 100644 (file)
@@ -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
index ca5b4e6f16ddb3165718d12349e36865aaa5a353..0840dc283aea21e1f847c774c0ea740e12f399e8 100644 (file)
@@ -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 {
index 520916d8800d416d9fa428dc8539efa939c746c7..3359301316e479f2bd6e0e502d224a31ddfdbb5a 100644 (file)
@@ -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) {