diff --git a/backend/local/local.go b/backend/local/local.go index 7f08be673..18763ace3 100644 --- a/backend/local/local.go +++ b/backend/local/local.go @@ -22,6 +22,7 @@ import ( "github.com/rclone/rclone/fs/config" "github.com/rclone/rclone/fs/config/configmap" "github.com/rclone/rclone/fs/config/configstruct" + "github.com/rclone/rclone/fs/filter" "github.com/rclone/rclone/fs/fserrors" "github.com/rclone/rclone/fs/hash" "github.com/rclone/rclone/lib/encoder" @@ -443,6 +444,8 @@ func (f *Fs) NewObject(ctx context.Context, remote string) (fs.Object, error) { // This should return ErrDirNotFound if the directory isn't // found. func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err error) { + filter, useFilter := filter.GetConfig(ctx), filter.GetUseFilter(ctx) + fsDirPath := f.localPath(dir) _, err = os.Stat(fsDirPath) if err != nil { @@ -493,6 +496,13 @@ func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err e continue } if fierr != nil { + // Don't report errors on any file names that are excluded + if useFilter { + newRemote := f.cleanRemote(dir, name) + if !filter.IncludeRemote(newRemote) { + continue + } + } err = fmt.Errorf("failed to read directory %q: %w", namepath, err) fs.Errorf(dir, "%v", fierr) _ = accounting.Stats(ctx).Error(fserrors.NoRetryError(fierr)) // fail the sync @@ -510,6 +520,11 @@ func (f *Fs) List(ctx context.Context, dir string) (entries fs.DirEntries, err e name := fi.Name() mode := fi.Mode() newRemote := f.cleanRemote(dir, name) + // Don't include non directory if not included + // we leave directory filtering to the layer above + if useFilter && !fi.IsDir() && !filter.IncludeRemote(newRemote) { + continue + } // Follow symlinks if required if f.opt.FollowSymlinks && (mode&os.ModeSymlink) != 0 { localPath := filepath.Join(fsDirPath, name) diff --git a/backend/local/local_internal_test.go b/backend/local/local_internal_test.go index 55b98bb53..698d263a8 100644 --- a/backend/local/local_internal_test.go +++ b/backend/local/local_internal_test.go @@ -9,11 +9,13 @@ import ( "path" "path/filepath" "runtime" + "sort" "testing" "time" "github.com/rclone/rclone/fs" "github.com/rclone/rclone/fs/config/configmap" + "github.com/rclone/rclone/fs/filter" "github.com/rclone/rclone/fs/hash" "github.com/rclone/rclone/fs/object" "github.com/rclone/rclone/fstest" @@ -366,3 +368,33 @@ func TestMetadata(t *testing.T) { }) } + +func TestFilter(t *testing.T) { + ctx := context.Background() + r := fstest.NewRun(t) + defer r.Finalise() + when := time.Now() + r.WriteFile("included", "included file", when) + r.WriteFile("excluded", "excluded file", when) + f := r.Flocal.(*Fs) + + // Add a filter + ctx, fi := filter.AddConfig(ctx) + require.NoError(t, fi.AddRule("+ included")) + require.NoError(t, fi.AddRule("- *")) + + // Check listing without use filter flag + entries, err := f.List(ctx, "") + require.NoError(t, err) + sort.Sort(entries) + require.Equal(t, "[excluded included]", fmt.Sprint(entries)) + + // Add user filter flag + ctx = filter.SetUseFilter(ctx, true) + + // Check listing with use filter flag + entries, err = f.List(ctx, "") + require.NoError(t, err) + sort.Sort(entries) + require.Equal(t, "[included]", fmt.Sprint(entries)) +}