tractor.dev/toolkit-go@v0.0.0-20241010005851-214d91207d07/engine/fs/memfs/fs.go (about) 1 // Copyright © 2014 Steve Francia <spf@spf13.com>. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package memfs 15 16 import ( 17 "io" 18 "io/fs" 19 "log" 20 "os" 21 "path/filepath" 22 "strings" 23 "sync" 24 "time" 25 ) 26 27 const filePathSeparator = string(filepath.Separator) 28 const chmodBits = fs.ModePerm | fs.ModeSetuid | fs.ModeSetgid | fs.ModeSticky // Only a subset of bits are allowed to be changed. Documented under os.Chmod() 29 30 type FS struct { 31 mu sync.RWMutex 32 data map[string]*FileData 33 init sync.Once 34 } 35 36 func New() *FS { 37 return &FS{} 38 } 39 40 func (m *FS) getData() map[string]*FileData { 41 m.init.Do(func() { 42 m.data = make(map[string]*FileData) 43 // Root should always exist, right? 44 // TODO: what about windows? 45 root := CreateDir(filePathSeparator) 46 SetMode(root, fs.ModeDir|0755) 47 m.data[filePathSeparator] = root 48 }) 49 return m.data 50 } 51 52 func (m *FS) Create(name string) (fs.File, error) { 53 name = normalizePath(name) 54 m.mu.Lock() 55 file := CreateFile(name) 56 m.getData()[name] = file 57 m.registerWithParent(file, 0) 58 m.mu.Unlock() 59 return NewFileHandle(file), nil 60 } 61 62 func (m *FS) unregisterWithParent(fileName string) error { 63 f, err := m.lockfreeOpen(fileName) 64 if err != nil { 65 return err 66 } 67 parent := m.findParent(f) 68 if parent == nil { 69 log.Panic("parent of ", f.Name(), " is nil") 70 } 71 72 parent.Lock() 73 RemoveFromMemDir(parent, f) 74 parent.Unlock() 75 return nil 76 } 77 78 func (m *FS) findParent(f *FileData) *FileData { 79 pdir, _ := filepath.Split(f.Name()) 80 pdir = filepath.Clean(pdir) 81 pfile, err := m.lockfreeOpen(pdir) 82 if err != nil { 83 return nil 84 } 85 return pfile 86 } 87 88 func (m *FS) registerWithParent(f *FileData, perm fs.FileMode) { 89 if f == nil { 90 return 91 } 92 parent := m.findParent(f) 93 if parent == nil { 94 pdir := filepath.Dir(filepath.Clean(f.Name())) 95 err := m.lockfreeMkdir(pdir, perm) 96 if err != nil { 97 //log.Println("Mkdir error:", err) 98 return 99 } 100 parent, err = m.lockfreeOpen(pdir) 101 if err != nil { 102 //log.Println("Open after Mkdir error:", err) 103 return 104 } 105 } 106 107 parent.Lock() 108 InitializeDir(parent) 109 AddToMemDir(parent, f) 110 parent.Unlock() 111 } 112 113 func (m *FS) lockfreeMkdir(name string, perm fs.FileMode) error { 114 name = normalizePath(name) 115 x, ok := m.getData()[name] 116 if ok { 117 // Only return fs.ErrExist if it's a file, not a directory. 118 i := FileInfo{FileData: x} 119 if !i.IsDir() { 120 return fs.ErrExist 121 } 122 } else { 123 item := CreateDir(name) 124 SetMode(item, fs.ModeDir|perm) 125 m.getData()[name] = item 126 m.registerWithParent(item, perm) 127 } 128 return nil 129 } 130 131 func (m *FS) Mkdir(name string, perm fs.FileMode) error { 132 perm &= chmodBits 133 name = normalizePath(name) 134 135 m.mu.RLock() 136 _, ok := m.getData()[name] 137 m.mu.RUnlock() 138 if ok { 139 return &os.PathError{Op: "mkdir", Path: name, Err: fs.ErrExist} 140 } 141 142 m.mu.Lock() 143 item := CreateDir(name) 144 SetMode(item, fs.ModeDir|perm) 145 m.getData()[name] = item 146 m.registerWithParent(item, perm) 147 m.mu.Unlock() 148 149 return m.setFileMode(name, perm|fs.ModeDir) 150 } 151 152 func (m *FS) MkdirAll(path string, perm fs.FileMode) error { 153 err := m.Mkdir(path, perm) 154 if err != nil { 155 if err.(*os.PathError).Err == fs.ErrExist { 156 return nil 157 } 158 return err 159 } 160 return nil 161 } 162 163 // Handle some relative paths 164 func normalizePath(path string) string { 165 path = filepath.Clean(path) 166 167 switch path { 168 case ".": 169 return filePathSeparator 170 case "..": 171 return filePathSeparator 172 default: 173 return path 174 } 175 } 176 177 func (m *FS) Open(name string) (fs.File, error) { 178 f, err := m.open(name) 179 if f != nil { 180 return NewROFileHandle(f), err 181 } 182 return nil, err 183 } 184 185 func (m *FS) openWrite(name string) (fs.File, error) { 186 f, err := m.open(name) 187 if f != nil { 188 return NewFileHandle(f), err 189 } 190 return nil, err 191 } 192 193 func (m *FS) open(name string) (*FileData, error) { 194 name = normalizePath(name) 195 196 m.mu.RLock() 197 f, ok := m.getData()[name] 198 m.mu.RUnlock() 199 if !ok { 200 return nil, &os.PathError{Op: "open", Path: name, Err: fs.ErrNotExist} 201 } 202 return f, nil 203 } 204 205 func (m *FS) lockfreeOpen(name string) (*FileData, error) { 206 name = normalizePath(name) 207 f, ok := m.getData()[name] 208 if ok { 209 return f, nil 210 } else { 211 return nil, fs.ErrNotExist 212 } 213 } 214 215 func (m *FS) OpenFile(name string, flag int, perm fs.FileMode) (fs.File, error) { 216 perm &= chmodBits 217 chmod := false 218 file, err := m.openWrite(name) 219 if err == nil && (flag&os.O_EXCL > 0) { 220 return nil, &os.PathError{Op: "open", Path: name, Err: fs.ErrExist} 221 } 222 if os.IsNotExist(err) && (flag&os.O_CREATE > 0) { 223 file, err = m.Create(name) 224 chmod = true 225 } 226 if err != nil { 227 return nil, err 228 } 229 if flag == os.O_RDONLY { 230 file = NewROFileHandle(file.(*File).Data()) 231 } 232 if flag&os.O_APPEND > 0 { 233 fseek, ok := file.(io.Seeker) 234 if !ok { 235 return nil, fs.ErrPermission 236 } 237 _, err = fseek.Seek(0, os.SEEK_END) 238 if err != nil { 239 file.Close() 240 return nil, err 241 } 242 } 243 if flag&os.O_TRUNC > 0 && flag&(os.O_RDWR|os.O_WRONLY) > 0 { 244 ftrunc, ok := file.(interface { 245 Truncate(size int64) error 246 }) 247 if !ok { 248 return nil, fs.ErrPermission 249 } 250 err = ftrunc.Truncate(0) 251 if err != nil { 252 file.Close() 253 return nil, err 254 } 255 } 256 if chmod { 257 return file, m.setFileMode(name, perm) 258 } 259 return file, nil 260 } 261 262 func (m *FS) Remove(name string) error { 263 name = normalizePath(name) 264 265 m.mu.Lock() 266 defer m.mu.Unlock() 267 268 if _, ok := m.getData()[name]; ok { 269 err := m.unregisterWithParent(name) 270 if err != nil { 271 return &os.PathError{Op: "remove", Path: name, Err: err} 272 } 273 delete(m.getData(), name) 274 } else { 275 return &os.PathError{Op: "remove", Path: name, Err: os.ErrNotExist} 276 } 277 return nil 278 } 279 280 func (m *FS) RemoveAll(path string) error { 281 path = normalizePath(path) 282 m.mu.Lock() 283 m.unregisterWithParent(path) 284 m.mu.Unlock() 285 286 m.mu.RLock() 287 defer m.mu.RUnlock() 288 289 for p := range m.getData() { 290 if strings.HasPrefix(p, path) { 291 m.mu.RUnlock() 292 m.mu.Lock() 293 delete(m.getData(), p) 294 m.mu.Unlock() 295 m.mu.RLock() 296 } 297 } 298 return nil 299 } 300 301 func (m *FS) Rename(oldname, newname string) error { 302 oldname = normalizePath(oldname) 303 newname = normalizePath(newname) 304 305 if oldname == newname { 306 return nil 307 } 308 309 m.mu.RLock() 310 defer m.mu.RUnlock() 311 if _, ok := m.getData()[oldname]; ok { 312 m.mu.RUnlock() 313 m.mu.Lock() 314 m.unregisterWithParent(oldname) 315 fileData := m.getData()[oldname] 316 delete(m.getData(), oldname) 317 ChangeFileName(fileData, newname) 318 m.getData()[newname] = fileData 319 m.registerWithParent(fileData, 0) 320 m.mu.Unlock() 321 m.mu.RLock() 322 } else { 323 return &os.PathError{Op: "rename", Path: oldname, Err: fs.ErrNotExist} 324 } 325 return nil 326 } 327 328 func (m *FS) Stat(name string) (fs.FileInfo, error) { 329 f, err := m.Open(name) 330 if err != nil { 331 return nil, err 332 } 333 fi := GetFileInfo(f.(*File).Data()) 334 return fi, nil 335 } 336 337 func (m *FS) Chmod(name string, mode fs.FileMode) error { 338 mode &= chmodBits 339 340 m.mu.RLock() 341 f, ok := m.getData()[name] 342 m.mu.RUnlock() 343 if !ok { 344 return &os.PathError{Op: "chmod", Path: name, Err: fs.ErrNotExist} 345 } 346 prevOtherBits := GetFileInfo(f).Mode() & ^chmodBits 347 348 mode = prevOtherBits | mode 349 return m.setFileMode(name, mode) 350 } 351 352 func (m *FS) setFileMode(name string, mode fs.FileMode) error { 353 name = normalizePath(name) 354 355 m.mu.RLock() 356 f, ok := m.getData()[name] 357 m.mu.RUnlock() 358 if !ok { 359 return &os.PathError{Op: "chmod", Path: name, Err: fs.ErrNotExist} 360 } 361 362 m.mu.Lock() 363 SetMode(f, mode) 364 m.mu.Unlock() 365 366 return nil 367 } 368 369 func (m *FS) Chown(name string, uid, gid int) error { 370 name = normalizePath(name) 371 372 m.mu.RLock() 373 f, ok := m.getData()[name] 374 m.mu.RUnlock() 375 if !ok { 376 return &os.PathError{Op: "chown", Path: name, Err: fs.ErrNotExist} 377 } 378 379 SetUID(f, uid) 380 SetGID(f, gid) 381 382 return nil 383 } 384 385 func (m *FS) Chtimes(name string, atime time.Time, mtime time.Time) error { 386 name = normalizePath(name) 387 388 m.mu.RLock() 389 f, ok := m.getData()[name] 390 m.mu.RUnlock() 391 if !ok { 392 return &os.PathError{Op: "chtimes", Path: name, Err: fs.ErrNotExist} 393 } 394 395 m.mu.Lock() 396 SetModTime(f, mtime) 397 m.mu.Unlock() 398 399 return nil 400 } 401 402 // func (m *FS) List() { 403 // for _, x := range m.data { 404 // y := mem.FileInfo{FileData: x} 405 // fmt.Println(x.Name(), y.Size()) 406 // } 407 // }