stat_windows.go (4592B)
1 //go:build windows 2 3 package diskfs 4 5 import ( 6 "fmt" 7 "io/fs" 8 "os" 9 "os/user" 10 "path" 11 "strconv" 12 "syscall" 13 "time" 14 15 "git.mtkn.jp/lib9p" 16 ) 17 18 func fileTimeToUnixTime(t syscall.Filetime) uint32 { 19 return uint32(uint64(t.HighDateTime<<32|t.LowDateTime)/1e7 - 11644473600) 20 } 21 22 // fiStat generates lib9p.Stat from the FileInfo. 23 // It requires QidPool and fileID to fill the Qid field of Stat. 24 func fiStat(pool *QidPool, id fileID, info fs.FileInfo) (*lib9p.Stat, error) { 25 qid, ok := pool.lookupID(id, info.ModTime()) 26 if !ok { 27 var err error 28 qid, err = pool.addID(id, info) 29 if err != nil { 30 return nil, fmt.Errorf("allocID: %v", err) 31 } 32 } 33 stat := info.Sys().(*syscall.Win32FileAttributeData) 34 /* 35 // TODO: get actual file uid 36 usr, err := user.LookupId(strconv.Itoa(syscall.Getuid())) 37 if err != nil { 38 return nil, fmt.Errorf("user: %v", err) 39 } 40 // TODO: get actual file gid 41 group, err := user.LookupGroupId(strconv.Itoa(syscall.Getgid())) 42 if err != nil { 43 return nil, fmt.Errorf("group: %v", err) 44 } 45 */ 46 return &lib9p.Stat{ 47 Type: 0, 48 Dev: 0, 49 Qid: qid, 50 Mode: info.Mode(), 51 Atime: fileTimeToUnixTime(stat.LastAccessTime), 52 Mtime: fileTimeToUnixTime(stat.LastWriteTime), 53 Length: info.Size(), 54 Name: info.Name(), 55 Uid: "glenda", //usr.Username, 56 Gid: "glenda", //group.Name, 57 Muid: "", 58 }, nil 59 } 60 61 var gidCache map[fileID]string 62 63 func init() { 64 gidCache = make(map[fileID]string) 65 } 66 67 // Stat is real implementation of File.Stat. 68 func (f *File) stat() (*lib9p.FileInfo, error) { 69 ospath := path.Join(f.fs.rootPath, f.path) 70 fsfi, err := os.Stat(ospath) 71 if err != nil { 72 return nil, fmt.Errorf("stat: %v", err) 73 } 74 id, err := f.id() 75 if err != nil { 76 return nil, fmt.Errorf("id: %v", err) 77 } 78 stat, err := fiStat(f.fs.qidPool, id, fsfi) 79 if err != nil { 80 return nil, fmt.Errorf("fiStat: %v", err) 81 } 82 return &lib9p.FileInfo{*stat}, nil 83 } 84 85 // wstat is the real implementation of File.WStat. 86 // TODO: when error occurs, file stat should be restored. 87 func (f *File) wstat(s *lib9p.Stat) error { 88 var file *os.File 89 fi, err := f.Stat() 90 if err != nil { 91 return fmt.Errorf("stat: %v", err) 92 } 93 oldStat := fi.Sys().(*lib9p.Stat) 94 if s.Length != oldStat.Length { 95 if file == nil { 96 file, err = os.OpenFile(path.Join(f.fs.rootPath, f.path), os.O_RDWR, 0) 97 if err != nil { 98 return err 99 } 100 defer file.Close() 101 } 102 if err := file.Truncate(s.Length); err != nil { 103 return fmt.Errorf("truncate: %v", err) 104 } 105 ret, err := file.Seek(0, 0) 106 if err != nil { 107 return fmt.Errorf("seek 0: %d, %w", ret, err) 108 } 109 } 110 if s.Mode != oldStat.Mode { 111 if file == nil { 112 file, err = os.OpenFile(path.Join(f.fs.rootPath, f.path), os.O_RDWR, 0) 113 if err != nil { 114 return err 115 } 116 defer file.Close() 117 } 118 if err := file.Chmod(s.Mode); err != nil { 119 return fmt.Errorf("chmod: %v", err) 120 } 121 } 122 if s.Mtime != oldStat.Mtime { 123 err := os.Chtimes(f.path, time.Time{}, time.Unix(int64(s.Mtime), 0)) 124 if err != nil { 125 return fmt.Errorf("chtimes: %v", err) 126 } 127 } 128 if s.Gid != oldStat.Gid { 129 group, err := user.LookupGroup(s.Gid) 130 if err != nil { 131 return fmt.Errorf("lookupgroup: %v", err) 132 } 133 usr, err := user.Lookup(oldStat.Uid) 134 if err != nil { 135 return fmt.Errorf("lookup user: %v", err) 136 } 137 u, err := strconv.Atoi(usr.Uid) 138 if err != nil { 139 return fmt.Errorf("convert uid: %v", err) 140 } 141 g, err := strconv.Atoi(group.Gid) 142 if err != nil { 143 return fmt.Errorf("convert gid: %v", err) 144 } 145 if file == nil { 146 file, err = os.OpenFile(path.Join(f.fs.rootPath, f.path), os.O_RDWR, 0) 147 if err != nil { 148 return err 149 } 150 defer file.Close() 151 } 152 if err := file.Chown(u, g); err != nil { 153 return fmt.Errorf("chown: %v", err) 154 } 155 } 156 if s.Name != oldStat.Name { 157 // TODO: check neither Names contains "/" 158 oldpath := path.Join(f.fs.rootPath, path.Dir(f.path), oldStat.Name) 159 newpath := path.Join(f.fs.rootPath, path.Dir(f.path), s.Name) 160 if err := os.Rename(oldpath, newpath); err != nil { 161 return fmt.Errorf("rename: %v", err) 162 } 163 } 164 return nil 165 } 166 167 func chown(ospath string, uid, gid string) error { 168 usr, err := user.Lookup(uid) 169 if err != nil { 170 return fmt.Errorf("lookup user: %v", err) 171 } 172 u, err := strconv.Atoi(usr.Uid) 173 if err != nil { 174 return fmt.Errorf("convert uid: %v", err) 175 } 176 group, err := user.LookupGroup(gid) 177 if err != nil { 178 return fmt.Errorf("lookupgroup: %v", err) 179 } 180 g, err := strconv.Atoi(group.Gid) 181 if err != nil { 182 return fmt.Errorf("convert gid: %v", err) 183 } 184 if err := os.Chown(ospath, u, g); err != nil { 185 return fmt.Errorf("set owner and group: %v", err) 186 } 187 return nil 188 }