github.com/ActiveState/cli@v0.0.0-20240508170324-6801f60cd051/internal/fileutils/fileutils_win.go (about) 1 //go:build windows 2 // +build windows 3 4 package fileutils 5 6 import ( 7 "errors" 8 "fmt" 9 "os" 10 "path/filepath" 11 "strings" 12 "syscall" 13 "unsafe" 14 15 "github.com/ActiveState/cli/internal/errs" 16 "github.com/ActiveState/cli/internal/logging" 17 "github.com/ActiveState/cli/internal/multilog" 18 "golang.org/x/sys/windows" 19 ) 20 21 const LineEnd = "\r\n" 22 23 // IsExecutable determines if the file at the given path has any execute permissions. 24 // This function does not care whether the current user can has enough privilege to 25 // execute the file. 26 func IsExecutable(path string) bool { 27 ext := strings.ToLower(filepath.Ext(path)) 28 if ext == ".exe" { 29 return true 30 } 31 32 pathExts := strings.Split(os.Getenv("PATHEXT"), ";") 33 for _, pe := range pathExts { 34 // pathext entries have `.` and are capitalize 35 if strings.EqualFold(ext, pe) { 36 return true 37 } 38 } 39 return false 40 } 41 42 // IsWritable returns true if the given path is writable 43 func IsWritable(path string) bool { 44 for !TargetExists(path) && path != "" { 45 path = filepath.Dir(path) 46 } 47 48 info, err := os.Stat(path) 49 if err != nil { 50 logging.Debug("os.Stat %s failed", path) 51 return false 52 } 53 54 // Check if the user bit is enabled in file permission 55 if info.Mode().Perm()&(1<<(uint(7))) == 0 { 56 logging.Debug("Write permission bit is not set on this file for user") 57 return false 58 } 59 60 return true 61 } 62 63 // ResolveUniquePath gets the absolute location of the provided path 64 // with the best effort attempt to produce the same result for all possible paths to the 65 // given target. 66 func ResolveUniquePath(path string) (string, error) { 67 evalPath, err := ResolvePath(filepath.Clean(path)) 68 if err != nil { 69 return "", errs.Wrap(err, "cannot resolve path") 70 } 71 72 longPath, err := GetLongPathName(evalPath) 73 if err != nil { 74 // GetLongPathName can fail on unsupported file-systems or if evalPath is not a physical path. 75 // => just log the error (unless err due to file not existing) and resume with resolved path 76 if !errors.Is(err, os.ErrNotExist) && !errors.Is(err, os.ErrNotExist) { 77 multilog.Error("could not resolve long version of %s: %v", evalPath, err) 78 } 79 return filepath.Clean(evalPath), nil 80 } 81 82 return filepath.Clean(longPath), nil 83 } 84 85 func HideFile(path string) error { 86 k32 := syscall.NewLazyDLL("kernel32.dll") 87 setFileAttrs := k32.NewProc("SetFileAttributesW") 88 89 utfPath, err := syscall.UTF16PtrFromString(path) 90 if err != nil { 91 return fmt.Errorf("Hide file (UTF16 conversion): %w", err) 92 } 93 uipPath := uintptr(unsafe.Pointer(utfPath)) 94 r1, _, err := setFileAttrs.Call(uipPath, 2) 95 if r1 == 0 && !errors.Is(err, windows.ERROR_SUCCESS) { 96 return fmt.Errorf("Hide file (set attributes): %w", err) 97 } 98 99 return nil 100 }