github.com/petermattis/pebble@v0.0.0-20190905164901-ab51a2166067/vfs/mem_fs.go (about) 1 // Copyright 2012 The LevelDB-Go and Pebble Authors. All rights reserved. Use 2 // of this source code is governed by a BSD-style license that can be found in 3 // the LICENSE file. 4 5 package vfs // import "github.com/petermattis/pebble/vfs" 6 7 import ( 8 "bytes" 9 "errors" 10 "fmt" 11 "io" 12 "os" 13 "sort" 14 "strings" 15 "sync" 16 "time" 17 ) 18 19 const sep = string(os.PathSeparator) 20 21 type nopCloser struct{} 22 23 func (nopCloser) Close() error { 24 return nil 25 } 26 27 // NewMem returns a new memory-backed FS implementation. 28 func NewMem() FS { 29 return &memFS{ 30 root: &memNode{ 31 children: make(map[string]*memNode), 32 isDir: true, 33 }, 34 } 35 } 36 37 // memFS implements FS. 38 type memFS struct { 39 mu sync.Mutex 40 root *memNode 41 } 42 43 var _ FS = &memFS{} 44 45 func (y *memFS) String() string { 46 y.mu.Lock() 47 defer y.mu.Unlock() 48 49 s := new(bytes.Buffer) 50 y.root.dump(s, 0) 51 return s.String() 52 } 53 54 // walk walks the directory tree for the fullname, calling f at each step. If 55 // f returns an error, the walk will be aborted and return that same error. 56 // 57 // Each walk is atomic: y's mutex is held for the entire operation, including 58 // all calls to f. 59 // 60 // dir is the directory at that step, frag is the name fragment, and final is 61 // whether it is the final step. For example, walking "/foo/bar/x" will result 62 // in 3 calls to f: 63 // - "/", "foo", false 64 // - "/foo/", "bar", false 65 // - "/foo/bar/", "x", true 66 // Similarly, walking "/y/z/", with a trailing slash, will result in 3 calls to f: 67 // - "/", "y", false 68 // - "/y/", "z", false 69 // - "/y/z/", "", true 70 func (y *memFS) walk(fullname string, f func(dir *memNode, frag string, final bool) error) error { 71 y.mu.Lock() 72 defer y.mu.Unlock() 73 74 // For memfs, the current working directory is the same as the root directory, 75 // so we strip off any leading "/"s to make fullname a relative path, and 76 // the walk starts at y.root. 77 for len(fullname) > 0 && fullname[0] == os.PathSeparator { 78 fullname = fullname[1:] 79 } 80 dir := y.root 81 82 for { 83 frag, remaining := fullname, "" 84 i := strings.IndexRune(fullname, os.PathSeparator) 85 final := i < 0 86 if !final { 87 frag, remaining = fullname[:i], fullname[i+1:] 88 for len(remaining) > 0 && remaining[0] == os.PathSeparator { 89 remaining = remaining[1:] 90 } 91 } 92 if err := f(dir, frag, final); err != nil { 93 return err 94 } 95 if final { 96 break 97 } 98 child := dir.children[frag] 99 if child == nil { 100 return errors.New("pebble/vfs: no such directory") 101 } 102 if !child.isDir { 103 return errors.New("pebble/vfs: not a directory") 104 } 105 dir, fullname = child, remaining 106 } 107 return nil 108 } 109 110 func (y *memFS) Create(fullname string) (File, error) { 111 var ret *memFile 112 err := y.walk(fullname, func(dir *memNode, frag string, final bool) error { 113 if final { 114 if frag == "" { 115 return errors.New("pebble/vfs: empty file name") 116 } 117 n := &memNode{name: frag} 118 dir.children[frag] = n 119 ret = &memFile{ 120 n: n, 121 write: true, 122 } 123 } 124 return nil 125 }) 126 if err != nil { 127 return nil, err 128 } 129 return ret, nil 130 } 131 132 func (y *memFS) Link(oldname, newname string) error { 133 var n *memNode 134 err := y.walk(oldname, func(dir *memNode, frag string, final bool) error { 135 if final { 136 if frag == "" { 137 return errors.New("pebble/vfs: empty file name") 138 } 139 n = dir.children[frag] 140 } 141 return nil 142 }) 143 if err != nil { 144 return err 145 } 146 if n == nil { 147 return errors.New("pebble/vfs: no such file or directory") 148 } 149 return y.walk(newname, func(dir *memNode, frag string, final bool) error { 150 if final { 151 if frag == "" { 152 return errors.New("pebble/vfs: empty file name") 153 } 154 dir.children[frag] = n 155 } 156 return nil 157 }) 158 } 159 160 func (y *memFS) open(fullname string, allowEmptyName bool) (File, error) { 161 var ret *memFile 162 err := y.walk(fullname, func(dir *memNode, frag string, final bool) error { 163 if final { 164 if frag == "" { 165 if !allowEmptyName { 166 return errors.New("pebble/vfs: empty file name") 167 } 168 ret = &memFile{ 169 n: dir, 170 } 171 return nil 172 } 173 if n := dir.children[frag]; n != nil { 174 ret = &memFile{ 175 n: n, 176 read: true, 177 } 178 } 179 } 180 return nil 181 }) 182 if err != nil { 183 return nil, err 184 } 185 if ret == nil { 186 return nil, &os.PathError{ 187 Op: "open", 188 Path: fullname, 189 Err: os.ErrNotExist, 190 } 191 } 192 return ret, nil 193 } 194 195 func (y *memFS) Open(fullname string, opts ...OpenOption) (File, error) { 196 return y.open(fullname, false /* allowEmptyName */) 197 } 198 199 func (y *memFS) OpenDir(fullname string) (File, error) { 200 return y.open(fullname, true /* allowEmptyName */) 201 } 202 203 func (y *memFS) Remove(fullname string) error { 204 return y.walk(fullname, func(dir *memNode, frag string, final bool) error { 205 if final { 206 if frag == "" { 207 return errors.New("pebble/vfs: empty file name") 208 } 209 _, ok := dir.children[frag] 210 if !ok { 211 return os.ErrNotExist 212 } 213 delete(dir.children, frag) 214 } 215 return nil 216 }) 217 } 218 219 func (y *memFS) Rename(oldname, newname string) error { 220 var n *memNode 221 err := y.walk(oldname, func(dir *memNode, frag string, final bool) error { 222 if final { 223 if frag == "" { 224 return errors.New("pebble/vfs: empty file name") 225 } 226 n = dir.children[frag] 227 delete(dir.children, frag) 228 } 229 return nil 230 }) 231 if err != nil { 232 return err 233 } 234 if n == nil { 235 return errors.New("pebble/vfs: no such file or directory") 236 } 237 return y.walk(newname, func(dir *memNode, frag string, final bool) error { 238 if final { 239 if frag == "" { 240 return errors.New("pebble/vfs: empty file name") 241 } 242 dir.children[frag] = n 243 } 244 return nil 245 }) 246 } 247 248 func (y *memFS) MkdirAll(dirname string, perm os.FileMode) error { 249 return y.walk(dirname, func(dir *memNode, frag string, final bool) error { 250 if frag == "" { 251 if final { 252 return nil 253 } 254 return errors.New("pebble/vfs: empty file name") 255 } 256 child := dir.children[frag] 257 if child == nil { 258 dir.children[frag] = &memNode{ 259 name: frag, 260 children: make(map[string]*memNode), 261 isDir: true, 262 } 263 return nil 264 } 265 if !child.isDir { 266 return errors.New("pebble/vfs: not a directory") 267 } 268 return nil 269 }) 270 } 271 272 func (y *memFS) Lock(fullname string) (io.Closer, error) { 273 // FS.Lock excludes other processes, but other processes cannot see this 274 // process' memory, so Lock is a no-op. 275 return nopCloser{}, nil 276 } 277 278 func (y *memFS) List(dirname string) ([]string, error) { 279 if !strings.HasSuffix(dirname, sep) { 280 dirname += sep 281 } 282 var ret []string 283 err := y.walk(dirname, func(dir *memNode, frag string, final bool) error { 284 if final { 285 if frag != "" { 286 panic("unreachable") 287 } 288 ret = make([]string, 0, len(dir.children)) 289 for s := range dir.children { 290 ret = append(ret, s) 291 } 292 } 293 return nil 294 }) 295 return ret, err 296 } 297 298 func (y *memFS) Stat(name string) (os.FileInfo, error) { 299 f, err := y.Open(name) 300 if err != nil { 301 if pe, ok := err.(*os.PathError); ok { 302 pe.Op = "stat" 303 } 304 return nil, err 305 } 306 defer f.Close() 307 return f.Stat() 308 } 309 310 // memNode holds a file's data or a directory's children, and implements os.FileInfo. 311 type memNode struct { 312 name string 313 data []byte 314 modTime time.Time 315 children map[string]*memNode 316 isDir bool 317 } 318 319 func (f *memNode) IsDir() bool { 320 return f.isDir 321 } 322 323 func (f *memNode) ModTime() time.Time { 324 return f.modTime 325 } 326 327 func (f *memNode) Mode() os.FileMode { 328 if f.isDir { 329 return os.ModeDir | 0755 330 } 331 return 0755 332 } 333 334 func (f *memNode) Name() string { 335 return f.name 336 } 337 338 func (f *memNode) Size() int64 { 339 return int64(len(f.data)) 340 } 341 342 func (f *memNode) Sys() interface{} { 343 return nil 344 } 345 346 func (f *memNode) dump(w *bytes.Buffer, level int) { 347 if f.isDir { 348 w.WriteString(" ") 349 } else { 350 fmt.Fprintf(w, "%8d ", len(f.data)) 351 } 352 for i := 0; i < level; i++ { 353 w.WriteString(" ") 354 } 355 w.WriteString(f.name) 356 if !f.isDir { 357 w.WriteByte('\n') 358 return 359 } 360 w.WriteByte(os.PathSeparator) 361 w.WriteByte('\n') 362 names := make([]string, 0, len(f.children)) 363 for name := range f.children { 364 names = append(names, name) 365 } 366 sort.Strings(names) 367 for _, name := range names { 368 f.children[name].dump(w, level+1) 369 } 370 } 371 372 // memFile is a reader or writer of a node's data, and implements File. 373 type memFile struct { 374 n *memNode 375 rpos int 376 read, write bool 377 } 378 379 func (f *memFile) Close() error { 380 return nil 381 } 382 383 func (f *memFile) Read(p []byte) (int, error) { 384 if !f.read { 385 return 0, errors.New("pebble/vfs: file was not opened for reading") 386 } 387 if f.n.isDir { 388 return 0, errors.New("pebble/vfs: cannot read a directory") 389 } 390 if f.rpos >= len(f.n.data) { 391 return 0, io.EOF 392 } 393 n := copy(p, f.n.data[f.rpos:]) 394 f.rpos += n 395 return n, nil 396 } 397 398 func (f *memFile) ReadAt(p []byte, off int64) (int, error) { 399 if !f.read { 400 return 0, errors.New("pebble/vfs: file was not opened for reading") 401 } 402 if f.n.isDir { 403 return 0, errors.New("pebble/vfs: cannot read a directory") 404 } 405 if off >= int64(len(f.n.data)) { 406 return 0, io.EOF 407 } 408 return copy(p, f.n.data[off:]), nil 409 } 410 411 func (f *memFile) Write(p []byte) (int, error) { 412 if !f.write { 413 return 0, errors.New("pebble/vfs: file was not created for writing") 414 } 415 if f.n.isDir { 416 return 0, errors.New("pebble/vfs: cannot write a directory") 417 } 418 f.n.modTime = time.Now() 419 f.n.data = append(f.n.data, p...) 420 return len(p), nil 421 } 422 423 func (f *memFile) Stat() (os.FileInfo, error) { 424 return f.n, nil 425 } 426 427 func (f *memFile) Sync() error { 428 return nil 429 }