github.com/petermattis/pebble@v0.0.0-20190905164901-ab51a2166067/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/petermattis/pebble/vfs"
     6  
     7  import (
     8  	"bytes"
     9  	"errors"
    10  	"fmt"
    11  	"io"
    12  	"os"
    13  	"sort"
    14  	"strings"
    15  	"sync"
    16  	"time"
    17  )
    18  
    19  const sep = string(os.PathSeparator)
    20  
    21  type nopCloser struct{}
    22  
    23  func (nopCloser) Close() error {
    24  	return nil
    25  }
    26  
    27  // NewMem returns a new memory-backed FS implementation.
    28  func NewMem() FS {
    29  	return &memFS{
    30  		root: &memNode{
    31  			children: make(map[string]*memNode),
    32  			isDir:    true,
    33  		},
    34  	}
    35  }
    36  
    37  // memFS implements FS.
    38  type memFS struct {
    39  	mu   sync.Mutex
    40  	root *memNode
    41  }
    42  
    43  var _ FS = &memFS{}
    44  
    45  func (y *memFS) String() string {
    46  	y.mu.Lock()
    47  	defer y.mu.Unlock()
    48  
    49  	s := new(bytes.Buffer)
    50  	y.root.dump(s, 0)
    51  	return s.String()
    52  }
    53  
    54  // walk walks the directory tree for the fullname, calling f at each step. If
    55  // f returns an error, the walk will be aborted and return that same error.
    56  //
    57  // Each walk is atomic: y's mutex is held for the entire operation, including
    58  // all calls to f.
    59  //
    60  // dir is the directory at that step, frag is the name fragment, and final is
    61  // whether it is the final step. For example, walking "/foo/bar/x" will result
    62  // in 3 calls to f:
    63  //   - "/", "foo", false
    64  //   - "/foo/", "bar", false
    65  //   - "/foo/bar/", "x", true
    66  // Similarly, walking "/y/z/", with a trailing slash, will result in 3 calls to f:
    67  //   - "/", "y", false
    68  //   - "/y/", "z", false
    69  //   - "/y/z/", "", true
    70  func (y *memFS) walk(fullname string, f func(dir *memNode, frag string, final bool) error) error {
    71  	y.mu.Lock()
    72  	defer y.mu.Unlock()
    73  
    74  	// For memfs, the current working directory is the same as the root directory,
    75  	// so we strip off any leading "/"s to make fullname a relative path, and
    76  	// the walk starts at y.root.
    77  	for len(fullname) > 0 && fullname[0] == os.PathSeparator {
    78  		fullname = fullname[1:]
    79  	}
    80  	dir := y.root
    81  
    82  	for {
    83  		frag, remaining := fullname, ""
    84  		i := strings.IndexRune(fullname, os.PathSeparator)
    85  		final := i < 0
    86  		if !final {
    87  			frag, remaining = fullname[:i], fullname[i+1:]
    88  			for len(remaining) > 0 && remaining[0] == os.PathSeparator {
    89  				remaining = remaining[1:]
    90  			}
    91  		}
    92  		if err := f(dir, frag, final); err != nil {
    93  			return err
    94  		}
    95  		if final {
    96  			break
    97  		}
    98  		child := dir.children[frag]
    99  		if child == nil {
   100  			return errors.New("pebble/vfs: no such directory")
   101  		}
   102  		if !child.isDir {
   103  			return errors.New("pebble/vfs: not a directory")
   104  		}
   105  		dir, fullname = child, remaining
   106  	}
   107  	return nil
   108  }
   109  
   110  func (y *memFS) Create(fullname string) (File, error) {
   111  	var ret *memFile
   112  	err := y.walk(fullname, func(dir *memNode, frag string, final bool) error {
   113  		if final {
   114  			if frag == "" {
   115  				return errors.New("pebble/vfs: empty file name")
   116  			}
   117  			n := &memNode{name: frag}
   118  			dir.children[frag] = n
   119  			ret = &memFile{
   120  				n:     n,
   121  				write: true,
   122  			}
   123  		}
   124  		return nil
   125  	})
   126  	if err != nil {
   127  		return nil, err
   128  	}
   129  	return ret, nil
   130  }
   131  
   132  func (y *memFS) Link(oldname, newname string) error {
   133  	var n *memNode
   134  	err := y.walk(oldname, func(dir *memNode, frag string, final bool) error {
   135  		if final {
   136  			if frag == "" {
   137  				return errors.New("pebble/vfs: empty file name")
   138  			}
   139  			n = dir.children[frag]
   140  		}
   141  		return nil
   142  	})
   143  	if err != nil {
   144  		return err
   145  	}
   146  	if n == nil {
   147  		return errors.New("pebble/vfs: no such file or directory")
   148  	}
   149  	return y.walk(newname, func(dir *memNode, frag string, final bool) error {
   150  		if final {
   151  			if frag == "" {
   152  				return errors.New("pebble/vfs: empty file name")
   153  			}
   154  			dir.children[frag] = n
   155  		}
   156  		return nil
   157  	})
   158  }
   159  
   160  func (y *memFS) open(fullname string, allowEmptyName bool) (File, error) {
   161  	var ret *memFile
   162  	err := y.walk(fullname, func(dir *memNode, frag string, final bool) error {
   163  		if final {
   164  			if frag == "" {
   165  				if !allowEmptyName {
   166  					return errors.New("pebble/vfs: empty file name")
   167  				}
   168  				ret = &memFile{
   169  					n: dir,
   170  				}
   171  				return nil
   172  			}
   173  			if n := dir.children[frag]; n != nil {
   174  				ret = &memFile{
   175  					n:    n,
   176  					read: true,
   177  				}
   178  			}
   179  		}
   180  		return nil
   181  	})
   182  	if err != nil {
   183  		return nil, err
   184  	}
   185  	if ret == nil {
   186  		return nil, &os.PathError{
   187  			Op:   "open",
   188  			Path: fullname,
   189  			Err:  os.ErrNotExist,
   190  		}
   191  	}
   192  	return ret, nil
   193  }
   194  
   195  func (y *memFS) Open(fullname string, opts ...OpenOption) (File, error) {
   196  	return y.open(fullname, false /* allowEmptyName */)
   197  }
   198  
   199  func (y *memFS) OpenDir(fullname string) (File, error) {
   200  	return y.open(fullname, true /* allowEmptyName */)
   201  }
   202  
   203  func (y *memFS) Remove(fullname string) error {
   204  	return y.walk(fullname, func(dir *memNode, frag string, final bool) error {
   205  		if final {
   206  			if frag == "" {
   207  				return errors.New("pebble/vfs: empty file name")
   208  			}
   209  			_, ok := dir.children[frag]
   210  			if !ok {
   211  				return os.ErrNotExist
   212  			}
   213  			delete(dir.children, frag)
   214  		}
   215  		return nil
   216  	})
   217  }
   218  
   219  func (y *memFS) Rename(oldname, newname string) error {
   220  	var n *memNode
   221  	err := y.walk(oldname, func(dir *memNode, frag string, final bool) error {
   222  		if final {
   223  			if frag == "" {
   224  				return errors.New("pebble/vfs: empty file name")
   225  			}
   226  			n = dir.children[frag]
   227  			delete(dir.children, frag)
   228  		}
   229  		return nil
   230  	})
   231  	if err != nil {
   232  		return err
   233  	}
   234  	if n == nil {
   235  		return errors.New("pebble/vfs: no such file or directory")
   236  	}
   237  	return y.walk(newname, 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  			dir.children[frag] = n
   243  		}
   244  		return nil
   245  	})
   246  }
   247  
   248  func (y *memFS) MkdirAll(dirname string, perm os.FileMode) error {
   249  	return y.walk(dirname, func(dir *memNode, frag string, final bool) error {
   250  		if frag == "" {
   251  			if final {
   252  				return nil
   253  			}
   254  			return errors.New("pebble/vfs: empty file name")
   255  		}
   256  		child := dir.children[frag]
   257  		if child == nil {
   258  			dir.children[frag] = &memNode{
   259  				name:     frag,
   260  				children: make(map[string]*memNode),
   261  				isDir:    true,
   262  			}
   263  			return nil
   264  		}
   265  		if !child.isDir {
   266  			return errors.New("pebble/vfs: not a directory")
   267  		}
   268  		return nil
   269  	})
   270  }
   271  
   272  func (y *memFS) Lock(fullname string) (io.Closer, error) {
   273  	// FS.Lock excludes other processes, but other processes cannot see this
   274  	// process' memory, so Lock is a no-op.
   275  	return nopCloser{}, nil
   276  }
   277  
   278  func (y *memFS) List(dirname string) ([]string, error) {
   279  	if !strings.HasSuffix(dirname, sep) {
   280  		dirname += sep
   281  	}
   282  	var ret []string
   283  	err := y.walk(dirname, func(dir *memNode, frag string, final bool) error {
   284  		if final {
   285  			if frag != "" {
   286  				panic("unreachable")
   287  			}
   288  			ret = make([]string, 0, len(dir.children))
   289  			for s := range dir.children {
   290  				ret = append(ret, s)
   291  			}
   292  		}
   293  		return nil
   294  	})
   295  	return ret, err
   296  }
   297  
   298  func (y *memFS) Stat(name string) (os.FileInfo, error) {
   299  	f, err := y.Open(name)
   300  	if err != nil {
   301  		if pe, ok := err.(*os.PathError); ok {
   302  			pe.Op = "stat"
   303  		}
   304  		return nil, err
   305  	}
   306  	defer f.Close()
   307  	return f.Stat()
   308  }
   309  
   310  // memNode holds a file's data or a directory's children, and implements os.FileInfo.
   311  type memNode struct {
   312  	name     string
   313  	data     []byte
   314  	modTime  time.Time
   315  	children map[string]*memNode
   316  	isDir    bool
   317  }
   318  
   319  func (f *memNode) IsDir() bool {
   320  	return f.isDir
   321  }
   322  
   323  func (f *memNode) ModTime() time.Time {
   324  	return f.modTime
   325  }
   326  
   327  func (f *memNode) Mode() os.FileMode {
   328  	if f.isDir {
   329  		return os.ModeDir | 0755
   330  	}
   331  	return 0755
   332  }
   333  
   334  func (f *memNode) Name() string {
   335  	return f.name
   336  }
   337  
   338  func (f *memNode) Size() int64 {
   339  	return int64(len(f.data))
   340  }
   341  
   342  func (f *memNode) Sys() interface{} {
   343  	return nil
   344  }
   345  
   346  func (f *memNode) dump(w *bytes.Buffer, level int) {
   347  	if f.isDir {
   348  		w.WriteString("          ")
   349  	} else {
   350  		fmt.Fprintf(w, "%8d  ", len(f.data))
   351  	}
   352  	for i := 0; i < level; i++ {
   353  		w.WriteString("  ")
   354  	}
   355  	w.WriteString(f.name)
   356  	if !f.isDir {
   357  		w.WriteByte('\n')
   358  		return
   359  	}
   360  	w.WriteByte(os.PathSeparator)
   361  	w.WriteByte('\n')
   362  	names := make([]string, 0, len(f.children))
   363  	for name := range f.children {
   364  		names = append(names, name)
   365  	}
   366  	sort.Strings(names)
   367  	for _, name := range names {
   368  		f.children[name].dump(w, level+1)
   369  	}
   370  }
   371  
   372  // memFile is a reader or writer of a node's data, and implements File.
   373  type memFile struct {
   374  	n           *memNode
   375  	rpos        int
   376  	read, write bool
   377  }
   378  
   379  func (f *memFile) Close() error {
   380  	return nil
   381  }
   382  
   383  func (f *memFile) Read(p []byte) (int, error) {
   384  	if !f.read {
   385  		return 0, errors.New("pebble/vfs: file was not opened for reading")
   386  	}
   387  	if f.n.isDir {
   388  		return 0, errors.New("pebble/vfs: cannot read a directory")
   389  	}
   390  	if f.rpos >= len(f.n.data) {
   391  		return 0, io.EOF
   392  	}
   393  	n := copy(p, f.n.data[f.rpos:])
   394  	f.rpos += n
   395  	return n, nil
   396  }
   397  
   398  func (f *memFile) ReadAt(p []byte, off int64) (int, error) {
   399  	if !f.read {
   400  		return 0, errors.New("pebble/vfs: file was not opened for reading")
   401  	}
   402  	if f.n.isDir {
   403  		return 0, errors.New("pebble/vfs: cannot read a directory")
   404  	}
   405  	if off >= int64(len(f.n.data)) {
   406  		return 0, io.EOF
   407  	}
   408  	return copy(p, f.n.data[off:]), nil
   409  }
   410  
   411  func (f *memFile) Write(p []byte) (int, error) {
   412  	if !f.write {
   413  		return 0, errors.New("pebble/vfs: file was not created for writing")
   414  	}
   415  	if f.n.isDir {
   416  		return 0, errors.New("pebble/vfs: cannot write a directory")
   417  	}
   418  	f.n.modTime = time.Now()
   419  	f.n.data = append(f.n.data, p...)
   420  	return len(p), nil
   421  }
   422  
   423  func (f *memFile) Stat() (os.FileInfo, error) {
   424  	return f.n, nil
   425  }
   426  
   427  func (f *memFile) Sync() error {
   428  	return nil
   429  }