Source file src/cmd/go/internal/work/shell_nonwindows.go
1 // Copyright 2025 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:build !windows 6 7 package work 8 9 import ( 10 "errors" 11 "io/fs" 12 "os" 13 "path/filepath" 14 ) 15 16 // move moves a file from src to dst setting the permissions 17 // on the destination file to inherit the permissions from the 18 // destination parent directory. 19 func (sh *Shell) move(src, dst string, perm fs.FileMode) error { 20 // If the destination directory has the group sticky bit set, 21 // we have to copy the file to retain the correct permissions. 22 // https://golang.org/issue/18878 23 if fi, err := os.Stat(filepath.Dir(dst)); err == nil { 24 if fi.IsDir() && (fi.Mode()&fs.ModeSetgid) != 0 { 25 return errors.ErrUnsupported 26 } 27 } 28 // The perm argument is meant to be adjusted according to umask, 29 // but we don't know what the umask is. 30 // Create a dummy file to find out. 31 // This works even on systems like Plan 9 where the 32 // file mask computation incorporates other information. 33 mode := perm 34 f, err := os.OpenFile(filepath.Clean(dst)+"-go-tmp-umask", os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm) 35 if err == nil { 36 fi, err := f.Stat() 37 if err == nil { 38 mode = fi.Mode() & 0777 39 } 40 name := f.Name() 41 f.Close() 42 os.Remove(name) 43 } 44 45 if err := os.Chmod(src, mode); err != nil { 46 return err 47 } 48 return os.Rename(src, dst) 49 } 50