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 }