github.com/westcoastroms/westcoastroms-build@v0.0.0-20190928114312-2350e5a73030/build/soong/finder/fs/fs.go (about)

     1  // Copyright 2017 Google Inc. All rights reserved.
     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 fs
    16  
    17  import (
    18  	"bytes"
    19  	"errors"
    20  	"fmt"
    21  	"io"
    22  	"io/ioutil"
    23  	"os"
    24  	"os/user"
    25  	"path/filepath"
    26  	"sync"
    27  	"time"
    28  )
    29  
    30  var OsFs FileSystem = osFs{}
    31  
    32  func NewMockFs(files map[string][]byte) *MockFs {
    33  	workDir := "/cwd"
    34  	fs := &MockFs{
    35  		Clock:   NewClock(time.Unix(2, 2)),
    36  		workDir: workDir,
    37  	}
    38  	fs.root = *fs.newDir()
    39  	fs.MkDirs(workDir)
    40  
    41  	for path, bytes := range files {
    42  		dir := filepath.Dir(path)
    43  		fs.MkDirs(dir)
    44  		fs.WriteFile(path, bytes, 0777)
    45  	}
    46  
    47  	return fs
    48  }
    49  
    50  type FileSystem interface {
    51  	// getting information about files
    52  	Open(name string) (file io.ReadCloser, err error)
    53  	Lstat(path string) (stats os.FileInfo, err error)
    54  	ReadDir(path string) (contents []DirEntryInfo, err error)
    55  
    56  	InodeNumber(info os.FileInfo) (number uint64, err error)
    57  	DeviceNumber(info os.FileInfo) (number uint64, err error)
    58  	PermTime(info os.FileInfo) (time time.Time, err error)
    59  
    60  	// changing contents of the filesystem
    61  	Rename(oldPath string, newPath string) (err error)
    62  	WriteFile(path string, data []byte, perm os.FileMode) (err error)
    63  	Remove(path string) (err error)
    64  	RemoveAll(path string) (err error)
    65  
    66  	// metadata about the filesystem
    67  	ViewId() (id string) // Some unique id of the user accessing the filesystem
    68  }
    69  
    70  // DentryInfo is a subset of the functionality available through os.FileInfo that might be able
    71  // to be gleaned through only a syscall.Getdents without requiring a syscall.Lstat of every file.
    72  type DirEntryInfo interface {
    73  	Name() string
    74  	Mode() os.FileMode // the file type encoded as an os.FileMode
    75  	IsDir() bool
    76  }
    77  
    78  type dirEntryInfo struct {
    79  	name       string
    80  	mode       os.FileMode
    81  	modeExists bool
    82  }
    83  
    84  var _ DirEntryInfo = os.FileInfo(nil)
    85  
    86  func (d *dirEntryInfo) Name() string      { return d.name }
    87  func (d *dirEntryInfo) Mode() os.FileMode { return d.mode }
    88  func (d *dirEntryInfo) IsDir() bool       { return d.mode.IsDir() }
    89  func (d *dirEntryInfo) String() string    { return d.name + ": " + d.mode.String() }
    90  
    91  // osFs implements FileSystem using the local disk.
    92  type osFs struct{}
    93  
    94  var _ FileSystem = (*osFs)(nil)
    95  
    96  func (osFs) Open(name string) (io.ReadCloser, error) { return os.Open(name) }
    97  
    98  func (osFs) Lstat(path string) (stats os.FileInfo, err error) {
    99  	return os.Lstat(path)
   100  }
   101  
   102  func (osFs) ReadDir(path string) (contents []DirEntryInfo, err error) {
   103  	entries, err := readdir(path)
   104  	if err != nil {
   105  		return nil, err
   106  	}
   107  	for _, entry := range entries {
   108  		contents = append(contents, entry)
   109  	}
   110  
   111  	return contents, nil
   112  }
   113  
   114  func (osFs) Rename(oldPath string, newPath string) error {
   115  	return os.Rename(oldPath, newPath)
   116  }
   117  
   118  func (osFs) WriteFile(path string, data []byte, perm os.FileMode) error {
   119  	return ioutil.WriteFile(path, data, perm)
   120  }
   121  
   122  func (osFs) Remove(path string) error {
   123  	return os.Remove(path)
   124  }
   125  
   126  func (osFs) RemoveAll(path string) error {
   127  	return os.RemoveAll(path)
   128  }
   129  
   130  func (osFs) ViewId() (id string) {
   131  	user, err := user.Current()
   132  	if err != nil {
   133  		return ""
   134  	}
   135  	username := user.Username
   136  
   137  	hostname, err := os.Hostname()
   138  	if err != nil {
   139  		return ""
   140  	}
   141  
   142  	return username + "@" + hostname
   143  }
   144  
   145  type Clock struct {
   146  	time time.Time
   147  }
   148  
   149  func NewClock(startTime time.Time) *Clock {
   150  	return &Clock{time: startTime}
   151  
   152  }
   153  
   154  func (c *Clock) Tick() {
   155  	c.time = c.time.Add(time.Microsecond)
   156  }
   157  
   158  func (c *Clock) Time() time.Time {
   159  	return c.time
   160  }
   161  
   162  // given "/a/b/c/d", pathSplit returns ("/a/b/c", "d")
   163  func pathSplit(path string) (dir string, leaf string) {
   164  	dir, leaf = filepath.Split(path)
   165  	if dir != "/" && len(dir) > 0 {
   166  		dir = dir[:len(dir)-1]
   167  	}
   168  	return dir, leaf
   169  }
   170  
   171  // MockFs supports singlethreaded writes and multithreaded reads
   172  type MockFs struct {
   173  	// configuration
   174  	viewId       string //
   175  	deviceNumber uint64
   176  
   177  	// implementation
   178  	root            mockDir
   179  	Clock           *Clock
   180  	workDir         string
   181  	nextInodeNumber uint64
   182  
   183  	// history of requests, for tests to check
   184  	StatCalls      []string
   185  	ReadDirCalls   []string
   186  	aggregatesLock sync.Mutex
   187  }
   188  
   189  var _ FileSystem = (*MockFs)(nil)
   190  
   191  type mockInode struct {
   192  	modTime     time.Time
   193  	permTime    time.Time
   194  	sys         interface{}
   195  	inodeNumber uint64
   196  	readErr     error
   197  }
   198  
   199  func (m mockInode) ModTime() time.Time {
   200  	return m.modTime
   201  }
   202  
   203  func (m mockInode) Sys() interface{} {
   204  	return m.sys
   205  }
   206  
   207  type mockFile struct {
   208  	bytes []byte
   209  
   210  	mockInode
   211  }
   212  
   213  type mockLink struct {
   214  	target string
   215  
   216  	mockInode
   217  }
   218  
   219  type mockDir struct {
   220  	mockInode
   221  
   222  	subdirs  map[string]*mockDir
   223  	files    map[string]*mockFile
   224  	symlinks map[string]*mockLink
   225  }
   226  
   227  func (m *MockFs) resolve(path string, followLastLink bool) (result string, err error) {
   228  	if !filepath.IsAbs(path) {
   229  		path = filepath.Join(m.workDir, path)
   230  	}
   231  	path = filepath.Clean(path)
   232  
   233  	return m.followLinks(path, followLastLink, 10)
   234  }
   235  
   236  // note that followLinks can return a file path that doesn't exist
   237  func (m *MockFs) followLinks(path string, followLastLink bool, count int) (canonicalPath string, err error) {
   238  	if path == "/" {
   239  		return path, nil
   240  	}
   241  
   242  	parentPath, leaf := pathSplit(path)
   243  	if parentPath == path {
   244  		err = fmt.Errorf("Internal error: %v yields itself as a parent", path)
   245  		panic(err.Error())
   246  		return "", fmt.Errorf("Internal error: %v yields itself as a parent", path)
   247  	}
   248  
   249  	parentPath, err = m.followLinks(parentPath, true, count)
   250  	if err != nil {
   251  		return "", err
   252  	}
   253  
   254  	parentNode, err := m.getDir(parentPath, false)
   255  	if err != nil {
   256  		return "", err
   257  	}
   258  	if parentNode.readErr != nil {
   259  		return "", &os.PathError{
   260  			Op:   "read",
   261  			Path: path,
   262  			Err:  parentNode.readErr,
   263  		}
   264  	}
   265  
   266  	link, isLink := parentNode.symlinks[leaf]
   267  	if isLink && followLastLink {
   268  		if count <= 0 {
   269  			// probably a loop
   270  			return "", &os.PathError{
   271  				Op:   "read",
   272  				Path: path,
   273  				Err:  fmt.Errorf("too many levels of symbolic links"),
   274  			}
   275  		}
   276  
   277  		if link.readErr != nil {
   278  			return "", &os.PathError{
   279  				Op:   "read",
   280  				Path: path,
   281  				Err:  link.readErr,
   282  			}
   283  		}
   284  
   285  		target := m.followLink(link, parentPath)
   286  		return m.followLinks(target, followLastLink, count-1)
   287  	}
   288  	return path, nil
   289  }
   290  
   291  func (m *MockFs) followLink(link *mockLink, parentPath string) (result string) {
   292  	return filepath.Clean(filepath.Join(parentPath, link.target))
   293  }
   294  
   295  func (m *MockFs) getFile(parentDir *mockDir, fileName string) (file *mockFile, err error) {
   296  	file, isFile := parentDir.files[fileName]
   297  	if !isFile {
   298  		_, isDir := parentDir.subdirs[fileName]
   299  		_, isLink := parentDir.symlinks[fileName]
   300  		if isDir || isLink {
   301  			return nil, &os.PathError{
   302  				Op:   "open",
   303  				Path: fileName,
   304  				Err:  os.ErrInvalid,
   305  			}
   306  		}
   307  
   308  		return nil, &os.PathError{
   309  			Op:   "open",
   310  			Path: fileName,
   311  			Err:  os.ErrNotExist,
   312  		}
   313  	}
   314  	if file.readErr != nil {
   315  		return nil, &os.PathError{
   316  			Op:   "open",
   317  			Path: fileName,
   318  			Err:  file.readErr,
   319  		}
   320  	}
   321  	return file, nil
   322  }
   323  
   324  func (m *MockFs) getInode(parentDir *mockDir, name string) (inode *mockInode, err error) {
   325  	file, isFile := parentDir.files[name]
   326  	if isFile {
   327  		return &file.mockInode, nil
   328  	}
   329  	link, isLink := parentDir.symlinks[name]
   330  	if isLink {
   331  		return &link.mockInode, nil
   332  	}
   333  	dir, isDir := parentDir.subdirs[name]
   334  	if isDir {
   335  		return &dir.mockInode, nil
   336  	}
   337  	return nil, &os.PathError{
   338  		Op:   "stat",
   339  		Path: name,
   340  		Err:  os.ErrNotExist,
   341  	}
   342  
   343  }
   344  
   345  func (m *MockFs) Open(path string) (io.ReadCloser, error) {
   346  	path, err := m.resolve(path, true)
   347  	if err != nil {
   348  		return nil, err
   349  	}
   350  
   351  	if err != nil {
   352  		return nil, err
   353  	}
   354  
   355  	parentPath, base := pathSplit(path)
   356  	parentDir, err := m.getDir(parentPath, false)
   357  	if err != nil {
   358  		return nil, err
   359  	}
   360  	file, err := m.getFile(parentDir, base)
   361  	if err != nil {
   362  		return nil, err
   363  	}
   364  	return struct {
   365  		io.Closer
   366  		*bytes.Reader
   367  	}{
   368  		ioutil.NopCloser(nil),
   369  		bytes.NewReader(file.bytes),
   370  	}, nil
   371  
   372  }
   373  
   374  // a mockFileInfo is for exporting file stats in a way that satisfies the FileInfo interface
   375  type mockFileInfo struct {
   376  	path         string
   377  	size         int64
   378  	modTime      time.Time // time at which the inode's contents were modified
   379  	permTime     time.Time // time at which the inode's permissions were modified
   380  	isDir        bool
   381  	inodeNumber  uint64
   382  	deviceNumber uint64
   383  }
   384  
   385  func (m *mockFileInfo) Name() string {
   386  	return m.path
   387  }
   388  
   389  func (m *mockFileInfo) Size() int64 {
   390  	return m.size
   391  }
   392  
   393  func (m *mockFileInfo) Mode() os.FileMode {
   394  	return 0
   395  }
   396  
   397  func (m *mockFileInfo) ModTime() time.Time {
   398  	return m.modTime
   399  }
   400  
   401  func (m *mockFileInfo) IsDir() bool {
   402  	return m.isDir
   403  }
   404  
   405  func (m *mockFileInfo) Sys() interface{} {
   406  	return nil
   407  }
   408  
   409  func (m *MockFs) dirToFileInfo(d *mockDir, path string) (info *mockFileInfo) {
   410  	return &mockFileInfo{
   411  		path:         path,
   412  		size:         1,
   413  		modTime:      d.modTime,
   414  		permTime:     d.permTime,
   415  		isDir:        true,
   416  		inodeNumber:  d.inodeNumber,
   417  		deviceNumber: m.deviceNumber,
   418  	}
   419  
   420  }
   421  
   422  func (m *MockFs) fileToFileInfo(f *mockFile, path string) (info *mockFileInfo) {
   423  	return &mockFileInfo{
   424  		path:         path,
   425  		size:         1,
   426  		modTime:      f.modTime,
   427  		permTime:     f.permTime,
   428  		isDir:        false,
   429  		inodeNumber:  f.inodeNumber,
   430  		deviceNumber: m.deviceNumber,
   431  	}
   432  }
   433  
   434  func (m *MockFs) linkToFileInfo(l *mockLink, path string) (info *mockFileInfo) {
   435  	return &mockFileInfo{
   436  		path:         path,
   437  		size:         1,
   438  		modTime:      l.modTime,
   439  		permTime:     l.permTime,
   440  		isDir:        false,
   441  		inodeNumber:  l.inodeNumber,
   442  		deviceNumber: m.deviceNumber,
   443  	}
   444  }
   445  
   446  func (m *MockFs) Lstat(path string) (stats os.FileInfo, err error) {
   447  	// update aggregates
   448  	m.aggregatesLock.Lock()
   449  	m.StatCalls = append(m.StatCalls, path)
   450  	m.aggregatesLock.Unlock()
   451  
   452  	// resolve symlinks
   453  	path, err = m.resolve(path, false)
   454  	if err != nil {
   455  		return nil, err
   456  	}
   457  
   458  	// special case for root dir
   459  	if path == "/" {
   460  		return m.dirToFileInfo(&m.root, "/"), nil
   461  	}
   462  
   463  	// determine type and handle appropriately
   464  	parentPath, baseName := pathSplit(path)
   465  	dir, err := m.getDir(parentPath, false)
   466  	if err != nil {
   467  		return nil, err
   468  	}
   469  	subdir, subdirExists := dir.subdirs[baseName]
   470  	if subdirExists {
   471  		return m.dirToFileInfo(subdir, path), nil
   472  	}
   473  	file, fileExists := dir.files[baseName]
   474  	if fileExists {
   475  		return m.fileToFileInfo(file, path), nil
   476  	}
   477  	link, linkExists := dir.symlinks[baseName]
   478  	if linkExists {
   479  		return m.linkToFileInfo(link, path), nil
   480  	}
   481  	// not found
   482  	return nil, &os.PathError{
   483  		Op:   "stat",
   484  		Path: path,
   485  		Err:  os.ErrNotExist,
   486  	}
   487  }
   488  
   489  func (m *MockFs) InodeNumber(info os.FileInfo) (number uint64, err error) {
   490  	mockInfo, ok := info.(*mockFileInfo)
   491  	if ok {
   492  		return mockInfo.inodeNumber, nil
   493  	}
   494  	return 0, fmt.Errorf("%v is not a mockFileInfo", info)
   495  }
   496  func (m *MockFs) DeviceNumber(info os.FileInfo) (number uint64, err error) {
   497  	mockInfo, ok := info.(*mockFileInfo)
   498  	if ok {
   499  		return mockInfo.deviceNumber, nil
   500  	}
   501  	return 0, fmt.Errorf("%v is not a mockFileInfo", info)
   502  }
   503  func (m *MockFs) PermTime(info os.FileInfo) (when time.Time, err error) {
   504  	mockInfo, ok := info.(*mockFileInfo)
   505  	if ok {
   506  		return mockInfo.permTime, nil
   507  	}
   508  	return time.Date(0, 0, 0, 0, 0, 0, 0, nil),
   509  		fmt.Errorf("%v is not a mockFileInfo", info)
   510  }
   511  
   512  func (m *MockFs) ReadDir(path string) (contents []DirEntryInfo, err error) {
   513  	// update aggregates
   514  	m.aggregatesLock.Lock()
   515  	m.ReadDirCalls = append(m.ReadDirCalls, path)
   516  	m.aggregatesLock.Unlock()
   517  
   518  	// locate directory
   519  	path, err = m.resolve(path, true)
   520  	if err != nil {
   521  		return nil, err
   522  	}
   523  	results := []DirEntryInfo{}
   524  	dir, err := m.getDir(path, false)
   525  	if err != nil {
   526  		return nil, err
   527  	}
   528  	if dir.readErr != nil {
   529  		return nil, &os.PathError{
   530  			Op:   "read",
   531  			Path: path,
   532  			Err:  dir.readErr,
   533  		}
   534  	}
   535  	// describe its contents
   536  	for name, subdir := range dir.subdirs {
   537  		dirInfo := m.dirToFileInfo(subdir, name)
   538  		results = append(results, dirInfo)
   539  	}
   540  	for name, file := range dir.files {
   541  		info := m.fileToFileInfo(file, name)
   542  		results = append(results, info)
   543  	}
   544  	for name, link := range dir.symlinks {
   545  		info := m.linkToFileInfo(link, name)
   546  		results = append(results, info)
   547  	}
   548  	return results, nil
   549  }
   550  
   551  func (m *MockFs) Rename(sourcePath string, destPath string) error {
   552  	// validate source parent exists
   553  	sourcePath, err := m.resolve(sourcePath, false)
   554  	if err != nil {
   555  		return err
   556  	}
   557  	sourceParentPath := filepath.Dir(sourcePath)
   558  	sourceParentDir, err := m.getDir(sourceParentPath, false)
   559  	if err != nil {
   560  		return err
   561  	}
   562  	if sourceParentDir == nil {
   563  		return &os.PathError{
   564  			Op:   "move",
   565  			Path: sourcePath,
   566  			Err:  os.ErrNotExist,
   567  		}
   568  	}
   569  	if sourceParentDir.readErr != nil {
   570  		return &os.PathError{
   571  			Op:   "move",
   572  			Path: sourcePath,
   573  			Err:  sourceParentDir.readErr,
   574  		}
   575  	}
   576  
   577  	// validate dest parent exists
   578  	destPath, err = m.resolve(destPath, false)
   579  	destParentPath := filepath.Dir(destPath)
   580  	destParentDir, err := m.getDir(destParentPath, false)
   581  	if err != nil {
   582  		return err
   583  	}
   584  	if destParentDir == nil {
   585  		return &os.PathError{
   586  			Op:   "move",
   587  			Path: destParentPath,
   588  			Err:  os.ErrNotExist,
   589  		}
   590  	}
   591  	if destParentDir.readErr != nil {
   592  		return &os.PathError{
   593  			Op:   "move",
   594  			Path: destParentPath,
   595  			Err:  destParentDir.readErr,
   596  		}
   597  	}
   598  	// check the source and dest themselves
   599  	sourceBase := filepath.Base(sourcePath)
   600  	destBase := filepath.Base(destPath)
   601  
   602  	file, sourceIsFile := sourceParentDir.files[sourceBase]
   603  	dir, sourceIsDir := sourceParentDir.subdirs[sourceBase]
   604  	link, sourceIsLink := sourceParentDir.symlinks[sourceBase]
   605  
   606  	// validate that the source exists
   607  	if !sourceIsFile && !sourceIsDir && !sourceIsLink {
   608  		return &os.PathError{
   609  			Op:   "move",
   610  			Path: sourcePath,
   611  			Err:  os.ErrNotExist,
   612  		}
   613  
   614  	}
   615  
   616  	// validate the destination doesn't already exist as an incompatible type
   617  	_, destWasFile := destParentDir.files[destBase]
   618  	_, destWasDir := destParentDir.subdirs[destBase]
   619  	_, destWasLink := destParentDir.symlinks[destBase]
   620  
   621  	if destWasDir {
   622  		return &os.PathError{
   623  			Op:   "move",
   624  			Path: destPath,
   625  			Err:  errors.New("destination exists as a directory"),
   626  		}
   627  	}
   628  
   629  	if sourceIsDir && (destWasFile || destWasLink) {
   630  		return &os.PathError{
   631  			Op:   "move",
   632  			Path: destPath,
   633  			Err:  errors.New("destination exists as a file"),
   634  		}
   635  	}
   636  
   637  	if destWasFile {
   638  		delete(destParentDir.files, destBase)
   639  	}
   640  	if destWasDir {
   641  		delete(destParentDir.subdirs, destBase)
   642  	}
   643  	if destWasLink {
   644  		delete(destParentDir.symlinks, destBase)
   645  	}
   646  
   647  	if sourceIsFile {
   648  		destParentDir.files[destBase] = file
   649  		delete(sourceParentDir.files, sourceBase)
   650  	}
   651  	if sourceIsDir {
   652  		destParentDir.subdirs[destBase] = dir
   653  		delete(sourceParentDir.subdirs, sourceBase)
   654  	}
   655  	if sourceIsLink {
   656  		destParentDir.symlinks[destBase] = link
   657  		delete(destParentDir.symlinks, sourceBase)
   658  	}
   659  
   660  	destParentDir.modTime = m.Clock.Time()
   661  	sourceParentDir.modTime = m.Clock.Time()
   662  	return nil
   663  }
   664  
   665  func (m *MockFs) newInodeNumber() uint64 {
   666  	result := m.nextInodeNumber
   667  	m.nextInodeNumber++
   668  	return result
   669  }
   670  
   671  func (m *MockFs) WriteFile(filePath string, data []byte, perm os.FileMode) error {
   672  	filePath, err := m.resolve(filePath, true)
   673  	if err != nil {
   674  		return err
   675  	}
   676  	parentPath := filepath.Dir(filePath)
   677  	parentDir, err := m.getDir(parentPath, false)
   678  	if err != nil || parentDir == nil {
   679  		return &os.PathError{
   680  			Op:   "write",
   681  			Path: parentPath,
   682  			Err:  os.ErrNotExist,
   683  		}
   684  	}
   685  	if parentDir.readErr != nil {
   686  		return &os.PathError{
   687  			Op:   "write",
   688  			Path: parentPath,
   689  			Err:  parentDir.readErr,
   690  		}
   691  	}
   692  
   693  	baseName := filepath.Base(filePath)
   694  	_, exists := parentDir.files[baseName]
   695  	if !exists {
   696  		parentDir.modTime = m.Clock.Time()
   697  		parentDir.files[baseName] = m.newFile()
   698  	} else {
   699  		readErr := parentDir.files[baseName].readErr
   700  		if readErr != nil {
   701  			return &os.PathError{
   702  				Op:   "write",
   703  				Path: filePath,
   704  				Err:  readErr,
   705  			}
   706  		}
   707  	}
   708  	file := parentDir.files[baseName]
   709  	file.bytes = data
   710  	file.modTime = m.Clock.Time()
   711  	return nil
   712  }
   713  
   714  func (m *MockFs) newFile() *mockFile {
   715  	newFile := &mockFile{}
   716  	newFile.inodeNumber = m.newInodeNumber()
   717  	newFile.modTime = m.Clock.Time()
   718  	newFile.permTime = newFile.modTime
   719  	return newFile
   720  }
   721  
   722  func (m *MockFs) newDir() *mockDir {
   723  	newDir := &mockDir{
   724  		subdirs:  make(map[string]*mockDir, 0),
   725  		files:    make(map[string]*mockFile, 0),
   726  		symlinks: make(map[string]*mockLink, 0),
   727  	}
   728  	newDir.inodeNumber = m.newInodeNumber()
   729  	newDir.modTime = m.Clock.Time()
   730  	newDir.permTime = newDir.modTime
   731  	return newDir
   732  }
   733  
   734  func (m *MockFs) newLink(target string) *mockLink {
   735  	newLink := &mockLink{
   736  		target: target,
   737  	}
   738  	newLink.inodeNumber = m.newInodeNumber()
   739  	newLink.modTime = m.Clock.Time()
   740  	newLink.permTime = newLink.modTime
   741  
   742  	return newLink
   743  }
   744  func (m *MockFs) MkDirs(path string) error {
   745  	_, err := m.getDir(path, true)
   746  	return err
   747  }
   748  
   749  // getDir doesn't support symlinks
   750  func (m *MockFs) getDir(path string, createIfMissing bool) (dir *mockDir, err error) {
   751  	cleanedPath := filepath.Clean(path)
   752  	if cleanedPath == "/" {
   753  		return &m.root, nil
   754  	}
   755  
   756  	parentPath, leaf := pathSplit(cleanedPath)
   757  	if len(parentPath) >= len(path) {
   758  		return &m.root, nil
   759  	}
   760  	parent, err := m.getDir(parentPath, createIfMissing)
   761  	if err != nil {
   762  		return nil, err
   763  	}
   764  	if parent.readErr != nil {
   765  		return nil, &os.PathError{
   766  			Op:   "stat",
   767  			Path: path,
   768  			Err:  parent.readErr,
   769  		}
   770  	}
   771  	childDir, dirExists := parent.subdirs[leaf]
   772  	if !dirExists {
   773  		if createIfMissing {
   774  			// confirm that a file with the same name doesn't already exist
   775  			_, fileExists := parent.files[leaf]
   776  			if fileExists {
   777  				return nil, &os.PathError{
   778  					Op:   "mkdir",
   779  					Path: path,
   780  					Err:  os.ErrExist,
   781  				}
   782  			}
   783  			// create this directory
   784  			childDir = m.newDir()
   785  			parent.subdirs[leaf] = childDir
   786  			parent.modTime = m.Clock.Time()
   787  		} else {
   788  			return nil, &os.PathError{
   789  				Op:   "stat",
   790  				Path: path,
   791  				Err:  os.ErrNotExist,
   792  			}
   793  		}
   794  	}
   795  	return childDir, nil
   796  
   797  }
   798  
   799  func (m *MockFs) Remove(path string) (err error) {
   800  	path, err = m.resolve(path, false)
   801  	parentPath, leaf := pathSplit(path)
   802  	if len(leaf) == 0 {
   803  		return fmt.Errorf("Cannot remove %v\n", path)
   804  	}
   805  	parentDir, err := m.getDir(parentPath, false)
   806  	if err != nil {
   807  		return err
   808  	}
   809  	if parentDir == nil {
   810  		return &os.PathError{
   811  			Op:   "remove",
   812  			Path: path,
   813  			Err:  os.ErrNotExist,
   814  		}
   815  	}
   816  	if parentDir.readErr != nil {
   817  		return &os.PathError{
   818  			Op:   "remove",
   819  			Path: path,
   820  			Err:  parentDir.readErr,
   821  		}
   822  	}
   823  	_, isDir := parentDir.subdirs[leaf]
   824  	if isDir {
   825  		return &os.PathError{
   826  			Op:   "remove",
   827  			Path: path,
   828  			Err:  os.ErrInvalid,
   829  		}
   830  	}
   831  	_, isLink := parentDir.symlinks[leaf]
   832  	if isLink {
   833  		delete(parentDir.symlinks, leaf)
   834  	} else {
   835  		_, isFile := parentDir.files[leaf]
   836  		if !isFile {
   837  			return &os.PathError{
   838  				Op:   "remove",
   839  				Path: path,
   840  				Err:  os.ErrNotExist,
   841  			}
   842  		}
   843  		delete(parentDir.files, leaf)
   844  	}
   845  	parentDir.modTime = m.Clock.Time()
   846  	return nil
   847  }
   848  
   849  func (m *MockFs) Symlink(oldPath string, newPath string) (err error) {
   850  	newPath, err = m.resolve(newPath, false)
   851  	if err != nil {
   852  		return err
   853  	}
   854  
   855  	newParentPath, leaf := pathSplit(newPath)
   856  	newParentDir, err := m.getDir(newParentPath, false)
   857  	if newParentDir.readErr != nil {
   858  		return &os.PathError{
   859  			Op:   "link",
   860  			Path: newPath,
   861  			Err:  newParentDir.readErr,
   862  		}
   863  	}
   864  	if err != nil {
   865  		return err
   866  	}
   867  	newParentDir.symlinks[leaf] = m.newLink(oldPath)
   868  	return nil
   869  }
   870  
   871  func (m *MockFs) RemoveAll(path string) (err error) {
   872  	path, err = m.resolve(path, false)
   873  	if err != nil {
   874  		return err
   875  	}
   876  	parentPath, leaf := pathSplit(path)
   877  	if len(leaf) == 0 {
   878  		return fmt.Errorf("Cannot remove %v\n", path)
   879  	}
   880  	parentDir, err := m.getDir(parentPath, false)
   881  	if err != nil {
   882  		return err
   883  	}
   884  	if parentDir == nil {
   885  		return &os.PathError{
   886  			Op:   "removeAll",
   887  			Path: path,
   888  			Err:  os.ErrNotExist,
   889  		}
   890  	}
   891  	if parentDir.readErr != nil {
   892  		return &os.PathError{
   893  			Op:   "removeAll",
   894  			Path: path,
   895  			Err:  parentDir.readErr,
   896  		}
   897  
   898  	}
   899  	_, isFile := parentDir.files[leaf]
   900  	_, isLink := parentDir.symlinks[leaf]
   901  	if isFile || isLink {
   902  		return m.Remove(path)
   903  	}
   904  	_, isDir := parentDir.subdirs[leaf]
   905  	if !isDir {
   906  		if !isDir {
   907  			return &os.PathError{
   908  				Op:   "removeAll",
   909  				Path: path,
   910  				Err:  os.ErrNotExist,
   911  			}
   912  		}
   913  	}
   914  
   915  	delete(parentDir.subdirs, leaf)
   916  	parentDir.modTime = m.Clock.Time()
   917  	return nil
   918  }
   919  
   920  func (m *MockFs) SetReadable(path string, readable bool) error {
   921  	var readErr error
   922  	if !readable {
   923  		readErr = os.ErrPermission
   924  	}
   925  	return m.SetReadErr(path, readErr)
   926  }
   927  
   928  func (m *MockFs) SetReadErr(path string, readErr error) error {
   929  	path, err := m.resolve(path, false)
   930  	if err != nil {
   931  		return err
   932  	}
   933  	parentPath, leaf := filepath.Split(path)
   934  	parentDir, err := m.getDir(parentPath, false)
   935  	if err != nil {
   936  		return err
   937  	}
   938  	if parentDir.readErr != nil {
   939  		return &os.PathError{
   940  			Op:   "chmod",
   941  			Path: parentPath,
   942  			Err:  parentDir.readErr,
   943  		}
   944  	}
   945  
   946  	inode, err := m.getInode(parentDir, leaf)
   947  	if err != nil {
   948  		return err
   949  	}
   950  	inode.readErr = readErr
   951  	inode.permTime = m.Clock.Time()
   952  	return nil
   953  }
   954  
   955  func (m *MockFs) ClearMetrics() {
   956  	m.ReadDirCalls = []string{}
   957  	m.StatCalls = []string{}
   958  }
   959  
   960  func (m *MockFs) ViewId() (id string) {
   961  	return m.viewId
   962  }
   963  
   964  func (m *MockFs) SetViewId(id string) {
   965  	m.viewId = id
   966  }
   967  func (m *MockFs) SetDeviceNumber(deviceNumber uint64) {
   968  	m.deviceNumber = deviceNumber
   969  }