github.com/cockroachdb/pebble@v0.0.0-20231214172447-ab4952c5f87b/vfs/mem_fs.go (about)

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