diff --git a/backend/walk.go b/backend/walk.go index c7281450..6d42e107 100644 --- a/backend/walk.go +++ b/backend/walk.go @@ -75,6 +75,9 @@ func Walk(ctx context.Context, fileSystem fs.FS, prefix, delimiter, marker strin return fs.SkipAll } + // After this point, return skipflag instead of nil + // so we can skip a directory without an early return + var skipflag error if d.IsDir() { // If prefix is defined and the directory does not match prefix, // do not descend into the directory because nothing will @@ -89,54 +92,52 @@ func Walk(ctx context.Context, fileSystem fs.FS, prefix, delimiter, marker strin return fs.SkipDir } - // Don't recurse into subdirectories when listing with delimiter. - if delimiter == "/" && - prefix != path+"/" && - strings.HasPrefix(path+"/", prefix) { - cpmap[path+"/"] = struct{}{} - return fs.SkipDir - } - - // TODO: can we do better here rather than a second readdir - // per directory? - ents, err := fs.ReadDir(fileSystem, path) - if err != nil { - return fmt.Errorf("readdir %q: %w", path, err) - } - - path += "/" - - if len(ents) == 0 && delimiter == "" { - dirobj, err := getObj(path, d) - if err == ErrSkipObj { - return nil - } + // Don't recurse into subdirectories which contain the delimiter + // after reaching the prefix + if delimiter != "" && + strings.HasPrefix(path+"/", prefix) && + strings.Contains(strings.TrimPrefix(path+"/", prefix), delimiter) { + skipflag = fs.SkipDir + } else { + // TODO: can we do better here rather than a second readdir + // per directory? + ents, err := fs.ReadDir(fileSystem, path) if err != nil { - return fmt.Errorf("directory to object %q: %w", path, err) + return fmt.Errorf("readdir %q: %w", path, err) + } + if len(ents) == 0 && delimiter == "" { + dirobj, err := getObj(path+"/", d) + if err == ErrSkipObj { + return skipflag + } + if err != nil { + return fmt.Errorf("directory to object %q: %w", path, err) + } + objects = append(objects, dirobj) + + return skipflag } - objects = append(objects, dirobj) - - return nil - } - if len(ents) != 0 { - return nil + if len(ents) != 0 { + return skipflag + } } + path += "/" } if !pastMarker { if path == marker { pastMarker = true - return nil + return skipflag } if path < marker { - return nil + return skipflag } } // If object doesn't have prefix, don't include in results. if prefix != "" && !strings.HasPrefix(path, prefix) { - return nil + return skipflag } if delimiter == "" { @@ -144,7 +145,7 @@ func Walk(ctx context.Context, fileSystem fs.FS, prefix, delimiter, marker strin // prefix are included in results obj, err := getObj(path, d) if err == ErrSkipObj { - return nil + return skipflag } if err != nil { return fmt.Errorf("file to object %q: %w", path, err) @@ -155,7 +156,7 @@ func Walk(ctx context.Context, fileSystem fs.FS, prefix, delimiter, marker strin pastMax = true } - return nil + return skipflag } // Since delimiter is specified, we only want results that @@ -184,7 +185,7 @@ func Walk(ctx context.Context, fileSystem fs.FS, prefix, delimiter, marker strin if !found { obj, err := getObj(path, d) if err == ErrSkipObj { - return nil + return skipflag } if err != nil { return fmt.Errorf("file to object %q: %w", path, err) @@ -193,7 +194,7 @@ func Walk(ctx context.Context, fileSystem fs.FS, prefix, delimiter, marker strin if (len(objects) + len(cpmap)) == int(max) { pastMax = true } - return nil + return skipflag } // Common prefixes are a set, so should not have duplicates. @@ -203,12 +204,12 @@ func Walk(ctx context.Context, fileSystem fs.FS, prefix, delimiter, marker strin cpref := prefix + before + delimiter if cpref == marker { pastMarker = true - return nil + return skipflag } if marker != "" && strings.HasPrefix(marker, cprefNoDelim) { // skip common prefixes that are before the marker - return nil + return skipflag } cpmap[cpref] = struct{}{} @@ -218,7 +219,7 @@ func Walk(ctx context.Context, fileSystem fs.FS, prefix, delimiter, marker strin return fs.SkipAll } - return nil + return skipflag }) if err != nil { return WalkResults{}, err