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