LCOV - code coverage report
Current view: top level - pebble/vfs - mem_fs.go (source / functions) Hit Total Coverage
Test: 2024-09-12 08:16Z e0afd551 - tests + meta.lcov Lines: 548 626 87.5 %
Date: 2024-09-12 08:17:31 Functions: 0 0 -

          Line data    Source code
       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/cockroachdb/pebble/vfs"
       6             : 
       7             : import (
       8             :         "bytes"
       9             :         "fmt"
      10             :         "io"
      11             :         "math/rand"
      12             :         "os"
      13             :         "path"
      14             :         "slices"
      15             :         "sort"
      16             :         "strings"
      17             :         "sync"
      18             :         "sync/atomic"
      19             :         "syscall"
      20             :         "time"
      21             : 
      22             :         "github.com/cockroachdb/errors"
      23             :         "github.com/cockroachdb/errors/oserror"
      24             :         "github.com/cockroachdb/pebble/internal/invariants"
      25             : )
      26             : 
      27             : const sep = "/"
      28             : 
      29             : // NewMem returns a new memory-backed FS implementation.
      30           2 : func NewMem() *MemFS {
      31           2 :         return &MemFS{
      32           2 :                 root: newRootMemNode(),
      33           2 :         }
      34           2 : }
      35             : 
      36             : // NewCrashableMem returns a memory-backed FS implementation that supports the
      37             : // CrashClone() method. This method can be used to obtain a copy of the FS after
      38             : // a simulated crash, where only data that was last synced is guaranteed to be
      39             : // there (with no guarantees one way or the other about more recently written
      40             : // data).
      41             : //
      42             : // Note: when CrashClone() is not necessary, NewMem() is faster and should be
      43             : // preferred.
      44             : //
      45             : // Expected usage:
      46             : //
      47             : //              fs := NewCrashableMem()
      48             : //              db := Open(..., &Options{FS: fs})
      49             : //              // Do and commit various operations.
      50             : //              ...
      51             : //              // Make a clone of the FS after a simulated crash.
      52             : //       crashedFS := fs.CrashClone(CrashCloneCfg{Probability: 50, RNG: rand.New(rand.NewSource(0))})
      53             : //
      54             : //              // This will finish any ongoing background flushes, compactions but none of these writes will
      55             : //              // affect crashedFS.
      56             : //              db.Close()
      57             : //
      58             : //              // Open the DB against the crash clone.
      59             : //              db := Open(..., &Options{FS: crashedFS})
      60           2 : func NewCrashableMem() *MemFS {
      61           2 :         return &MemFS{
      62           2 :                 root:      newRootMemNode(),
      63           2 :                 crashable: true,
      64           2 :         }
      65           2 : }
      66             : 
      67             : // NewMemFile returns a memory-backed File implementation. The memory-backed
      68             : // file takes ownership of data.
      69           1 : func NewMemFile(data []byte) File {
      70           1 :         n := &memNode{}
      71           1 :         n.refs.Store(1)
      72           1 :         n.mu.data = data
      73           1 :         n.mu.modTime = time.Now()
      74           1 :         return &memFile{
      75           1 :                 n:    n,
      76           1 :                 read: true,
      77           1 :         }
      78           1 : }
      79             : 
      80             : // MemFS implements FS.
      81             : type MemFS struct {
      82             :         mu   sync.Mutex
      83             :         root *memNode
      84             : 
      85             :         // cloneMu is used to block all modification operations while we clone the
      86             :         // filesystem. Only used when crashable is true.
      87             :         cloneMu sync.RWMutex
      88             : 
      89             :         // lockFiles holds a map of open file locks. Presence in this map indicates
      90             :         // a file lock is currently held. Keys are strings holding the path of the
      91             :         // locked file. The stored value is untyped and  unused; only presence of
      92             :         // the key within the map is significant.
      93             :         lockedFiles sync.Map
      94             :         crashable   bool
      95             :         // Windows has peculiar semantics with respect to hard links and deleting
      96             :         // open files. In tests meant to exercise this behavior, this flag can be
      97             :         // set to error if removing an open file.
      98             :         windowsSemantics bool
      99             : }
     100             : 
     101             : var _ FS = &MemFS{}
     102             : 
     103             : // UseWindowsSemantics configures whether the MemFS implements Windows-style
     104             : // semantics, in particular with respect to whether any of an open file's links
     105             : // may be removed. Windows semantics default to off.
     106           1 : func (y *MemFS) UseWindowsSemantics(windowsSemantics bool) {
     107           1 :         y.mu.Lock()
     108           1 :         defer y.mu.Unlock()
     109           1 :         y.windowsSemantics = windowsSemantics
     110           1 : }
     111             : 
     112             : // String dumps the contents of the MemFS.
     113           1 : func (y *MemFS) String() string {
     114           1 :         y.mu.Lock()
     115           1 :         defer y.mu.Unlock()
     116           1 : 
     117           1 :         s := new(bytes.Buffer)
     118           1 :         y.root.dump(s, 0, sep)
     119           1 :         return s.String()
     120           1 : }
     121             : 
     122             : // CrashCloneCfg configures a CrashClone call. The zero value corresponds to the
     123             : // crash clone containing exactly the data that was last synced.
     124             : type CrashCloneCfg struct {
     125             :         // UnsyncedDataPercent is the probability that a data block or directory entry
     126             :         // that was not synced will be part of the clone. If 0, the clone will contain
     127             :         // exactly the data that was last synced. If 100, the clone will be identical
     128             :         // to the current filesystem.
     129             :         UnsyncedDataPercent int
     130             :         // RNG must be set if UnsyncedDataPercent > 0.
     131             :         RNG *rand.Rand
     132             : }
     133             : 
     134             : // CrashClone creates a new filesystem that reflects a possible state of this
     135             : // filesystem after a crash at this moment. The new filesystem will contain all
     136             : // data that was synced, and some fraction of the data that was not synced. The
     137             : // latter is controlled by CrashCloneCfg.
     138           2 : func (y *MemFS) CrashClone(cfg CrashCloneCfg) *MemFS {
     139           2 :         if !y.crashable {
     140           0 :                 panic("not a crashable MemFS")
     141             :         }
     142             :         // Block all modification operations while we clone.
     143           2 :         y.cloneMu.Lock()
     144           2 :         defer y.cloneMu.Unlock()
     145           2 :         newFS := &MemFS{crashable: true}
     146           2 :         newFS.windowsSemantics = y.windowsSemantics
     147           2 :         newFS.root = y.root.CrashClone(&cfg)
     148           2 :         return newFS
     149             : }
     150             : 
     151             : // walk walks the directory tree for the fullname, calling f at each step. If
     152             : // f returns an error, the walk will be aborted and return that same error.
     153             : //
     154             : // Each walk is atomic: y's mutex is held for the entire operation, including
     155             : // all calls to f.
     156             : //
     157             : // dir is the directory at that step, frag is the name fragment, and final is
     158             : // whether it is the final step. For example, walking "/foo/bar/x" will result
     159             : // in 3 calls to f:
     160             : //   - "/", "foo", false
     161             : //   - "/foo/", "bar", false
     162             : //   - "/foo/bar/", "x", true
     163             : //
     164             : // Similarly, walking "/y/z/", with a trailing slash, will result in 3 calls to f:
     165             : //   - "/", "y", false
     166             : //   - "/y/", "z", false
     167             : //   - "/y/z/", "", true
     168           2 : func (y *MemFS) walk(fullname string, f func(dir *memNode, frag string, final bool) error) error {
     169           2 :         y.mu.Lock()
     170           2 :         defer y.mu.Unlock()
     171           2 : 
     172           2 :         // For memfs, the current working directory is the same as the root directory,
     173           2 :         // so we strip off any leading "/"s to make fullname a relative path, and
     174           2 :         // the walk starts at y.root.
     175           2 :         for len(fullname) > 0 && fullname[0] == sep[0] {
     176           2 :                 fullname = fullname[1:]
     177           2 :         }
     178           2 :         if fullname == "." {
     179           2 :                 fullname = ""
     180           2 :         }
     181           2 :         dir := y.root
     182           2 : 
     183           2 :         for {
     184           2 :                 frag, remaining := fullname, ""
     185           2 :                 i := strings.IndexRune(fullname, rune(sep[0]))
     186           2 :                 final := i < 0
     187           2 :                 if !final {
     188           2 :                         frag, remaining = fullname[:i], fullname[i+1:]
     189           2 :                         for len(remaining) > 0 && remaining[0] == sep[0] {
     190           0 :                                 remaining = remaining[1:]
     191           0 :                         }
     192             :                 }
     193           2 :                 if err := f(dir, frag, final); err != nil {
     194           2 :                         return err
     195           2 :                 }
     196           2 :                 if final {
     197           2 :                         break
     198             :                 }
     199           2 :                 child := dir.children[frag]
     200           2 :                 if child == nil {
     201           2 :                         return &os.PathError{
     202           2 :                                 Op:   "open",
     203           2 :                                 Path: fullname,
     204           2 :                                 Err:  oserror.ErrNotExist,
     205           2 :                         }
     206           2 :                 }
     207           2 :                 if !child.isDir {
     208           1 :                         return &os.PathError{
     209           1 :                                 Op:   "open",
     210           1 :                                 Path: fullname,
     211           1 :                                 Err:  errors.New("not a directory"),
     212           1 :                         }
     213           1 :                 }
     214           2 :                 dir, fullname = child, remaining
     215             :         }
     216           2 :         return nil
     217             : }
     218             : 
     219             : // Create implements FS.Create.
     220           2 : func (y *MemFS) Create(fullname string, category DiskWriteCategory) (File, error) {
     221           2 :         if y.crashable {
     222           2 :                 y.cloneMu.RLock()
     223           2 :                 defer y.cloneMu.RUnlock()
     224           2 :         }
     225           2 :         var ret *memFile
     226           2 :         err := y.walk(fullname, func(dir *memNode, frag string, final bool) error {
     227           2 :                 if final {
     228           2 :                         if frag == "" {
     229           0 :                                 return errors.New("pebble/vfs: empty file name")
     230           0 :                         }
     231           2 :                         n := &memNode{}
     232           2 :                         dir.children[frag] = n
     233           2 :                         ret = &memFile{
     234           2 :                                 name:  frag,
     235           2 :                                 n:     n,
     236           2 :                                 fs:    y,
     237           2 :                                 read:  true,
     238           2 :                                 write: true,
     239           2 :                         }
     240             :                 }
     241           2 :                 return nil
     242             :         })
     243           2 :         if err != nil {
     244           1 :                 return nil, err
     245           1 :         }
     246           2 :         ret.n.refs.Add(1)
     247           2 :         return ret, nil
     248             : }
     249             : 
     250             : // Link implements FS.Link.
     251           2 : func (y *MemFS) Link(oldname, newname string) error {
     252           2 :         if y.crashable {
     253           2 :                 y.cloneMu.RLock()
     254           2 :                 defer y.cloneMu.RUnlock()
     255           2 :         }
     256           2 :         var n *memNode
     257           2 :         err := y.walk(oldname, func(dir *memNode, frag string, final bool) error {
     258           2 :                 if final {
     259           2 :                         if frag == "" {
     260           0 :                                 return errors.New("pebble/vfs: empty file name")
     261           0 :                         }
     262           2 :                         n = dir.children[frag]
     263             :                 }
     264           2 :                 return nil
     265             :         })
     266           2 :         if err != nil {
     267           0 :                 return err
     268           0 :         }
     269           2 :         if n == nil {
     270           1 :                 return &os.LinkError{
     271           1 :                         Op:  "link",
     272           1 :                         Old: oldname,
     273           1 :                         New: newname,
     274           1 :                         Err: oserror.ErrNotExist,
     275           1 :                 }
     276           1 :         }
     277           2 :         return y.walk(newname, func(dir *memNode, frag string, final bool) error {
     278           2 :                 if final {
     279           2 :                         if frag == "" {
     280           0 :                                 return errors.New("pebble/vfs: empty file name")
     281           0 :                         }
     282           2 :                         y.cloneMu.RLock()
     283           2 :                         defer y.cloneMu.RUnlock()
     284           2 :                         if _, ok := dir.children[frag]; ok {
     285           1 :                                 return &os.LinkError{
     286           1 :                                         Op:  "link",
     287           1 :                                         Old: oldname,
     288           1 :                                         New: newname,
     289           1 :                                         Err: oserror.ErrExist,
     290           1 :                                 }
     291           1 :                         }
     292           2 :                         dir.children[frag] = n
     293             :                 }
     294           2 :                 return nil
     295             :         })
     296             : }
     297             : 
     298           2 : func (y *MemFS) open(fullname string, openForWrite bool) (File, error) {
     299           2 :         var ret *memFile
     300           2 :         err := y.walk(fullname, func(dir *memNode, frag string, final bool) error {
     301           2 :                 if final {
     302           2 :                         if frag == "" {
     303           2 :                                 ret = &memFile{
     304           2 :                                         name: sep, // this is the root directory
     305           2 :                                         n:    dir,
     306           2 :                                         fs:   y,
     307           2 :                                 }
     308           2 :                                 return nil
     309           2 :                         }
     310           2 :                         if n := dir.children[frag]; n != nil {
     311           2 :                                 ret = &memFile{
     312           2 :                                         name:  frag,
     313           2 :                                         n:     n,
     314           2 :                                         fs:    y,
     315           2 :                                         read:  true,
     316           2 :                                         write: openForWrite,
     317           2 :                                 }
     318           2 :                         }
     319             :                 }
     320           2 :                 return nil
     321             :         })
     322           2 :         if err != nil {
     323           2 :                 return nil, err
     324           2 :         }
     325           2 :         if ret == nil {
     326           2 :                 return nil, &os.PathError{
     327           2 :                         Op:   "open",
     328           2 :                         Path: fullname,
     329           2 :                         Err:  oserror.ErrNotExist,
     330           2 :                 }
     331           2 :         }
     332           2 :         ret.n.refs.Add(1)
     333           2 :         return ret, nil
     334             : }
     335             : 
     336             : // Open implements FS.Open.
     337           2 : func (y *MemFS) Open(fullname string, opts ...OpenOption) (File, error) {
     338           2 :         return y.open(fullname, false /* openForWrite */)
     339           2 : }
     340             : 
     341             : // OpenReadWrite implements FS.OpenReadWrite.
     342             : func (y *MemFS) OpenReadWrite(
     343             :         fullname string, category DiskWriteCategory, opts ...OpenOption,
     344           2 : ) (File, error) {
     345           2 :         f, err := y.open(fullname, true /* openForWrite */)
     346           2 :         pathErr, ok := err.(*os.PathError)
     347           2 :         if ok && pathErr.Err == oserror.ErrNotExist {
     348           2 :                 return y.Create(fullname, category)
     349           2 :         }
     350           1 :         return f, err
     351             : }
     352             : 
     353             : // OpenDir implements FS.OpenDir.
     354           2 : func (y *MemFS) OpenDir(fullname string) (File, error) {
     355           2 :         return y.open(fullname, false /* openForWrite */)
     356           2 : }
     357             : 
     358             : // Remove implements FS.Remove.
     359           2 : func (y *MemFS) Remove(fullname string) error {
     360           2 :         if y.crashable {
     361           2 :                 y.cloneMu.RLock()
     362           2 :                 defer y.cloneMu.RUnlock()
     363           2 :         }
     364           2 :         return y.walk(fullname, func(dir *memNode, frag string, final bool) error {
     365           2 :                 if final {
     366           2 :                         if frag == "" {
     367           0 :                                 return errors.New("pebble/vfs: empty file name")
     368           0 :                         }
     369           2 :                         child, ok := dir.children[frag]
     370           2 :                         if !ok {
     371           2 :                                 return oserror.ErrNotExist
     372           2 :                         }
     373           2 :                         if y.windowsSemantics {
     374           1 :                                 // Disallow removal of open files/directories which implements
     375           1 :                                 // Windows semantics. This ensures that we don't regress in the
     376           1 :                                 // ordering of operations and try to remove a file while it is
     377           1 :                                 // still open.
     378           1 :                                 if n := child.refs.Load(); n > 0 {
     379           1 :                                         return oserror.ErrInvalid
     380           1 :                                 }
     381             :                         }
     382           2 :                         if len(child.children) > 0 {
     383           1 :                                 return errNotEmpty
     384           1 :                         }
     385           2 :                         delete(dir.children, frag)
     386             :                 }
     387           2 :                 return nil
     388             :         })
     389             : }
     390             : 
     391             : // RemoveAll implements FS.RemoveAll.
     392           1 : func (y *MemFS) RemoveAll(fullname string) error {
     393           1 :         if y.crashable {
     394           0 :                 y.cloneMu.RLock()
     395           0 :                 defer y.cloneMu.RUnlock()
     396           0 :         }
     397           1 :         err := y.walk(fullname, func(dir *memNode, frag string, final bool) error {
     398           1 :                 if final {
     399           1 :                         if frag == "" {
     400           0 :                                 return errors.New("pebble/vfs: empty file name")
     401           0 :                         }
     402           1 :                         _, ok := dir.children[frag]
     403           1 :                         if !ok {
     404           1 :                                 return nil
     405           1 :                         }
     406           1 :                         delete(dir.children, frag)
     407             :                 }
     408           1 :                 return nil
     409             :         })
     410             :         // Match os.RemoveAll which returns a nil error even if the parent
     411             :         // directories don't exist.
     412           1 :         if oserror.IsNotExist(err) {
     413           1 :                 err = nil
     414           1 :         }
     415           1 :         return err
     416             : }
     417             : 
     418             : // Rename implements FS.Rename.
     419           2 : func (y *MemFS) Rename(oldname, newname string) error {
     420           2 :         if y.crashable {
     421           2 :                 y.cloneMu.RLock()
     422           2 :                 defer y.cloneMu.RUnlock()
     423           2 :         }
     424           2 :         var n *memNode
     425           2 :         err := y.walk(oldname, func(dir *memNode, frag string, final bool) error {
     426           2 :                 if final {
     427           2 :                         if frag == "" {
     428           0 :                                 return errors.New("pebble/vfs: empty file name")
     429           0 :                         }
     430           2 :                         n = dir.children[frag]
     431           2 :                         delete(dir.children, frag)
     432             :                 }
     433           2 :                 return nil
     434             :         })
     435           2 :         if err != nil {
     436           0 :                 return err
     437           0 :         }
     438           2 :         if n == nil {
     439           1 :                 return &os.PathError{
     440           1 :                         Op:   "open",
     441           1 :                         Path: oldname,
     442           1 :                         Err:  oserror.ErrNotExist,
     443           1 :                 }
     444           1 :         }
     445           2 :         return y.walk(newname, func(dir *memNode, frag string, final bool) error {
     446           2 :                 if final {
     447           2 :                         if frag == "" {
     448           0 :                                 return errors.New("pebble/vfs: empty file name")
     449           0 :                         }
     450           2 :                         dir.children[frag] = n
     451             :                 }
     452           2 :                 return nil
     453             :         })
     454             : }
     455             : 
     456             : // ReuseForWrite implements FS.ReuseForWrite.
     457           1 : func (y *MemFS) ReuseForWrite(oldname, newname string, category DiskWriteCategory) (File, error) {
     458           1 :         if y.crashable {
     459           1 :                 y.cloneMu.RLock()
     460           1 :                 defer y.cloneMu.RUnlock()
     461           1 :         }
     462           1 :         if err := y.Rename(oldname, newname); err != nil {
     463           1 :                 return nil, err
     464           1 :         }
     465           1 :         f, err := y.Open(newname)
     466           1 :         if err != nil {
     467           0 :                 return nil, err
     468           0 :         }
     469           1 :         y.mu.Lock()
     470           1 :         defer y.mu.Unlock()
     471           1 : 
     472           1 :         mf := f.(*memFile)
     473           1 :         mf.read = false
     474           1 :         mf.write = true
     475           1 :         return f, nil
     476             : }
     477             : 
     478             : // MkdirAll implements FS.MkdirAll.
     479           2 : func (y *MemFS) MkdirAll(dirname string, perm os.FileMode) error {
     480           2 :         if y.crashable {
     481           2 :                 y.cloneMu.RLock()
     482           2 :                 defer y.cloneMu.RUnlock()
     483           2 :         }
     484           2 :         return y.walk(dirname, func(dir *memNode, frag string, final bool) error {
     485           2 :                 if frag == "" {
     486           1 :                         if final {
     487           1 :                                 return nil
     488           1 :                         }
     489           0 :                         return errors.New("pebble/vfs: empty file name")
     490             :                 }
     491           2 :                 child := dir.children[frag]
     492           2 :                 if child == nil {
     493           2 :                         dir.children[frag] = &memNode{
     494           2 :                                 children: make(map[string]*memNode),
     495           2 :                                 isDir:    true,
     496           2 :                         }
     497           2 :                         return nil
     498           2 :                 }
     499           2 :                 if !child.isDir {
     500           0 :                         return &os.PathError{
     501           0 :                                 Op:   "open",
     502           0 :                                 Path: dirname,
     503           0 :                                 Err:  errors.New("not a directory"),
     504           0 :                         }
     505           0 :                 }
     506           2 :                 return nil
     507             :         })
     508             : }
     509             : 
     510             : // Lock implements FS.Lock.
     511           2 : func (y *MemFS) Lock(fullname string) (io.Closer, error) {
     512           2 :         if y.crashable {
     513           2 :                 y.cloneMu.RLock()
     514           2 :                 defer y.cloneMu.RUnlock()
     515           2 :         }
     516             :         // FS.Lock excludes other processes, but other processes cannot see this
     517             :         // process' memory. However some uses (eg, Cockroach tests) may open and
     518             :         // close the same MemFS-backed database multiple times. We want mutual
     519             :         // exclusion in this case too. See cockroachdb/cockroach#110645.
     520           2 :         _, loaded := y.lockedFiles.Swap(fullname, nil /* the value itself is insignificant */)
     521           2 :         if loaded {
     522           1 :                 // This file lock has already been acquired. On unix, this results in
     523           1 :                 // either EACCES or EAGAIN so we mimic.
     524           1 :                 return nil, syscall.EAGAIN
     525           1 :         }
     526             :         // Otherwise, we successfully acquired the lock. Locks are visible in the
     527             :         // parent directory listing, and they also must be created under an existent
     528             :         // directory. Create the path so that we have the normal detection of
     529             :         // non-existent directory paths, and make the lock visible when listing
     530             :         // directory entries.
     531           2 :         f, err := y.Create(fullname, WriteCategoryUnspecified)
     532           2 :         if err != nil {
     533           1 :                 // "Release" the lock since we failed.
     534           1 :                 y.lockedFiles.Delete(fullname)
     535           1 :                 return nil, err
     536           1 :         }
     537           2 :         return &memFileLock{
     538           2 :                 y:        y,
     539           2 :                 f:        f,
     540           2 :                 fullname: fullname,
     541           2 :         }, nil
     542             : }
     543             : 
     544             : // List implements FS.List.
     545           2 : func (y *MemFS) List(dirname string) ([]string, error) {
     546           2 :         if !strings.HasSuffix(dirname, sep) {
     547           2 :                 dirname += sep
     548           2 :         }
     549           2 :         var ret []string
     550           2 :         err := y.walk(dirname, func(dir *memNode, frag string, final bool) error {
     551           2 :                 if final {
     552           2 :                         if frag != "" {
     553           0 :                                 panic("unreachable")
     554             :                         }
     555           2 :                         ret = make([]string, 0, len(dir.children))
     556           2 :                         for s := range dir.children {
     557           2 :                                 ret = append(ret, s)
     558           2 :                         }
     559             :                 }
     560           2 :                 return nil
     561             :         })
     562           2 :         return ret, err
     563             : }
     564             : 
     565             : // Stat implements FS.Stat.
     566           2 : func (y *MemFS) Stat(name string) (FileInfo, error) {
     567           2 :         f, err := y.Open(name)
     568           2 :         if err != nil {
     569           2 :                 if pe, ok := err.(*os.PathError); ok {
     570           2 :                         pe.Op = "stat"
     571           2 :                 }
     572           2 :                 return nil, err
     573             :         }
     574           2 :         defer f.Close()
     575           2 :         return f.Stat()
     576             : }
     577             : 
     578             : // PathBase implements FS.PathBase.
     579           2 : func (*MemFS) PathBase(p string) string {
     580           2 :         // Note that MemFS uses forward slashes for its separator, hence the use of
     581           2 :         // path.Base, not filepath.Base.
     582           2 :         return path.Base(p)
     583           2 : }
     584             : 
     585             : // PathJoin implements FS.PathJoin.
     586           2 : func (*MemFS) PathJoin(elem ...string) string {
     587           2 :         // Note that MemFS uses forward slashes for its separator, hence the use of
     588           2 :         // path.Join, not filepath.Join.
     589           2 :         return path.Join(elem...)
     590           2 : }
     591             : 
     592             : // PathDir implements FS.PathDir.
     593           2 : func (*MemFS) PathDir(p string) string {
     594           2 :         // Note that MemFS uses forward slashes for its separator, hence the use of
     595           2 :         // path.Dir, not filepath.Dir.
     596           2 :         return path.Dir(p)
     597           2 : }
     598             : 
     599             : // GetDiskUsage implements FS.GetDiskUsage.
     600           2 : func (*MemFS) GetDiskUsage(string) (DiskUsage, error) {
     601           2 :         return DiskUsage{}, ErrUnsupported
     602           2 : }
     603             : 
     604             : // Unwrap implements FS.Unwrap.
     605           2 : func (*MemFS) Unwrap() FS { return nil }
     606             : 
     607             : // memNode holds a file's data or a directory's children.
     608             : type memNode struct {
     609             :         isDir bool
     610             :         refs  atomic.Int32
     611             : 
     612             :         // Mutable state.
     613             :         // - For a file: data, syncedDate, modTime: A file is only being mutated by a single goroutine,
     614             :         //   but there can be concurrent readers e.g. DB.Checkpoint() which can read WAL or MANIFEST
     615             :         //   files that are being written to. Additionally Sync() calls can be concurrent with writing.
     616             :         // - For a directory: children and syncedChildren. Concurrent writes are possible, and
     617             :         //   these are protected using MemFS.mu.
     618             :         mu struct {
     619             :                 sync.Mutex
     620             :                 data       []byte
     621             :                 syncedData []byte
     622             :                 modTime    time.Time
     623             :         }
     624             : 
     625             :         children       map[string]*memNode
     626             :         syncedChildren map[string]*memNode
     627             : }
     628             : 
     629           2 : func newRootMemNode() *memNode {
     630           2 :         return &memNode{
     631           2 :                 children: make(map[string]*memNode),
     632           2 :                 isDir:    true,
     633           2 :         }
     634           2 : }
     635             : 
     636           2 : func cloneChildren(f map[string]*memNode) map[string]*memNode {
     637           2 :         m := make(map[string]*memNode)
     638           2 :         for k, v := range f {
     639           2 :                 m[k] = v
     640           2 :         }
     641           2 :         return m
     642             : }
     643             : 
     644           1 : func (f *memNode) dump(w *bytes.Buffer, level int, name string) {
     645           1 :         if f.isDir {
     646           1 :                 w.WriteString("          ")
     647           1 :         } else {
     648           1 :                 f.mu.Lock()
     649           1 :                 fmt.Fprintf(w, "%8d  ", len(f.mu.data))
     650           1 :                 f.mu.Unlock()
     651           1 :         }
     652           1 :         for i := 0; i < level; i++ {
     653           1 :                 w.WriteString("  ")
     654           1 :         }
     655           1 :         w.WriteString(name)
     656           1 :         if !f.isDir {
     657           1 :                 w.WriteByte('\n')
     658           1 :                 return
     659           1 :         }
     660           1 :         if level > 0 { // deal with the fact that the root's name is already "/"
     661           1 :                 w.WriteByte(sep[0])
     662           1 :         }
     663           1 :         w.WriteByte('\n')
     664           1 :         names := make([]string, 0, len(f.children))
     665           1 :         for name := range f.children {
     666           1 :                 names = append(names, name)
     667           1 :         }
     668           1 :         sort.Strings(names)
     669           1 :         for _, name := range names {
     670           1 :                 f.children[name].dump(w, level+1, name)
     671           1 :         }
     672             : }
     673             : 
     674             : // CrashClone creates a crash-consistent clone of the subtree rooted at f, and
     675             : // returns the new subtree. cloneMu must be held (in write mode).
     676           2 : func (f *memNode) CrashClone(cfg *CrashCloneCfg) *memNode {
     677           2 :         newNode := &memNode{isDir: f.isDir}
     678           2 :         if f.isDir {
     679           2 :                 newNode.children = cloneChildren(f.syncedChildren)
     680           2 :                 // Randomly include some non-synced children.
     681           2 :                 for name, child := range f.children {
     682           2 :                         if cfg.UnsyncedDataPercent > 0 && cfg.RNG.Intn(100) < cfg.UnsyncedDataPercent {
     683           1 :                                 newNode.children[name] = child
     684           1 :                         }
     685             :                 }
     686           2 :                 for name, child := range newNode.children {
     687           2 :                         newNode.children[name] = child.CrashClone(cfg)
     688           2 :                 }
     689           2 :                 newNode.syncedChildren = cloneChildren(newNode.children)
     690           2 :         } else {
     691           2 :                 newNode.mu.data = slices.Clone(f.mu.syncedData)
     692           2 :                 newNode.mu.modTime = f.mu.modTime
     693           2 :                 // Randomly include some non-synced blocks.
     694           2 :                 const blockSize = 4096
     695           2 :                 for i := 0; i < len(f.mu.data); i += blockSize {
     696           2 :                         if cfg.UnsyncedDataPercent > 0 && cfg.RNG.Intn(100) < cfg.UnsyncedDataPercent {
     697           1 :                                 block := f.mu.data[i:min(i+blockSize, len(f.mu.data))]
     698           1 :                                 if grow := i + len(block) - len(newNode.mu.data); grow > 0 {
     699           1 :                                         // Grow the file, leaving 0s for any unsynced blocks past the synced
     700           1 :                                         // length.
     701           1 :                                         newNode.mu.data = append(newNode.mu.data, make([]byte, grow)...)
     702           1 :                                 }
     703           1 :                                 copy(newNode.mu.data[i:], block)
     704             :                         }
     705             :                 }
     706           2 :                 newNode.mu.syncedData = slices.Clone(newNode.mu.data)
     707             :         }
     708           2 :         return newNode
     709             : }
     710             : 
     711             : // memFile is a reader or writer of a node's data. Implements File.
     712             : type memFile struct {
     713             :         name        string
     714             :         n           *memNode
     715             :         fs          *MemFS // nil for a standalone memFile
     716             :         pos         int
     717             :         read, write bool
     718             : }
     719             : 
     720             : var _ File = (*memFile)(nil)
     721             : 
     722           2 : func (f *memFile) Close() error {
     723           2 :         if n := f.n.refs.Add(-1); n < 0 {
     724           0 :                 panic(fmt.Sprintf("pebble: close of unopened file: %d", n))
     725             :         }
     726             :         // Set node pointer to nil, to cause panic on any subsequent method call. This
     727             :         // is a defence-in-depth to catch use-after-close or double-close bugs.
     728           2 :         f.n = nil
     729           2 :         return nil
     730             : }
     731             : 
     732           2 : func (f *memFile) Read(p []byte) (int, error) {
     733           2 :         if !f.read {
     734           0 :                 return 0, errors.New("pebble/vfs: file was not opened for reading")
     735           0 :         }
     736           2 :         if f.n.isDir {
     737           0 :                 return 0, errors.New("pebble/vfs: cannot read a directory")
     738           0 :         }
     739           2 :         f.n.mu.Lock()
     740           2 :         defer f.n.mu.Unlock()
     741           2 :         if f.pos >= len(f.n.mu.data) {
     742           2 :                 return 0, io.EOF
     743           2 :         }
     744           2 :         n := copy(p, f.n.mu.data[f.pos:])
     745           2 :         f.pos += n
     746           2 :         return n, nil
     747             : }
     748             : 
     749           2 : func (f *memFile) ReadAt(p []byte, off int64) (int, error) {
     750           2 :         if !f.read {
     751           0 :                 return 0, errors.New("pebble/vfs: file was not opened for reading")
     752           0 :         }
     753           2 :         if f.n.isDir {
     754           0 :                 return 0, errors.New("pebble/vfs: cannot read a directory")
     755           0 :         }
     756           2 :         f.n.mu.Lock()
     757           2 :         defer f.n.mu.Unlock()
     758           2 :         if off >= int64(len(f.n.mu.data)) {
     759           1 :                 return 0, io.EOF
     760           1 :         }
     761           2 :         n := copy(p, f.n.mu.data[off:])
     762           2 :         if n < len(p) {
     763           1 :                 return n, io.EOF
     764           1 :         }
     765           2 :         return n, nil
     766             : }
     767             : 
     768           2 : func (f *memFile) Write(p []byte) (int, error) {
     769           2 :         if f.fs.crashable {
     770           2 :                 f.fs.cloneMu.RLock()
     771           2 :                 defer f.fs.cloneMu.RUnlock()
     772           2 :         }
     773           2 :         if !f.write {
     774           0 :                 return 0, errors.New("pebble/vfs: file was not created for writing")
     775           0 :         }
     776           2 :         if f.n.isDir {
     777           0 :                 return 0, errors.New("pebble/vfs: cannot write a directory")
     778           0 :         }
     779           2 :         f.n.mu.Lock()
     780           2 :         defer f.n.mu.Unlock()
     781           2 :         f.n.mu.modTime = time.Now()
     782           2 :         if f.pos+len(p) <= len(f.n.mu.data) {
     783           2 :                 n := copy(f.n.mu.data[f.pos:f.pos+len(p)], p)
     784           2 :                 if n != len(p) {
     785           0 :                         panic("stuff")
     786             :                 }
     787           2 :         } else {
     788           2 :                 if grow := f.pos - len(f.n.mu.data); grow > 0 {
     789           0 :                         f.n.mu.data = append(f.n.mu.data, make([]byte, grow)...)
     790           0 :                 }
     791           2 :                 f.n.mu.data = append(f.n.mu.data[:f.pos], p...)
     792             :         }
     793           2 :         f.pos += len(p)
     794           2 : 
     795           2 :         if invariants.Enabled {
     796           2 :                 // Mutate the input buffer to flush out bugs in Pebble which expect the
     797           2 :                 // input buffer to be unmodified.
     798           2 :                 for i := range p {
     799           2 :                         p[i] ^= 0xff
     800           2 :                 }
     801             :         }
     802           2 :         return len(p), nil
     803             : }
     804             : 
     805           2 : func (f *memFile) WriteAt(p []byte, ofs int64) (int, error) {
     806           2 :         if f.fs.crashable {
     807           2 :                 f.fs.cloneMu.RLock()
     808           2 :                 defer f.fs.cloneMu.RUnlock()
     809           2 :         }
     810           2 :         if !f.write {
     811           0 :                 return 0, errors.New("pebble/vfs: file was not created for writing")
     812           0 :         }
     813           2 :         if f.n.isDir {
     814           0 :                 return 0, errors.New("pebble/vfs: cannot write a directory")
     815           0 :         }
     816           2 :         f.n.mu.Lock()
     817           2 :         defer f.n.mu.Unlock()
     818           2 :         f.n.mu.modTime = time.Now()
     819           2 : 
     820           2 :         for len(f.n.mu.data) < int(ofs)+len(p) {
     821           2 :                 f.n.mu.data = append(f.n.mu.data, 0)
     822           2 :         }
     823             : 
     824           2 :         n := copy(f.n.mu.data[int(ofs):int(ofs)+len(p)], p)
     825           2 :         if n != len(p) {
     826           0 :                 panic("stuff")
     827             :         }
     828             : 
     829           2 :         return len(p), nil
     830             : }
     831             : 
     832           2 : func (f *memFile) Prefetch(offset int64, length int64) error { return nil }
     833           2 : func (f *memFile) Preallocate(offset, length int64) error    { return nil }
     834             : 
     835           2 : func (f *memFile) Stat() (FileInfo, error) {
     836           2 :         f.n.mu.Lock()
     837           2 :         defer f.n.mu.Unlock()
     838           2 :         return &memFileInfo{
     839           2 :                 name:    f.name,
     840           2 :                 size:    int64(len(f.n.mu.data)),
     841           2 :                 modTime: f.n.mu.modTime,
     842           2 :                 isDir:   f.n.isDir,
     843           2 :         }, nil
     844           2 : }
     845             : 
     846           2 : func (f *memFile) Sync() error {
     847           2 :         if f.fs == nil || !f.fs.crashable {
     848           2 :                 return nil
     849           2 :         }
     850           2 :         f.fs.cloneMu.RLock()
     851           2 :         defer f.fs.cloneMu.RUnlock()
     852           2 :         f.fs.mu.Lock()
     853           2 :         defer f.fs.mu.Unlock()
     854           2 :         if f.n.isDir {
     855           2 :                 f.n.syncedChildren = cloneChildren(f.n.children)
     856           2 :         } else {
     857           2 :                 f.n.mu.Lock()
     858           2 :                 f.n.mu.syncedData = append(f.n.mu.syncedData[:0], f.n.mu.data...)
     859           2 :                 f.n.mu.Unlock()
     860           2 :         }
     861           2 :         return nil
     862             : }
     863             : 
     864           2 : func (f *memFile) SyncData() error {
     865           2 :         return f.Sync()
     866           2 : }
     867             : 
     868           0 : func (f *memFile) SyncTo(length int64) (fullSync bool, err error) {
     869           0 :         // NB: This SyncTo implementation lies, with its return values claiming it
     870           0 :         // synced the data up to `length`. When fullSync=false, SyncTo provides no
     871           0 :         // durability guarantees, so this can help surface bugs where we improperly
     872           0 :         // rely on SyncTo providing durability.
     873           0 :         return false, nil
     874           0 : }
     875             : 
     876           2 : func (f *memFile) Fd() uintptr {
     877           2 :         return InvalidFd
     878           2 : }
     879             : 
     880             : // Flush is a no-op and present only to prevent buffering at higher levels
     881             : // (e.g. it prevents sstable.Writer from using a bufio.Writer).
     882           1 : func (f *memFile) Flush() error {
     883           1 :         return nil
     884           1 : }
     885             : 
     886             : // memFileInfo implements os.FileInfo for a memFile.
     887             : type memFileInfo struct {
     888             :         name    string
     889             :         size    int64
     890             :         modTime time.Time
     891             :         isDir   bool
     892             : }
     893             : 
     894             : var _ os.FileInfo = (*memFileInfo)(nil)
     895             : 
     896           1 : func (f *memFileInfo) Name() string {
     897           1 :         return f.name
     898           1 : }
     899             : 
     900           0 : func (f *memFileInfo) DeviceID() DeviceID {
     901           0 :         return DeviceID{}
     902           0 : }
     903             : 
     904           2 : func (f *memFileInfo) Size() int64 {
     905           2 :         return f.size
     906           2 : }
     907             : 
     908           0 : func (f *memFileInfo) Mode() os.FileMode {
     909           0 :         if f.isDir {
     910           0 :                 return os.ModeDir | 0755
     911           0 :         }
     912           0 :         return 0755
     913             : }
     914             : 
     915           0 : func (f *memFileInfo) ModTime() time.Time {
     916           0 :         return f.modTime
     917           0 : }
     918             : 
     919           1 : func (f *memFileInfo) IsDir() bool {
     920           1 :         return f.isDir
     921           1 : }
     922             : 
     923           0 : func (f *memFileInfo) Sys() interface{} {
     924           0 :         return nil
     925           0 : }
     926             : 
     927             : type memFileLock struct {
     928             :         y        *MemFS
     929             :         f        File
     930             :         fullname string
     931             : }
     932             : 
     933           2 : func (l *memFileLock) Close() error {
     934           2 :         if l.y == nil {
     935           0 :                 return nil
     936           0 :         }
     937           2 :         l.y.lockedFiles.Delete(l.fullname)
     938           2 :         l.y = nil
     939           2 :         return l.f.Close()
     940             : }

Generated by: LCOV version 1.14