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