github.com/cozy/cozy-stack@v0.0.0-20240327093429-939e4a21320e/model/vfs/vfsafero/impl.go (about)

     1  // Package vfsafero is the implementation of the Virtual File System by using
     2  // afero. Afero is a library for manipulating files and directory on the local
     3  // file system.
     4  package vfsafero
     5  
     6  import (
     7  	"bytes"
     8  	"crypto/md5"
     9  	"errors"
    10  	"fmt"
    11  	"hash"
    12  	"io"
    13  	"net/url"
    14  	"os"
    15  	"path"
    16  	"path/filepath"
    17  	"strings"
    18  	"sync"
    19  
    20  	"github.com/cozy/cozy-stack/model/vfs"
    21  	"github.com/cozy/cozy-stack/pkg/filetype"
    22  	"github.com/cozy/cozy-stack/pkg/lock"
    23  
    24  	"github.com/spf13/afero"
    25  )
    26  
    27  var memfsMap sync.Map
    28  
    29  // aferoVFS is a struct implementing the vfs.VFS interface associated with
    30  // an afero.Fs filesystem. The indexing of the elements of the filesystem is
    31  // done in couchdb.
    32  type aferoVFS struct {
    33  	vfs.Indexer
    34  	vfs.DiskThresholder
    35  
    36  	cluster int
    37  	domain  string
    38  	prefix  string
    39  	context string
    40  	fs      afero.Fs
    41  	mu      lock.ErrorRWLocker
    42  	pth     string
    43  
    44  	// whether or not the localfilesystem requires an initialisation of its root
    45  	// directory
    46  	osFS bool
    47  }
    48  
    49  // GetMemFS returns a file system in memory for the given key
    50  func GetMemFS(key string) afero.Fs {
    51  	val, ok := memfsMap.Load(key)
    52  	if !ok {
    53  		val, _ = memfsMap.LoadOrStore(key, afero.NewMemMapFs())
    54  	}
    55  	return val.(afero.Fs)
    56  }
    57  
    58  // New returns a vfs.VFS instance associated with the specified indexer and
    59  // storage url.
    60  //
    61  // The supported scheme of the storage url are file://, for an OS-FS store, and
    62  // mem:// for an in-memory store. The backend used is the afero package.
    63  func New(db vfs.Prefixer, index vfs.Indexer, disk vfs.DiskThresholder, mu lock.ErrorRWLocker, fsURL *url.URL, pathSegment string) (vfs.VFS, error) {
    64  	if fsURL.Scheme != "mem" && fsURL.Path == "" {
    65  		return nil, fmt.Errorf("vfsafero: please check the supplied fs url: %s",
    66  			fsURL.String())
    67  	}
    68  	if pathSegment == "" {
    69  		return nil, fmt.Errorf("vfsafero: specified path segment is empty")
    70  	}
    71  	pth := path.Join(fsURL.Path, pathSegment)
    72  	var fs afero.Fs
    73  	switch fsURL.Scheme {
    74  	case "file":
    75  		fs = afero.NewBasePathFs(afero.NewOsFs(), pth)
    76  	case "mem":
    77  		fs = GetMemFS(db.DomainName())
    78  	default:
    79  		return nil, fmt.Errorf("vfsafero: non supported scheme %s", fsURL.Scheme)
    80  	}
    81  	return &aferoVFS{
    82  		Indexer:         index,
    83  		DiskThresholder: disk,
    84  
    85  		cluster: db.DBCluster(),
    86  		domain:  db.DomainName(),
    87  		prefix:  db.DBPrefix(),
    88  		context: db.GetContextName(),
    89  		fs:      fs,
    90  		mu:      mu,
    91  		pth:     pth,
    92  		// for now, only the file:// scheme needs a specific initialisation of its
    93  		// root directory.
    94  		osFS: fsURL.Scheme == "file",
    95  	}, nil
    96  }
    97  
    98  func (afs *aferoVFS) MaxFileSize() int64 {
    99  	return -1 // no limit
   100  }
   101  
   102  func (afs *aferoVFS) DBCluster() int {
   103  	return afs.cluster
   104  }
   105  
   106  func (afs *aferoVFS) DomainName() string {
   107  	return afs.domain
   108  }
   109  
   110  func (afs *aferoVFS) DBPrefix() string {
   111  	return afs.prefix
   112  }
   113  
   114  func (afs *aferoVFS) GetContextName() string {
   115  	return afs.context
   116  }
   117  
   118  func (afs *aferoVFS) GetIndexer() vfs.Indexer {
   119  	return afs.Indexer
   120  }
   121  
   122  func (afs *aferoVFS) UseSharingIndexer(index vfs.Indexer) vfs.VFS {
   123  	return &aferoVFS{
   124  		Indexer:         index,
   125  		DiskThresholder: afs.DiskThresholder,
   126  		domain:          afs.domain,
   127  		prefix:          afs.prefix,
   128  		fs:              afs.fs,
   129  		mu:              afs.mu,
   130  		pth:             afs.pth,
   131  		osFS:            afs.osFS,
   132  	}
   133  }
   134  
   135  // Init creates the root directory document and the trash directory for this
   136  // file system.
   137  func (afs *aferoVFS) InitFs() error {
   138  	if lockerr := afs.mu.Lock(); lockerr != nil {
   139  		return lockerr
   140  	}
   141  	defer afs.mu.Unlock()
   142  	if err := afs.Indexer.InitIndex(); err != nil {
   143  		return err
   144  	}
   145  	// for a file:// fs, we need to create the root directory container
   146  	if afs.osFS {
   147  		if err := afero.NewOsFs().MkdirAll(afs.pth, 0755); err != nil {
   148  			return err
   149  		}
   150  	}
   151  	if err := afs.fs.Mkdir(vfs.TrashDirName, 0755); err != nil && !os.IsExist(err) {
   152  		return err
   153  	}
   154  	return nil
   155  }
   156  
   157  // Delete removes all the elements associated with the filesystem.
   158  func (afs *aferoVFS) Delete() error {
   159  	if lockerr := afs.mu.Lock(); lockerr != nil {
   160  		return lockerr
   161  	}
   162  	defer afs.mu.Unlock()
   163  	if afs.osFS {
   164  		return afero.NewOsFs().RemoveAll(afs.pth)
   165  	}
   166  	return nil
   167  }
   168  
   169  func (afs *aferoVFS) CreateDir(doc *vfs.DirDoc) error {
   170  	if lockerr := afs.mu.Lock(); lockerr != nil {
   171  		return lockerr
   172  	}
   173  	defer afs.mu.Unlock()
   174  	err := afs.fs.Mkdir(doc.Fullpath, 0755)
   175  	if err != nil {
   176  		return err
   177  	}
   178  	if doc.ID() == "" {
   179  		err = afs.Indexer.CreateDirDoc(doc)
   180  	} else {
   181  		err = afs.Indexer.CreateNamedDirDoc(doc)
   182  	}
   183  	if err != nil {
   184  		_ = afs.fs.Remove(doc.Fullpath)
   185  	}
   186  	return err
   187  }
   188  
   189  func (afs *aferoVFS) CreateFile(newdoc, olddoc *vfs.FileDoc, opts ...vfs.CreateOptions) (vfs.File, error) {
   190  	if lockerr := afs.mu.Lock(); lockerr != nil {
   191  		return nil, lockerr
   192  	}
   193  	defer afs.mu.Unlock()
   194  
   195  	newsize, maxsize, capsize, err := vfs.CheckAvailableDiskSpace(afs, newdoc)
   196  	if err != nil {
   197  		return nil, err
   198  	}
   199  
   200  	if olddoc != nil {
   201  		newdoc.SetID(olddoc.ID())
   202  		newdoc.SetRev(olddoc.Rev())
   203  		newdoc.CreatedAt = olddoc.CreatedAt
   204  	}
   205  
   206  	newpath, err := afs.Indexer.FilePath(newdoc)
   207  	if err != nil {
   208  		return nil, err
   209  	}
   210  	if strings.HasPrefix(newpath, vfs.TrashDirName+"/") {
   211  		if !vfs.OptionsAllowCreationInTrash(opts) {
   212  			return nil, vfs.ErrParentInTrash
   213  		}
   214  	}
   215  
   216  	if olddoc == nil {
   217  		exists, err := afs.Indexer.DirChildExists(newdoc.DirID, newdoc.DocName)
   218  		if err != nil {
   219  			return nil, err
   220  		}
   221  		if exists {
   222  			return nil, os.ErrExist
   223  		}
   224  	}
   225  
   226  	f, err := afero.TempFile(afs.fs, "/", newdoc.DocName)
   227  	if err != nil {
   228  		return nil, err
   229  	}
   230  	tmppath := path.Join("/", f.Name())
   231  
   232  	hash := md5.New()
   233  	extractor := vfs.NewMetaExtractor(newdoc)
   234  
   235  	return &aferoFileCreation{
   236  		afs:     afs,
   237  		f:       f,
   238  		newdoc:  newdoc,
   239  		olddoc:  olddoc,
   240  		tmppath: tmppath,
   241  		w:       0,
   242  		size:    newsize,
   243  		maxsize: maxsize,
   244  		capsize: capsize,
   245  		hash:    hash,
   246  		meta:    extractor,
   247  	}, nil
   248  }
   249  
   250  func (afs *aferoVFS) CopyFile(olddoc, newdoc *vfs.FileDoc) (err error) {
   251  	var newfile *aferoFileCreation
   252  	defer func() {
   253  		// XXX: we need to release the VFS lock before closing newfile as
   254  		// aferoFileCreation.Close requests its own lock.
   255  		// Therefore, this defer method needs to come before the afs.mu.Unlock
   256  		// deferred call.
   257  		if newfile == nil {
   258  			return
   259  		}
   260  		if cerr := newfile.Close(); cerr != nil && err == nil {
   261  			err = cerr
   262  		}
   263  	}()
   264  
   265  	if lockerr := afs.mu.Lock(); lockerr != nil {
   266  		return lockerr
   267  	}
   268  	defer afs.mu.Unlock()
   269  
   270  	newsize, maxsize, capsize, err := vfs.CheckAvailableDiskSpace(afs, olddoc)
   271  	if err != nil {
   272  		return err
   273  	}
   274  
   275  	f, err := afero.TempFile(afs.fs, "/", newdoc.DocName)
   276  	if err != nil {
   277  		return err
   278  	}
   279  	tmppath := path.Join("/", f.Name())
   280  
   281  	// XXX: we use the internal openFile method as we already have a VFS lock
   282  	content, err := afs.openFile(olddoc)
   283  	if err != nil {
   284  		return err
   285  	}
   286  	defer content.Close()
   287  
   288  	hash := md5.New()
   289  
   290  	newfile = &aferoFileCreation{
   291  		afs:     afs,
   292  		f:       f,
   293  		newdoc:  newdoc,
   294  		tmppath: tmppath,
   295  		w:       0,
   296  		size:    newsize,
   297  		maxsize: maxsize,
   298  		capsize: capsize,
   299  		hash:    hash,
   300  	}
   301  
   302  	_, err = io.Copy(newfile, content)
   303  	return err
   304  }
   305  
   306  func (afs *aferoVFS) DissociateFile(src, dst *vfs.FileDoc) error {
   307  	if lockerr := afs.mu.Lock(); lockerr != nil {
   308  		return lockerr
   309  	}
   310  	defer afs.mu.Unlock()
   311  
   312  	// Move the source file to the destination
   313  	needRename := true
   314  	from, err := afs.Indexer.FilePath(src)
   315  	if errors.Is(err, vfs.ErrParentDoesNotExist) {
   316  		needRename = false // The parent directory has already been dissociated
   317  	} else if err != nil {
   318  		return err
   319  	}
   320  	to, err := afs.Indexer.FilePath(dst)
   321  	if err != nil {
   322  		return err
   323  	}
   324  	if from == to {
   325  		needRename = false
   326  	}
   327  
   328  	if needRename {
   329  		if err = safeRenameFile(afs.fs, from, to); err != nil {
   330  			return err
   331  		}
   332  	}
   333  	if err = afs.Indexer.CreateFileDoc(dst); err != nil {
   334  		if needRename {
   335  			_ = afs.fs.Rename(to, from)
   336  		}
   337  		return err
   338  	}
   339  
   340  	// Clean the source file and its versions
   341  	if err = afs.Indexer.DeleteFileDoc(src); err != nil {
   342  		return err
   343  	}
   344  	_ = afs.fs.RemoveAll(pathForVersions(src.DocID))
   345  	versions, err := vfs.VersionsFor(afs, src.DocID)
   346  	if err != nil {
   347  		return nil
   348  	}
   349  	if from != to {
   350  		_ = afs.Indexer.BatchDeleteVersions(versions)
   351  	}
   352  	return nil
   353  }
   354  
   355  func (afs *aferoVFS) DissociateDir(src, dst *vfs.DirDoc) error {
   356  	if lockerr := afs.mu.Lock(); lockerr != nil {
   357  		return lockerr
   358  	}
   359  	defer afs.mu.Unlock()
   360  
   361  	from := src.Fullpath
   362  	to := dst.Fullpath
   363  	needRename := from != to
   364  	if needRename {
   365  		if _, err := afs.fs.Stat(from); os.IsNotExist(err) {
   366  			needRename = false // The parent directory of the dir was moved
   367  		}
   368  	}
   369  	if needRename {
   370  		if err := safeRenameDir(afs, from, to); err != nil {
   371  			return err
   372  		}
   373  	}
   374  	if err := afs.Indexer.CreateDirDoc(dst); err != nil {
   375  		if needRename {
   376  			_ = afs.fs.Rename(to, from)
   377  		}
   378  		return err
   379  	}
   380  	return afs.Indexer.DeleteDirDoc(src)
   381  }
   382  
   383  func (afs *aferoVFS) DestroyDirContent(doc *vfs.DirDoc, push func(vfs.TrashJournal) error) error {
   384  	if lockerr := afs.mu.Lock(); lockerr != nil {
   385  		return lockerr
   386  	}
   387  	defer afs.mu.Unlock()
   388  	diskUsage, _ := afs.DiskUsage()
   389  	files, destroyed, err := afs.Indexer.DeleteDirDocAndContent(doc, true)
   390  	if err != nil {
   391  		return err
   392  	}
   393  	vfs.DiskQuotaAfterDestroy(afs, diskUsage, destroyed)
   394  	infos, err := afero.ReadDir(afs.fs, doc.Fullpath)
   395  	if err != nil {
   396  		return err
   397  	}
   398  	for _, info := range infos {
   399  		fullpath := path.Join(doc.Fullpath, info.Name())
   400  		if info.IsDir() {
   401  			err = afs.fs.RemoveAll(fullpath)
   402  		} else {
   403  			err = afs.fs.Remove(fullpath)
   404  		}
   405  		if err != nil {
   406  			return err
   407  		}
   408  	}
   409  	var allVersions []*vfs.Version
   410  	for _, file := range files {
   411  		_ = afs.fs.RemoveAll(pathForVersions(file.DocID))
   412  		if versions, err := vfs.VersionsFor(afs, file.DocID); err == nil {
   413  			allVersions = append(allVersions, versions...)
   414  		}
   415  	}
   416  	return afs.Indexer.BatchDeleteVersions(allVersions)
   417  }
   418  
   419  func (afs *aferoVFS) DestroyDirAndContent(doc *vfs.DirDoc, push func(vfs.TrashJournal) error) error {
   420  	if lockerr := afs.mu.Lock(); lockerr != nil {
   421  		return lockerr
   422  	}
   423  	defer afs.mu.Unlock()
   424  	diskUsage, _ := afs.DiskUsage()
   425  	files, destroyed, err := afs.Indexer.DeleteDirDocAndContent(doc, false)
   426  	if err != nil {
   427  		return err
   428  	}
   429  	vfs.DiskQuotaAfterDestroy(afs, diskUsage, destroyed)
   430  	if err = afs.fs.RemoveAll(doc.Fullpath); err != nil {
   431  		return err
   432  	}
   433  	var allVersions []*vfs.Version
   434  	for _, file := range files {
   435  		_ = afs.fs.RemoveAll(pathForVersions(file.DocID))
   436  		if versions, err := vfs.VersionsFor(afs, file.DocID); err == nil {
   437  			allVersions = append(allVersions, versions...)
   438  		}
   439  	}
   440  	return afs.Indexer.BatchDeleteVersions(allVersions)
   441  }
   442  
   443  func (afs *aferoVFS) DestroyFile(doc *vfs.FileDoc) error {
   444  	if lockerr := afs.mu.Lock(); lockerr != nil {
   445  		return lockerr
   446  	}
   447  	defer afs.mu.Unlock()
   448  	diskUsage, _ := afs.DiskUsage()
   449  	name, err := afs.Indexer.FilePath(doc)
   450  	if err != nil {
   451  		return err
   452  	}
   453  	vfs.DiskQuotaAfterDestroy(afs, diskUsage, doc.ByteSize)
   454  	err = afs.fs.Remove(name)
   455  	if err != nil && !os.IsNotExist(err) {
   456  		return err
   457  	}
   458  	if err = afs.Indexer.DeleteFileDoc(doc); err != nil {
   459  		return err
   460  	}
   461  	versions, err := vfs.VersionsFor(afs, doc.DocID)
   462  	if err != nil {
   463  		return err
   464  	}
   465  	_ = afs.fs.RemoveAll(pathForVersions(doc.DocID))
   466  	return afs.Indexer.BatchDeleteVersions(versions)
   467  }
   468  
   469  func (afs *aferoVFS) openFile(doc *vfs.FileDoc) (vfs.File, error) {
   470  	name, err := afs.Indexer.FilePath(doc)
   471  	if err != nil {
   472  		return nil, err
   473  	}
   474  	f, err := afs.fs.Open(name)
   475  	if err != nil {
   476  		return nil, err
   477  	}
   478  	return &aferoFileOpen{f}, nil
   479  }
   480  
   481  func (afs *aferoVFS) OpenFile(doc *vfs.FileDoc) (vfs.File, error) {
   482  	if lockerr := afs.mu.RLock(); lockerr != nil {
   483  		return nil, lockerr
   484  	}
   485  	defer afs.mu.RUnlock()
   486  	return afs.openFile(doc)
   487  }
   488  
   489  func (afs *aferoVFS) EnsureErased(journal vfs.TrashJournal) error {
   490  	return errors.New("EnsureErased is only for Swift")
   491  }
   492  
   493  func (afs *aferoVFS) OpenFileVersion(doc *vfs.FileDoc, version *vfs.Version) (vfs.File, error) {
   494  	if lockerr := afs.mu.RLock(); lockerr != nil {
   495  		return nil, lockerr
   496  	}
   497  	defer afs.mu.RUnlock()
   498  	f, err := afs.fs.Open(pathForVersion(version))
   499  	if err != nil {
   500  		return nil, err
   501  	}
   502  	return &aferoFileOpen{f}, nil
   503  }
   504  
   505  func (afs *aferoVFS) ImportFileVersion(version *vfs.Version, content io.ReadCloser) error {
   506  	if lockerr := afs.mu.Lock(); lockerr != nil {
   507  		return lockerr
   508  	}
   509  	defer afs.mu.Unlock()
   510  
   511  	diskQuota := afs.DiskQuota()
   512  	if diskQuota > 0 {
   513  		diskUsage, err := afs.DiskUsage()
   514  		if err != nil {
   515  			return err
   516  		}
   517  		if diskUsage+version.ByteSize > diskQuota {
   518  			return vfs.ErrFileTooBig
   519  		}
   520  	}
   521  
   522  	vPath := pathForVersion(version)
   523  	_ = afs.fs.MkdirAll(filepath.Dir(vPath), 0755)
   524  	err := afero.WriteReader(afs.fs, vPath, content)
   525  	if errc := content.Close(); err == nil {
   526  		err = errc
   527  	}
   528  	if err != nil {
   529  		// remove the temporary file if an error occurred
   530  		_ = afs.fs.Remove(vPath)
   531  		return err
   532  	}
   533  
   534  	return afs.Indexer.CreateVersion(version)
   535  }
   536  
   537  func (afs *aferoVFS) RevertFileVersion(doc *vfs.FileDoc, version *vfs.Version) error {
   538  	if lockerr := afs.mu.Lock(); lockerr != nil {
   539  		return lockerr
   540  	}
   541  	defer afs.mu.Unlock()
   542  
   543  	mainpath, err := afs.Indexer.FilePath(doc)
   544  	if err != nil {
   545  		return err
   546  	}
   547  
   548  	save := vfs.NewVersion(doc)
   549  	savepath := pathForVersion(save)
   550  	frompath := pathForVersion(version)
   551  
   552  	if err = afs.fs.Rename(mainpath, savepath); err != nil {
   553  		return err
   554  	}
   555  
   556  	if err = afs.fs.Rename(frompath, mainpath); err != nil {
   557  		_ = afs.fs.Rename(savepath, mainpath)
   558  		return err
   559  	}
   560  
   561  	newdoc := doc.Clone().(*vfs.FileDoc)
   562  	vfs.SetMetaFromVersion(newdoc, version)
   563  	if err = afs.Indexer.UpdateFileDoc(doc, newdoc); err != nil {
   564  		_ = afs.fs.Rename(mainpath, frompath)
   565  		_ = afs.fs.Rename(savepath, mainpath)
   566  		return err
   567  	}
   568  
   569  	_ = afs.Indexer.DeleteVersion(version)
   570  
   571  	if err = afs.Indexer.CreateVersion(save); err != nil {
   572  		_ = afs.fs.Remove(savepath)
   573  	}
   574  
   575  	return nil
   576  }
   577  
   578  func (afs *aferoVFS) CopyFileFromOtherFS(
   579  	newdoc, olddoc *vfs.FileDoc,
   580  	srcFS vfs.Fs,
   581  	srcDoc *vfs.FileDoc,
   582  ) error {
   583  	content, err := srcFS.OpenFile(srcDoc)
   584  	if err != nil {
   585  		return err
   586  	}
   587  	defer content.Close()
   588  
   589  	fd, err := afs.CreateFile(newdoc, olddoc)
   590  	if err != nil {
   591  		return err
   592  	}
   593  
   594  	_, err = io.Copy(fd, content)
   595  	errc := fd.Close()
   596  	if err != nil {
   597  		return err
   598  	}
   599  	return errc
   600  }
   601  
   602  // UpdateFileDoc overrides the indexer's one since the afero.Fs is by essence
   603  // also indexed by path. When moving a file, the index has to be moved and the
   604  // filesystem should also be updated.
   605  //
   606  // @override Indexer.UpdateFileDoc
   607  func (afs *aferoVFS) UpdateFileDoc(olddoc, newdoc *vfs.FileDoc) error {
   608  	if lockerr := afs.mu.Lock(); lockerr != nil {
   609  		return lockerr
   610  	}
   611  	defer afs.mu.Unlock()
   612  	if newdoc.DirID != olddoc.DirID || newdoc.DocName != olddoc.DocName {
   613  		oldpath, err := afs.Indexer.FilePath(olddoc)
   614  		if err != nil {
   615  			return err
   616  		}
   617  		newpath, err := afs.Indexer.FilePath(newdoc)
   618  		if err != nil {
   619  			return err
   620  		}
   621  		err = safeRenameFile(afs.fs, oldpath, newpath)
   622  		if err != nil {
   623  			return err
   624  		}
   625  	}
   626  	if newdoc.Executable != olddoc.Executable {
   627  		newpath, err := afs.Indexer.FilePath(newdoc)
   628  		if err != nil {
   629  			return err
   630  		}
   631  		err = afs.fs.Chmod(newpath, newdoc.Mode())
   632  		if err != nil {
   633  			return err
   634  		}
   635  	}
   636  	return afs.Indexer.UpdateFileDoc(olddoc, newdoc)
   637  }
   638  
   639  // UpdateDirDoc overrides the indexer's one since the afero.Fs is by essence
   640  // also indexed by path. When moving a file, the index has to be moved and the
   641  // filesystem should also be updated.
   642  //
   643  // @override Indexer.UpdateDirDoc
   644  func (afs *aferoVFS) UpdateDirDoc(olddoc, newdoc *vfs.DirDoc) error {
   645  	if lockerr := afs.mu.Lock(); lockerr != nil {
   646  		return lockerr
   647  	}
   648  	defer afs.mu.Unlock()
   649  	if newdoc.Fullpath != olddoc.Fullpath {
   650  		if err := safeRenameDir(afs, olddoc.Fullpath, newdoc.Fullpath); err != nil {
   651  			return err
   652  		}
   653  	}
   654  	return afs.Indexer.UpdateDirDoc(olddoc, newdoc)
   655  }
   656  
   657  func (afs *aferoVFS) DirByID(fileID string) (*vfs.DirDoc, error) {
   658  	if lockerr := afs.mu.RLock(); lockerr != nil {
   659  		return nil, lockerr
   660  	}
   661  	defer afs.mu.RUnlock()
   662  	return afs.Indexer.DirByID(fileID)
   663  }
   664  
   665  func (afs *aferoVFS) DirByPath(name string) (*vfs.DirDoc, error) {
   666  	if lockerr := afs.mu.RLock(); lockerr != nil {
   667  		return nil, lockerr
   668  	}
   669  	defer afs.mu.RUnlock()
   670  	return afs.Indexer.DirByPath(name)
   671  }
   672  
   673  func (afs *aferoVFS) FileByID(fileID string) (*vfs.FileDoc, error) {
   674  	if lockerr := afs.mu.RLock(); lockerr != nil {
   675  		return nil, lockerr
   676  	}
   677  	defer afs.mu.RUnlock()
   678  	return afs.Indexer.FileByID(fileID)
   679  }
   680  
   681  func (afs *aferoVFS) FileByPath(name string) (*vfs.FileDoc, error) {
   682  	if lockerr := afs.mu.RLock(); lockerr != nil {
   683  		return nil, lockerr
   684  	}
   685  	defer afs.mu.RUnlock()
   686  	return afs.Indexer.FileByPath(name)
   687  }
   688  
   689  func (afs *aferoVFS) FilePath(doc *vfs.FileDoc) (string, error) {
   690  	if lockerr := afs.mu.RLock(); lockerr != nil {
   691  		return "", lockerr
   692  	}
   693  	defer afs.mu.RUnlock()
   694  	return afs.Indexer.FilePath(doc)
   695  }
   696  
   697  func (afs *aferoVFS) DirOrFileByID(fileID string) (*vfs.DirDoc, *vfs.FileDoc, error) {
   698  	if lockerr := afs.mu.RLock(); lockerr != nil {
   699  		return nil, nil, lockerr
   700  	}
   701  	defer afs.mu.RUnlock()
   702  	return afs.Indexer.DirOrFileByID(fileID)
   703  }
   704  
   705  func (afs *aferoVFS) DirOrFileByPath(name string) (*vfs.DirDoc, *vfs.FileDoc, error) {
   706  	if lockerr := afs.mu.RLock(); lockerr != nil {
   707  		return nil, nil, lockerr
   708  	}
   709  	defer afs.mu.RUnlock()
   710  	return afs.Indexer.DirOrFileByPath(name)
   711  }
   712  
   713  // aferoFileOpen represents a file handle opened for reading.
   714  type aferoFileOpen struct {
   715  	f afero.File
   716  }
   717  
   718  func (f *aferoFileOpen) Read(p []byte) (int, error) {
   719  	return f.f.Read(p)
   720  }
   721  
   722  func (f *aferoFileOpen) ReadAt(p []byte, off int64) (int, error) {
   723  	return f.f.ReadAt(p, off)
   724  }
   725  
   726  func (f *aferoFileOpen) Seek(offset int64, whence int) (int64, error) {
   727  	return f.f.Seek(offset, whence)
   728  }
   729  
   730  func (f *aferoFileOpen) Write(p []byte) (int, error) {
   731  	return 0, os.ErrInvalid
   732  }
   733  
   734  func (f *aferoFileOpen) Close() error {
   735  	return f.f.Close()
   736  }
   737  
   738  // aferoFileCreation represents a file open for writing. It is used to create a
   739  // file or to modify the content of a file.
   740  //
   741  // aferoFileCreation implements io.WriteCloser.
   742  type aferoFileCreation struct {
   743  	afs     *aferoVFS          // parent vfs
   744  	f       afero.File         // file handle
   745  	newdoc  *vfs.FileDoc       // new document
   746  	olddoc  *vfs.FileDoc       // old document
   747  	tmppath string             // temporary file path for uploading a new version of this file
   748  	w       int64              // total size written
   749  	size    int64              // total file size, -1 if unknown
   750  	maxsize int64              // maximum size allowed for the file
   751  	capsize int64              // size cap from which we send a notification to the user
   752  	hash    hash.Hash          // hash we build up along the file
   753  	meta    *vfs.MetaExtractor // extracts metadata from the content
   754  	err     error              // write error
   755  }
   756  
   757  func (f *aferoFileCreation) Read(p []byte) (int, error) {
   758  	return 0, os.ErrInvalid
   759  }
   760  
   761  func (f *aferoFileCreation) ReadAt(p []byte, off int64) (int, error) {
   762  	return 0, os.ErrInvalid
   763  }
   764  
   765  func (f *aferoFileCreation) Seek(offset int64, whence int) (int64, error) {
   766  	return 0, os.ErrInvalid
   767  }
   768  
   769  func (f *aferoFileCreation) Write(p []byte) (int, error) {
   770  	if f.meta != nil {
   771  		if _, err := (*f.meta).Write(p); err != nil && !errors.Is(err, io.ErrClosedPipe) {
   772  			(*f.meta).Abort(err)
   773  			f.meta = nil
   774  		}
   775  	}
   776  
   777  	n, err := f.f.Write(p)
   778  	if err != nil {
   779  		f.err = err
   780  		return n, err
   781  	}
   782  
   783  	f.w += int64(n)
   784  	if f.maxsize >= 0 && f.w > f.maxsize {
   785  		f.err = vfs.ErrFileTooBig
   786  		return n, f.err
   787  	}
   788  
   789  	if f.size >= 0 && f.w > f.size {
   790  		f.err = vfs.ErrContentLengthMismatch
   791  		return n, f.err
   792  	}
   793  
   794  	_, err = f.hash.Write(p)
   795  	return n, err
   796  }
   797  
   798  func (f *aferoFileCreation) Close() (err error) {
   799  	defer func() {
   800  		if err != nil {
   801  			// Remove the temporary file if an error occurred
   802  			_ = f.afs.fs.Remove(f.tmppath)
   803  			// If an error has occurred when creating a new file, we should
   804  			// also delete the file from the index.
   805  			if f.olddoc == nil {
   806  				_ = f.afs.Indexer.DeleteFileDoc(f.newdoc)
   807  			}
   808  		}
   809  	}()
   810  
   811  	if err = f.f.Close(); err != nil {
   812  		if f.meta != nil {
   813  			(*f.meta).Abort(err)
   814  		}
   815  		if f.err == nil {
   816  			f.err = err
   817  		}
   818  	}
   819  
   820  	newdoc, olddoc, written := f.newdoc, f.olddoc, f.w
   821  
   822  	if f.meta != nil {
   823  		if errc := (*f.meta).Close(); errc == nil {
   824  			vfs.MergeMetadata(newdoc, (*f.meta).Result())
   825  		}
   826  	}
   827  
   828  	if f.err != nil {
   829  		return f.err
   830  	}
   831  
   832  	md5sum := f.hash.Sum(nil)
   833  	if newdoc.MD5Sum == nil {
   834  		newdoc.MD5Sum = md5sum
   835  	}
   836  
   837  	if !bytes.Equal(newdoc.MD5Sum, md5sum) {
   838  		return vfs.ErrInvalidHash
   839  	}
   840  
   841  	if f.size < 0 {
   842  		newdoc.ByteSize = written
   843  	}
   844  
   845  	if newdoc.ByteSize != written {
   846  		return vfs.ErrContentLengthMismatch
   847  	}
   848  
   849  	lockerr := f.afs.mu.Lock()
   850  	if lockerr != nil {
   851  		return lockerr
   852  	}
   853  	defer f.afs.mu.Unlock()
   854  
   855  	// Check again that a file with the same path does not exist. It can happen
   856  	// when the same file is uploaded twice in parallel.
   857  	if olddoc == nil {
   858  		exists, err := f.afs.Indexer.DirChildExists(newdoc.DirID, newdoc.DocName)
   859  		if err != nil {
   860  			return err
   861  		}
   862  		if exists {
   863  			return os.ErrExist
   864  		}
   865  	}
   866  
   867  	var newpath string
   868  	newpath, err = f.afs.Indexer.FilePath(newdoc)
   869  	if err != nil {
   870  		return err
   871  	}
   872  	if strings.HasPrefix(newpath, vfs.TrashDirName+"/") {
   873  		return vfs.ErrParentInTrash
   874  	}
   875  
   876  	var v *vfs.Version
   877  	if olddoc != nil {
   878  		v = vfs.NewVersion(olddoc)
   879  		err = f.afs.Indexer.UpdateFileDoc(olddoc, newdoc)
   880  	} else if newdoc.ID() == "" {
   881  		err = f.afs.Indexer.CreateFileDoc(newdoc)
   882  	} else {
   883  		err = f.afs.Indexer.CreateNamedFileDoc(newdoc)
   884  	}
   885  	if err != nil {
   886  		return err
   887  	}
   888  
   889  	if v != nil {
   890  		vPath := pathForVersion(v)
   891  		_ = f.afs.fs.MkdirAll(filepath.Dir(vPath), 0755)
   892  		if err = f.afs.fs.Rename(newpath, vPath); err != nil {
   893  			// If we can't move the content, we just don't create the version,
   894  			// but still let the upload for the new content finishes.
   895  			v = nil
   896  		}
   897  	}
   898  
   899  	// move the temporary file to its final location
   900  	if err = f.afs.fs.Rename(f.tmppath, newpath); err != nil {
   901  		if v != nil {
   902  			vPath := pathForVersion(v)
   903  			_ = f.afs.fs.Rename(vPath, newpath)
   904  		}
   905  		return err
   906  	}
   907  
   908  	if v != nil {
   909  		actionV, toClean, _ := vfs.FindVersionsToClean(f.afs, newdoc.DocID, v)
   910  		if bytes.Equal(newdoc.MD5Sum, olddoc.MD5Sum) {
   911  			actionV = vfs.CleanCandidateVersion
   912  		}
   913  		if actionV == vfs.KeepCandidateVersion {
   914  			if errv := f.afs.Indexer.CreateVersion(v); errv != nil {
   915  				actionV = vfs.CleanCandidateVersion
   916  			}
   917  		}
   918  		if actionV == vfs.CleanCandidateVersion {
   919  			vPath := pathForVersion(v)
   920  			_ = f.afs.fs.Remove(vPath)
   921  		}
   922  		for _, old := range toClean {
   923  			_ = cleanOldVersion(f.afs, old)
   924  		}
   925  	}
   926  
   927  	if f.capsize > 0 && f.size >= f.capsize {
   928  		vfs.PushDiskQuotaAlert(f.afs, true)
   929  	}
   930  
   931  	return nil
   932  }
   933  
   934  func safeRenameFile(fs afero.Fs, oldpath, newpath string) error {
   935  	newpath = path.Clean(newpath)
   936  	oldpath = path.Clean(oldpath)
   937  
   938  	if !path.IsAbs(newpath) || !path.IsAbs(oldpath) {
   939  		return vfs.ErrNonAbsolutePath
   940  	}
   941  
   942  	_, err := fs.Stat(newpath)
   943  	if err == nil {
   944  		return os.ErrExist
   945  	}
   946  	if !os.IsNotExist(err) {
   947  		return err
   948  	}
   949  
   950  	return fs.Rename(oldpath, newpath)
   951  }
   952  
   953  func safeRenameDir(afs *aferoVFS, oldpath, newpath string) error {
   954  	newpath = path.Clean(newpath)
   955  	oldpath = path.Clean(oldpath)
   956  
   957  	if !path.IsAbs(newpath) || !path.IsAbs(oldpath) {
   958  		return vfs.ErrNonAbsolutePath
   959  	}
   960  
   961  	if strings.HasPrefix(newpath, oldpath+"/") {
   962  		return vfs.ErrForbiddenDocMove
   963  	}
   964  
   965  	_, err := afs.fs.Stat(newpath)
   966  	if err == nil {
   967  		return os.ErrExist
   968  	}
   969  	if !os.IsNotExist(err) {
   970  		return err
   971  	}
   972  
   973  	return afs.fs.Rename(oldpath, newpath)
   974  }
   975  
   976  func extractContentTypeAndMD5(filename string) (contentType string, md5sum []byte, err error) {
   977  	f, err := os.Open(filename)
   978  	if err != nil {
   979  		return
   980  	}
   981  	defer f.Close()
   982  	var r io.Reader
   983  	contentType, r = filetype.FromReader(f)
   984  	h := md5.New()
   985  	if _, err = io.Copy(h, r); err != nil {
   986  		return
   987  	}
   988  	md5sum = h.Sum(nil)
   989  	return
   990  }
   991  
   992  func (afs *aferoVFS) CleanOldVersion(fileID string, version *vfs.Version) error {
   993  	if lockerr := afs.mu.Lock(); lockerr != nil {
   994  		return lockerr
   995  	}
   996  	defer afs.mu.Unlock()
   997  	return cleanOldVersion(afs, version)
   998  }
   999  
  1000  func cleanOldVersion(afs *aferoVFS, version *vfs.Version) error {
  1001  	if err := afs.Indexer.DeleteVersion(version); err != nil {
  1002  		return err
  1003  	}
  1004  	vPath := pathForVersion(version)
  1005  	return afs.fs.Remove(vPath)
  1006  }
  1007  
  1008  func pathForVersion(v *vfs.Version) string {
  1009  	parts := strings.SplitN(v.DocID, "/", 2)
  1010  	fileID := parts[0]
  1011  	versionID := parts[0]
  1012  	if len(parts) > 1 {
  1013  		versionID = parts[1]
  1014  	}
  1015  	return path.Join(pathForVersions(fileID), versionID)
  1016  }
  1017  
  1018  func pathForVersions(fileID string) string {
  1019  	// Avoid too many files in the same directory by using some sub-directories
  1020  	return path.Join(vfs.VersionsDirName, fileID[:4], fileID[4:])
  1021  }
  1022  
  1023  func (afs *aferoVFS) ClearOldVersions() error {
  1024  	if lockerr := afs.mu.Lock(); lockerr != nil {
  1025  		return lockerr
  1026  	}
  1027  	defer afs.mu.Unlock()
  1028  	versions, err := afs.Indexer.AllVersions()
  1029  	if err != nil {
  1030  		return err
  1031  	}
  1032  	if err := afs.Indexer.BatchDeleteVersions(versions); err != nil {
  1033  		return err
  1034  	}
  1035  	return afs.fs.RemoveAll(vfs.VersionsDirName)
  1036  }
  1037  
  1038  var (
  1039  	_ vfs.VFS  = &aferoVFS{}
  1040  	_ vfs.File = &aferoFileOpen{}
  1041  	_ vfs.File = &aferoFileCreation{}
  1042  )