diff --git a/repo/storage/webdav/webdav_props.go b/repo/storage/webdav/webdav_props.go index 39f511d17..73085f28d 100644 --- a/repo/storage/webdav/webdav_props.go +++ b/repo/storage/webdav/webdav_props.go @@ -56,7 +56,10 @@ type webdavDirEntry struct { } func (d *davStorage) propFindChildren(urlStr string) ([]webdavDirEntry, error) { - req, err := d.propFindRequest(urlStr, "1") + if urlStr == "" { + urlStr = "/" + } + req, err := d.propFindRequest(d.URL+urlStr, "1") if err != nil { return nil, fmt.Errorf("can't create PROPFIND request: %v", err) } @@ -67,32 +70,33 @@ func (d *davStorage) propFindChildren(urlStr string) ([]webdavDirEntry, error) { } defer resp.Body.Close() // nolint:errcheck - var ms multiResponse + dec := xml.NewDecoder(resp.Body) if err := dec.Decode(&ms); err != nil { - return nil, fmt.Errorf("unable to decode webdav response: %v", err) + return nil, fmt.Errorf("unable to decode webdav response %v: %v", urlStr, err) } var entries []webdavDirEntry for _, r := range ms.Responses { - var e webdavDirEntry - + if r.Href == urlStr { + continue + } for _, p := range r.Props { if !strings.Contains(p.Status, " 200 ") { continue } + var e webdavDirEntry e.name = p.Name e.length, _ = strconv.ParseInt(p.Size, 10, 64) e.modTime, _ = time.Parse(time.RFC1123, p.Modified) e.isCollection = p.Type.Local == "collection" + entries = append(entries, e) + break } - - entries = append(entries, e) } - return entries, nil } diff --git a/repo/storage/webdav/webdav_storage.go b/repo/storage/webdav/webdav_storage.go index b8aa2dbcc..bc773f6e8 100644 --- a/repo/storage/webdav/webdav_storage.go +++ b/repo/storage/webdav/webdav_storage.go @@ -7,6 +7,7 @@ "io/ioutil" "net/http" "os" + "sort" "strings" "github.com/kopia/kopia/internal/kopialogging" @@ -82,15 +83,20 @@ func (d *davStorage) ListBlocks(ctx context.Context, prefix string, callback fun walkDir = func(urlStr string, currentPrefix string) error { entries, err := d.propFindChildren(urlStr) if err != nil { - return err + return fmt.Errorf("PROPFIND error on %v: %v", urlStr, err) } + sort.Slice(entries, func(i, j int) bool { + return entries[i].name < entries[j].name + }) + for _, e := range entries { if e.isCollection { newPrefix := currentPrefix + e.name var match bool if len(prefix) > len(newPrefix) { + // looking for 'abcd', got 'ab' so far, worth trying match = strings.HasPrefix(prefix, newPrefix) } else { match = strings.HasPrefix(newPrefix, prefix) @@ -117,7 +123,7 @@ func (d *davStorage) ListBlocks(ctx context.Context, prefix string, callback fun return nil } - return walkDir(d.URL, "") + return walkDir("", "") } func (d *davStorage) makeCollectionAll(urlStr string) error { diff --git a/repo/storage/webdav/webdav_storage_test.go b/repo/storage/webdav/webdav_storage_test.go index 6e546c96a..65e9f6b05 100644 --- a/repo/storage/webdav/webdav_storage_test.go +++ b/repo/storage/webdav/webdav_storage_test.go @@ -2,6 +2,7 @@ import ( "context" + "fmt" "io/ioutil" "net/http" "net/http/httptest" @@ -14,10 +15,11 @@ ) func TestWebDAVStorage(t *testing.T) { - t.Skip() tmpDir, _ := ioutil.TempDir("", "webdav") defer os.RemoveAll(tmpDir) + t.Logf("tmpDir: %v", tmpDir) + mux := http.NewServeMux() mux.Handle("/", &webdav.Handler{ FileSystem: webdav.Dir(tmpDir), @@ -38,20 +40,22 @@ func TestWebDAVStorage(t *testing.T) { []int{1, 2}, []int{2, 2, 2}, } { - if err := os.RemoveAll(tmpDir); err != nil { - t.Errorf("can't remove all: %q", tmpDir) - } - os.MkdirAll(tmpDir, 0700) + t.Run(fmt.Sprintf("shards-%v", shardSpec), func(t *testing.T) { + if err := os.RemoveAll(tmpDir); err != nil { + t.Errorf("can't remove all: %q", tmpDir) + } + os.MkdirAll(tmpDir, 0700) - r, err := New(context.Background(), &Options{ - URL: server.URL, - DirectoryShards: shardSpec, + r, err := New(context.Background(), &Options{ + URL: server.URL, + DirectoryShards: shardSpec, + }) + + if r == nil || err != nil { + t.Errorf("unexpected result: %v %v", r, err) + } + + storagetesting.VerifyStorage(ctx, t, r) }) - - if r == nil || err != nil { - t.Errorf("unexpected result: %v %v", r, err) - } - - storagetesting.VerifyStorage(ctx, t, r) } }