github.com/10XDev/rclone@v1.52.3-0.20200626220027-16af9ab76b2a/fs/list/list.go (about) 1 // Package list contains list functions 2 package list 3 4 import ( 5 "context" 6 "sort" 7 "strings" 8 9 "github.com/pkg/errors" 10 "github.com/rclone/rclone/fs" 11 "github.com/rclone/rclone/fs/filter" 12 ) 13 14 // DirSorted reads Object and *Dir into entries for the given Fs. 15 // 16 // dir is the start directory, "" for root 17 // 18 // If includeAll is specified all files will be added, otherwise only 19 // files and directories passing the filter will be added. 20 // 21 // Files will be returned in sorted order 22 func DirSorted(ctx context.Context, f fs.Fs, includeAll bool, dir string) (entries fs.DirEntries, err error) { 23 // Get unfiltered entries from the fs 24 entries, err = f.List(ctx, dir) 25 if err != nil { 26 return nil, err 27 } 28 // This should happen only if exclude files lives in the 29 // starting directory, otherwise ListDirSorted should not be 30 // called. 31 if !includeAll && filter.Active.ListContainsExcludeFile(entries) { 32 fs.Debugf(dir, "Excluded") 33 return nil, nil 34 } 35 return filterAndSortDir(ctx, entries, includeAll, dir, filter.Active.IncludeObject, filter.Active.IncludeDirectory(ctx, f)) 36 } 37 38 // filter (if required) and check the entries, then sort them 39 func filterAndSortDir(ctx context.Context, entries fs.DirEntries, includeAll bool, dir string, 40 IncludeObject func(ctx context.Context, o fs.Object) bool, 41 IncludeDirectory func(remote string) (bool, error)) (newEntries fs.DirEntries, err error) { 42 newEntries = entries[:0] // in place filter 43 prefix := "" 44 if dir != "" { 45 prefix = dir + "/" 46 } 47 for _, entry := range entries { 48 ok := true 49 // check includes and types 50 switch x := entry.(type) { 51 case fs.Object: 52 // Make sure we don't delete excluded files if not required 53 if !includeAll && !IncludeObject(ctx, x) { 54 ok = false 55 fs.Debugf(x, "Excluded") 56 } 57 case fs.Directory: 58 if !includeAll { 59 include, err := IncludeDirectory(x.Remote()) 60 if err != nil { 61 return nil, err 62 } 63 if !include { 64 ok = false 65 fs.Debugf(x, "Excluded") 66 } 67 } 68 default: 69 return nil, errors.Errorf("unknown object type %T", entry) 70 } 71 // check remote name belongs in this directory 72 remote := entry.Remote() 73 switch { 74 case !ok: 75 // ignore 76 case !strings.HasPrefix(remote, prefix): 77 ok = false 78 fs.Errorf(entry, "Entry doesn't belong in directory %q (too short) - ignoring", dir) 79 case remote == prefix: 80 ok = false 81 fs.Errorf(entry, "Entry doesn't belong in directory %q (same as directory) - ignoring", dir) 82 case strings.ContainsRune(remote[len(prefix):], '/'): 83 ok = false 84 fs.Errorf(entry, "Entry doesn't belong in directory %q (contains subdir) - ignoring", dir) 85 default: 86 // ok 87 } 88 if ok { 89 newEntries = append(newEntries, entry) 90 } 91 } 92 entries = newEntries 93 94 // Sort the directory entries by Remote 95 // 96 // We use a stable sort here just in case there are 97 // duplicates. Assuming the remote delivers the entries in a 98 // consistent order, this will give the best user experience 99 // in syncing as it will use the first entry for the sync 100 // comparison. 101 sort.Stable(entries) 102 return entries, nil 103 }