github.com/icexin/eggos@v0.4.2-0.20220216025428-78b167e4f349/fs/mount/mountfs.go (about)

     1  // Copyright © 2017 Blake Williams <code@shabbyrobe.org>
     2  // Copyright © 2020 fanbingxin <fanbingxin.me@gmail.com>
     3  //
     4  // Licensed under the Apache License, Version 2.0 (the "License");
     5  // you may not use this file except in compliance with the License.
     6  // You may obtain a copy of the License at
     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 mount
    16  
    17  import (
    18  	"errors"
    19  	"os"
    20  	"path/filepath"
    21  	"sort"
    22  	"strings"
    23  	"time"
    24  
    25  	. "github.com/spf13/afero"
    26  )
    27  
    28  // assert that mount.MountableFs implements afero.Fs.
    29  var _ Fs = (*MountableFs)(nil)
    30  
    31  // MountableFs allows different paths in a hierarchy to be served by different
    32  // afero.Fs objects.
    33  type MountableFs struct {
    34  	node *mountableNode
    35  
    36  	// If true, it is possible to mount an Fs over an existing file or directory.
    37  	// If false, attempting to do so will result in an error.
    38  	AllowMasking bool
    39  
    40  	// If true, the same Fs can be mounted inside an existing mount of the same Fs,
    41  	// for e.g:
    42  	//	child := afero.NewMemMapFs()
    43  	//	mfs.Mount("/yep", child)
    44  	//	mfs.Mount("/yep/yep", child)
    45  	AllowRecursiveMount bool
    46  
    47  	now func() time.Time
    48  }
    49  
    50  func NewMountableFs(base Fs) *MountableFs {
    51  	if base == nil {
    52  		base = NewMemMapFs()
    53  	}
    54  	mfs := &MountableFs{
    55  		now:  time.Now,
    56  		node: &mountableNode{fs: base, nodes: map[string]*mountableNode{}},
    57  	}
    58  	return mfs
    59  }
    60  
    61  // Mount an afero.Fs at the specified path.
    62  //
    63  // This will fail if there is already a Fs at the path, or
    64  // any existing mounted Fs contains a file at that path.
    65  //
    66  // You must wrap an afero.OsFs in an afero.BasePathFs to mount it,
    67  // even if that's just to dispose of the Windows drive letter.
    68  func (m *MountableFs) Mount(path string, fs Fs) error {
    69  	// No idea what to do with windows drive letters here, so force BasePathFs
    70  	if _, ok := fs.(*OsFs); ok {
    71  		return errOsFs
    72  	}
    73  
    74  	if info, err := m.Stat(path); err != nil {
    75  		if !os.IsNotExist(err) {
    76  			return err
    77  		}
    78  	} else {
    79  		if !m.AllowMasking && info != nil && !IsMountNode(info) {
    80  			return os.ErrExist
    81  		}
    82  	}
    83  
    84  	parts := splitPath(path)
    85  
    86  	cur := m.node
    87  	for i, p := range parts {
    88  		var next *mountableNode
    89  		var ok bool
    90  		if next, ok = cur.nodes[p]; !ok {
    91  			next = &mountableNode{
    92  				nodes:   map[string]*mountableNode{},
    93  				parent:  cur,
    94  				name:    p,
    95  				depth:   i + 1,
    96  				modTime: m.now()}
    97  		}
    98  		if next.fs == fs && !m.AllowRecursiveMount {
    99  			return errRecursiveMount
   100  		}
   101  		cur.nodes[p] = next
   102  		cur = next
   103  	}
   104  	if cur.fs != nil {
   105  		return errAlreadyMounted
   106  	}
   107  	if cur.parent != nil {
   108  		cur.parent.mountedNodes++
   109  	}
   110  
   111  	cur.fs = fs
   112  	return nil
   113  }
   114  
   115  func (m *MountableFs) Umount(path string) error {
   116  	parts := splitPath(path)
   117  
   118  	cur := m.node
   119  	for _, p := range parts {
   120  		if next, ok := cur.nodes[p]; ok {
   121  			cur = next
   122  		} else {
   123  			return &os.PathError{Err: errNotMounted, Op: "Umount", Path: path}
   124  		}
   125  	}
   126  	if cur.fs == nil {
   127  		return &os.PathError{Err: errNotMounted, Op: "Umount", Path: path}
   128  	}
   129  
   130  	for cur != nil {
   131  		// Don't stuff around with the root node!
   132  		if cur.parent != nil {
   133  			cur.fs = nil
   134  			cur.parent.mountedNodes--
   135  			if len(cur.nodes) == 0 {
   136  				delete(cur.parent.nodes, cur.name)
   137  			}
   138  		}
   139  		cur = cur.parent
   140  	}
   141  
   142  	return nil
   143  }
   144  
   145  func (m *MountableFs) Remount(path string, fs Fs) error {
   146  	if err := m.Umount(path); err != nil {
   147  		return wrapErrorPath(path, err)
   148  	}
   149  	return m.Mount(path, fs)
   150  }
   151  
   152  func (m *MountableFs) Mkdir(name string, perm os.FileMode) error {
   153  	node := m.node.findNode(name)
   154  	if node != nil {
   155  		// if the path points to an intermediate node and the intermediate node
   156  		// doesn't mask a real directory on the underlying filesystem,
   157  		// make the directory inside the parent filesystem.
   158  		if exists, err := m.reallyExists(name); err != nil || exists {
   159  			return wrapErrorPath(name, err)
   160  		}
   161  		fsNode := node.parentWithFs()
   162  		if fsNode == nil {
   163  			return &os.PathError{Err: os.ErrNotExist, Op: "Mkdir", Path: name}
   164  		}
   165  		rel, err := filepath.Rel(fsNode.fullName(), name)
   166  		if err != nil {
   167  			return wrapErrorPath(name, err)
   168  		}
   169  		rel = string(filepath.Separator) + rel
   170  		if err := fsNode.fs.Mkdir(rel, perm); err != nil {
   171  			return wrapErrorPath(name, err)
   172  		}
   173  		return nil
   174  
   175  	} else {
   176  		fs, _, rel := m.node.findPath(name)
   177  		err := wrapErrorPath(name, fs.Mkdir(rel, perm))
   178  		return err
   179  	}
   180  }
   181  
   182  func (m *MountableFs) MkdirAll(path string, perm os.FileMode) error {
   183  	parts := splitPath(path)
   184  	partlen := len(parts)
   185  	for i := 0; i <= partlen; i++ {
   186  		cur := joinPath(parts[0:i])
   187  		if err := m.Mkdir(cur, perm); err != nil && !os.IsExist(err) {
   188  			return err
   189  		}
   190  	}
   191  	return nil
   192  }
   193  
   194  func (m *MountableFs) Create(name string) (File, error) {
   195  	return m.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
   196  }
   197  
   198  func (m *MountableFs) Open(name string) (File, error) {
   199  	return m.OpenFile(name, os.O_RDONLY, 0)
   200  }
   201  
   202  func (m *MountableFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
   203  	fs, _, rel := m.node.findPath(name)
   204  
   205  	exists := true
   206  	isdir, err := IsDir(fs, rel)
   207  	if err != nil {
   208  		if !os.IsNotExist(err) {
   209  			return nil, wrapErrorPath(name, err)
   210  		} else {
   211  			exists = false
   212  		}
   213  	}
   214  
   215  	if isdir || !exists {
   216  		node := m.node.findNode(name)
   217  		if node != nil {
   218  			file, err := fs.OpenFile(rel, flag, perm)
   219  			if err != nil && !os.IsNotExist(err) {
   220  				return nil, wrapErrorPath(name, err)
   221  			}
   222  			mf := &mountableFile{file: file, node: node, base: rel, name: node.name}
   223  			return mf, nil
   224  		}
   225  	}
   226  
   227  	// if we try to write a file into an intermediate node not backed by a
   228  	// directory, we should create it to preserve their illusion:
   229  	if flag&os.O_CREATE == os.O_CREATE {
   230  		parentName := filepath.Dir(name)
   231  		parent := m.node.findNode(parentName)
   232  
   233  		if parent != nil && parent.fs == nil {
   234  			parts := splitPath(parentName)
   235  			i := len(parts)
   236  
   237  			var fs Fs
   238  			next := parent
   239  			for next != nil && fs == nil {
   240  				if next.fs != nil {
   241  					fs = next.fs
   242  				}
   243  				if next.parent != nil {
   244  					i--
   245  				}
   246  				next = next.parent
   247  			}
   248  			for j := range parts[i:] {
   249  				if err := fs.Mkdir(joinPath(parts[i:i+j+1]), perm|0111); err != nil && !os.IsExist(err) {
   250  					return nil, wrapErrorPath(name, err)
   251  				}
   252  			}
   253  		}
   254  	}
   255  
   256  	return fs.OpenFile(rel, flag, perm)
   257  }
   258  
   259  func (m *MountableFs) Remove(name string) error {
   260  	fs, _, rel := m.node.findPath(name)
   261  	return wrapErrorPath(name, fs.Remove(rel))
   262  }
   263  
   264  func (m *MountableFs) RemoveAll(path string) error {
   265  	info, err := lstatIfPossible(m, path)
   266  	if err != nil {
   267  		return wrapErrorPath(path, err)
   268  	}
   269  	err = departWalk(m, path, info, func(path string, info os.FileInfo, err error) error {
   270  		if err != nil {
   271  			return err
   272  		}
   273  		if IsMountNode(info) {
   274  			return nil
   275  		} else {
   276  			if info.IsDir() {
   277  				node := m.node.findNode(path)
   278  				if node != nil {
   279  					return nil
   280  				}
   281  			}
   282  			return m.Remove(path)
   283  		}
   284  	})
   285  	return wrapErrorPath(path, err)
   286  }
   287  
   288  func (m *MountableFs) Rename(oldname string, newname string) error {
   289  	ofs, _, orel := m.node.findPath(oldname)
   290  	nfs, _, nrel := m.node.findPath(newname)
   291  
   292  	if ofs == nfs {
   293  		return wrapErrorPath(oldname, ofs.Rename(orel, nrel))
   294  	} else {
   295  		return errCrossFsRename
   296  	}
   297  }
   298  
   299  func (m *MountableFs) Stat(name string) (os.FileInfo, error) {
   300  	node := m.node.findNode(name)
   301  	if node != nil && node != m.node {
   302  		return mountedDirFromNode(node)
   303  	}
   304  	fs, _, rel := m.node.findPath(name)
   305  	info, err := fs.Stat(rel)
   306  	if err != nil {
   307  		return nil, wrapErrorPath(name, err)
   308  	}
   309  	return info, nil
   310  }
   311  
   312  func (m *MountableFs) Name() string {
   313  	return "MountableFs"
   314  }
   315  
   316  func (m *MountableFs) Chmod(name string, mode os.FileMode) error {
   317  	fs, _, rel := m.node.findPath(name)
   318  	return wrapErrorPath(name, fs.Chmod(rel, mode))
   319  }
   320  
   321  // Chown changes the uid and gid of the named file.
   322  func (m *MountableFs) Chown(name string, uid, gid int) error {
   323  	fs, _, rel := m.node.findPath(name)
   324  	return wrapErrorPath(name, fs.Chown(rel, uid, gid))
   325  }
   326  
   327  func (m *MountableFs) Chtimes(name string, atime time.Time, mtime time.Time) error {
   328  	fs, _, rel := m.node.findPath(name)
   329  	ok, err := Exists(fs, rel)
   330  	if err != nil {
   331  		return wrapErrorPath(name, err)
   332  	}
   333  	if !ok {
   334  		node := m.node.findNode(name)
   335  		if node == nil {
   336  			return &os.PathError{Err: os.ErrNotExist, Op: "Chtimes", Path: name}
   337  		}
   338  		node.modTime = mtime
   339  		return nil
   340  	} else {
   341  		return wrapErrorPath(name, fs.Chtimes(rel, atime, mtime))
   342  	}
   343  }
   344  
   345  // reallyExists returns true if the file or directory exists on the
   346  // base fs or any of the mounted fs, but not if the path is an intermediate
   347  // mounted node (i.e. if you mount a path but the in-between directories don't
   348  // exist).
   349  func (m *MountableFs) reallyExists(name string) (bool, error) {
   350  	s, err := m.Stat(name)
   351  	if os.IsNotExist(err) {
   352  		return false, nil
   353  	} else if err != nil {
   354  		return false, err
   355  	} else if IsMountNode(s) {
   356  		return false, nil
   357  	}
   358  	return true, nil
   359  }
   360  
   361  func wrapErrorPath(path string, err error) error {
   362  	if err == nil {
   363  		return nil
   364  	}
   365  	switch err := err.(type) {
   366  	case *os.PathError:
   367  		err.Path = path
   368  	}
   369  	return err
   370  }
   371  
   372  type mountableNode struct {
   373  	fs           Fs
   374  	parent       *mountableNode
   375  	nodes        map[string]*mountableNode
   376  	name         string
   377  	mountedNodes int
   378  	modTime      time.Time
   379  	depth        int
   380  }
   381  
   382  func (n *mountableNode) parentWithFs() (node *mountableNode) {
   383  	node = n.parent
   384  	for node != nil {
   385  		if node.fs != nil {
   386  			return
   387  		}
   388  		node = node.parent
   389  	}
   390  	return
   391  }
   392  
   393  func (n *mountableNode) fullName() string {
   394  	out := []string{}
   395  	cur := n
   396  	for cur != nil {
   397  		if cur.name != "" {
   398  			out = append([]string{cur.name}, out...)
   399  		}
   400  		cur = cur.parent
   401  	}
   402  	return joinPath(out)
   403  }
   404  
   405  func (n *mountableNode) findNode(path string) *mountableNode {
   406  	parts := splitPath(path)
   407  	cur := n
   408  	for _, p := range parts {
   409  		if next, ok := cur.nodes[p]; ok && next != nil {
   410  			cur = next
   411  		} else {
   412  			return nil
   413  		}
   414  	}
   415  	return cur
   416  }
   417  
   418  func (n *mountableNode) findPath(path string) (fs Fs, base, rel string) {
   419  	parts := splitPath(path)
   420  
   421  	var out Fs
   422  	outIdx := -1
   423  	out = n.fs
   424  	cur := n
   425  	for i, p := range parts {
   426  		if next, ok := cur.nodes[p]; ok {
   427  			cur = next
   428  			if cur.fs != nil {
   429  				out = cur.fs
   430  				outIdx = i
   431  			}
   432  		} else {
   433  			break
   434  		}
   435  	}
   436  
   437  	// afero is a bit fussy and unpredictable about leading slashes.
   438  	return out,
   439  		string(filepath.Separator) + filepath.Join(parts[:outIdx+1]...),
   440  		string(filepath.Separator) + filepath.Join(parts[outIdx+1:]...)
   441  }
   442  
   443  type mountableFile struct {
   444  	name string
   445  	file File
   446  	node *mountableNode
   447  	base string
   448  }
   449  
   450  func (m *mountableFile) Readdir(count int) (out []os.FileInfo, err error) {
   451  	if m.file != nil {
   452  		out, err = m.file.Readdir(count)
   453  		if err != nil {
   454  			return
   455  		}
   456  	}
   457  	if m.node != nil {
   458  		for _, node := range m.node.nodes {
   459  			var mdi *mountedDirInfo
   460  			mdi, err = mountedDirFromNode(node)
   461  			if err != nil {
   462  				return
   463  			}
   464  			out = append(out, mdi)
   465  		}
   466  	}
   467  	return
   468  }
   469  
   470  func (m *mountableFile) Readdirnames(n int) (out []string, err error) {
   471  	if m.file != nil {
   472  		out, err = m.file.Readdirnames(n)
   473  		if err != nil {
   474  			return
   475  		}
   476  	}
   477  	if m.node != nil {
   478  		for part := range m.node.nodes {
   479  			out = append(out, part)
   480  		}
   481  	}
   482  	return
   483  }
   484  
   485  func (m *mountableFile) Close() error {
   486  	if m.file != nil {
   487  		return m.file.Close()
   488  	}
   489  	return nil
   490  }
   491  
   492  func (m *mountableFile) Read(p []byte) (n int, err error) {
   493  	if m.file != nil {
   494  		return m.file.Read(p)
   495  	}
   496  	return 0, errNotAFile
   497  }
   498  
   499  func (m *mountableFile) ReadAt(p []byte, off int64) (n int, err error) {
   500  	if m.file != nil {
   501  		return m.file.ReadAt(p, off)
   502  	}
   503  	return 0, errNotAFile
   504  }
   505  
   506  func (m *mountableFile) Seek(offset int64, whence int) (int64, error) {
   507  	if m.file != nil {
   508  		return m.file.Seek(offset, whence)
   509  	}
   510  	return 0, errNotAFile
   511  }
   512  
   513  func (m *mountableFile) Write(p []byte) (n int, err error) {
   514  	if m.file != nil {
   515  		return m.file.Write(p)
   516  	}
   517  	return 0, errNotAFile
   518  }
   519  
   520  func (m *mountableFile) WriteAt(p []byte, off int64) (n int, err error) {
   521  	if m.file != nil {
   522  		return m.file.WriteAt(p, off)
   523  	}
   524  	return 0, errNotAFile
   525  }
   526  
   527  func (m *mountableFile) Name() string { return m.name }
   528  
   529  func (m *mountableFile) Stat() (os.FileInfo, error) {
   530  	if m.file != nil {
   531  		return m.file.Stat()
   532  	} else {
   533  		if m.node != nil {
   534  			mdi, err := mountedDirFromNode(m.node)
   535  			if err != nil {
   536  				return nil, err
   537  			}
   538  			return mdi, nil
   539  		}
   540  	}
   541  	return nil, os.ErrNotExist
   542  }
   543  
   544  func (m *mountableFile) Sync() error {
   545  	if m.file != nil {
   546  		return m.file.Sync()
   547  	}
   548  	return errNotAFile
   549  }
   550  
   551  func (m *mountableFile) Truncate(size int64) error {
   552  	if m.file != nil {
   553  		return m.file.Truncate(size)
   554  	}
   555  	return errNotAFile
   556  }
   557  
   558  func (m *mountableFile) WriteString(s string) (ret int, err error) {
   559  	if m.file != nil {
   560  		return m.file.WriteString(s)
   561  	}
   562  	return 0, errNotAFile
   563  }
   564  
   565  type mountedDirInfo struct {
   566  	name    string
   567  	mode    os.FileMode
   568  	modTime time.Time
   569  }
   570  
   571  func (m *mountedDirInfo) Name() string       { return m.name }
   572  func (m *mountedDirInfo) Mode() os.FileMode  { return m.mode | os.ModeDir }
   573  func (m *mountedDirInfo) ModTime() time.Time { return m.modTime }
   574  func (m *mountedDirInfo) IsDir() bool        { return true }
   575  func (m *mountedDirInfo) Sys() interface{}   { return nil }
   576  
   577  func (m *mountedDirInfo) Size() int64 {
   578  	// copied from afero, not sure why it's 42.
   579  	return int64(42)
   580  }
   581  
   582  func mountedDirFromNode(node *mountableNode) (*mountedDirInfo, error) {
   583  	if node.name == "" {
   584  		panic("missing name from node")
   585  	}
   586  	mdi := &mountedDirInfo{
   587  		name:    node.name,
   588  		mode:    0777,
   589  		modTime: node.modTime,
   590  	}
   591  	if node.fs != nil {
   592  		// dir should inherit stat info of mounted fs root node
   593  		info, err := node.fs.Stat("/")
   594  		if err != nil {
   595  			return nil, err
   596  		}
   597  		mdi.modTime = info.ModTime()
   598  		mdi.mode = info.Mode()
   599  	}
   600  	return mdi, nil
   601  }
   602  
   603  var (
   604  	errCrossFsRename  = errors.New("cross-fs rename")
   605  	errRecursiveMount = errors.New("recursive mount")
   606  	errShortCopy      = errors.New("short copy")
   607  	errAlreadyMounted = errors.New("already mounted")
   608  	errNotMounted     = errors.New("not mounted")
   609  	errNotAFile       = errors.New("not a file")
   610  	errOsFs           = errors.New("afero.OsFs should not be mounted - use afero.BasePathFs instead")
   611  )
   612  
   613  func underlyingError(err error) error {
   614  	switch err := err.(type) {
   615  	case *os.PathError:
   616  		return err.Err
   617  	}
   618  	return err
   619  }
   620  
   621  func IsErrCrossFsRename(err error) bool  { return underlyingError(err) == errCrossFsRename }
   622  func IsErrRecursiveMount(err error) bool { return underlyingError(err) == errRecursiveMount }
   623  func IsErrShortCopy(err error) bool      { return underlyingError(err) == errShortCopy }
   624  func IsErrAlreadyMounted(err error) bool { return underlyingError(err) == errAlreadyMounted }
   625  func IsErrNotMounted(err error) bool     { return underlyingError(err) == errNotMounted }
   626  func IsErrNotAFile(err error) bool       { return underlyingError(err) == errNotAFile }
   627  func IsErrOsFs(err error) bool           { return underlyingError(err) == errOsFs }
   628  
   629  func splitPath(path string) []string {
   630  	in := strings.Trim(path, string(filepath.Separator))
   631  	if in == "" {
   632  		return nil
   633  	}
   634  	return strings.Split(in, string(filepath.Separator))
   635  }
   636  
   637  func joinPath(parts []string) string {
   638  	return string(filepath.Separator) + strings.Join(parts, string(filepath.Separator))
   639  }
   640  
   641  func IsMountNode(info os.FileInfo) bool {
   642  	if _, ok := info.(*mountedDirInfo); ok {
   643  		return true
   644  	}
   645  	return false
   646  }
   647  
   648  // departWalk recursively descends path, calling walkFn.
   649  // it calls walkFn on departure rather than arrival, allowing removal
   650  // adapted from afero.walk
   651  func departWalk(fs Fs, path string, info os.FileInfo, walkFn filepath.WalkFunc) error {
   652  	if info.IsDir() {
   653  		names, err := readDirNames(fs, path)
   654  		if err != nil {
   655  			return walkFn(path, info, err)
   656  		}
   657  
   658  		for _, name := range names {
   659  			filename := filepath.Join(path, name)
   660  			fileInfo, err := lstatIfPossible(fs, filename)
   661  			if err != nil {
   662  				if err := walkFn(filename, fileInfo, err); err != nil {
   663  					return err
   664  				}
   665  			} else {
   666  				err = departWalk(fs, filename, fileInfo, walkFn)
   667  				if err != nil {
   668  					return err
   669  				}
   670  			}
   671  		}
   672  	}
   673  	return walkFn(path, info, nil)
   674  }
   675  
   676  // if the filesystem supports it, use Lstat, else use fs.Stat
   677  func lstatIfPossible(fs Fs, path string) (os.FileInfo, error) {
   678  	if lfs, ok := fs.(Lstater); ok {
   679  		fi, _, err := lfs.LstatIfPossible(path)
   680  		return fi, err
   681  	}
   682  	return fs.Stat(path)
   683  }
   684  
   685  // readDirNames reads the directory named by dirname and returns
   686  // a sorted list of directory entries.
   687  // adapted from https://golang.org/src/path/filepath/path.go
   688  func readDirNames(fs Fs, dirname string) ([]string, error) {
   689  	f, err := fs.Open(dirname)
   690  	if err != nil {
   691  		return nil, err
   692  	}
   693  	names, err := f.Readdirnames(-1)
   694  	f.Close()
   695  	if err != nil {
   696  		return nil, err
   697  	}
   698  	sort.Strings(names)
   699  	return names, nil
   700  }