Kaynağa Gözat

drive: fix StatCache mishandling of paths with spaces

Fix "file not found" errors when WebDAV clients access files/dirs inside
directories with spaces.

The issue occurred because StatCache was mixing URL-escaped and
unescaped paths, causing cache key mismatches.
Specifically, StatCache.set() parsed WebDAV responses containing
URL-escaped paths (ex. "Dir%20Space/file1.txt") and stored them
alongside unescaped cache keys (ex. "Dir Space/file1.txt").
This mismatch prevented StatCache.get() from correctly determining whether
a child file existed.

See https://github.com/tailscale/tailscale/issues/13632#issuecomment-3243522449
for the full explanation of the issue.

The decision to keep all paths references unescaped inside the StatCache
is consistent with net/http.Request.URL.Path and rewrite.go (sole consumer)

Update unit test to detect this directory space mishandling.

Fixes tailscale#13632

Signed-off-by: Craig Hesling <[email protected]>
Craig Hesling 6 ay önce
ebeveyn
işleme
2b9d055101

+ 7 - 1
drive/driveimpl/compositedav/stat_cache.go

@@ -8,6 +8,7 @@ import (
 	"encoding/xml"
 	"log"
 	"net/http"
+	"net/url"
 	"sync"
 	"time"
 
@@ -165,7 +166,12 @@ func (c *StatCache) set(name string, depth int, ce *cacheEntry) {
 			children = make(map[string]*cacheEntry, len(ms.Responses)-1)
 			for i := 0; i < len(ms.Responses); i++ {
 				response := ms.Responses[i]
-				name := shared.Normalize(response.Href)
+				name, err := url.PathUnescape(response.Href)
+				if err != nil {
+					log.Printf("statcache.set child parse error: %s", err)
+					return
+				}
+				name = shared.Normalize(name)
 				raw := marshalMultiStatus(response)
 				entry := newCacheEntry(ce.Status, raw)
 				if i == 0 {

+ 4 - 4
drive/driveimpl/compositedav/stat_cache_test.go

@@ -16,12 +16,12 @@ import (
 	"tailscale.com/tstest"
 )
 
-var parentPath = "/parent"
+var parentPath = "/parent with spaces"
 
-var childPath = "/parent/child.txt"
+var childPath = "/parent with spaces/child.txt"
 
 var parentResponse = `<D:response>
-<D:href>/parent/</D:href>
+<D:href>/parent%20with%20spaces/</D:href>
 <D:propstat>
 <D:prop>
 <D:getlastmodified>Mon, 29 Apr 2024 19:52:23 GMT</D:getlastmodified>
@@ -36,7 +36,7 @@ var parentResponse = `<D:response>
 
 var childResponse = `
 <D:response>
-<D:href>/parent/child.txt</D:href>
+<D:href>/parent%20with%20spaces/child.txt</D:href>
 <D:propstat>
 <D:prop>
 <D:getlastmodified>Mon, 29 Apr 2024 19:52:23 GMT</D:getlastmodified>