github.com/cozy/cozy-stack@v0.0.0-20240603063001-31110fa4cae1/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 )