github.com/telepresenceio/telepresence/v2@v2.20.0-pro.6.0.20240517030216-236ea954e789/pkg/client/logging/stat_windows.go (about) 1 package logging 2 3 import ( 4 "errors" 5 "fmt" 6 "runtime" 7 "syscall" //nolint:depguard // We specifically need "syscall.Win32FileAttributeData" rather than "windows.Win32FileAttributeData" for fs.File.Sys(). 8 "time" 9 10 "github.com/hectane/go-acl/api" 11 "golang.org/x/sys/windows" 12 13 "github.com/telepresenceio/telepresence/v2/pkg/dos" 14 ) 15 16 type WindowsSysInfo interface { 17 SysInfo 18 Owner() *windows.SID 19 Group() *windows.SID 20 DACL() windows.Handle 21 SACL() windows.Handle 22 SecurityDescriptor() windows.Handle 23 } 24 25 type windowsSysInfo struct { 26 path string 27 data *syscall.Win32FileAttributeData 28 owner *windows.SID 29 group *windows.SID 30 dacl windows.Handle 31 sacl windows.Handle 32 secDesc windows.Handle 33 } 34 35 func osFStat(file dos.File) (SysInfo, error) { 36 info, err := file.Stat() 37 if err != nil { 38 return nil, fmt.Errorf("failed to stat %s: %w", file.Name(), err) 39 } 40 sys, ok := info.Sys().(*syscall.Win32FileAttributeData) 41 if !ok { 42 return nil, fmt.Errorf("files of type %T don't support Fstat", file) 43 } 44 wi := windowsSysInfo{ 45 path: file.Name(), 46 data: sys, 47 } 48 err = api.GetNamedSecurityInfo( 49 wi.path, 50 api.SE_FILE_OBJECT, 51 api.OWNER_SECURITY_INFORMATION, 52 &wi.owner, 53 &wi.group, 54 &wi.dacl, 55 &wi.sacl, 56 &wi.secDesc, 57 ) 58 if err != nil && !errors.Is(err, windows.ERROR_SUCCESS) { 59 return nil, err 60 } 61 runtime.SetFinalizer(&wi, func(wi *windowsSysInfo) { 62 _, _ = windows.LocalFree(wi.secDesc) 63 }) 64 return &wi, nil 65 } 66 67 func (wi *windowsSysInfo) Size() int64 { 68 return int64(wi.data.FileSizeHigh)<<32 + int64(wi.data.FileSizeLow) 69 } 70 71 func (wi *windowsSysInfo) SetOwnerAndGroup(name string) error { 72 err := api.SetNamedSecurityInfo(name, api.SE_FILE_OBJECT, api.OWNER_SECURITY_INFORMATION, wi.owner, wi.group, wi.dacl, wi.sacl) 73 if err != nil { 74 // On some systems it seems SetNamedSecurityInfo will return ERROR_SUCCESS on success... this is an odd violation of the principle 75 // that windows APIs return err = nil on success but okay 76 if errors.Is(err, windows.ERROR_SUCCESS) { 77 return nil 78 } 79 return err 80 } 81 return nil 82 } 83 84 func (wi *windowsSysInfo) HaveSameOwnerAndGroup(s SysInfo) bool { 85 eq := func(a, b *windows.SID) bool { 86 if a == b { 87 return true 88 } 89 if a == nil || b == nil { 90 return false 91 } 92 if a.IsValid() { 93 if b.IsValid() { 94 return a.Equals(b) 95 } 96 return false 97 } 98 return !b.IsValid() 99 } 100 owi, ok := s.(*windowsSysInfo) 101 return ok && eq(wi.owner, owi.owner) && eq(wi.group, owi.group) 102 } 103 104 func (wi *windowsSysInfo) BirthTime() time.Time { 105 return time.Unix(0, wi.data.CreationTime.Nanoseconds()) 106 } 107 108 func (wi *windowsSysInfo) ModifyTime() time.Time { 109 return time.Unix(0, wi.data.LastWriteTime.Nanoseconds()) 110 } 111 112 func (wi *windowsSysInfo) ChangeTime() time.Time { 113 return time.Unix(0, wi.data.LastWriteTime.Nanoseconds()) 114 } 115 116 func (wi *windowsSysInfo) Owner() *windows.SID { 117 return wi.owner 118 } 119 120 func (wi *windowsSysInfo) Group() *windows.SID { 121 return wi.group 122 } 123 124 func (wi *windowsSysInfo) DACL() windows.Handle { 125 return wi.dacl 126 } 127 128 func (wi *windowsSysInfo) SACL() windows.Handle { 129 return wi.sacl 130 } 131 132 func (wi *windowsSysInfo) String() string { 133 ov := "invalid" 134 if wi.owner != nil && wi.owner.IsValid() { 135 ov = wi.owner.String() 136 } 137 gv := "invalid" 138 if wi.group != nil && wi.group.IsValid() { 139 gv = wi.group.String() 140 } 141 return fmt.Sprintf("CTIME %v, UID %v, GID %v", wi.BirthTime(), ov, gv) 142 }