github.com/zuoyebang/bitalosdb@v1.1.1-0.20240516111551-79a8c4d8ce20/internal/vfs/mem_fs.go (about)

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