Revert "cmd/go: use os.Rename to move files on Windows"

This reverts CL 691255.

Reason for revert: SetNamedSecurityInfo sometimes fails when the path contains symbolic links.

Fixes #74864

Change-Id: Iaf1a5692b35d58c523fd513f27bad9a2e7a334cb
Reviewed-on: https://go-review.googlesource.com/c/go/+/702155
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Quim Muntal <quimmuntal@gmail.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: Damien Neil <dneil@google.com>
This commit is contained in:
Quim Muntal 2025-09-09 09:02:28 -07:00 committed by Gopher Robot
parent e3223518b8
commit 3b3b16957c
6 changed files with 40 additions and 138 deletions

View file

@ -132,11 +132,47 @@ func (sh *Shell) moveOrCopyFile(dst, src string, perm fs.FileMode, force bool) e
return sh.CopyFile(dst, src, perm, force)
}
if err := sh.move(src, dst, perm); err == nil {
if cfg.BuildX {
sh.ShowCmd("", "mv %s %s", src, dst)
// On Windows, always copy the file, so that we respect the NTFS
// permissions of the parent folder. https://golang.org/issue/22343.
// What matters here is not cfg.Goos (the system we are building
// for) but runtime.GOOS (the system we are building on).
if runtime.GOOS == "windows" {
return sh.CopyFile(dst, src, perm, force)
}
// If the destination directory has the group sticky bit set,
// we have to copy the file to retain the correct permissions.
// https://golang.org/issue/18878
if fi, err := os.Stat(filepath.Dir(dst)); err == nil {
if fi.IsDir() && (fi.Mode()&fs.ModeSetgid) != 0 {
return sh.CopyFile(dst, src, perm, force)
}
}
// The perm argument is meant to be adjusted according to umask,
// but we don't know what the umask is.
// Create a dummy file to find out.
// This avoids build tags and works even on systems like Plan 9
// where the file mask computation incorporates other information.
mode := perm
f, err := os.OpenFile(filepath.Clean(dst)+"-go-tmp-umask", os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm)
if err == nil {
fi, err := f.Stat()
if err == nil {
mode = fi.Mode() & 0777
}
name := f.Name()
f.Close()
os.Remove(name)
}
if err := os.Chmod(src, mode); err == nil {
if err := os.Rename(src, dst); err == nil {
if cfg.BuildX {
sh.ShowCmd("", "mv %s %s", src, dst)
}
return nil
}
return nil
}
return sh.CopyFile(dst, src, perm, force)

View file

@ -1,49 +0,0 @@
// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !windows
package work
import (
"errors"
"io/fs"
"os"
"path/filepath"
)
// move moves a file from src to dst setting the permissions
// on the destination file to inherit the permissions from the
// destination parent directory.
func (sh *Shell) move(src, dst string, perm fs.FileMode) error {
// If the destination directory has the group sticky bit set,
// we have to copy the file to retain the correct permissions.
// https://golang.org/issue/18878
if fi, err := os.Stat(filepath.Dir(dst)); err == nil {
if fi.IsDir() && (fi.Mode()&fs.ModeSetgid) != 0 {
return errors.ErrUnsupported
}
}
// The perm argument is meant to be adjusted according to umask,
// but we don't know what the umask is.
// Create a dummy file to find out.
// This works even on systems like Plan 9 where the
// file mask computation incorporates other information.
mode := perm
f, err := os.OpenFile(filepath.Clean(dst)+"-go-tmp-umask", os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm)
if err == nil {
fi, err := f.Stat()
if err == nil {
mode = fi.Mode() & 0777
}
name := f.Name()
f.Close()
os.Remove(name)
}
if err := os.Chmod(src, mode); err != nil {
return err
}
return os.Rename(src, dst)
}

View file

@ -1,37 +0,0 @@
// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package work
import (
"internal/syscall/windows"
"io/fs"
"os"
"unsafe"
)
// move moves a file from src to dst, setting the security information
// on the destination file to inherit the permissions from the
// destination parent directory.
func (sh *Shell) move(src, dst string, perm fs.FileMode) (err error) {
if err := os.Rename(src, dst); err != nil {
return err
}
defer func() {
if err != nil {
os.Remove(dst) // clean up if we failed to set the mode or security info
}
}()
if err := os.Chmod(dst, perm); err != nil {
return err
}
// We need to respect the ACL permissions of the destination parent folder.
// https://go.dev/issue/22343.
var acl windows.ACL
if err := windows.InitializeAcl(&acl, uint32(unsafe.Sizeof(acl)), windows.ACL_REVISION); err != nil {
return err
}
secInfo := windows.DACL_SECURITY_INFORMATION | windows.UNPROTECTED_DACL_SECURITY_INFORMATION
return windows.SetNamedSecurityInfo(dst, windows.SE_FILE_OBJECT, secInfo, nil, nil, &acl, nil)
}

View file

@ -262,6 +262,3 @@ func GetSidSubAuthorityCount(sid *syscall.SID) uint8 {
defer runtime.KeepAlive(sid)
return *(*uint8)(unsafe.Pointer(getSidSubAuthorityCount(sid)))
}
//sys InitializeAcl(acl *ACL, length uint32, revision uint32) (err error) = advapi32.InitializeAcl
//sys SetNamedSecurityInfo(objectName string, objectType SE_OBJECT_TYPE, securityInformation SECURITY_INFORMATION, owner *syscall.SID, group *syscall.SID, dacl *ACL, sacl *ACL) (ret error) = advapi32.SetNamedSecurityInfoW

View file

@ -276,21 +276,3 @@ type FILE_COMPLETION_INFORMATION struct {
// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-osversioninfoexa
// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_osversioninfoexw
const VER_NT_WORKSTATION = 0x0000001
// https://learn.microsoft.com/en-us/windows/win32/api/accctrl/ne-accctrl-se_object_type
type SE_OBJECT_TYPE uint32
const (
SE_UNKNOWN_OBJECT_TYPE SE_OBJECT_TYPE = 0
SE_FILE_OBJECT SE_OBJECT_TYPE = 1
)
// https://learn.microsoft.com/en-us/windows/win32/secauthz/security-information
type SECURITY_INFORMATION uint32
const (
DACL_SECURITY_INFORMATION SECURITY_INFORMATION = 0x00000004
UNPROTECTED_DACL_SECURITY_INFORMATION SECURITY_INFORMATION = 0x20000000
)
const ACL_REVISION = 2

View file

@ -54,7 +54,6 @@ var (
procGetSidSubAuthorityCount = modadvapi32.NewProc("GetSidSubAuthorityCount")
procImpersonateLoggedOnUser = modadvapi32.NewProc("ImpersonateLoggedOnUser")
procImpersonateSelf = modadvapi32.NewProc("ImpersonateSelf")
procInitializeAcl = modadvapi32.NewProc("InitializeAcl")
procIsValidSid = modadvapi32.NewProc("IsValidSid")
procLogonUserW = modadvapi32.NewProc("LogonUserW")
procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW")
@ -63,7 +62,6 @@ var (
procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken")
procQueryServiceStatus = modadvapi32.NewProc("QueryServiceStatus")
procRevertToSelf = modadvapi32.NewProc("RevertToSelf")
procSetNamedSecurityInfoW = modadvapi32.NewProc("SetNamedSecurityInfoW")
procSetTokenInformation = modadvapi32.NewProc("SetTokenInformation")
procProcessPrng = modbcryptprimitives.NewProc("ProcessPrng")
procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses")
@ -168,14 +166,6 @@ func ImpersonateSelf(impersonationlevel uint32) (err error) {
return
}
func InitializeAcl(acl *ACL, length uint32, revision uint32) (err error) {
r1, _, e1 := syscall.Syscall(procInitializeAcl.Addr(), 3, uintptr(unsafe.Pointer(acl)), uintptr(length), uintptr(revision))
if r1 == 0 {
err = errnoErr(e1)
}
return
}
func IsValidSid(sid *syscall.SID) (valid bool) {
r0, _, _ := syscall.Syscall(procIsValidSid.Addr(), 1, uintptr(unsafe.Pointer(sid)), 0, 0)
valid = r0 != 0
@ -244,23 +234,6 @@ func RevertToSelf() (err error) {
return
}
func SetNamedSecurityInfo(objectName string, objectType SE_OBJECT_TYPE, securityInformation SECURITY_INFORMATION, owner *syscall.SID, group *syscall.SID, dacl *ACL, sacl *ACL) (ret error) {
var _p0 *uint16
_p0, ret = syscall.UTF16PtrFromString(objectName)
if ret != nil {
return
}
return _SetNamedSecurityInfo(_p0, objectType, securityInformation, owner, group, dacl, sacl)
}
func _SetNamedSecurityInfo(objectName *uint16, objectType SE_OBJECT_TYPE, securityInformation SECURITY_INFORMATION, owner *syscall.SID, group *syscall.SID, dacl *ACL, sacl *ACL) (ret error) {
r0, _, _ := syscall.Syscall9(procSetNamedSecurityInfoW.Addr(), 7, uintptr(unsafe.Pointer(objectName)), uintptr(objectType), uintptr(securityInformation), uintptr(unsafe.Pointer(owner)), uintptr(unsafe.Pointer(group)), uintptr(unsafe.Pointer(dacl)), uintptr(unsafe.Pointer(sacl)), 0, 0)
if r0 != 0 {
ret = syscall.Errno(r0)
}
return
}
func SetTokenInformation(tokenHandle syscall.Token, tokenInformationClass uint32, tokenInformation unsafe.Pointer, tokenInformationLength uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procSetTokenInformation.Addr(), 4, uintptr(tokenHandle), uintptr(tokenInformationClass), uintptr(tokenInformation), uintptr(tokenInformationLength), 0, 0)
if r1 == 0 {