diff --git a/backend/union/policy/epff.go b/backend/union/policy/epff.go index e984bf2fd..459ab326f 100644 --- a/backend/union/policy/epff.go +++ b/backend/union/policy/epff.go @@ -16,11 +16,14 @@ func init() { // Given the order of the candidates, act on the first one found where the relative path exists. type EpFF struct{} -func (p *EpFF) epff(ctx context.Context, upstreams []*upstream.Fs, filePath string) (*upstream.Fs, error) { +func (p *EpFF) epffIsLocal(ctx context.Context, upstreams []*upstream.Fs, filePath string, isLocal bool) (*upstream.Fs, error) { ch := make(chan *upstream.Fs, len(upstreams)) ctx, cancel := context.WithCancel(ctx) defer cancel() for _, u := range upstreams { + if u.IsLocal() != isLocal { + continue + } u := u // Closure go func() { rfs := u.RootFs @@ -32,7 +35,10 @@ func (p *EpFF) epff(ctx context.Context, upstreams []*upstream.Fs, filePath stri }() } var u *upstream.Fs - for range upstreams { + for _, upstream := range upstreams { + if upstream.IsLocal() != isLocal { + continue + } u = <-ch if u != nil { break @@ -44,6 +50,15 @@ func (p *EpFF) epff(ctx context.Context, upstreams []*upstream.Fs, filePath stri return u, nil } +func (p *EpFF) epff(ctx context.Context, upstreams []*upstream.Fs, filePath string) (*upstream.Fs, error) { + // search local disks first + u, err := p.epffIsLocal(ctx, upstreams, filePath, true) + if err == fs.ErrorObjectNotFound { + u, err = p.epffIsLocal(ctx, upstreams, filePath, false) + } + return u, err +} + // Action category policy, governing the modification of files and directories func (p *EpFF) Action(ctx context.Context, upstreams []*upstream.Fs, path string) ([]*upstream.Fs, error) { if len(upstreams) == 0 { diff --git a/backend/union/upstream/upstream.go b/backend/union/upstream/upstream.go index 7b9f44fd2..d39b2ae19 100644 --- a/backend/union/upstream/upstream.go +++ b/backend/union/upstream/upstream.go @@ -34,6 +34,7 @@ type Fs struct { Opt *common.Options writable bool creatable bool + isLocal bool usage *fs.Usage // Cache the usage cacheTime time.Duration // cache duration cacheMutex sync.RWMutex @@ -95,6 +96,7 @@ func New(ctx context.Context, remote, root string, opt *common.Options) (*Fs, er return nil, err } f.RootFs = rFs + f.isLocal = rFs.Features().IsLocal rootString := fspath.JoinRootPath(remote, root) myFs, err := cache.Get(ctx, rootString) if err != nil && err != fs.ErrorIsFile { @@ -142,6 +144,11 @@ func (f *Fs) WrapEntry(e fs.DirEntry) (Entry, error) { } } +// IsLocal true if the upstream Fs is a local disk +func (f *Fs) IsLocal() bool { + return f.isLocal +} + // UpstreamFs get the upstream Fs the entry is stored in func (e *Directory) UpstreamFs() *Fs { return e.f