gitlab.com/SkynetLabs/skyd@v1.6.9/skymodules/renter/filesystem/filesystem.go (about)

     1  package filesystem
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"io/ioutil"
     7  	"math"
     8  	"os"
     9  	"path/filepath"
    10  	"sort"
    11  	"sync"
    12  
    13  	"gitlab.com/NebulousLabs/errors"
    14  	"gitlab.com/NebulousLabs/fastrand"
    15  	"gitlab.com/NebulousLabs/writeaheadlog"
    16  	"gitlab.com/SkynetLabs/skyd/build"
    17  	"gitlab.com/SkynetLabs/skyd/persist"
    18  	"gitlab.com/SkynetLabs/skyd/skymodules"
    19  	"gitlab.com/SkynetLabs/skyd/skymodules/renter/filesystem/siadir"
    20  	"gitlab.com/SkynetLabs/skyd/skymodules/renter/filesystem/siafile"
    21  	"go.sia.tech/siad/crypto"
    22  )
    23  
    24  var (
    25  	// ErrNotExist is returned when a file or folder can't be found on disk.
    26  	ErrNotExist = errors.New("path does not exist")
    27  
    28  	// ErrExists is returned when a file or folder already exists at a given
    29  	// location.
    30  	ErrExists = errors.New("a file or folder already exists at the specified path")
    31  
    32  	// ErrDeleteFileIsDir is returned when the file delete method is used but
    33  	// the filename corresponds to a directory
    34  	ErrDeleteFileIsDir = errors.New("cannot delete file, file is a directory")
    35  )
    36  
    37  type (
    38  	// FileSystem implements a thread-safe filesystem for Sia for loading
    39  	// SiaFiles, SiaDirs and potentially other supported Sia types in the
    40  	// future.
    41  	FileSystem struct {
    42  		DirNode
    43  	}
    44  
    45  	// node is a struct that contains the common fields of every node.
    46  	node struct {
    47  		// fields that all copies of a node share.
    48  		path      *string
    49  		parent    *DirNode
    50  		name      *string
    51  		staticWal *writeaheadlog.WAL
    52  		threads   map[threadUID]struct{} // tracks all the threadUIDs of evey copy of the node
    53  		staticLog *persist.Logger
    54  		staticUID uint64
    55  		mu        *sync.Mutex
    56  
    57  		// fields that differ between copies of the same node.
    58  		threadUID threadUID // unique ID of a copy of a node
    59  	}
    60  	threadUID uint64
    61  )
    62  
    63  // newNode is a convenience function to initialize a node.
    64  func newNode(parent *DirNode, path, name string, uid threadUID, wal *writeaheadlog.WAL, log *persist.Logger) node {
    65  	return node{
    66  		path:      &path,
    67  		parent:    parent,
    68  		name:      &name,
    69  		staticLog: log,
    70  		staticUID: newInode(),
    71  		staticWal: wal,
    72  		threads:   make(map[threadUID]struct{}),
    73  		threadUID: uid,
    74  		mu:        new(sync.Mutex),
    75  	}
    76  }
    77  
    78  // managedLockWithParent is a helper method which correctly acquires the lock of
    79  // a node and it's parent. If no parent it available it will return 'nil'. In
    80  // either case the node and potential parent will be locked after the call.
    81  func (n *node) managedLockWithParent() *DirNode {
    82  	var parent *DirNode
    83  	for {
    84  		// If a parent exists, we need to lock it while closing a child.
    85  		n.mu.Lock()
    86  		parent = n.parent
    87  		n.mu.Unlock()
    88  		if parent != nil {
    89  			parent.mu.Lock()
    90  		}
    91  		n.mu.Lock()
    92  		if n.parent != parent {
    93  			n.mu.Unlock()
    94  			parent.mu.Unlock()
    95  			continue // try again
    96  		}
    97  		break
    98  	}
    99  	return parent
   100  }
   101  
   102  // NID returns the node's unique identifier.
   103  func (n *node) Inode() uint64 {
   104  	return n.staticUID
   105  }
   106  
   107  // newThreadUID returns a random threadUID to be used as the threadUID in the
   108  // threads map of the node.
   109  func newThreadUID() threadUID {
   110  	return threadUID(fastrand.Uint64n(math.MaxUint64))
   111  }
   112  
   113  // newInode will create a unique identifier for a filesystem node.
   114  //
   115  // TODO: replace this with a function that doesn't repeat itself.
   116  func newInode() uint64 {
   117  	return fastrand.Uint64n(math.MaxUint64)
   118  }
   119  
   120  // nodeSiaPath returns the SiaPath of a node relative to a root path.
   121  func nodeSiaPath(rootPath string, n *node) (sp skymodules.SiaPath) {
   122  	if err := sp.FromSysPath(n.managedAbsPath(), rootPath); err != nil {
   123  		build.Critical("FileSystem.managedSiaPath: should never fail", err)
   124  	}
   125  	return sp
   126  }
   127  
   128  // closeNode removes a thread from the node's threads map. This should only be
   129  // called from within other 'close' methods.
   130  func (n *node) closeNode() {
   131  	if _, exists := n.threads[n.threadUID]; !exists {
   132  		build.Critical("threaduid doesn't exist in threads map: ", n.threadUID, len(n.threads))
   133  	}
   134  	delete(n.threads, n.threadUID)
   135  }
   136  
   137  // absPath returns the absolute path of the node.
   138  func (n *node) absPath() string {
   139  	return *n.path
   140  }
   141  
   142  // managedAbsPath returns the absolute path of the node.
   143  func (n *node) managedAbsPath() string {
   144  	n.mu.Lock()
   145  	defer n.mu.Unlock()
   146  	return n.absPath()
   147  }
   148  
   149  // New creates a new FileSystem at the specified root path. The folder will be
   150  // created if it doesn't exist already.
   151  func New(root string, log *persist.Logger, wal *writeaheadlog.WAL) (*FileSystem, error) {
   152  	fs := &FileSystem{
   153  		DirNode: DirNode{
   154  			// The root doesn't require a parent, a name or uid.
   155  			node:        newNode(nil, root, "", 0, wal, log),
   156  			directories: make(map[string]*DirNode),
   157  			files:       make(map[string]*FileNode),
   158  			lazySiaDir:  new(*siadir.SiaDir),
   159  		},
   160  	}
   161  	// Prepare root folder.
   162  	err := fs.NewSiaDir(skymodules.RootSiaPath(), skymodules.DefaultDirPerm)
   163  	if err != nil && !errors.Contains(err, ErrExists) {
   164  		return nil, err
   165  	}
   166  	return fs, nil
   167  }
   168  
   169  // AddSiaFileFromReader adds an existing SiaFile to the set and stores it on
   170  // disk. If the exact same file already exists, this is a no-op. If a file
   171  // already exists with a different UID, the UID will be updated and a unique
   172  // path will be chosen. If no file exists, the UID will be updated but the path
   173  // remains the same.
   174  func (fs *FileSystem) AddSiaFileFromReader(rs io.ReadSeeker, siaPath skymodules.SiaPath) (err error) {
   175  	// Load the file.
   176  	path := fs.FilePath(siaPath)
   177  	sf, chunks, err := siafile.LoadSiaFileFromReaderWithChunks(rs, path, fs.staticWal)
   178  	if err != nil {
   179  		return err
   180  	}
   181  	// Create dir with same Mode as file if it doesn't exist already and open
   182  	// it.
   183  	dirSiaPath, err := siaPath.Dir()
   184  	if err != nil {
   185  		return err
   186  	}
   187  	if err := fs.managedNewSiaDir(dirSiaPath, sf.Mode()); err != nil {
   188  		return err
   189  	}
   190  	dir, err := fs.managedOpenDir(dirSiaPath.String())
   191  	if err != nil {
   192  		return err
   193  	}
   194  	defer func() {
   195  		err = errors.Compose(err, dir.Close())
   196  	}()
   197  	// Add the file to the dir.
   198  	return dir.managedNewSiaFileFromExisting(sf, chunks)
   199  }
   200  
   201  // CachedFileInfo returns the cached File Information of the siafile
   202  func (fs *FileSystem) CachedFileInfo(siaPath skymodules.SiaPath) (skymodules.FileInfo, error) {
   203  	return fs.managedFileInfo(siaPath, true, nil, nil, nil)
   204  }
   205  
   206  // CachedList lists the files and directories within a SiaDir.
   207  func (fs *FileSystem) CachedList(siaPath skymodules.SiaPath, recursive bool, flf skymodules.FileListFunc, dlf skymodules.DirListFunc) error {
   208  	return fs.managedList(siaPath, recursive, true, nil, nil, nil, flf, dlf)
   209  }
   210  
   211  // CachedListOnNode will return the files and directories within a given siadir
   212  // node in a non-recursive way.
   213  func (fs *FileSystem) CachedListOnNode(d *DirNode) (fis []skymodules.FileInfo, dis []skymodules.DirectoryInfo, err error) {
   214  	var fmu, dmu sync.Mutex
   215  	flf := func(fi skymodules.FileInfo) {
   216  		fmu.Lock()
   217  		fis = append(fis, fi)
   218  		fmu.Unlock()
   219  	}
   220  	dlf := func(di skymodules.DirectoryInfo) {
   221  		dmu.Lock()
   222  		dis = append(dis, di)
   223  		dmu.Unlock()
   224  	}
   225  	err = d.managedList(fs.managedAbsPath(), false, true, nil, nil, nil, flf, dlf)
   226  
   227  	// Sort slices by SiaPath.
   228  	sort.Slice(dis, func(i, j int) bool {
   229  		return dis[i].SiaPath.String() < dis[j].SiaPath.String()
   230  	})
   231  	sort.Slice(fis, func(i, j int) bool {
   232  		return fis[i].SiaPath.String() < fis[j].SiaPath.String()
   233  	})
   234  	return
   235  }
   236  
   237  // DeleteDir deletes a dir from the filesystem. The dir will be marked as
   238  // 'deleted' which should cause all remaining instances of the dir to be close
   239  // shortly. Only when all instances of the dir are closed it will be removed
   240  // from the tree. This means that as long as the deletion is in progress, no new
   241  // file of the same path can be created and the existing file can't be opened
   242  // until all instances of it are closed.
   243  func (fs *FileSystem) DeleteDir(siaPath skymodules.SiaPath) error {
   244  	return fs.managedDeleteDir(siaPath.String())
   245  }
   246  
   247  // DeleteFile deletes a file from the filesystem. The file will be marked as
   248  // 'deleted' which should cause all remaining instances of the file to be closed
   249  // shortly. Only when all instances of the file are closed it will be removed
   250  // from the tree. This means that as long as the deletion is in progress, no new
   251  // file of the same path can be created and the existing file can't be opened
   252  // until all instances of it are closed.
   253  func (fs *FileSystem) DeleteFile(siaPath skymodules.SiaPath) error {
   254  	return fs.managedDeleteFile(siaPath.String())
   255  }
   256  
   257  // DirInfo returns the Directory Information of the siadir
   258  func (fs *FileSystem) DirInfo(siaPath skymodules.SiaPath) (_ skymodules.DirectoryInfo, err error) {
   259  	dir, err := fs.managedOpenDir(siaPath.String())
   260  	if err != nil {
   261  		return skymodules.DirectoryInfo{}, nil
   262  	}
   263  	defer func() {
   264  		err = errors.Compose(err, dir.Close())
   265  	}()
   266  	di, err := dir.managedInfo(siaPath)
   267  	if err != nil {
   268  		return skymodules.DirectoryInfo{}, err
   269  	}
   270  	di.SiaPath = siaPath
   271  	return di, nil
   272  }
   273  
   274  // DirNodeInfo will return the DirectoryInfo of a siadir given the node. This is
   275  // more efficient than calling fs.DirInfo.
   276  func (fs *FileSystem) DirNodeInfo(n *DirNode) (skymodules.DirectoryInfo, error) {
   277  	sp := fs.DirSiaPath(n)
   278  	return n.managedInfo(sp)
   279  }
   280  
   281  // FileInfo returns the File Information of the siafile
   282  func (fs *FileSystem) FileInfo(siaPath skymodules.SiaPath, offline map[string]bool, goodForRenew map[string]bool, contracts map[string]skymodules.RenterContract) (skymodules.FileInfo, error) {
   283  	return fs.managedFileInfo(siaPath, false, offline, goodForRenew, contracts)
   284  }
   285  
   286  // FileNodeInfo returns the FileInfo of a siafile given the node for the
   287  // siafile. This is faster than calling fs.FileInfo.
   288  func (fs *FileSystem) FileNodeInfo(n *FileNode) (skymodules.FileInfo, error) {
   289  	sp := fs.FileSiaPath(n)
   290  	return n.staticCachedInfo(sp)
   291  }
   292  
   293  // List lists the files and directories within a SiaDir.
   294  func (fs *FileSystem) List(siaPath skymodules.SiaPath, recursive bool, offlineMap, goodForRenewMap map[string]bool, contractsMap map[string]skymodules.RenterContract, flf skymodules.FileListFunc, dlf skymodules.DirListFunc) error {
   295  	return fs.managedList(siaPath, recursive, false, offlineMap, goodForRenewMap, contractsMap, flf, dlf)
   296  }
   297  
   298  // FileExists checks to see if a file with the provided siaPath already exists
   299  // in the renter.
   300  func (fs *FileSystem) FileExists(siaPath skymodules.SiaPath) (bool, error) {
   301  	path := fs.FilePath(siaPath)
   302  	_, err := os.Stat(path)
   303  	if os.IsNotExist(err) {
   304  		return false, nil
   305  	}
   306  	return true, err
   307  }
   308  
   309  // FilePath converts a SiaPath into a file's system path.
   310  func (fs *FileSystem) FilePath(siaPath skymodules.SiaPath) string {
   311  	return siaPath.SiaFileSysPath(fs.managedAbsPath())
   312  }
   313  
   314  // NewSiaDir creates the folder for the specified siaPath.
   315  func (fs *FileSystem) NewSiaDir(siaPath skymodules.SiaPath, mode os.FileMode) error {
   316  	return fs.managedNewSiaDir(siaPath, mode)
   317  }
   318  
   319  // NewSiaFile creates a SiaFile at the specified siaPath.
   320  func (fs *FileSystem) NewSiaFile(siaPath skymodules.SiaPath, source string, ec skymodules.ErasureCoder, mk crypto.CipherKey, fileSize uint64, fileMode os.FileMode) error {
   321  	// Create SiaDir for file.
   322  	dirSiaPath, err := siaPath.Dir()
   323  	if err != nil {
   324  		return err
   325  	}
   326  	if err = fs.NewSiaDir(dirSiaPath, fileMode); err != nil {
   327  		return errors.AddContext(err, fmt.Sprintf("failed to create SiaDir %v for SiaFile %v", dirSiaPath.String(), siaPath.String()))
   328  	}
   329  	return fs.managedNewSiaFile(siaPath.String(), source, ec, mk, fileSize, fileMode)
   330  }
   331  
   332  // ReadDir reads all the fileinfos of the specified dir.
   333  func (fs *FileSystem) ReadDir(siaPath skymodules.SiaPath) ([]os.FileInfo, error) {
   334  	// Open dir.
   335  	dirPath := siaPath.SiaDirSysPath(fs.managedAbsPath())
   336  	f, err := os.Open(dirPath)
   337  	if err != nil {
   338  		return nil, err
   339  	}
   340  	// Read it and close it.
   341  	fis, err1 := f.Readdir(-1)
   342  	err2 := f.Close()
   343  	err = errors.Compose(err1, err2)
   344  	return fis, err
   345  }
   346  
   347  // DirExists checks to see if a dir with the provided siaPath already exists in
   348  // the renter.
   349  func (fs *FileSystem) DirExists(siaPath skymodules.SiaPath) (bool, error) {
   350  	path := fs.DirPath(siaPath)
   351  	_, err := os.Stat(path)
   352  	if os.IsNotExist(err) {
   353  		return false, nil
   354  	}
   355  	return true, err
   356  }
   357  
   358  // DirPath converts a SiaPath into a dir's system path.
   359  func (fs *FileSystem) DirPath(siaPath skymodules.SiaPath) string {
   360  	return siaPath.SiaDirSysPath(fs.managedAbsPath())
   361  }
   362  
   363  // Root returns the root system path of the FileSystem.
   364  func (fs *FileSystem) Root() string {
   365  	return fs.DirPath(skymodules.RootSiaPath())
   366  }
   367  
   368  // FileSiaPath returns the SiaPath of a file node.
   369  func (fs *FileSystem) FileSiaPath(n *FileNode) (sp skymodules.SiaPath) {
   370  	return fs.managedSiaPath(&n.node)
   371  }
   372  
   373  // DirSiaPath returns the SiaPath of a dir node.
   374  func (fs *FileSystem) DirSiaPath(n *DirNode) (sp skymodules.SiaPath) {
   375  	return fs.managedSiaPath(&n.node)
   376  }
   377  
   378  // UpdateDirMetadata updates the metadata of a SiaDir.
   379  func (fs *FileSystem) UpdateDirMetadata(siaPath skymodules.SiaPath, metadata siadir.Metadata) (err error) {
   380  	dir, err := fs.OpenSiaDir(siaPath)
   381  	if err != nil {
   382  		return err
   383  	}
   384  	defer func() {
   385  		err = errors.Compose(err, dir.Close())
   386  	}()
   387  	return dir.UpdateMetadata(metadata)
   388  }
   389  
   390  // managedSiaPath returns the SiaPath of a node.
   391  func (fs *FileSystem) managedSiaPath(n *node) skymodules.SiaPath {
   392  	return nodeSiaPath(fs.managedAbsPath(), n)
   393  }
   394  
   395  // Stat is a wrapper for os.Stat which takes a SiaPath as an argument instead of
   396  // a system path.
   397  func (fs *FileSystem) Stat(siaPath skymodules.SiaPath) (os.FileInfo, error) {
   398  	path := siaPath.SiaDirSysPath(fs.managedAbsPath())
   399  	return os.Stat(path)
   400  }
   401  
   402  // Walk is a wrapper for filepath.Walk which takes a SiaPath as an argument
   403  // instead of a system path.
   404  func (fs *FileSystem) Walk(siaPath skymodules.SiaPath, walkFn filepath.WalkFunc) error {
   405  	dirPath := siaPath.SiaDirSysPath(fs.managedAbsPath())
   406  	return filepath.Walk(dirPath, walkFn)
   407  }
   408  
   409  // WriteFile is a wrapper for ioutil.WriteFile which takes a SiaPath as an
   410  // argument instead of a system path.
   411  func (fs *FileSystem) WriteFile(siaPath skymodules.SiaPath, data []byte, perm os.FileMode) error {
   412  	path := siaPath.SiaFileSysPath(fs.managedAbsPath())
   413  	return ioutil.WriteFile(path, data, perm)
   414  }
   415  
   416  // NewSiaFileFromLegacyData creates a new SiaFile from data that was previously loaded
   417  // from a legacy file.
   418  func (fs *FileSystem) NewSiaFileFromLegacyData(fd siafile.FileData) (_ *FileNode, err error) {
   419  	// Get file's SiaPath.
   420  	sp, err := skymodules.UserFolder.Join(fd.Name)
   421  	if err != nil {
   422  		return nil, err
   423  	}
   424  	// Get siapath of dir.
   425  	dirSiaPath, err := sp.Dir()
   426  	if err != nil {
   427  		return nil, err
   428  	}
   429  	// Create the dir if it doesn't exist.
   430  	if err := fs.NewSiaDir(dirSiaPath, 0755); err != nil {
   431  		return nil, err
   432  	}
   433  	// Open dir.
   434  	dir, err := fs.managedOpenDir(dirSiaPath.String())
   435  	if err != nil {
   436  		return nil, err
   437  	}
   438  	defer func() {
   439  		err = errors.Compose(err, dir.Close())
   440  	}()
   441  	// Add the file to the dir.
   442  	return dir.managedNewSiaFileFromLegacyData(sp.Name(), fd)
   443  }
   444  
   445  // OpenSiaDir opens a SiaDir and adds it and all of its parents to the
   446  // filesystem tree.
   447  func (fs *FileSystem) OpenSiaDir(siaPath skymodules.SiaPath) (*DirNode, error) {
   448  	return fs.OpenSiaDirCustom(siaPath, false)
   449  }
   450  
   451  // OpenSiaDirCustom opens a SiaDir and adds it and all of its parents to the
   452  // filesystem tree. If create is true it will create the dir if it doesn't
   453  // exist.
   454  func (fs *FileSystem) OpenSiaDirCustom(siaPath skymodules.SiaPath, create bool) (*DirNode, error) {
   455  	dn, err := fs.managedOpenSiaDir(siaPath)
   456  	if create && errors.Contains(err, ErrNotExist) {
   457  		// If siadir doesn't exist create one
   458  		err = fs.NewSiaDir(siaPath, skymodules.DefaultDirPerm)
   459  		if err != nil && !errors.Contains(err, ErrExists) {
   460  			return nil, err
   461  		}
   462  		return fs.managedOpenSiaDir(siaPath)
   463  	}
   464  	return dn, err
   465  }
   466  
   467  // OpenSiaFile opens a SiaFile and adds it and all of its parents to the
   468  // filesystem tree.
   469  func (fs *FileSystem) OpenSiaFile(siaPath skymodules.SiaPath) (*FileNode, error) {
   470  	sf, err := fs.managedOpenFile(siaPath.String())
   471  	if err != nil {
   472  		return nil, err
   473  	}
   474  	return sf, nil
   475  }
   476  
   477  // RenameFile renames the file with oldSiaPath to newSiaPath.
   478  func (fs *FileSystem) RenameFile(oldSiaPath, newSiaPath skymodules.SiaPath) (err error) {
   479  	// Open SiaDir for file at old location.
   480  	oldDirSiaPath, err := oldSiaPath.Dir()
   481  	if err != nil {
   482  		return err
   483  	}
   484  	oldDir, err := fs.managedOpenSiaDir(oldDirSiaPath)
   485  	if err != nil {
   486  		return err
   487  	}
   488  	defer func() {
   489  		err = errors.Compose(err, oldDir.Close())
   490  	}()
   491  	// Open the file.
   492  	sf, err := oldDir.managedOpenFile(oldSiaPath.Name())
   493  	if errors.Contains(err, ErrNotExist) {
   494  		return ErrNotExist
   495  	}
   496  	if err != nil {
   497  		return errors.AddContext(err, "failed to open file for renaming")
   498  	}
   499  	defer func() {
   500  		err = errors.Compose(err, sf.Close())
   501  	}()
   502  
   503  	// Create and Open SiaDir for file at new location.
   504  	newDirSiaPath, err := newSiaPath.Dir()
   505  	if err != nil {
   506  		return err
   507  	}
   508  	if err := fs.NewSiaDir(newDirSiaPath, sf.managedMode()); err != nil {
   509  		return errors.AddContext(err, fmt.Sprintf("failed to create SiaDir %v for SiaFile %v", newDirSiaPath.String(), oldSiaPath.String()))
   510  	}
   511  	newDir, err := fs.managedOpenSiaDir(newDirSiaPath)
   512  	if err != nil {
   513  		return err
   514  	}
   515  	defer func() {
   516  		err = errors.Compose(err, newDir.Close())
   517  	}()
   518  	// Rename the file.
   519  	return sf.managedRename(newSiaPath.Name(), oldDir, newDir)
   520  }
   521  
   522  // RenameDir takes an existing directory and changes the path. The original
   523  // directory must exist, and there must not be any directory that already has
   524  // the replacement path.  All sia files within directory will also be renamed
   525  func (fs *FileSystem) RenameDir(oldSiaPath, newSiaPath skymodules.SiaPath) error {
   526  	// Open SiaDir for parent dir at old location.
   527  	oldDirSiaPath, err := oldSiaPath.Dir()
   528  	if err != nil {
   529  		return err
   530  	}
   531  	oldDir, err := fs.managedOpenSiaDir(oldDirSiaPath)
   532  	if err != nil {
   533  		return err
   534  	}
   535  	defer func() {
   536  		oldDir.Close()
   537  	}()
   538  	// Open the dir to rename.
   539  	sd, err := oldDir.managedOpenDir(oldSiaPath.Name())
   540  	if errors.Contains(err, ErrNotExist) {
   541  		return ErrNotExist
   542  	}
   543  	if err != nil {
   544  		return errors.AddContext(err, "failed to open file for renaming")
   545  	}
   546  	defer func() {
   547  		sd.Close()
   548  	}()
   549  
   550  	// Create and Open parent SiaDir for dir at new location.
   551  	newDirSiaPath, err := newSiaPath.Dir()
   552  	if err != nil {
   553  		return err
   554  	}
   555  	md, err := sd.Metadata()
   556  	if err != nil {
   557  		return err
   558  	}
   559  	if err := fs.NewSiaDir(newDirSiaPath, md.Mode); err != nil {
   560  		return errors.AddContext(err, fmt.Sprintf("failed to create SiaDir %v for SiaFile %v", newDirSiaPath.String(), oldSiaPath.String()))
   561  	}
   562  	newDir, err := fs.managedOpenSiaDir(newDirSiaPath)
   563  	if err != nil {
   564  		return err
   565  	}
   566  	defer func() {
   567  		newDir.Close()
   568  	}()
   569  	// Rename the dir.
   570  	err = sd.managedRename(newSiaPath.Name(), oldDir, newDir)
   571  	return err
   572  }
   573  
   574  // managedDeleteFile opens the parent folder of the file to delete and calls
   575  // managedDeleteFile on it.
   576  func (fs *FileSystem) managedDeleteFile(relPath string) (err error) {
   577  	// Open the folder that contains the file.
   578  	dirPath, fileName := filepath.Split(relPath)
   579  	var dir *DirNode
   580  	if dirPath == string(filepath.Separator) || dirPath == "." || dirPath == "" {
   581  		dir = &fs.DirNode // file is in the root dir
   582  	} else {
   583  		var err error
   584  		dir, err = fs.managedOpenDir(filepath.Dir(relPath))
   585  		if err != nil {
   586  			return errors.AddContext(err, "failed to open parent dir of file")
   587  		}
   588  		// Close the dir since we are not returning it. The open file keeps it
   589  		// loaded in memory.
   590  		defer func() {
   591  			err = errors.Compose(err, dir.Close())
   592  		}()
   593  	}
   594  	return dir.managedDeleteFile(fileName)
   595  }
   596  
   597  // managedDeleteDir opens the parent folder of the dir to delete and calls
   598  // managedDelete on it.
   599  func (fs *FileSystem) managedDeleteDir(path string) (err error) {
   600  	// Open the dir.
   601  	dir, err := fs.managedOpenDir(path)
   602  	if err != nil {
   603  		return errors.AddContext(err, "failed to open parent dir of file")
   604  	}
   605  	// Close the dir since we are not returning it. The open file keeps it
   606  	// loaded in memory.
   607  	defer func() {
   608  		err = errors.Compose(err, dir.Close())
   609  	}()
   610  	return dir.managedDelete()
   611  }
   612  
   613  // managedFileInfo returns the FileInfo of the siafile.
   614  func (fs *FileSystem) managedFileInfo(siaPath skymodules.SiaPath, cached bool, offline map[string]bool, goodForRenew map[string]bool, contracts map[string]skymodules.RenterContract) (_ skymodules.FileInfo, err error) {
   615  	// Open the file.
   616  	file, err := fs.managedOpenFile(siaPath.String())
   617  	if err != nil {
   618  		return skymodules.FileInfo{}, err
   619  	}
   620  	defer func() {
   621  		err = errors.Compose(err, file.Close())
   622  	}()
   623  	if cached {
   624  		return file.staticCachedInfo(siaPath)
   625  	}
   626  	return file.managedFileInfo(siaPath, offline, goodForRenew, contracts)
   627  }
   628  
   629  // managedList returns the files and dirs within the SiaDir specified by siaPath.
   630  // offlineMap, goodForRenewMap and contractMap don't need to be provided if
   631  // 'cached' is set to 'true'.
   632  func (fs *FileSystem) managedList(siaPath skymodules.SiaPath, recursive, cached bool, offlineMap map[string]bool, goodForRenewMap map[string]bool, contractsMap map[string]skymodules.RenterContract, flf skymodules.FileListFunc, dlf skymodules.DirListFunc) (err error) {
   633  	// Open the folder.
   634  	dir, err := fs.managedOpenDir(siaPath.String())
   635  	if err != nil {
   636  		return errors.AddContext(err, fmt.Sprintf("failed to open folder '%v' specified by FileList", siaPath))
   637  	}
   638  	defer func() {
   639  		err = errors.Compose(err, dir.Close())
   640  	}()
   641  	return dir.managedList(fs.managedAbsPath(), recursive, cached, offlineMap, goodForRenewMap, contractsMap, flf, dlf)
   642  }
   643  
   644  // managedNewSiaDir creates the folder at the specified siaPath.
   645  func (fs *FileSystem) managedNewSiaDir(siaPath skymodules.SiaPath, mode os.FileMode) (err error) {
   646  	// If siaPath is the root dir we just create the metadata for it.
   647  	if siaPath.IsRoot() {
   648  		fs.mu.Lock()
   649  		defer fs.mu.Unlock()
   650  		dirPath := siaPath.SiaDirSysPath(fs.absPath())
   651  		_, err := siadir.New(dirPath, fs.absPath(), mode)
   652  		// If the SiaDir already exists on disk, return without an error.
   653  		if errors.Contains(err, os.ErrExist) {
   654  			return nil // nothing to do
   655  		}
   656  		return err
   657  	}
   658  	// If siaPath isn't the root dir we need to grab the parent.
   659  	parentPath, err := siaPath.Dir()
   660  	if err != nil {
   661  		return err
   662  	}
   663  	parent, err := fs.managedOpenDir(parentPath.String())
   664  	if errors.Contains(err, ErrNotExist) {
   665  		// If the parent doesn't exist yet we create it.
   666  		err = fs.managedNewSiaDir(parentPath, mode)
   667  		if err == nil {
   668  			parent, err = fs.managedOpenDir(parentPath.String())
   669  		}
   670  	}
   671  	if err != nil {
   672  		return err
   673  	}
   674  	defer func() {
   675  		err = errors.Compose(err, parent.Close())
   676  	}()
   677  	// Create the dir within the parent.
   678  	return parent.managedNewSiaDir(siaPath.Name(), fs.managedAbsPath(), mode)
   679  }
   680  
   681  // managedOpenFile opens a SiaFile and adds it and all of its parents to the
   682  // filesystem tree.
   683  func (fs *FileSystem) managedOpenFile(relPath string) (_ *FileNode, err error) {
   684  	// Open the folder that contains the file.
   685  	dirPath, fileName := filepath.Split(relPath)
   686  	var dir *DirNode
   687  	if dirPath == string(filepath.Separator) || dirPath == "." || dirPath == "" {
   688  		dir = &fs.DirNode // file is in the root dir
   689  	} else {
   690  		var err error
   691  		dir, err = fs.managedOpenDir(filepath.Dir(relPath))
   692  		if err != nil {
   693  			return nil, errors.AddContext(err, "failed to open parent dir of file")
   694  		}
   695  		// Close the dir since we are not returning it. The open file keeps it
   696  		// loaded in memory.
   697  		defer func() {
   698  			err = errors.Compose(err, dir.Close())
   699  		}()
   700  	}
   701  	return dir.managedOpenFile(fileName)
   702  }
   703  
   704  // managedNewSiaFile opens the parent folder of the new SiaFile and calls
   705  // managedNewSiaFile on it.
   706  func (fs *FileSystem) managedNewSiaFile(relPath string, source string, ec skymodules.ErasureCoder, mk crypto.CipherKey, fileSize uint64, fileMode os.FileMode) (err error) {
   707  	// Open the folder that contains the file.
   708  	dirPath, fileName := filepath.Split(relPath)
   709  	var dir *DirNode
   710  	if dirPath == string(filepath.Separator) || dirPath == "." || dirPath == "" {
   711  		dir = &fs.DirNode // file is in the root dir
   712  	} else {
   713  		var err error
   714  		dir, err = fs.managedOpenDir(filepath.Dir(relPath))
   715  		if err != nil {
   716  			return errors.AddContext(err, "failed to open parent dir of new file")
   717  		}
   718  		defer func() {
   719  			err = errors.Compose(err, dir.Close())
   720  		}()
   721  	}
   722  	return dir.managedNewSiaFile(fileName, source, ec, mk, fileSize, fileMode)
   723  }
   724  
   725  // managedOpenSiaDir opens a SiaDir and adds it and all of its parents to the
   726  // filesystem tree.
   727  func (fs *FileSystem) managedOpenSiaDir(siaPath skymodules.SiaPath) (*DirNode, error) {
   728  	if siaPath.IsRoot() {
   729  		// Make sure the metadata exists.
   730  		_, err := os.Stat(filepath.Join(fs.absPath(), skymodules.SiaDirExtension))
   731  		if os.IsNotExist(err) {
   732  			return nil, ErrNotExist
   733  		}
   734  		return fs.DirNode.managedCopy(), nil
   735  	}
   736  	dir, err := fs.DirNode.managedOpenDir(siaPath.String())
   737  	if err != nil {
   738  		return nil, err
   739  	}
   740  	return dir, nil
   741  }