Files
seaweedfs/weed/server/wrapped_webdav_fs.go
T
Chris Lu e9a464840c webdav: describe a listed entry the way clients expect (#10993)
* webdav: name the entry, not its path, in a listing

DAV:displayname carried the full path of every entry. A client that
takes displayname for the child's name - Windows Explorer does - then
looks for /dir/name under /dir and finds nothing, so a folder shows up
empty while the root, where the two spellings differ only by a leading
slash, still lists.

Readdir now builds its entries with toFileInfo like stat does, so a
listing and a lookup describe a child the same way, and the wrapper that
was trimming the sub-folder back off a name goes away with it.

Claude-Session: https://claude.ai/code/session_01XCeuCWpF9xo9CfyHvCQE9c

* webdav: derive an ETag when nothing hashed the entry

Uploads through this gateway carry no content MD5, so filer.ETag comes
back empty and every file in a PROPFIND answered with an empty
DAV:getetag, which is not a valid entity-tag. Report it as unimplemented
instead, the way the sub-folder wrapper already did, and webdav falls
back to modification time and size. The wrapper's copy went with it - it
swallowed the stat error a caller was meant to see.

Claude-Session: https://claude.ai/code/session_01XCeuCWpF9xo9CfyHvCQE9c
2026-08-27 16:21:14 -07:00

58 lines
1.6 KiB
Go

package weed_server
import (
"context"
"os"
"path"
"golang.org/x/net/webdav"
)
type wrappedFs struct {
subFolder string
webdav.FileSystem
}
// confine joins a client-supplied name under subFolder. The name is cleaned as
// a rooted path first so `..` segments cannot climb above subFolder; cleaning
// after the concat (as the underlying FileSystem does) would resolve them across
// the confinement boundary.
func (w wrappedFs) confine(name string) string {
return w.subFolder + path.Clean("/"+name)
}
// NewWrappedFs returns a webdav.FileSystem identical to fs, except that it
// serves only the subFolder sub-tree of fs.
func NewWrappedFs(fs webdav.FileSystem, subFolder string) webdav.FileSystem {
return wrappedFs{
subFolder: subFolder,
FileSystem: fs,
}
}
func (w wrappedFs) Mkdir(ctx context.Context, name string, perm os.FileMode) error {
name = w.confine(name)
return w.FileSystem.Mkdir(ctx, name, perm)
}
func (w wrappedFs) OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (webdav.File, error) {
name = w.confine(name)
return w.FileSystem.OpenFile(ctx, name, flag, perm)
}
func (w wrappedFs) RemoveAll(ctx context.Context, name string) error {
name = w.confine(name)
return w.FileSystem.RemoveAll(ctx, name)
}
func (w wrappedFs) Rename(ctx context.Context, oldName, newName string) error {
oldName = w.confine(oldName)
newName = w.confine(newName)
return w.FileSystem.Rename(ctx, oldName, newName)
}
func (w wrappedFs) Stat(ctx context.Context, name string) (os.FileInfo, error) {
name = w.confine(name)
return w.FileSystem.Stat(ctx, name)
}