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

Generated by: LCOV version 1.14