github.com/10XDev/rclone@v1.52.3-0.20200626220027-16af9ab76b2a/backend/union/union.go (about) 1 package union 2 3 import ( 4 "bufio" 5 "context" 6 "fmt" 7 "io" 8 "path" 9 "path/filepath" 10 "strings" 11 "sync" 12 "time" 13 14 "github.com/pkg/errors" 15 "github.com/rclone/rclone/backend/union/policy" 16 "github.com/rclone/rclone/backend/union/upstream" 17 "github.com/rclone/rclone/fs" 18 "github.com/rclone/rclone/fs/config/configmap" 19 "github.com/rclone/rclone/fs/config/configstruct" 20 "github.com/rclone/rclone/fs/hash" 21 "github.com/rclone/rclone/fs/operations" 22 "github.com/rclone/rclone/fs/walk" 23 ) 24 25 // Register with Fs 26 func init() { 27 fsi := &fs.RegInfo{ 28 Name: "union", 29 Description: "Union merges the contents of several upstream fs", 30 NewFs: NewFs, 31 Options: []fs.Option{{ 32 Name: "upstreams", 33 Help: "List of space separated upstreams.\nCan be 'upstreama:test/dir upstreamb:', '\"upstreama:test/space:ro dir\" upstreamb:', etc.\n", 34 Required: true, 35 }, { 36 Name: "action_policy", 37 Help: "Policy to choose upstream on ACTION category.", 38 Required: true, 39 Default: "epall", 40 }, { 41 Name: "create_policy", 42 Help: "Policy to choose upstream on CREATE category.", 43 Required: true, 44 Default: "epmfs", 45 }, { 46 Name: "search_policy", 47 Help: "Policy to choose upstream on SEARCH category.", 48 Required: true, 49 Default: "ff", 50 }, { 51 Name: "cache_time", 52 Help: "Cache time of usage and free space (in seconds). This option is only useful when a path preserving policy is used.", 53 Required: true, 54 Default: 120, 55 }}, 56 } 57 fs.Register(fsi) 58 } 59 60 // Options defines the configuration for this backend 61 type Options struct { 62 Upstreams fs.SpaceSepList `config:"upstreams"` 63 Remotes fs.SpaceSepList `config:"remotes"` // Depreated 64 ActionPolicy string `config:"action_policy"` 65 CreatePolicy string `config:"create_policy"` 66 SearchPolicy string `config:"search_policy"` 67 CacheTime int `config:"cache_time"` 68 } 69 70 // Fs represents a union of upstreams 71 type Fs struct { 72 name string // name of this remote 73 features *fs.Features // optional features 74 opt Options // options for this Fs 75 root string // the path we are working on 76 upstreams []*upstream.Fs // slice of upstreams 77 hashSet hash.Set // intersection of hash types 78 actionPolicy policy.Policy // policy for ACTION 79 createPolicy policy.Policy // policy for CREATE 80 searchPolicy policy.Policy // policy for SEARCH 81 } 82 83 // Wrap candidate objects in to a union Object 84 func (f *Fs) wrapEntries(entries ...upstream.Entry) (entry, error) { 85 e, err := f.searchEntries(entries...) 86 if err != nil { 87 return nil, err 88 } 89 switch e.(type) { 90 case *upstream.Object: 91 return &Object{ 92 Object: e.(*upstream.Object), 93 fs: f, 94 co: entries, 95 }, nil 96 case *upstream.Directory: 97 return &Directory{ 98 Directory: e.(*upstream.Directory), 99 cd: entries, 100 }, nil 101 default: 102 return nil, errors.Errorf("unknown object type %T", e) 103 } 104 } 105 106 // Name of the remote (as passed into NewFs) 107 func (f *Fs) Name() string { 108 return f.name 109 } 110 111 // Root of the remote (as passed into NewFs) 112 func (f *Fs) Root() string { 113 return f.root 114 } 115 116 // String converts this Fs to a string 117 func (f *Fs) String() string { 118 return fmt.Sprintf("union root '%s'", f.root) 119 } 120 121 // Features returns the optional features of this Fs 122 func (f *Fs) Features() *fs.Features { 123 return f.features 124 } 125 126 // Rmdir removes the root directory of the Fs object 127 func (f *Fs) Rmdir(ctx context.Context, dir string) error { 128 upstreams, err := f.action(ctx, dir) 129 if err != nil { 130 return err 131 } 132 errs := Errors(make([]error, len(upstreams))) 133 multithread(len(upstreams), func(i int) { 134 err := upstreams[i].Rmdir(ctx, dir) 135 errs[i] = errors.Wrap(err, upstreams[i].Name()) 136 }) 137 return errs.Err() 138 } 139 140 // Hashes returns hash.HashNone to indicate remote hashing is unavailable 141 func (f *Fs) Hashes() hash.Set { 142 return f.hashSet 143 } 144 145 // Mkdir makes the root directory of the Fs object 146 func (f *Fs) Mkdir(ctx context.Context, dir string) error { 147 upstreams, err := f.create(ctx, dir) 148 if err == fs.ErrorObjectNotFound && dir != parentDir(dir) { 149 if err := f.Mkdir(ctx, parentDir(dir)); err != nil { 150 return err 151 } 152 upstreams, err = f.create(ctx, dir) 153 } 154 if err != nil { 155 return err 156 } 157 errs := Errors(make([]error, len(upstreams))) 158 multithread(len(upstreams), func(i int) { 159 err := upstreams[i].Mkdir(ctx, dir) 160 errs[i] = errors.Wrap(err, upstreams[i].Name()) 161 }) 162 return errs.Err() 163 } 164 165 // Purge all files in the root and the root directory 166 // 167 // Implement this if you have a way of deleting all the files 168 // quicker than just running Remove() on the result of List() 169 // 170 // Return an error if it doesn't exist 171 func (f *Fs) Purge(ctx context.Context) error { 172 for _, r := range f.upstreams { 173 if r.Features().Purge == nil { 174 return fs.ErrorCantPurge 175 } 176 } 177 upstreams, err := f.action(ctx, "") 178 if err != nil { 179 return err 180 } 181 errs := Errors(make([]error, len(upstreams))) 182 multithread(len(upstreams), func(i int) { 183 err := upstreams[i].Features().Purge(ctx) 184 errs[i] = errors.Wrap(err, upstreams[i].Name()) 185 }) 186 return errs.Err() 187 } 188 189 // Copy src to this remote using server side copy operations. 190 // 191 // This is stored with the remote path given 192 // 193 // It returns the destination Object and a possible error 194 // 195 // Will only be called if src.Fs().Name() == f.Name() 196 // 197 // If it isn't possible then return fs.ErrorCantCopy 198 func (f *Fs) Copy(ctx context.Context, src fs.Object, remote string) (fs.Object, error) { 199 srcObj, ok := src.(*Object) 200 if !ok { 201 fs.Debugf(src, "Can't copy - not same remote type") 202 return nil, fs.ErrorCantCopy 203 } 204 o := srcObj.UnWrap() 205 su := o.UpstreamFs() 206 if su.Features().Copy == nil { 207 return nil, fs.ErrorCantCopy 208 } 209 var du *upstream.Fs 210 for _, u := range f.upstreams { 211 if operations.Same(u.RootFs, su.RootFs) { 212 du = u 213 } 214 } 215 if du == nil { 216 return nil, fs.ErrorCantCopy 217 } 218 if !du.IsCreatable() { 219 return nil, fs.ErrorPermissionDenied 220 } 221 co, err := du.Features().Copy(ctx, o, remote) 222 if err != nil || co == nil { 223 return nil, err 224 } 225 wo, err := f.wrapEntries(du.WrapObject(co)) 226 return wo.(*Object), err 227 } 228 229 // Move src to this remote using server side move operations. 230 // 231 // This is stored with the remote path given 232 // 233 // It returns the destination Object and a possible error 234 // 235 // Will only be called if src.Fs().Name() == f.Name() 236 // 237 // If it isn't possible then return fs.ErrorCantMove 238 func (f *Fs) Move(ctx context.Context, src fs.Object, remote string) (fs.Object, error) { 239 o, ok := src.(*Object) 240 if !ok { 241 fs.Debugf(src, "Can't move - not same remote type") 242 return nil, fs.ErrorCantMove 243 } 244 entries, err := f.actionEntries(o.candidates()...) 245 if err != nil { 246 return nil, err 247 } 248 for _, e := range entries { 249 if e.UpstreamFs().Features().Move == nil { 250 return nil, fs.ErrorCantMove 251 } 252 } 253 objs := make([]*upstream.Object, len(entries)) 254 errs := Errors(make([]error, len(entries))) 255 multithread(len(entries), func(i int) { 256 su := entries[i].UpstreamFs() 257 o, ok := entries[i].(*upstream.Object) 258 if !ok { 259 errs[i] = errors.Wrap(fs.ErrorNotAFile, su.Name()) 260 return 261 } 262 var du *upstream.Fs 263 for _, u := range f.upstreams { 264 if operations.Same(u.RootFs, su.RootFs) { 265 du = u 266 } 267 } 268 if du == nil { 269 errs[i] = errors.Wrap(fs.ErrorCantMove, su.Name()+":"+remote) 270 return 271 } 272 mo, err := du.Features().Move(ctx, o.UnWrap(), remote) 273 if err != nil || mo == nil { 274 errs[i] = errors.Wrap(err, su.Name()) 275 return 276 } 277 objs[i] = du.WrapObject(mo) 278 }) 279 var en []upstream.Entry 280 for _, o := range objs { 281 if o != nil { 282 en = append(en, o) 283 } 284 } 285 e, err := f.wrapEntries(en...) 286 if err != nil { 287 return nil, err 288 } 289 return e.(*Object), errs.Err() 290 } 291 292 // DirMove moves src, srcRemote to this remote at dstRemote 293 // using server side move operations. 294 // 295 // Will only be called if src.Fs().Name() == f.Name() 296 // 297 // If it isn't possible then return fs.ErrorCantDirMove 298 // 299 // If destination exists then return fs.ErrorDirExists 300 func (f *Fs) DirMove(ctx context.Context, src fs.Fs, srcRemote, dstRemote string) error { 301 sfs, ok := src.(*Fs) 302 if !ok { 303 fs.Debugf(src, "Can't move directory - not same remote type") 304 return fs.ErrorCantDirMove 305 } 306 upstreams, err := sfs.action(ctx, srcRemote) 307 if err != nil { 308 return err 309 } 310 for _, u := range upstreams { 311 if u.Features().DirMove == nil { 312 return fs.ErrorCantDirMove 313 } 314 } 315 errs := Errors(make([]error, len(upstreams))) 316 multithread(len(upstreams), func(i int) { 317 su := upstreams[i] 318 var du *upstream.Fs 319 for _, u := range f.upstreams { 320 if operations.Same(u.RootFs, su.RootFs) { 321 du = u 322 } 323 } 324 if du == nil { 325 errs[i] = errors.Wrap(fs.ErrorCantDirMove, su.Name()+":"+su.Root()) 326 return 327 } 328 err := du.Features().DirMove(ctx, su.Fs, srcRemote, dstRemote) 329 errs[i] = errors.Wrap(err, du.Name()+":"+du.Root()) 330 }) 331 errs = errs.FilterNil() 332 if len(errs) == 0 { 333 return nil 334 } 335 for _, e := range errs { 336 if errors.Cause(e) != fs.ErrorDirExists { 337 return errs 338 } 339 } 340 return fs.ErrorDirExists 341 } 342 343 // ChangeNotify calls the passed function with a path 344 // that has had changes. If the implementation 345 // uses polling, it should adhere to the given interval. 346 // At least one value will be written to the channel, 347 // specifying the initial value and updated values might 348 // follow. A 0 Duration should pause the polling. 349 // The ChangeNotify implementation must empty the channel 350 // regularly. When the channel gets closed, the implementation 351 // should stop polling and release resources. 352 func (f *Fs) ChangeNotify(ctx context.Context, fn func(string, fs.EntryType), ch <-chan time.Duration) { 353 var uChans []chan time.Duration 354 355 for _, u := range f.upstreams { 356 if ChangeNotify := u.Features().ChangeNotify; ChangeNotify != nil { 357 ch := make(chan time.Duration) 358 uChans = append(uChans, ch) 359 ChangeNotify(ctx, fn, ch) 360 } 361 } 362 363 go func() { 364 for i := range ch { 365 for _, c := range uChans { 366 c <- i 367 } 368 } 369 for _, c := range uChans { 370 close(c) 371 } 372 }() 373 } 374 375 // DirCacheFlush resets the directory cache - used in testing 376 // as an optional interface 377 func (f *Fs) DirCacheFlush() { 378 multithread(len(f.upstreams), func(i int) { 379 if do := f.upstreams[i].Features().DirCacheFlush; do != nil { 380 do() 381 } 382 }) 383 } 384 385 func (f *Fs) put(ctx context.Context, in io.Reader, src fs.ObjectInfo, stream bool, options ...fs.OpenOption) (fs.Object, error) { 386 srcPath := src.Remote() 387 upstreams, err := f.create(ctx, srcPath) 388 if err == fs.ErrorObjectNotFound { 389 if err := f.Mkdir(ctx, parentDir(srcPath)); err != nil { 390 return nil, err 391 } 392 upstreams, err = f.create(ctx, srcPath) 393 } 394 if err != nil { 395 return nil, err 396 } 397 if len(upstreams) == 1 { 398 u := upstreams[0] 399 var o fs.Object 400 var err error 401 if stream { 402 o, err = u.Features().PutStream(ctx, in, src, options...) 403 } else { 404 o, err = u.Put(ctx, in, src, options...) 405 } 406 if err != nil { 407 return nil, err 408 } 409 e, err := f.wrapEntries(u.WrapObject(o)) 410 return e.(*Object), err 411 } 412 errs := Errors(make([]error, len(upstreams)+1)) 413 // Get multiple reader 414 readers := make([]io.Reader, len(upstreams)) 415 writers := make([]io.Writer, len(upstreams)) 416 for i := range writers { 417 r, w := io.Pipe() 418 bw := bufio.NewWriter(w) 419 readers[i], writers[i] = r, bw 420 defer func() { 421 err := w.Close() 422 if err != nil { 423 panic(err) 424 } 425 }() 426 } 427 go func() { 428 mw := io.MultiWriter(writers...) 429 es := make([]error, len(writers)+1) 430 _, es[len(es)-1] = io.Copy(mw, in) 431 for i, bw := range writers { 432 es[i] = bw.(*bufio.Writer).Flush() 433 } 434 errs[len(upstreams)] = Errors(es).Err() 435 }() 436 // Multi-threading 437 objs := make([]upstream.Entry, len(upstreams)) 438 multithread(len(upstreams), func(i int) { 439 u := upstreams[i] 440 var o fs.Object 441 var err error 442 if stream { 443 o, err = u.Features().PutStream(ctx, readers[i], src, options...) 444 } else { 445 o, err = u.Put(ctx, readers[i], src, options...) 446 } 447 if err != nil { 448 errs[i] = errors.Wrap(err, u.Name()) 449 return 450 } 451 objs[i] = u.WrapObject(o) 452 }) 453 err = errs.Err() 454 if err != nil { 455 return nil, err 456 } 457 e, err := f.wrapEntries(objs...) 458 return e.(*Object), err 459 } 460 461 // Put in to the remote path with the modTime given of the given size 462 // 463 // May create the object even if it returns an error - if so 464 // will return the object and the error, otherwise will return 465 // nil and the error 466 func (f *Fs) Put(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) { 467 o, err := f.NewObject(ctx, src.Remote()) 468 switch err { 469 case nil: 470 return o, o.Update(ctx, in, src, options...) 471 case fs.ErrorObjectNotFound: 472 return f.put(ctx, in, src, false, options...) 473 default: 474 return nil, err 475 } 476 } 477 478 // PutStream uploads to the remote path with the modTime given of indeterminate size 479 // 480 // May create the object even if it returns an error - if so 481 // will return the object and the error, otherwise will return 482 // nil and the error 483 func (f *Fs) PutStream(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) { 484 o, err := f.NewObject(ctx, src.Remote()) 485 switch err { 486 case nil: 487 return o, o.Update(ctx, in, src, options...) 488 case fs.ErrorObjectNotFound: 489 return f.put(ctx, in, src, true, options...) 490 default: 491 return nil, err 492 } 493 } 494 495 // About gets quota information from the Fs 496 func (f *Fs) About(ctx context.Context) (*fs.Usage, error) { 497 usage := &fs.Usage{ 498 Total: new(int64), 499 Used: new(int64), 500 Trashed: new(int64), 501 Other: new(int64), 502 Free: new(int64), 503 Objects: new(int64), 504 } 505 for _, u := range f.upstreams { 506 usg, err := u.About(ctx) 507 if err != nil { 508 return nil, err 509 } 510 if usg.Total != nil && usage.Total != nil { 511 *usage.Total += *usg.Total 512 } else { 513 usage.Total = nil 514 } 515 if usg.Used != nil && usage.Used != nil { 516 *usage.Used += *usg.Used 517 } else { 518 usage.Used = nil 519 } 520 if usg.Trashed != nil && usage.Trashed != nil { 521 *usage.Trashed += *usg.Trashed 522 } else { 523 usage.Trashed = nil 524 } 525 if usg.Other != nil && usage.Other != nil { 526 *usage.Other += *usg.Other 527 } else { 528 usage.Other = nil 529 } 530 if usg.Free != nil && usage.Free != nil { 531 *usage.Free += *usg.Free 532 } else { 533 usage.Free = nil 534 } 535 if usg.Objects != nil && usage.Objects != nil { 536 *usage.Objects += *usg.Objects 537 } else { 538 usage.Objects = nil 539 } 540 } 541 return usage, nil 542 } 543 544 // List the objects and directories in dir into entries. The 545 // entries can be returned in any order but should be for a 546 // complete directory. 547 // 548 // dir should be "" to list the root, and should not have 549 // trailing slashes. 550 // 551 // This should return ErrDirNotFound if the directory isn't 552 // found. 553 func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err error) { 554 entriess := make([][]upstream.Entry, len(f.upstreams)) 555 errs := Errors(make([]error, len(f.upstreams))) 556 multithread(len(f.upstreams), func(i int) { 557 u := f.upstreams[i] 558 entries, err := u.List(ctx, dir) 559 if err != nil { 560 errs[i] = errors.Wrap(err, u.Name()) 561 return 562 } 563 uEntries := make([]upstream.Entry, len(entries)) 564 for j, e := range entries { 565 uEntries[j], _ = u.WrapEntry(e) 566 } 567 entriess[i] = uEntries 568 }) 569 if len(errs) == len(errs.FilterNil()) { 570 errs = errs.Map(func(e error) error { 571 if errors.Cause(e) == fs.ErrorDirNotFound { 572 return nil 573 } 574 return e 575 }) 576 if len(errs) == 0 { 577 return nil, fs.ErrorDirNotFound 578 } 579 return nil, errs.Err() 580 } 581 return f.mergeDirEntries(entriess) 582 } 583 584 // ListR lists the objects and directories of the Fs starting 585 // from dir recursively into out. 586 // 587 // dir should be "" to start from the root, and should not 588 // have trailing slashes. 589 // 590 // This should return ErrDirNotFound if the directory isn't 591 // found. 592 // 593 // It should call callback for each tranche of entries read. 594 // These need not be returned in any particular order. If 595 // callback returns an error then the listing will stop 596 // immediately. 597 // 598 // Don't implement this unless you have a more efficient way 599 // of listing recursively that doing a directory traversal. 600 func (f *Fs) ListR(ctx context.Context, dir string, callback fs.ListRCallback) (err error) { 601 var entriess [][]upstream.Entry 602 errs := Errors(make([]error, len(f.upstreams))) 603 var mutex sync.Mutex 604 multithread(len(f.upstreams), func(i int) { 605 u := f.upstreams[i] 606 var err error 607 callback := func(entries fs.DirEntries) error { 608 uEntries := make([]upstream.Entry, len(entries)) 609 for j, e := range entries { 610 uEntries[j], _ = u.WrapEntry(e) 611 } 612 mutex.Lock() 613 entriess = append(entriess, uEntries) 614 mutex.Unlock() 615 return nil 616 } 617 do := u.Features().ListR 618 if do != nil { 619 err = do(ctx, dir, callback) 620 } else { 621 err = walk.ListR(ctx, u, dir, true, -1, walk.ListAll, callback) 622 } 623 if err != nil { 624 errs[i] = errors.Wrap(err, u.Name()) 625 return 626 } 627 }) 628 if len(errs) == len(errs.FilterNil()) { 629 errs = errs.Map(func(e error) error { 630 if errors.Cause(e) == fs.ErrorDirNotFound { 631 return nil 632 } 633 return e 634 }) 635 if len(errs) == 0 { 636 return fs.ErrorDirNotFound 637 } 638 return errs.Err() 639 } 640 entries, err := f.mergeDirEntries(entriess) 641 if err != nil { 642 return err 643 } 644 return callback(entries) 645 } 646 647 // NewObject creates a new remote union file object 648 func (f *Fs) NewObject(ctx context.Context, remote string) (fs.Object, error) { 649 objs := make([]*upstream.Object, len(f.upstreams)) 650 errs := Errors(make([]error, len(f.upstreams))) 651 multithread(len(f.upstreams), func(i int) { 652 u := f.upstreams[i] 653 o, err := u.NewObject(ctx, remote) 654 if err != nil && err != fs.ErrorObjectNotFound { 655 errs[i] = errors.Wrap(err, u.Name()) 656 return 657 } 658 objs[i] = u.WrapObject(o) 659 }) 660 var entries []upstream.Entry 661 for _, o := range objs { 662 if o != nil { 663 entries = append(entries, o) 664 } 665 } 666 if len(entries) == 0 { 667 return nil, fs.ErrorObjectNotFound 668 } 669 e, err := f.wrapEntries(entries...) 670 if err != nil { 671 return nil, err 672 } 673 return e.(*Object), errs.Err() 674 } 675 676 // Precision is the greatest Precision of all upstreams 677 func (f *Fs) Precision() time.Duration { 678 var greatestPrecision time.Duration 679 for _, u := range f.upstreams { 680 if u.Precision() > greatestPrecision { 681 greatestPrecision = u.Precision() 682 } 683 } 684 return greatestPrecision 685 } 686 687 func (f *Fs) action(ctx context.Context, path string) ([]*upstream.Fs, error) { 688 return f.actionPolicy.Action(ctx, f.upstreams, path) 689 } 690 691 func (f *Fs) actionEntries(entries ...upstream.Entry) ([]upstream.Entry, error) { 692 return f.actionPolicy.ActionEntries(entries...) 693 } 694 695 func (f *Fs) create(ctx context.Context, path string) ([]*upstream.Fs, error) { 696 return f.createPolicy.Create(ctx, f.upstreams, path) 697 } 698 699 func (f *Fs) createEntries(entries ...upstream.Entry) ([]upstream.Entry, error) { 700 return f.createPolicy.CreateEntries(entries...) 701 } 702 703 func (f *Fs) search(ctx context.Context, path string) (*upstream.Fs, error) { 704 return f.searchPolicy.Search(ctx, f.upstreams, path) 705 } 706 707 func (f *Fs) searchEntries(entries ...upstream.Entry) (upstream.Entry, error) { 708 return f.searchPolicy.SearchEntries(entries...) 709 } 710 711 func (f *Fs) mergeDirEntries(entriess [][]upstream.Entry) (fs.DirEntries, error) { 712 entryMap := make(map[string]([]upstream.Entry)) 713 for _, en := range entriess { 714 if en == nil { 715 continue 716 } 717 for _, entry := range en { 718 remote := entry.Remote() 719 if f.Features().CaseInsensitive { 720 remote = strings.ToLower(remote) 721 } 722 entryMap[remote] = append(entryMap[remote], entry) 723 } 724 } 725 var entries fs.DirEntries 726 for path := range entryMap { 727 e, err := f.wrapEntries(entryMap[path]...) 728 if err != nil { 729 return nil, err 730 } 731 entries = append(entries, e) 732 } 733 return entries, nil 734 } 735 736 // NewFs constructs an Fs from the path. 737 // 738 // The returned Fs is the actual Fs, referenced by remote in the config 739 func NewFs(name, root string, m configmap.Mapper) (fs.Fs, error) { 740 // Parse config into Options struct 741 opt := new(Options) 742 err := configstruct.Set(m, opt) 743 if err != nil { 744 return nil, err 745 } 746 // Backward compatible to old config 747 if len(opt.Upstreams) == 0 && len(opt.Remotes) > 0 { 748 for i := 0; i < len(opt.Remotes)-1; i++ { 749 opt.Remotes[i] = opt.Remotes[i] + ":ro" 750 } 751 opt.Upstreams = opt.Remotes 752 } 753 if len(opt.Upstreams) == 0 { 754 return nil, errors.New("union can't point to an empty upstream - check the value of the upstreams setting") 755 } 756 if len(opt.Upstreams) == 1 { 757 return nil, errors.New("union can't point to a single upstream - check the value of the upstreams setting") 758 } 759 for _, u := range opt.Upstreams { 760 if strings.HasPrefix(u, name+":") { 761 return nil, errors.New("can't point union remote at itself - check the value of the upstreams setting") 762 } 763 } 764 765 upstreams := make([]*upstream.Fs, len(opt.Upstreams)) 766 errs := Errors(make([]error, len(opt.Upstreams))) 767 multithread(len(opt.Upstreams), func(i int) { 768 u := opt.Upstreams[i] 769 upstreams[i], errs[i] = upstream.New(u, root, time.Duration(opt.CacheTime)*time.Second) 770 }) 771 var usedUpstreams []*upstream.Fs 772 var fserr error 773 for i, err := range errs { 774 if err != nil && err != fs.ErrorIsFile { 775 return nil, err 776 } 777 // Only the upstreams returns ErrorIsFile would be used if any 778 if err == fs.ErrorIsFile { 779 usedUpstreams = append(usedUpstreams, upstreams[i]) 780 fserr = fs.ErrorIsFile 781 } 782 } 783 if fserr == nil { 784 usedUpstreams = upstreams 785 } 786 787 f := &Fs{ 788 name: name, 789 root: root, 790 opt: *opt, 791 upstreams: usedUpstreams, 792 } 793 f.actionPolicy, err = policy.Get(opt.ActionPolicy) 794 if err != nil { 795 return nil, err 796 } 797 f.createPolicy, err = policy.Get(opt.CreatePolicy) 798 if err != nil { 799 return nil, err 800 } 801 f.searchPolicy, err = policy.Get(opt.SearchPolicy) 802 if err != nil { 803 return nil, err 804 } 805 var features = (&fs.Features{ 806 CaseInsensitive: true, 807 DuplicateFiles: false, 808 ReadMimeType: true, 809 WriteMimeType: true, 810 CanHaveEmptyDirectories: true, 811 BucketBased: true, 812 SetTier: true, 813 GetTier: true, 814 }).Fill(f) 815 for _, f := range upstreams { 816 features = features.Mask(f) // Mask all upstream fs 817 } 818 819 // Enable ListR when upstreams either support ListR or is local 820 // But not when all upstreams are local 821 if features.ListR == nil { 822 for _, u := range upstreams { 823 if u.Features().ListR != nil { 824 features.ListR = f.ListR 825 } else if !u.Features().IsLocal { 826 features.ListR = nil 827 break 828 } 829 } 830 } 831 832 f.features = features 833 834 // Get common intersection of hashes 835 hashSet := f.upstreams[0].Hashes() 836 for _, u := range f.upstreams[1:] { 837 hashSet = hashSet.Overlap(u.Hashes()) 838 } 839 f.hashSet = hashSet 840 841 return f, fserr 842 } 843 844 func parentDir(absPath string) string { 845 parent := path.Dir(strings.TrimRight(filepath.ToSlash(absPath), "/")) 846 if parent == "." { 847 parent = "" 848 } 849 return parent 850 } 851 852 func multithread(num int, fn func(int)) { 853 var wg sync.WaitGroup 854 for i := 0; i < num; i++ { 855 wg.Add(1) 856 i := i 857 go func() { 858 defer wg.Done() 859 fn(i) 860 }() 861 } 862 wg.Wait() 863 } 864 865 // Check the interfaces are satisfied 866 var ( 867 _ fs.Fs = (*Fs)(nil) 868 _ fs.Purger = (*Fs)(nil) 869 _ fs.PutStreamer = (*Fs)(nil) 870 _ fs.Copier = (*Fs)(nil) 871 _ fs.Mover = (*Fs)(nil) 872 _ fs.DirMover = (*Fs)(nil) 873 _ fs.DirCacheFlusher = (*Fs)(nil) 874 _ fs.ChangeNotifier = (*Fs)(nil) 875 _ fs.Abouter = (*Fs)(nil) 876 _ fs.ListRer = (*Fs)(nil) 877 )