diff --git a/amazonclouddrive/amazonclouddrive_test.go b/amazonclouddrive/amazonclouddrive_test.go index ff9be5034..86a17b8ab 100644 --- a/amazonclouddrive/amazonclouddrive_test.go +++ b/amazonclouddrive/amazonclouddrive_test.go @@ -31,6 +31,7 @@ func TestFsPutFile2(t *testing.T) { fstests.TestFsPutFile2(t) } func TestFsListDirFile2(t *testing.T) { fstests.TestFsListDirFile2(t) } func TestFsListDirRoot(t *testing.T) { fstests.TestFsListDirRoot(t) } func TestFsListSubdir(t *testing.T) { fstests.TestFsListSubdir(t) } +func TestFsListLevel2(t *testing.T) { fstests.TestFsListLevel2(t) } func TestFsListFile1(t *testing.T) { fstests.TestFsListFile1(t) } func TestFsNewFsObject(t *testing.T) { fstests.TestFsNewFsObject(t) } func TestFsListFile1and2(t *testing.T) { fstests.TestFsListFile1and2(t) } diff --git a/b2/b2.go b/b2/b2.go index 4b71859cb..f80bfe0d6 100644 --- a/b2/b2.go +++ b/b2/b2.go @@ -329,6 +329,34 @@ func (f *Fs) NewFsObject(remote string) fs.Object { return f.newFsObjectWithInfo(remote, nil) } +// sendDir works out given a lastDir and a remote which directories should be sent +func sendDir(lastDir string, remote string, level int) (dirNames []string, newLastDir string) { + dir := path.Dir(remote) + if dir == "." { + // No slashes - nothing to do! + return nil, lastDir + } + if dir == lastDir { + // Still in same directory + return nil, lastDir + } + newLastDir = lastDir + for { + slashes := strings.Count(dir, "/") + if !strings.HasPrefix(lastDir, dir) && slashes < level { + dirNames = append([]string{dir}, dirNames...) + } + if newLastDir == lastDir { + newLastDir = dir + } + dir = path.Dir(dir) + if dir == "." { + break + } + } + return dirNames, newLastDir +} + // listFn is called from list to handle an object type listFn func(remote string, object *api.File, isDirectory bool) error @@ -377,7 +405,7 @@ func (f *Fs) list(dir string, level int, prefix string, limit int, hidden bool, if hidden { opts.Path = "/b2_list_file_versions" } - lastDir := "" + lastDir := dir for { err := f.pacer.Call(func() (bool, error) { resp, err := f.srv.CallJSON(&opts, &request, &response) @@ -395,24 +423,21 @@ func (f *Fs) list(dir string, level int, prefix string, limit int, hidden bool, remote := file.Name[len(f.root):] slashes := strings.Count(remote, "/") - // Check if this file makes a new directory - if slash := strings.IndexRune(remote, '/'); slash >= 0 { - if dir := remote[:slash]; dir != lastDir { - if slashes-1 < fs.MaxLevel { - err = fn(dir, nil, true) - if err != nil { - if err == errEndList { - return nil - } - return err - } + // Check if this file makes a new directories + var dirNames []string + dirNames, lastDir = sendDir(lastDir, remote, level) + for _, dirName := range dirNames { + err = fn(dirName, nil, true) + if err != nil { + if err == errEndList { + return nil } - lastDir = dir + return err } } // Send the file - if slashes < fs.MaxLevel { + if slashes < level { err = fn(remote, file, false) if err != nil { if err == errEndList { diff --git a/b2/b2_internal_test.go b/b2/b2_internal_test.go index f64e83bcb..25ac2f1b2 100644 --- a/b2/b2_internal_test.go +++ b/b2/b2_internal_test.go @@ -5,6 +5,7 @@ import ( "time" "github.com/ncw/rclone/fstest" + "github.com/stretchr/testify/assert" ) // Test b2 string encoding @@ -168,3 +169,116 @@ func TestParseTimeString(t *testing.T) { } } + +func TestSendDir(t *testing.T) { + for _, test := range []struct { + lastDir string + remote string + level int + dirNames []string + newLastDir string + }{ + { + lastDir: "", + remote: "test.txt", + level: 100, + dirNames: nil, + newLastDir: "", + }, + { + lastDir: "", + remote: "potato/test.txt", + level: 100, + dirNames: []string{"potato"}, + newLastDir: "potato", + }, + { + lastDir: "potato", + remote: "potato/test.txt", + level: 100, + dirNames: nil, + newLastDir: "potato", + }, + { + lastDir: "", + remote: "potato/sausage/test.txt", + level: 100, + dirNames: []string{"potato", "potato/sausage"}, + newLastDir: "potato/sausage", + }, + { + lastDir: "potato", + remote: "potato/sausage/test.txt", + level: 100, + dirNames: []string{"potato/sausage"}, + newLastDir: "potato/sausage", + }, + { + lastDir: "potato/sausage", + remote: "potato/sausage/test.txt", + level: 100, + dirNames: nil, + newLastDir: "potato/sausage", + }, + { + lastDir: "", + remote: "a/b/c/d/e/f.txt", + level: 100, + dirNames: []string{"a", "a/b", "a/b/c", "a/b/c/d", "a/b/c/d/e"}, + newLastDir: "a/b/c/d/e", + }, + { + lastDir: "a/b/c/d/e", + remote: "a/b/c/d/E/f.txt", + level: 100, + dirNames: []string{"a/b/c/d/E"}, + newLastDir: "a/b/c/d/E", + }, + { + lastDir: "a/b/c/d/e", + remote: "a/b/C/D/E/f.txt", + level: 100, + dirNames: []string{"a/b/C", "a/b/C/D", "a/b/C/D/E"}, + newLastDir: "a/b/C/D/E", + }, + { + lastDir: "a/b/c", + remote: "a/b/c/d/e/f.txt", + level: 100, + dirNames: []string{"a/b/c/d", "a/b/c/d/e"}, + newLastDir: "a/b/c/d/e", + }, + { + lastDir: "", + remote: "a/b/c/d/e/f.txt", + level: 1, + dirNames: []string{"a"}, + newLastDir: "a/b/c/d/e", + }, + { + lastDir: "a/b/c", + remote: "a/b/c/d/e/f.txt", + level: 1, + dirNames: nil, + newLastDir: "a/b/c/d/e", + }, + { + lastDir: "", + remote: "a/b/c/d/e/f.txt", + level: 3, + dirNames: []string{"a", "a/b", "a/b/c"}, + newLastDir: "a/b/c/d/e", + }, + { + lastDir: "a/b/C/D/E", + remote: "a/b/c/d/e/f.txt", + level: 3, + dirNames: []string{"a/b/c"}, + newLastDir: "a/b/c/d/e", + }, + } { + dirNames, newLastDir := sendDir(test.lastDir, test.remote, test.level) + assert.Equal(t, test.dirNames, dirNames, "dirNames fail for sendDir(%q,%q,%v)", test.lastDir, test.remote, test.level) + assert.Equal(t, test.newLastDir, newLastDir, "newLastDir fail for sendDir(%q,%q,%v)", test.lastDir, test.remote, test.level) + } +} diff --git a/b2/b2_test.go b/b2/b2_test.go index 25a177105..b96d30cfa 100644 --- a/b2/b2_test.go +++ b/b2/b2_test.go @@ -31,6 +31,7 @@ func TestFsPutFile2(t *testing.T) { fstests.TestFsPutFile2(t) } func TestFsListDirFile2(t *testing.T) { fstests.TestFsListDirFile2(t) } func TestFsListDirRoot(t *testing.T) { fstests.TestFsListDirRoot(t) } func TestFsListSubdir(t *testing.T) { fstests.TestFsListSubdir(t) } +func TestFsListLevel2(t *testing.T) { fstests.TestFsListLevel2(t) } func TestFsListFile1(t *testing.T) { fstests.TestFsListFile1(t) } func TestFsNewFsObject(t *testing.T) { fstests.TestFsNewFsObject(t) } func TestFsListFile1and2(t *testing.T) { fstests.TestFsListFile1and2(t) } diff --git a/drive/drive.go b/drive/drive.go index beb0addad..bbd586156 100644 --- a/drive/drive.go +++ b/drive/drive.go @@ -462,7 +462,7 @@ func (f *Fs) ListDir(out fs.ListOpts, job dircache.ListDirJob) (jobs []dircache. case item.MimeType == driveFolderType: if out.IncludeDirectory(remote) { dir := &fs.Dir{ - Name: item.Title, + Name: remote, Bytes: -1, Count: -1, } diff --git a/drive/drive_test.go b/drive/drive_test.go index 1d91af6f3..7c9dd3b97 100644 --- a/drive/drive_test.go +++ b/drive/drive_test.go @@ -31,6 +31,7 @@ func TestFsPutFile2(t *testing.T) { fstests.TestFsPutFile2(t) } func TestFsListDirFile2(t *testing.T) { fstests.TestFsListDirFile2(t) } func TestFsListDirRoot(t *testing.T) { fstests.TestFsListDirRoot(t) } func TestFsListSubdir(t *testing.T) { fstests.TestFsListSubdir(t) } +func TestFsListLevel2(t *testing.T) { fstests.TestFsListLevel2(t) } func TestFsListFile1(t *testing.T) { fstests.TestFsListFile1(t) } func TestFsNewFsObject(t *testing.T) { fstests.TestFsNewFsObject(t) } func TestFsListFile1and2(t *testing.T) { fstests.TestFsListFile1and2(t) } diff --git a/dropbox/dropbox.go b/dropbox/dropbox.go index b5e53de4a..fbffeaa9b 100644 --- a/dropbox/dropbox.go +++ b/dropbox/dropbox.go @@ -226,12 +226,25 @@ func (f *Fs) NewFsObject(remote string) fs.Object { } // Strips the root off path and returns it -func (f *Fs) stripRoot(path string) (string, error) { - lowercase := strings.ToLower(path) - if !strings.HasPrefix(lowercase, f.slashRootSlash) { - return "", fmt.Errorf("Path %q is not under root %q", path, f.slashRootSlash) +func strip(path, root string) (string, error) { + if len(root) > 0 { + if root[0] != '/' { + root = "/" + root + } + if root[len(root)-1] != '/' { + root += "/" + } } - return path[len(f.slashRootSlash):], nil + lowercase := strings.ToLower(path) + if !strings.HasPrefix(lowercase, root) { + return "", fmt.Errorf("Path %q is not under root %q", path, root) + } + return path[len(root):], nil +} + +// Strips the root off path and returns it +func (f *Fs) stripRoot(path string) (string, error) { + return strip(path, f.slashRootSlash) } // Walk the root returning a channel of FsObjects @@ -252,81 +265,80 @@ func (f *Fs) list(out fs.ListOpts, dir string) { for { deltaPage, err := f.db.Delta(cursor, root) if err != nil { + out.SetError(fmt.Errorf("Couldn't list: %s", err)) + return + } + if deltaPage.Reset && cursor != "" { + fs.ErrorLog(f, "Unexpected reset during listing - try again") fs.Stats.Error() - fs.ErrorLog(f, "Couldn't list: %s", err) break - } else { - if deltaPage.Reset && cursor != "" { - fs.ErrorLog(f, "Unexpected reset during listing - try again") - fs.Stats.Error() - break - } - fs.Debug(f, "%d delta entries received", len(deltaPage.Entries)) - for i := range deltaPage.Entries { - deltaEntry := &deltaPage.Entries[i] - entry := deltaEntry.Entry - if entry == nil { - // This notifies of a deleted object + } + fs.Debug(f, "%d delta entries received", len(deltaPage.Entries)) + for i := range deltaPage.Entries { + deltaEntry := &deltaPage.Entries[i] + entry := deltaEntry.Entry + if entry == nil { + // This notifies of a deleted object + } else { + if len(entry.Path) <= 1 || entry.Path[0] != '/' { + fs.Stats.Error() + fs.ErrorLog(f, "dropbox API inconsistency: a path should always start with a slash and be at least 2 characters: %s", entry.Path) + continue + } + + lastSlashIndex := strings.LastIndex(entry.Path, "/") + + var parentPath string + if lastSlashIndex == 0 { + parentPath = "" } else { - if len(entry.Path) <= 1 || entry.Path[0] != '/' { - fs.Stats.Error() - fs.ErrorLog(f, "dropbox API inconsistency: a path should always start with a slash and be at least 2 characters: %s", entry.Path) - continue + parentPath = entry.Path[1:lastSlashIndex] + } + lastComponent := entry.Path[lastSlashIndex+1:] + + if entry.IsDir { + nameTree.PutCaseCorrectDirectoryName(parentPath, lastComponent) + name, err := f.stripRoot(entry.Path + "/") + if err != nil { + out.SetError(err) + return } - - lastSlashIndex := strings.LastIndex(entry.Path, "/") - - var parentPath string - if lastSlashIndex == 0 { - parentPath = "" - } else { - parentPath = entry.Path[1:lastSlashIndex] + name = strings.Trim(name, "/") + if name != "" && name != dir { + dir := &fs.Dir{ + Name: name, + When: time.Time(entry.ClientMtime), + Bytes: entry.Bytes, + Count: -1, + } + if out.AddDir(dir) { + return + } } - lastComponent := entry.Path[lastSlashIndex+1:] - - if entry.IsDir { - nameTree.PutCaseCorrectDirectoryName(parentPath, lastComponent) - name, err := f.stripRoot(entry.Path + "/") + } else { + parentPathCorrectCase := nameTree.GetPathWithCorrectCase(parentPath) + if parentPathCorrectCase != nil { + path, err := f.stripRoot(*parentPathCorrectCase + "/" + lastComponent) if err != nil { out.SetError(err) return } - name = strings.Trim(name, "/") - if name != "" { - dir := &fs.Dir{ - Name: name, - When: time.Time(entry.ClientMtime), - Bytes: entry.Bytes, - Count: -1, - } - if out.AddDir(dir) { + if o := f.newFsObjectWithInfo(path, entry); o != nil { + if out.Add(o) { return } } } else { - parentPathCorrectCase := nameTree.GetPathWithCorrectCase(parentPath) - if parentPathCorrectCase != nil { - path, err := f.stripRoot(*parentPathCorrectCase + "/" + lastComponent) - if err != nil { - out.SetError(err) - return - } - if o := f.newFsObjectWithInfo(path, entry); o != nil { - if out.Add(o) { - return - } - } - } else { - nameTree.PutFile(parentPath, lastComponent, entry) - } + nameTree.PutFile(parentPath, lastComponent, entry) } } } - if !deltaPage.HasMore { - break - } - cursor = deltaPage.Cursor.Cursor } + if !deltaPage.HasMore { + break + } + cursor = deltaPage.Cursor.Cursor + } walkFunc := func(caseCorrectFilePath string, entry *dropbox.Entry) error { @@ -347,41 +359,56 @@ func (f *Fs) list(out fs.ListOpts, dir string) { } } -// List walks the path returning a channel of FsObjects -func (f *Fs) List(out fs.ListOpts, dir string) { - defer out.Finished() - f.list(out, dir) -} - -// ListDir walks the path returning a channel of FsObjects -func (f *Fs) ListDir() fs.DirChan { - out := make(fs.DirChan, fs.Config.Checkers) - go func() { - defer close(out) - entry, err := f.db.Metadata(f.root, true, false, "", "", metadataLimit) +// listOneLevel walks the path one level deep +func (f *Fs) listOneLevel(out fs.ListOpts, dir string) { + root := f.root + if dir != "" { + root += "/" + dir + } + entry, err := f.db.Metadata(root, true, false, "", "", metadataLimit) + if err != nil { + out.SetError(fmt.Errorf("Couldn't list single level: %s", err)) + return + } + for i := range entry.Contents { + entry := &entry.Contents[i] + remote, err := strip(entry.Path, root) if err != nil { - fs.Stats.Error() - fs.ErrorLog(f, "Couldn't list directories in root: %s", err) + out.SetError(err) + return + } + if entry.IsDir { + dir := &fs.Dir{ + Name: remote, + When: time.Time(entry.ClientMtime), + Bytes: entry.Bytes, + Count: -1, + } + if out.AddDir(dir) { + return + } } else { - for i := range entry.Contents { - entry := &entry.Contents[i] - if entry.IsDir { - name, err := f.stripRoot(entry.Path) - if err != nil { - continue - } - - out <- &fs.Dir{ - Name: name, - When: time.Time(entry.ClientMtime), - Bytes: entry.Bytes, - Count: -1, - } + if o := f.newFsObjectWithInfo(remote, entry); o != nil { + if out.Add(o) { + return } } } - }() - return out + } +} + +// List walks the path returning a channel of FsObjects +func (f *Fs) List(out fs.ListOpts, dir string) { + defer out.Finished() + level := out.Level() + switch level { + case 1: + f.listOneLevel(out, dir) + case fs.MaxLevel: + f.list(out, dir) + default: + out.SetError(fs.ErrorLevelNotSupported) + } } // A read closer which doesn't close the input diff --git a/dropbox/dropbox_test.go b/dropbox/dropbox_test.go index deea5cf76..56a62e00b 100644 --- a/dropbox/dropbox_test.go +++ b/dropbox/dropbox_test.go @@ -31,6 +31,7 @@ func TestFsPutFile2(t *testing.T) { fstests.TestFsPutFile2(t) } func TestFsListDirFile2(t *testing.T) { fstests.TestFsListDirFile2(t) } func TestFsListDirRoot(t *testing.T) { fstests.TestFsListDirRoot(t) } func TestFsListSubdir(t *testing.T) { fstests.TestFsListSubdir(t) } +func TestFsListLevel2(t *testing.T) { fstests.TestFsListLevel2(t) } func TestFsListFile1(t *testing.T) { fstests.TestFsListFile1(t) } func TestFsNewFsObject(t *testing.T) { fstests.TestFsNewFsObject(t) } func TestFsListFile1and2(t *testing.T) { fstests.TestFsListFile1and2(t) } diff --git a/fs/fs.go b/fs/fs.go index 9e5ca4279..7e55f1e23 100644 --- a/fs/fs.go +++ b/fs/fs.go @@ -457,6 +457,23 @@ func (o *Lister) Get() (Object, *Dir, error) { } } +// Get all the objects and dirs from the listing. +func (o *Lister) GetAll() (objs []Object, dirs []*Dir, err error) { + for { + obj, dir, err := o.Get() + switch { + case err != nil: + return nil, nil, err + case obj != nil: + objs = append(objs, obj) + case dir != nil: + dirs = append(dirs, dir) + default: + return objs, dirs, nil + } + } +} + // GetObject will return an object from the listing. // It will skip over any directories. // Will return (nil, nil) when all objects have been returned. diff --git a/fstest/fstests/fstests.go b/fstest/fstests/fstests.go index 3f0c520c4..dffeaceb2 100644 --- a/fstest/fstests/fstests.go +++ b/fstest/fstests/fstests.go @@ -12,6 +12,7 @@ import ( "log" "os" "path" + "sort" "strings" "testing" "time" @@ -129,14 +130,42 @@ func TestFsListEmpty(t *testing.T) { fstest.CheckListing(t, remote, []fstest.Item{}) } +// winPath converts a path into a windows safe path +func winPath(s string) string { + s = strings.Replace(s, "?", "_", -1) + s = strings.Replace(s, `"`, "_", -1) + s = strings.Replace(s, "<", "_", -1) + s = strings.Replace(s, ">", "_", -1) + return s +} + +// dirsToNames returns a sorted list of names +func dirsToNames(dirs []*fs.Dir) []string { + names := []string{} + for _, dir := range dirs { + names = append(names, winPath(dir.Name)) + } + sort.Strings(names) + return names +} + +// objsToNames returns a sorted list of object names +func objsToNames(objs []fs.Object) []string { + names := []string{} + for _, obj := range objs { + names = append(names, winPath(obj.Remote())) + } + sort.Strings(names) + return names +} + // TestFsListDirEmpty tests listing the directories from an empty directory func TestFsListDirEmpty(t *testing.T) { skipIfNotOk(t) - dirs, err := fs.NewLister().SetLevel(1).Start(remote, "").GetDirs() + objs, dirs, err := fs.NewLister().SetLevel(1).Start(remote, "").GetAll() require.NoError(t, err) - for _, dir := range dirs { - t.Errorf("Found unexpected item %q", dir.Name) - } + assert.Equal(t, []string{}, objsToNames(objs)) + assert.Equal(t, []string{}, dirsToNames(dirs)) } // TestFsNewFsObjectNotFound tests not finding a object @@ -164,14 +193,24 @@ func findObject(t *testing.T, Name string) fs.Object { } func testPut(t *testing.T, file *fstest.Item) { +again: buf := bytes.NewBufferString(fstest.RandomString(100)) hash := fs.NewMultiHasher() in := io.TeeReader(buf, hash) + tries := 1 + const maxTries = 10 file.Size = int64(buf.Len()) obji := fs.NewStaticObjectInfo(file.Path, file.ModTime, file.Size, true, nil, nil) obj, err := remote.Put(in, obji) if err != nil { + // Retry if err returned a retry error + if r, ok := err.(fs.Retry); ok && r.Retry() && tries < maxTries { + t.Logf("Put error: %v - low level retry %d/%d", err, tries, maxTries) + + tries++ + goto again + } t.Fatal("Put error", err) } file.Hashes = hash.Sums() @@ -196,26 +235,20 @@ func TestFsPutFile2(t *testing.T) { // TestFsListDirFile2 tests the files are correctly uploaded func TestFsListDirFile2(t *testing.T) { skipIfNotOk(t) - found := false + var objNames, dirNames []string for i := 1; i <= eventualConsistencyRetries; i++ { - dirs, err := fs.NewLister().SetLevel(1).Start(remote, "").GetDirs() + objs, dirs, err := fs.NewLister().SetLevel(1).Start(remote, "").GetAll() require.NoError(t, err) - for _, dir := range dirs { - if dir.Name != `hello? sausage` && dir.Name != `hello_ sausage` { - t.Errorf("Found unexpected item %q", dir.Name) - } else { - found = true - } - } - if found { + objNames = objsToNames(objs) + dirNames = dirsToNames(dirs) + if len(objNames) >= 1 && len(dirNames) >= 1 { break } t.Logf("Sleeping for 1 second for TestFsListDirFile2 eventual consistency: %d/%d", i, eventualConsistencyRetries) time.Sleep(1 * time.Second) } - if !found { - t.Errorf("Didn't find %q", `hello? sausage`) - } + assert.Equal(t, []string{`hello_ sausage`}, dirNames) + assert.Equal(t, []string{file1.Path}, objNames) } // TestFsListDirRoot tests that DirList works in the root @@ -225,17 +258,9 @@ func TestFsListDirRoot(t *testing.T) { if err != nil { t.Fatalf("Failed to make remote %q: %v", RemoteName, err) } - found := false dirs, err := fs.NewLister().SetLevel(1).Start(rootRemote, "").GetDirs() require.NoError(t, err) - for _, dir := range dirs { - if dir.Name == subRemoteLeaf { - found = true - } - } - if !found { - t.Errorf("Didn't find %q", subRemoteLeaf) - } + assert.Contains(t, dirsToNames(dirs), subRemoteLeaf, "Remote leaf not found") } // TestFsListSubdir tests List works for a subdirectory @@ -244,18 +269,31 @@ func TestFsListSubdir(t *testing.T) { test := func(fileName string) bool { dir, _ := path.Split(fileName) dir = dir[:len(dir)-1] - objs, err := fs.NewLister().Start(remote, dir).GetObjects() + objs, dirs, err := fs.NewLister().Start(remote, dir).GetAll() if err == fs.ErrorDirNotFound { return false } require.NoError(t, err) require.Len(t, objs, 1) assert.Equal(t, fileName, objs[0].Remote()) + require.Len(t, dirs, 0) return true } assert.True(t, test(file2.Path) || test(file2.WinPath), "normal and alternative lists failed") } +// TestFsListLevel2 tests List works for 2 levels +func TestFsListLevel2(t *testing.T) { + skipIfNotOk(t) + objs, dirs, err := fs.NewLister().SetLevel(2).Start(remote, "").GetAll() + if err == fs.ErrorLevelNotSupported { + return + } + require.NoError(t, err) + assert.Equal(t, []string{file1.Path}, objsToNames(objs)) + assert.Equal(t, []string{`hello_ sausage`, `hello_ sausage/êé`}, dirsToNames(dirs)) +} + // TestFsListFile1 tests file present func TestFsListFile1(t *testing.T) { skipIfNotOk(t) diff --git a/googlecloudstorage/googlecloudstorage.go b/googlecloudstorage/googlecloudstorage.go index 90074c9c6..e77ff0862 100644 --- a/googlecloudstorage/googlecloudstorage.go +++ b/googlecloudstorage/googlecloudstorage.go @@ -305,6 +305,7 @@ type listFn func(remote string, object *storage.Object, isDirectory bool) error // If directories is set it only sends directories func (f *Fs) list(dir string, level int, fn listFn) error { root := f.root + rootLength := len(root) if dir != "" { root += dir + "/" } @@ -316,7 +317,6 @@ func (f *Fs) list(dir string, level int, fn listFn) error { default: return fs.ErrorLevelNotSupported } - rootLength := len(root) for { objects, err := list.Do() if err != nil { diff --git a/googlecloudstorage/googlecloudstorage_test.go b/googlecloudstorage/googlecloudstorage_test.go index 0cb81a5b9..7fe017f8b 100644 --- a/googlecloudstorage/googlecloudstorage_test.go +++ b/googlecloudstorage/googlecloudstorage_test.go @@ -31,6 +31,7 @@ func TestFsPutFile2(t *testing.T) { fstests.TestFsPutFile2(t) } func TestFsListDirFile2(t *testing.T) { fstests.TestFsListDirFile2(t) } func TestFsListDirRoot(t *testing.T) { fstests.TestFsListDirRoot(t) } func TestFsListSubdir(t *testing.T) { fstests.TestFsListSubdir(t) } +func TestFsListLevel2(t *testing.T) { fstests.TestFsListLevel2(t) } func TestFsListFile1(t *testing.T) { fstests.TestFsListFile1(t) } func TestFsNewFsObject(t *testing.T) { fstests.TestFsNewFsObject(t) } func TestFsListFile1and2(t *testing.T) { fstests.TestFsListFile1and2(t) } diff --git a/hubic/hubic_test.go b/hubic/hubic_test.go index 9a615ee17..943fb0fe7 100644 --- a/hubic/hubic_test.go +++ b/hubic/hubic_test.go @@ -31,6 +31,7 @@ func TestFsPutFile2(t *testing.T) { fstests.TestFsPutFile2(t) } func TestFsListDirFile2(t *testing.T) { fstests.TestFsListDirFile2(t) } func TestFsListDirRoot(t *testing.T) { fstests.TestFsListDirRoot(t) } func TestFsListSubdir(t *testing.T) { fstests.TestFsListSubdir(t) } +func TestFsListLevel2(t *testing.T) { fstests.TestFsListLevel2(t) } func TestFsListFile1(t *testing.T) { fstests.TestFsListFile1(t) } func TestFsNewFsObject(t *testing.T) { fstests.TestFsNewFsObject(t) } func TestFsListFile1and2(t *testing.T) { fstests.TestFsListFile1and2(t) } diff --git a/local/local.go b/local/local.go index b9c0b7321..8c7e2b6d4 100644 --- a/local/local.go +++ b/local/local.go @@ -225,7 +225,7 @@ func (f *Fs) List(out fs.ListOpts, dir string) { // Start the process traversing.Add(1) - in <- listArgs{remote: "", dirpath: root, level: out.Level() - 1} + in <- listArgs{remote: dir, dirpath: root, level: out.Level() - 1} for i := 0; i < fs.Config.Checkers; i++ { wg.Add(1) go func() { diff --git a/local/local_test.go b/local/local_test.go index 6af4b578e..b11d30208 100644 --- a/local/local_test.go +++ b/local/local_test.go @@ -31,6 +31,7 @@ func TestFsPutFile2(t *testing.T) { fstests.TestFsPutFile2(t) } func TestFsListDirFile2(t *testing.T) { fstests.TestFsListDirFile2(t) } func TestFsListDirRoot(t *testing.T) { fstests.TestFsListDirRoot(t) } func TestFsListSubdir(t *testing.T) { fstests.TestFsListSubdir(t) } +func TestFsListLevel2(t *testing.T) { fstests.TestFsListLevel2(t) } func TestFsListFile1(t *testing.T) { fstests.TestFsListFile1(t) } func TestFsNewFsObject(t *testing.T) { fstests.TestFsNewFsObject(t) } func TestFsListFile1and2(t *testing.T) { fstests.TestFsListFile1and2(t) } diff --git a/onedrive/onedrive_test.go b/onedrive/onedrive_test.go index b728e242b..d44849348 100644 --- a/onedrive/onedrive_test.go +++ b/onedrive/onedrive_test.go @@ -31,6 +31,7 @@ func TestFsPutFile2(t *testing.T) { fstests.TestFsPutFile2(t) } func TestFsListDirFile2(t *testing.T) { fstests.TestFsListDirFile2(t) } func TestFsListDirRoot(t *testing.T) { fstests.TestFsListDirRoot(t) } func TestFsListSubdir(t *testing.T) { fstests.TestFsListSubdir(t) } +func TestFsListLevel2(t *testing.T) { fstests.TestFsListLevel2(t) } func TestFsListFile1(t *testing.T) { fstests.TestFsListFile1(t) } func TestFsNewFsObject(t *testing.T) { fstests.TestFsNewFsObject(t) } func TestFsListFile1and2(t *testing.T) { fstests.TestFsListFile1and2(t) } diff --git a/s3/s3_test.go b/s3/s3_test.go index add4d4abf..3301fe165 100644 --- a/s3/s3_test.go +++ b/s3/s3_test.go @@ -31,6 +31,7 @@ func TestFsPutFile2(t *testing.T) { fstests.TestFsPutFile2(t) } func TestFsListDirFile2(t *testing.T) { fstests.TestFsListDirFile2(t) } func TestFsListDirRoot(t *testing.T) { fstests.TestFsListDirRoot(t) } func TestFsListSubdir(t *testing.T) { fstests.TestFsListSubdir(t) } +func TestFsListLevel2(t *testing.T) { fstests.TestFsListLevel2(t) } func TestFsListFile1(t *testing.T) { fstests.TestFsListFile1(t) } func TestFsNewFsObject(t *testing.T) { fstests.TestFsNewFsObject(t) } func TestFsListFile1and2(t *testing.T) { fstests.TestFsListFile1and2(t) } diff --git a/swift/swift_test.go b/swift/swift_test.go index bdaee6ab5..2507831c5 100644 --- a/swift/swift_test.go +++ b/swift/swift_test.go @@ -31,6 +31,7 @@ func TestFsPutFile2(t *testing.T) { fstests.TestFsPutFile2(t) } func TestFsListDirFile2(t *testing.T) { fstests.TestFsListDirFile2(t) } func TestFsListDirRoot(t *testing.T) { fstests.TestFsListDirRoot(t) } func TestFsListSubdir(t *testing.T) { fstests.TestFsListSubdir(t) } +func TestFsListLevel2(t *testing.T) { fstests.TestFsListLevel2(t) } func TestFsListFile1(t *testing.T) { fstests.TestFsListFile1(t) } func TestFsNewFsObject(t *testing.T) { fstests.TestFsNewFsObject(t) } func TestFsListFile1and2(t *testing.T) { fstests.TestFsListFile1and2(t) } diff --git a/yandex/yandex_test.go b/yandex/yandex_test.go index 3e5ad58e2..59b320928 100644 --- a/yandex/yandex_test.go +++ b/yandex/yandex_test.go @@ -31,6 +31,7 @@ func TestFsPutFile2(t *testing.T) { fstests.TestFsPutFile2(t) } func TestFsListDirFile2(t *testing.T) { fstests.TestFsListDirFile2(t) } func TestFsListDirRoot(t *testing.T) { fstests.TestFsListDirRoot(t) } func TestFsListSubdir(t *testing.T) { fstests.TestFsListSubdir(t) } +func TestFsListLevel2(t *testing.T) { fstests.TestFsListLevel2(t) } func TestFsListFile1(t *testing.T) { fstests.TestFsListFile1(t) } func TestFsNewFsObject(t *testing.T) { fstests.TestFsNewFsObject(t) } func TestFsListFile1and2(t *testing.T) { fstests.TestFsListFile1and2(t) }