github.com/zuoyebang/bitalosdb@v1.1.1-0.20240516111551-79a8c4d8ce20/internal/vfs/mem_fs.go (about) 1 // Copyright 2021 The Bitalosdb author(hustxrb@163.com) and other contributors. 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 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package vfs 16 17 import ( 18 "bytes" 19 "fmt" 20 "io" 21 "os" 22 "path" 23 "sort" 24 "strings" 25 "sync" 26 "sync/atomic" 27 "time" 28 29 "github.com/zuoyebang/bitalosdb/internal/invariants" 30 31 "github.com/cockroachdb/errors" 32 "github.com/cockroachdb/errors/oserror" 33 ) 34 35 const sep = "/" 36 37 // NewMem returns a new memory-backed FS implementation. 38 func NewMem() *MemFS { 39 return &MemFS{ 40 root: &memNode{ 41 children: make(map[string]*memNode), 42 isDir: true, 43 }, 44 } 45 } 46 47 // NewStrictMem returns a "strict" memory-backed FS implementation. The behaviour is strict wrt 48 // needing a Sync() call on files or directories for the state changes to be finalized. Any 49 // changes that are not finalized are visible to reads until MemFS.ResetToSyncedState() is called, 50 // at which point they are discarded and no longer visible. 51 // 52 // Expected usage: 53 // 54 // strictFS := NewStrictMem() 55 // db := Open(..., &Options{FS: strictFS}) 56 // // Do and commit various operations. 57 // ... 58 // // Prevent any more changes to finalized state. 59 // strictFS.SetIgnoreSyncs(true) 60 // // This will finish any ongoing background flushes, compactions but none of these writes will 61 // // be finalized since syncs are being ignored. 62 // db.Close() 63 // // Discard unsynced state. 64 // strictFS.ResetToSyncedState() 65 // // Allow changes to finalized state. 66 // strictFS.SetIgnoreSyncs(false) 67 // // Open the DB. This DB should have the same state as if the earlier strictFS operations and 68 // // db.Close() were not called. 69 // db := Open(..., &Options{FS: strictFS}) 70 func NewStrictMem() *MemFS { 71 return &MemFS{ 72 root: &memNode{ 73 children: make(map[string]*memNode), 74 isDir: true, 75 }, 76 strict: true, 77 } 78 } 79 80 // NewMemFile returns a memory-backed File implementation. The memory-backed 81 // file takes ownership of data. 82 func NewMemFile(data []byte) File { 83 n := &memNode{refs: 1} 84 n.mu.data = data 85 n.mu.modTime = time.Now() 86 return &memFile{ 87 n: n, 88 read: true, 89 } 90 } 91 92 // MemFS implements FS. 93 type MemFS struct { 94 mu sync.Mutex 95 root *memNode 96 97 strict bool 98 ignoreSyncs bool 99 } 100 101 var _ FS = &MemFS{} 102 103 // String dumps the contents of the MemFS. 104 func (y *MemFS) String() string { 105 y.mu.Lock() 106 defer y.mu.Unlock() 107 108 s := new(bytes.Buffer) 109 y.root.dump(s, 0) 110 return s.String() 111 } 112 113 // SetIgnoreSyncs sets the MemFS.ignoreSyncs field. See the usage comment with NewStrictMem() for 114 // details. 115 func (y *MemFS) SetIgnoreSyncs(ignoreSyncs bool) { 116 y.mu.Lock() 117 if !y.strict { 118 // noop 119 return 120 } 121 y.ignoreSyncs = ignoreSyncs 122 y.mu.Unlock() 123 } 124 125 // ResetToSyncedState discards state in the FS that is not synced. See the usage comment with 126 // NewStrictMem() for details. 127 func (y *MemFS) ResetToSyncedState() { 128 if !y.strict { 129 // noop 130 return 131 } 132 y.mu.Lock() 133 y.root.resetToSyncedState() 134 y.mu.Unlock() 135 } 136 137 // walk walks the directory tree for the fullname, calling f at each step. If 138 // f returns an error, the walk will be aborted and return that same error. 139 // 140 // Each walk is atomic: y's mutex is held for the entire operation, including 141 // all calls to f. 142 // 143 // dir is the directory at that step, frag is the name fragment, and final is 144 // whether it is the final step. For example, walking "/foo/bar/x" will result 145 // in 3 calls to f: 146 // - "/", "foo", false 147 // - "/foo/", "bar", false 148 // - "/foo/bar/", "x", true 149 // 150 // Similarly, walking "/y/z/", with a trailing slash, will result in 3 calls to f: 151 // - "/", "y", false 152 // - "/y/", "z", false 153 // - "/y/z/", "", true 154 func (y *MemFS) walk(fullname string, f func(dir *memNode, frag string, final bool) error) error { 155 y.mu.Lock() 156 defer y.mu.Unlock() 157 158 // For memfs, the current working directory is the same as the root directory, 159 // so we strip off any leading "/"s to make fullname a relative path, and 160 // the walk starts at y.root. 161 for len(fullname) > 0 && fullname[0] == sep[0] { 162 fullname = fullname[1:] 163 } 164 dir := y.root 165 166 for { 167 frag, remaining := fullname, "" 168 i := strings.IndexRune(fullname, rune(sep[0])) 169 final := i < 0 170 if !final { 171 frag, remaining = fullname[:i], fullname[i+1:] 172 for len(remaining) > 0 && remaining[0] == sep[0] { 173 remaining = remaining[1:] 174 } 175 } 176 if err := f(dir, frag, final); err != nil { 177 return err 178 } 179 if final { 180 break 181 } 182 child := dir.children[frag] 183 if child == nil { 184 return &os.PathError{ 185 Op: "open", 186 Path: fullname, 187 Err: oserror.ErrNotExist, 188 } 189 } 190 if !child.isDir { 191 return &os.PathError{ 192 Op: "open", 193 Path: fullname, 194 Err: errors.New("not a directory"), 195 } 196 } 197 dir, fullname = child, remaining 198 } 199 return nil 200 } 201 202 // Create implements FS.Create. 203 func (y *MemFS) Create(fullname string) (File, error) { 204 var ret *memFile 205 err := y.walk(fullname, func(dir *memNode, frag string, final bool) error { 206 if final { 207 if frag == "" { 208 return errors.New("bitalosdb/vfs: empty file name") 209 } 210 n := &memNode{name: frag} 211 dir.children[frag] = n 212 ret = &memFile{ 213 n: n, 214 fs: y, 215 write: true, 216 read: true, 217 } 218 } 219 return nil 220 }) 221 if err != nil { 222 return nil, err 223 } 224 atomic.AddInt32(&ret.n.refs, 1) 225 return ret, nil 226 } 227 228 // Link implements FS.Link. 229 func (y *MemFS) Link(oldname, newname string) error { 230 var n *memNode 231 err := y.walk(oldname, func(dir *memNode, frag string, final bool) error { 232 if final { 233 if frag == "" { 234 return errors.New("bitalosdb/vfs: empty file name") 235 } 236 n = dir.children[frag] 237 } 238 return nil 239 }) 240 if err != nil { 241 return err 242 } 243 if n == nil { 244 return &os.LinkError{ 245 Op: "link", 246 Old: oldname, 247 New: newname, 248 Err: oserror.ErrNotExist, 249 } 250 } 251 return y.walk(newname, func(dir *memNode, frag string, final bool) error { 252 if final { 253 if frag == "" { 254 return errors.New("bitalosdb/vfs: empty file name") 255 } 256 if _, ok := dir.children[frag]; ok { 257 return &os.LinkError{ 258 Op: "link", 259 Old: oldname, 260 New: newname, 261 Err: oserror.ErrExist, 262 } 263 } 264 dir.children[frag] = n 265 } 266 return nil 267 }) 268 } 269 270 func (y *MemFS) open(fullname string, allowEmptyName bool) (File, error) { 271 var ret *memFile 272 err := y.walk(fullname, func(dir *memNode, frag string, final bool) error { 273 if final { 274 if frag == "" { 275 if !allowEmptyName { 276 return errors.New("bitalosdb/vfs: empty file name") 277 } 278 ret = &memFile{ 279 n: dir, 280 fs: y, 281 } 282 return nil 283 } 284 if n := dir.children[frag]; n != nil { 285 ret = &memFile{ 286 n: n, 287 fs: y, 288 read: true, 289 } 290 } 291 } 292 return nil 293 }) 294 if err != nil { 295 return nil, err 296 } 297 if ret == nil { 298 return nil, &os.PathError{ 299 Op: "open", 300 Path: fullname, 301 Err: oserror.ErrNotExist, 302 } 303 } 304 atomic.AddInt32(&ret.n.refs, 1) 305 return ret, nil 306 } 307 308 // Open implements FS.Open. 309 func (y *MemFS) Open(fullname string, _ ...OpenOption) (File, error) { 310 return y.open(fullname, false) 311 } 312 313 // OpenDir implements FS.OpenDir. 314 func (y *MemFS) OpenDir(fullname string) (File, error) { 315 return y.open(fullname, true /* allowEmptyName */) 316 } 317 318 // Remove implements FS.Remove. 319 func (y *MemFS) Remove(fullname string) error { 320 return y.walk(fullname, func(dir *memNode, frag string, final bool) error { 321 if final { 322 if frag == "" { 323 return errors.New("bitalosdb/vfs: empty file name") 324 } 325 child, ok := dir.children[frag] 326 if !ok { 327 return oserror.ErrNotExist 328 } 329 // Disallow removal of open files/directories which implements Windows 330 // semantics. This ensures that we don't regress in the ordering of 331 // operations and try to remove a file while it is still open. 332 if n := atomic.LoadInt32(&child.refs); n > 0 { 333 return oserror.ErrInvalid 334 } 335 if len(child.children) > 0 { 336 return errNotEmpty 337 } 338 delete(dir.children, frag) 339 } 340 return nil 341 }) 342 } 343 344 // RemoveAll implements FS.RemoveAll. 345 func (y *MemFS) RemoveAll(fullname string) error { 346 err := y.walk(fullname, func(dir *memNode, frag string, final bool) error { 347 if final { 348 if frag == "" { 349 return errors.New("bitalosdb/vfs: empty file name") 350 } 351 _, ok := dir.children[frag] 352 if !ok { 353 return nil 354 } 355 delete(dir.children, frag) 356 } 357 return nil 358 }) 359 // Match os.RemoveAll which returns a nil error even if the parent 360 // directories don't exist. 361 if oserror.IsNotExist(err) { 362 err = nil 363 } 364 return err 365 } 366 367 // Rename implements FS.Rename. 368 func (y *MemFS) Rename(oldname, newname string) error { 369 var n *memNode 370 err := y.walk(oldname, func(dir *memNode, frag string, final bool) error { 371 if final { 372 if frag == "" { 373 return errors.New("bitalosdb/vfs: empty file name") 374 } 375 n = dir.children[frag] 376 delete(dir.children, frag) 377 } 378 return nil 379 }) 380 if err != nil { 381 return err 382 } 383 if n == nil { 384 return &os.PathError{ 385 Op: "open", 386 Path: oldname, 387 Err: oserror.ErrNotExist, 388 } 389 } 390 return y.walk(newname, func(dir *memNode, frag string, final bool) error { 391 if final { 392 if frag == "" { 393 return errors.New("bitalosdb/vfs: empty file name") 394 } 395 dir.children[frag] = n 396 n.name = frag 397 } 398 return nil 399 }) 400 } 401 402 // ReuseForWrite implements FS.ReuseForWrite. 403 func (y *MemFS) ReuseForWrite(oldname, newname string) (File, error) { 404 if err := y.Rename(oldname, newname); err != nil { 405 return nil, err 406 } 407 f, err := y.Open(newname) 408 if err != nil { 409 return nil, err 410 } 411 y.mu.Lock() 412 defer y.mu.Unlock() 413 414 mf := f.(*memFile) 415 mf.read = false 416 mf.write = true 417 return f, nil 418 } 419 420 func (y *MemFS) OpenForWrite(name string) (File, error) { 421 f, err := y.Open(name) 422 if err != nil { 423 return nil, err 424 } 425 y.mu.Lock() 426 defer y.mu.Unlock() 427 428 mf := f.(*memFile) 429 mf.read = false 430 mf.write = true 431 return f, nil 432 } 433 434 func (y *MemFS) OpenWR(name string) (File, error) { 435 f, err := y.Open(name) 436 if err != nil { 437 return nil, err 438 } 439 y.mu.Lock() 440 defer y.mu.Unlock() 441 442 mf := f.(*memFile) 443 mf.read = false 444 mf.write = true 445 return f, nil 446 } 447 448 // MkdirAll implements FS.MkdirAll. 449 func (y *MemFS) MkdirAll(dirname string, _ os.FileMode) error { 450 return y.walk(dirname, func(dir *memNode, frag string, final bool) error { 451 if frag == "" { 452 if final { 453 return nil 454 } 455 return errors.New("bitalosdb/vfs: empty file name") 456 } 457 child := dir.children[frag] 458 if child == nil { 459 dir.children[frag] = &memNode{ 460 name: frag, 461 children: make(map[string]*memNode), 462 isDir: true, 463 } 464 return nil 465 } 466 if !child.isDir { 467 return &os.PathError{ 468 Op: "open", 469 Path: dirname, 470 Err: errors.New("not a directory"), 471 } 472 } 473 return nil 474 }) 475 } 476 477 // Lock implements FS.Lock. 478 func (y *MemFS) Lock(fullname string) (io.Closer, error) { 479 // FS.Lock excludes other processes, but other processes cannot see this 480 // process' memory. We translate Lock into Create so that have the normal 481 // detection of non-existent directory paths. 482 return y.Create(fullname) 483 } 484 485 // List implements FS.List. 486 func (y *MemFS) List(dirname string) ([]string, error) { 487 if !strings.HasSuffix(dirname, sep) { 488 dirname += sep 489 } 490 var ret []string 491 err := y.walk(dirname, func(dir *memNode, frag string, final bool) error { 492 if final { 493 if frag != "" { 494 panic("unreachable") 495 } 496 ret = make([]string, 0, len(dir.children)) 497 for s := range dir.children { 498 ret = append(ret, s) 499 } 500 } 501 return nil 502 }) 503 return ret, err 504 } 505 506 // Stat implements FS.Stat. 507 func (y *MemFS) Stat(name string) (os.FileInfo, error) { 508 f, err := y.Open(name) 509 if err != nil { 510 if pe, ok := err.(*os.PathError); ok { 511 pe.Op = "stat" 512 } 513 return nil, err 514 } 515 defer f.Close() 516 return f.Stat() 517 } 518 519 // PathBase implements FS.PathBase. 520 func (*MemFS) PathBase(p string) string { 521 // Note that MemFS uses forward slashes for its separator, hence the use of 522 // path.Base, not filepath.Base. 523 return path.Base(p) 524 } 525 526 // PathJoin implements FS.PathJoin. 527 func (*MemFS) PathJoin(elem ...string) string { 528 // Note that MemFS uses forward slashes for its separator, hence the use of 529 // path.Join, not filepath.Join. 530 return path.Join(elem...) 531 } 532 533 // PathDir implements FS.PathDir. 534 func (*MemFS) PathDir(p string) string { 535 // Note that MemFS uses forward slashes for its separator, hence the use of 536 // path.Dir, not filepath.Dir. 537 return path.Dir(p) 538 } 539 540 // GetDiskUsage implements FS.GetDiskUsage. 541 func (*MemFS) GetDiskUsage(string) (DiskUsage, error) { 542 return DiskUsage{}, errors.New("bitalosdb: not supported") 543 } 544 545 // memNode holds a file's data or a directory's children, and implements os.FileInfo. 546 type memNode struct { 547 name string 548 isDir bool 549 refs int32 550 551 // Mutable state. 552 // - For a file: data, syncedDate, modTime: A file is only being mutated by a single goroutine, 553 // but there can be concurrent readers e.g. DB.Checkpoint() which can read WAL or MANIFEST 554 // files that are being written to. Additionally Sync() calls can be concurrent with writing. 555 // - For a directory: children and syncedChildren. Concurrent writes are possible, and 556 // these are protected using MemFS.mu. 557 mu struct { 558 sync.Mutex 559 data []byte 560 syncedData []byte 561 modTime time.Time 562 } 563 564 children map[string]*memNode 565 syncedChildren map[string]*memNode 566 } 567 568 func (f *memNode) IsDir() bool { 569 return f.isDir 570 } 571 572 func (f *memNode) ModTime() time.Time { 573 f.mu.Lock() 574 defer f.mu.Unlock() 575 return f.mu.modTime 576 } 577 578 func (f *memNode) Mode() os.FileMode { 579 if f.isDir { 580 return os.ModeDir | 0755 581 } 582 return 0755 583 } 584 585 func (f *memNode) Name() string { 586 return f.name 587 } 588 589 func (f *memNode) Size() int64 { 590 f.mu.Lock() 591 defer f.mu.Unlock() 592 return int64(len(f.mu.data)) 593 } 594 595 func (f *memNode) Sys() interface{} { 596 return nil 597 } 598 599 func (f *memNode) dump(w *bytes.Buffer, level int) { 600 if f.isDir { 601 w.WriteString(" ") 602 } else { 603 f.mu.Lock() 604 fmt.Fprintf(w, "%8d ", len(f.mu.data)) 605 f.mu.Unlock() 606 } 607 for i := 0; i < level; i++ { 608 w.WriteString(" ") 609 } 610 w.WriteString(f.name) 611 if !f.isDir { 612 w.WriteByte('\n') 613 return 614 } 615 w.WriteByte(sep[0]) 616 w.WriteByte('\n') 617 names := make([]string, 0, len(f.children)) 618 for name := range f.children { 619 names = append(names, name) 620 } 621 sort.Strings(names) 622 for _, name := range names { 623 f.children[name].dump(w, level+1) 624 } 625 } 626 627 func (f *memNode) resetToSyncedState() { 628 if f.isDir { 629 f.children = make(map[string]*memNode) 630 for k, v := range f.syncedChildren { 631 f.children[k] = v 632 } 633 for _, v := range f.children { 634 v.resetToSyncedState() 635 } 636 } else { 637 f.mu.Lock() 638 f.mu.data = append([]byte(nil), f.mu.syncedData...) 639 f.mu.Unlock() 640 } 641 } 642 643 // memFile is a reader or writer of a node's data, and implements File. 644 type memFile struct { 645 n *memNode 646 fs *MemFS // nil for a standalone memFile 647 rpos int 648 wpos int 649 read, write bool 650 } 651 652 func (f *memFile) Close() error { 653 if n := atomic.AddInt32(&f.n.refs, -1); n < 0 { 654 panic(fmt.Sprintf("bitalosdb: close of unopened file: %d", n)) 655 } 656 f.n = nil 657 return nil 658 } 659 660 func (f *memFile) Read(p []byte) (int, error) { 661 if !f.read { 662 return 0, errors.New("bitalosdb/vfs: file was not opened for reading") 663 } 664 if f.n.isDir { 665 return 0, errors.New("bitalosdb/vfs: cannot read a directory") 666 } 667 f.n.mu.Lock() 668 defer f.n.mu.Unlock() 669 if f.rpos >= len(f.n.mu.data) { 670 return 0, io.EOF 671 } 672 n := copy(p, f.n.mu.data[f.rpos:]) 673 f.rpos += n 674 return n, nil 675 } 676 677 func (f *memFile) ReadAt(p []byte, off int64) (int, error) { 678 if !f.read { 679 return 0, errors.New("bitalosdb/vfs: file was not opened for reading") 680 } 681 if f.n.isDir { 682 return 0, errors.New("bitalosdb/vfs: cannot read a directory") 683 } 684 f.n.mu.Lock() 685 defer f.n.mu.Unlock() 686 if off >= int64(len(f.n.mu.data)) { 687 return 0, io.EOF 688 } 689 return copy(p, f.n.mu.data[off:]), nil 690 } 691 692 func (f *memFile) Write(p []byte) (int, error) { 693 if !f.write { 694 return 0, errors.New("bitalosdb/vfs: file was not created for writing") 695 } 696 if f.n.isDir { 697 return 0, errors.New("bitalosdb/vfs: cannot write a directory") 698 } 699 f.n.mu.Lock() 700 defer f.n.mu.Unlock() 701 f.n.mu.modTime = time.Now() 702 if f.wpos+len(p) <= len(f.n.mu.data) { 703 n := copy(f.n.mu.data[f.wpos:f.wpos+len(p)], p) 704 if n != len(p) { 705 panic("stuff") 706 } 707 } else { 708 f.n.mu.data = append(f.n.mu.data[:f.wpos], p...) 709 } 710 f.wpos += len(p) 711 712 if invariants.Enabled { 713 for i := range p { 714 p[i] ^= 0xff 715 } 716 } 717 return len(p), nil 718 } 719 720 func (f *memFile) Seek(_ int64, _ int) (n int64, err error) { 721 return n, err 722 } 723 724 func (f *memFile) Stat() (os.FileInfo, error) { 725 return f.n, nil 726 } 727 728 func (f *memFile) Sync() error { 729 if f.fs != nil && f.fs.strict { 730 f.fs.mu.Lock() 731 defer f.fs.mu.Unlock() 732 if f.fs.ignoreSyncs { 733 return nil 734 } 735 if f.n.isDir { 736 f.n.syncedChildren = make(map[string]*memNode) 737 for k, v := range f.n.children { 738 f.n.syncedChildren[k] = v 739 } 740 } else { 741 f.n.mu.Lock() 742 f.n.mu.syncedData = append([]byte(nil), f.n.mu.data...) 743 f.n.mu.Unlock() 744 } 745 } 746 return nil 747 } 748 749 func (f *memFile) Flush() error { 750 return nil 751 }