github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/utils/filesys/inmemfs.go (about)

     1  // Copyright 2019 Dolthub, Inc.
     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 filesys
    16  
    17  import (
    18  	"bytes"
    19  	"encoding/base32"
    20  	"errors"
    21  	"fmt"
    22  	"io"
    23  	"math/rand"
    24  	"os"
    25  	"path/filepath"
    26  	"strings"
    27  	"sync"
    28  	"time"
    29  
    30  	"github.com/dolthub/dolt/go/libraries/utils/iohelp"
    31  	"github.com/dolthub/dolt/go/libraries/utils/lockutil"
    32  	"github.com/dolthub/dolt/go/libraries/utils/osutil"
    33  )
    34  
    35  // InMemNowFunc is a func() time.Time that can be used to supply the current time.  The default value gets the current
    36  // time from the system clock, but it can be set to something else in order to support reproducible tests.
    37  var InMemNowFunc = time.Now
    38  
    39  type memObj interface {
    40  	isDir() bool
    41  	parent() *memDir
    42  	modTime() time.Time
    43  }
    44  
    45  type memFile struct {
    46  	absPath   string
    47  	data      []byte
    48  	parentDir *memDir
    49  	time      time.Time
    50  }
    51  
    52  func (mf *memFile) isDir() bool {
    53  	return false
    54  }
    55  
    56  func (mf *memFile) parent() *memDir {
    57  	return mf.parentDir
    58  }
    59  
    60  func (mf *memFile) modTime() time.Time {
    61  	return mf.time
    62  }
    63  
    64  type memDir struct {
    65  	absPath   string
    66  	objs      map[string]memObj
    67  	parentDir *memDir
    68  	time      time.Time
    69  }
    70  
    71  func newEmptyDir(path string, parent *memDir) *memDir {
    72  	return &memDir{path, make(map[string]memObj), parent, InMemNowFunc()}
    73  }
    74  
    75  func (md *memDir) isDir() bool {
    76  	return true
    77  }
    78  
    79  func (md *memDir) parent() *memDir {
    80  	return md.parentDir
    81  }
    82  
    83  func (md *memDir) modTime() time.Time {
    84  	return md.time
    85  }
    86  
    87  // InMemFS is an in memory filesystem implementation that is primarily intended for testing
    88  type InMemFS struct {
    89  	rwLock *sync.RWMutex
    90  	cwd    string
    91  	objs   map[string]memObj
    92  }
    93  
    94  var _ Filesys = (*InMemFS)(nil)
    95  
    96  // EmptyInMemFS creates an empty InMemFS instance
    97  func EmptyInMemFS(workingDir string) *InMemFS {
    98  	return NewInMemFS([]string{}, map[string][]byte{}, workingDir)
    99  }
   100  
   101  // NewInMemFS creates an InMemFS with directories and folders provided.
   102  func NewInMemFS(dirs []string, files map[string][]byte, cwd string) *InMemFS {
   103  	if cwd == "" {
   104  		cwd = osutil.FileSystemRoot
   105  	}
   106  	cwd = osutil.PathToNative(cwd)
   107  
   108  	if !filepath.IsAbs(cwd) {
   109  		panic("cwd for InMemFilesys must be absolute path.")
   110  	}
   111  
   112  	fs := &InMemFS{&sync.RWMutex{}, cwd, map[string]memObj{osutil.FileSystemRoot: newEmptyDir(osutil.FileSystemRoot, nil)}}
   113  
   114  	if dirs != nil {
   115  		for _, dir := range dirs {
   116  			absDir := fs.getAbsPath(dir)
   117  			fs.mkDirs(absDir)
   118  		}
   119  	}
   120  
   121  	if files != nil {
   122  		for path, val := range files {
   123  			path = fs.getAbsPath(path)
   124  
   125  			dir := filepath.Dir(path)
   126  			targetDir, err := fs.mkDirs(dir)
   127  
   128  			if err != nil {
   129  				panic("Initializing InMemFS with invalid data.")
   130  			}
   131  
   132  			now := InMemNowFunc()
   133  			newFile := &memFile{path, val, targetDir, now}
   134  
   135  			targetDir.time = now
   136  			targetDir.objs[path] = newFile
   137  			fs.objs[path] = newFile
   138  		}
   139  	}
   140  
   141  	return fs
   142  }
   143  
   144  // WithWorkingDir returns a copy of this file system with the current working dir set to the path given
   145  func (fs InMemFS) WithWorkingDir(path string) (Filesys, error) {
   146  	abs, err := fs.Abs(path)
   147  	if err != nil {
   148  		return nil, err
   149  	}
   150  
   151  	fs.cwd = abs
   152  	return &fs, nil
   153  }
   154  
   155  func (fs *InMemFS) getAbsPath(path string) string {
   156  	path = fs.pathToNative(path)
   157  	if strings.HasPrefix(path, osutil.FileSystemRoot) {
   158  		return filepath.Clean(path)
   159  	}
   160  
   161  	return filepath.Join(fs.cwd, path)
   162  }
   163  
   164  // Exists will tell you if a file or directory with a given path already exists, and if it does is it a directory
   165  func (fs *InMemFS) Exists(path string) (exists bool, isDir bool) {
   166  	fs.rwLock.RLock()
   167  	defer fs.rwLock.RUnlock()
   168  
   169  	return fs.exists(path)
   170  }
   171  
   172  func (fs *InMemFS) exists(path string) (exists bool, isDir bool) {
   173  	path = fs.getAbsPath(path)
   174  
   175  	if obj, ok := fs.objs[path]; ok {
   176  		return true, obj.isDir()
   177  	}
   178  
   179  	return false, false
   180  }
   181  
   182  type iterEntry struct {
   183  	path  string
   184  	size  int64
   185  	isDir bool
   186  }
   187  
   188  func (fs *InMemFS) getIterEntries(path string, recursive bool) ([]iterEntry, error) {
   189  	var entries []iterEntry
   190  	_, err := fs.iter(fs.getAbsPath(path), recursive, func(path string, size int64, isDir bool) (stop bool) {
   191  		entries = append(entries, iterEntry{path, size, isDir})
   192  		return false
   193  	})
   194  
   195  	if err != nil {
   196  		return nil, err
   197  	}
   198  
   199  	return entries, nil
   200  }
   201  
   202  // Iter iterates over the files and subdirectories within a given directory (Optionally recursively).  There
   203  // are no guarantees about the ordering of results. It is also possible that concurrent delete operations could render
   204  // a file path invalid when the callback is made.
   205  func (fs *InMemFS) Iter(path string, recursive bool, cb FSIterCB) error {
   206  	entries, err := func() ([]iterEntry, error) {
   207  		fs.rwLock.RLock()
   208  		defer fs.rwLock.RUnlock()
   209  
   210  		return fs.getIterEntries(path, recursive)
   211  	}()
   212  
   213  	if err != nil {
   214  		return err
   215  	}
   216  
   217  	for _, entry := range entries {
   218  		cb(entry.path, entry.size, entry.isDir)
   219  	}
   220  
   221  	return nil
   222  }
   223  
   224  func (fs *InMemFS) iter(path string, recursive bool, cb FSIterCB) (bool, error) {
   225  	path = filepath.Clean(path)
   226  	obj, ok := fs.objs[path]
   227  
   228  	if !ok {
   229  		return true, os.ErrNotExist
   230  	} else if !obj.isDir() {
   231  		return true, ErrIsDir
   232  	}
   233  
   234  	dir := obj.(*memDir)
   235  
   236  	for k, v := range dir.objs {
   237  		var size int
   238  		if !v.isDir() {
   239  			size = len(v.(*memFile).data)
   240  		}
   241  
   242  		stop := cb(k, int64(size), v.isDir())
   243  
   244  		if stop {
   245  			return true, nil
   246  		}
   247  
   248  		if v.isDir() && recursive {
   249  			stop, err := fs.iter(k, recursive, cb)
   250  
   251  			if stop || err != nil {
   252  				return stop, err
   253  			}
   254  		}
   255  	}
   256  
   257  	return false, nil
   258  }
   259  
   260  // OpenForRead opens a file for reading
   261  func (fs *InMemFS) OpenForRead(fp string) (io.ReadCloser, error) {
   262  	fs.rwLock.RLock()
   263  	defer fs.rwLock.RUnlock()
   264  
   265  	fp = fs.getAbsPath(fp)
   266  
   267  	if exists, isDir := fs.exists(fp); !exists {
   268  		return nil, os.ErrNotExist
   269  	} else if isDir {
   270  		return nil, ErrIsDir
   271  	}
   272  
   273  	fileObj := fs.objs[fp].(*memFile)
   274  	buf := bytes.NewReader(fileObj.data)
   275  
   276  	return io.NopCloser(buf), nil
   277  }
   278  
   279  // ReadFile reads the entire contents of a file
   280  func (fs *InMemFS) ReadFile(fp string) ([]byte, error) {
   281  	fp = fs.getAbsPath(fp)
   282  	r, err := fs.OpenForRead(fp)
   283  
   284  	if err != nil {
   285  		return nil, err
   286  	}
   287  
   288  	return io.ReadAll(r)
   289  }
   290  
   291  type inMemFSWriteCloser struct {
   292  	path      string
   293  	parentDir *memDir
   294  	fs        *InMemFS
   295  	buf       *bytes.Buffer
   296  	rwLock    *sync.RWMutex
   297  }
   298  
   299  func (fsw *inMemFSWriteCloser) Write(p []byte) (int, error) {
   300  	return fsw.buf.Write(p)
   301  }
   302  
   303  func (fsw *inMemFSWriteCloser) Close() error {
   304  	fsw.rwLock.Lock()
   305  	defer fsw.rwLock.Unlock()
   306  
   307  	now := InMemNowFunc()
   308  	data := fsw.buf.Bytes()
   309  	newFile := &memFile{fsw.path, data, fsw.parentDir, now}
   310  	fsw.parentDir.time = now
   311  	fsw.parentDir.objs[fsw.path] = newFile
   312  	fsw.fs.objs[fsw.path] = newFile
   313  
   314  	return nil
   315  }
   316  
   317  // OpenForWrite opens a file for writing.  The file will be created if it does not exist, and if it does exist
   318  // it will be overwritten.
   319  func (fs *InMemFS) OpenForWrite(fp string, perm os.FileMode) (io.WriteCloser, error) {
   320  	fs.rwLock.Lock()
   321  	defer fs.rwLock.Unlock()
   322  
   323  	fp = fs.getAbsPath(fp)
   324  
   325  	if exists, isDir := fs.exists(fp); exists && isDir {
   326  		return nil, ErrIsDir
   327  	}
   328  
   329  	dir := filepath.Dir(fp)
   330  	parentDir, err := fs.mkDirs(dir)
   331  
   332  	if err != nil {
   333  		return nil, err
   334  	}
   335  
   336  	return &inMemFSWriteCloser{fp, parentDir, fs, bytes.NewBuffer(make([]byte, 0, 512)), fs.rwLock}, nil
   337  }
   338  
   339  // OpenForWriteAppend opens a file for writing.  The file will be created if it does not exist, and if it does exist
   340  // it will append to existing file.
   341  func (fs *InMemFS) OpenForWriteAppend(fp string, perm os.FileMode) (io.WriteCloser, error) {
   342  	fs.rwLock.Lock()
   343  	defer fs.rwLock.Unlock()
   344  
   345  	fp = fs.getAbsPath(fp)
   346  
   347  	if exists, isDir := fs.exists(fp); exists && isDir {
   348  		return nil, ErrIsDir
   349  	}
   350  
   351  	dir := filepath.Dir(fp)
   352  	parentDir, err := fs.mkDirs(dir)
   353  
   354  	if err != nil {
   355  		return nil, err
   356  	}
   357  
   358  	return &inMemFSWriteCloser{fp, parentDir, fs, bytes.NewBuffer(make([]byte, 0, 512)), fs.rwLock}, nil
   359  }
   360  
   361  // WriteFile writes the entire data buffer to a given file.  The file will be created if it does not exist,
   362  // and if it does exist it will be overwritten.
   363  func (fs *InMemFS) WriteFile(fp string, data []byte, perm os.FileMode) error {
   364  	w, err := fs.OpenForWrite(fp, perm)
   365  
   366  	if err != nil {
   367  		return err
   368  	}
   369  
   370  	err = iohelp.WriteAll(w, data)
   371  
   372  	if err != nil {
   373  		return err
   374  	}
   375  
   376  	return w.Close()
   377  }
   378  
   379  // MkDirs creates a folder and all the parent folders that are necessary to create it.
   380  func (fs *InMemFS) MkDirs(path string) error {
   381  	fs.rwLock.Lock()
   382  	defer fs.rwLock.Unlock()
   383  
   384  	_, err := fs.mkDirs(path)
   385  	return err
   386  }
   387  
   388  func (fs *InMemFS) mkDirs(path string) (*memDir, error) {
   389  	path = fs.getAbsPath(path)
   390  	elements := strings.Split(path, osutil.PathDelimiter)
   391  
   392  	currPath := osutil.FileSystemRoot
   393  	parentObj, ok := fs.objs[currPath]
   394  
   395  	if !ok {
   396  		panic("Filesystem does not have a root directory.")
   397  	}
   398  
   399  	parentDir := parentObj.(*memDir)
   400  	for i, element := range elements {
   401  		// When iterating Windows-style paths, the first slash is after the volume, e.g. C:/
   402  		// We check if the first element (like "C:") plus the delimiter is the same as the system root
   403  		// If so, we skip it as we add the system root when creating the InMemFS
   404  		if i == 0 && osutil.IsWindows && element+osutil.PathDelimiter == osutil.FileSystemRoot {
   405  			continue
   406  		}
   407  		currPath = filepath.Join(currPath, element)
   408  
   409  		if obj, ok := fs.objs[currPath]; !ok {
   410  			newDir := newEmptyDir(currPath, parentDir)
   411  			parentDir.objs[currPath] = newDir
   412  			fs.objs[currPath] = newDir
   413  			parentDir = newDir
   414  		} else if !obj.isDir() {
   415  			return nil, errors.New("Could not create directory with same path as existing file: " + currPath)
   416  		} else {
   417  			parentDir = obj.(*memDir)
   418  		}
   419  	}
   420  
   421  	return parentDir, nil
   422  }
   423  
   424  // DeleteFile will delete a file at the given path
   425  func (fs *InMemFS) DeleteFile(path string) error {
   426  	fs.rwLock.Lock()
   427  	defer fs.rwLock.Unlock()
   428  
   429  	return fs.deleteFile(path)
   430  }
   431  
   432  func (fs *InMemFS) deleteFile(path string) error {
   433  	path = fs.getAbsPath(path)
   434  
   435  	if obj, ok := fs.objs[path]; ok {
   436  		if obj.isDir() {
   437  			return ErrIsDir
   438  		}
   439  
   440  		delete(fs.objs, path)
   441  
   442  		parentDir := obj.parent()
   443  		if parentDir != nil {
   444  			delete(parentDir.objs, path)
   445  		}
   446  	} else {
   447  		return os.ErrNotExist
   448  	}
   449  
   450  	return nil
   451  }
   452  
   453  // Delete will delete an empty directory, or a file.  If trying delete a directory that is not empty you can set force to
   454  // true in order to delete the dir and all of it's contents
   455  func (fs *InMemFS) Delete(path string, force bool) error {
   456  	fs.rwLock.Lock()
   457  	defer fs.rwLock.Unlock()
   458  
   459  	path = fs.getAbsPath(path)
   460  
   461  	if exists, isDir := fs.exists(path); !exists {
   462  		return os.ErrNotExist
   463  	} else if !isDir {
   464  		return fs.deleteFile(path)
   465  	}
   466  
   467  	toDelete := map[string]bool{path: true}
   468  	entries, err := fs.getIterEntries(path, true)
   469  
   470  	if err != nil {
   471  		return err
   472  	}
   473  
   474  	for _, entry := range entries {
   475  		toDelete[entry.path] = entry.isDir
   476  	}
   477  
   478  	isEmpty := len(toDelete) == 1
   479  
   480  	if !force && !isEmpty {
   481  		return errors.New(path + " is a directory which is not empty. Delete the contents first, or set force to true")
   482  	}
   483  
   484  	for currPath := range toDelete {
   485  		currObj := fs.objs[currPath]
   486  		delete(fs.objs, currPath)
   487  
   488  		parentDir := currObj.parent()
   489  		if parentDir != nil {
   490  			delete(parentDir.objs, currPath)
   491  		}
   492  	}
   493  
   494  	return nil
   495  }
   496  
   497  func (fs *InMemFS) MoveDir(srcPath, destPath string) error {
   498  	fs.rwLock.Lock()
   499  	defer fs.rwLock.Unlock()
   500  
   501  	srcPath = fs.getAbsPath(srcPath)
   502  	destPath = fs.getAbsPath(destPath)
   503  
   504  	destPathParent, _ := filepath.Split(destPath)
   505  	if exists, destIsDir := fs.exists(destPathParent); !exists || !destIsDir {
   506  		return ErrDirNotExist
   507  	}
   508  
   509  	obj, ok := fs.objs[srcPath]
   510  	if !ok {
   511  		return os.ErrNotExist
   512  	}
   513  
   514  	if !obj.isDir() {
   515  		return ErrIsFile
   516  	}
   517  
   518  	return fs.moveDirHelper(obj.(*memDir), destPath)
   519  }
   520  
   521  func (fs *InMemFS) moveDirHelper(dir *memDir, destPath string) error {
   522  	// All calls to moveDirHelper MUST happen with the filesystem's read-write mutex locked
   523  	if err := lockutil.AssertRWMutexIsLocked(fs.rwLock); err != nil {
   524  		return fmt.Errorf("moveDirHelper called without first aquiring filesystem read-write lock")
   525  	}
   526  
   527  	if _, exists := fs.objs[destPath]; exists {
   528  		return fmt.Errorf("destination path exists: %s", destPath)
   529  	}
   530  
   531  	if _, exists := fs.objs[filepath.Dir(destPath)]; !exists {
   532  		return fmt.Errorf("destination parent dir does NOT exist: %s", filepath.Dir(destPath))
   533  	}
   534  
   535  	// Create the base directory in the new location before we process the files in dir
   536  	parentDir := filepath.Dir(destPath)
   537  	destParentDir := fs.objs[parentDir].(*memDir)
   538  	destObj := &memDir{
   539  		absPath:   destPath,
   540  		objs:      make(map[string]memObj),
   541  		parentDir: destParentDir,
   542  		time:      InMemNowFunc(),
   543  	}
   544  	fs.objs[destPath] = destObj
   545  	destParentDir.objs[destPath] = destObj
   546  	destParentDir.time = InMemNowFunc()
   547  
   548  	for _, v := range dir.objs {
   549  		switch obj := v.(type) {
   550  		case *memDir:
   551  			base := filepath.Base(obj.absPath)
   552  			newPath := filepath.Join(destPath, base)
   553  			if err := fs.moveDirHelper(obj, newPath); err != nil {
   554  				return err
   555  			}
   556  		case *memFile:
   557  			base := filepath.Base(obj.absPath)
   558  			newDestPath := filepath.Join(destPath, base)
   559  			if err := fs.moveFileHelper(obj, newDestPath); err != nil {
   560  				return err
   561  			}
   562  			delete(dir.objs, obj.absPath)
   563  			delete(fs.objs, obj.absPath)
   564  		default:
   565  			return fmt.Errorf("unexpected type of memory object: %T", v)
   566  		}
   567  	}
   568  
   569  	delete(dir.parentDir.objs, dir.absPath)
   570  	delete(fs.objs, dir.absPath)
   571  	return nil
   572  }
   573  
   574  // MoveFile will move a file from the srcPath in the filesystem to the destPath
   575  func (fs *InMemFS) MoveFile(srcPath, destPath string) error {
   576  	fs.rwLock.Lock()
   577  	defer fs.rwLock.Unlock()
   578  
   579  	srcPath = fs.getAbsPath(srcPath)
   580  	destPath = fs.getAbsPath(destPath)
   581  
   582  	if exists, destIsDir := fs.exists(destPath); exists && destIsDir {
   583  		return ErrIsDir
   584  	}
   585  
   586  	if obj, ok := fs.objs[srcPath]; ok {
   587  		if obj.isDir() {
   588  			return ErrIsDir
   589  		}
   590  
   591  		return fs.moveFileHelper(obj.(*memFile), destPath)
   592  	}
   593  
   594  	return os.ErrNotExist
   595  }
   596  
   597  func (fs *InMemFS) moveFileHelper(obj *memFile, destPath string) error {
   598  	// All calls to moveFileHelper MUST happen with the filesystem's read-write mutex locked
   599  	if err := lockutil.AssertRWMutexIsLocked(fs.rwLock); err != nil {
   600  		return fmt.Errorf("moveFileHelper called without first aquiring filesystem read-write lock")
   601  	}
   602  
   603  	destDir := filepath.Dir(destPath)
   604  	destParentDir, err := fs.mkDirs(destDir)
   605  	if err != nil {
   606  		return err
   607  	}
   608  
   609  	now := InMemNowFunc()
   610  	destObj := &memFile{destPath, obj.data, destParentDir, now}
   611  
   612  	fs.objs[destPath] = destObj
   613  
   614  	delete(fs.objs, obj.absPath)
   615  
   616  	parentDir := obj.parent()
   617  	if parentDir != nil {
   618  		parentDir.time = now
   619  		delete(parentDir.objs, obj.absPath)
   620  	}
   621  
   622  	destParentDir.objs[destPath] = destObj
   623  	destParentDir.time = now
   624  
   625  	return nil
   626  }
   627  
   628  func (fs *InMemFS) CopyFile(srcPath, destPath string) error {
   629  	fs.rwLock.Lock()
   630  	defer fs.rwLock.Unlock()
   631  
   632  	srcPath = fs.getAbsPath(srcPath)
   633  	destPath = fs.getAbsPath(destPath)
   634  
   635  	if exists, destIsDir := fs.exists(destPath); exists && destIsDir {
   636  		return ErrIsDir
   637  	}
   638  
   639  	if obj, ok := fs.objs[srcPath]; ok {
   640  		if obj.isDir() {
   641  			return ErrIsDir
   642  		}
   643  
   644  		destDir := filepath.Dir(destPath)
   645  		destParentDir, err := fs.mkDirs(destDir)
   646  		if err != nil {
   647  			return err
   648  		}
   649  
   650  		destData := make([]byte, len(obj.(*memFile).data))
   651  		copy(destData, obj.(*memFile).data)
   652  
   653  		now := InMemNowFunc()
   654  		destObj := &memFile{destPath, destData, destParentDir, now}
   655  
   656  		fs.objs[destPath] = destObj
   657  		destParentDir.objs[destPath] = destObj
   658  		destParentDir.time = now
   659  
   660  		return nil
   661  	}
   662  
   663  	return os.ErrNotExist
   664  }
   665  
   666  // converts a path to an absolute path.  If it's already an absolute path the input path will be returned unaltered
   667  func (fs *InMemFS) Abs(path string) (string, error) {
   668  	path = fs.pathToNative(path)
   669  	if filepath.IsAbs(path) {
   670  		return path, nil
   671  	}
   672  
   673  	return filepath.Join(fs.cwd, path), nil
   674  }
   675  
   676  // LastModified gets the last modified timestamp for a file or directory at a given path
   677  func (fs *InMemFS) LastModified(path string) (t time.Time, exists bool) {
   678  	fs.rwLock.RLock()
   679  	defer fs.rwLock.RUnlock()
   680  
   681  	path = fs.getAbsPath(path)
   682  
   683  	if obj, ok := fs.objs[path]; ok {
   684  		return obj.modTime(), true
   685  	}
   686  
   687  	return time.Time{}, false
   688  }
   689  
   690  func (fs *InMemFS) TempDir() string {
   691  	buf := make([]byte, 16)
   692  	rand.Read(buf)
   693  	s := base32.HexEncoding.EncodeToString(buf)
   694  	return "/var/folders/gc/" + s + "/T/"
   695  }
   696  
   697  func (fs *InMemFS) pathToNative(path string) string {
   698  	if len(path) >= 1 {
   699  		if path[0] == '.' {
   700  			if len(path) == 1 {
   701  				return fs.cwd
   702  			}
   703  			if len(path) >= 2 && (path[1] == '/' || path[1] == '\\') {
   704  				return filepath.Join(fs.cwd, path[2:])
   705  			}
   706  			return filepath.Join(fs.cwd, path)
   707  		} else if !osutil.StartsWithWindowsVolume(path) && path[0] != '/' && path[0] != '\\' {
   708  			return filepath.Join(fs.cwd, path)
   709  		}
   710  	}
   711  	return osutil.PathToNative(path)
   712  }