mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-19 12:34:29 +02:00
* webdav: delete a folder with its contents * webdav: guard the recursive delete against stale and confined paths - propagate stat lookup errors instead of returning an error-bearing FileInfo: OpenFile and DELETE would otherwise take the recursive removeAll on a path whose type was never confirmed, wiping a directory on a transient filer error - refuse wrappedFs.Rename from or to the served root, matching the RemoveAll guard and webdav.Dir - normalize trailing slashes in removeAll so the delete names the entry itself, and refuse the root for every caller * webdav: keep file-only deletes shallow and refuse ancestor destinations - removeAll used by OpenFile and failed-upload cleanup now deletes non-recursively, so a folder swapped in after the stat check is not emptied by a pending file delete - MOVE and COPY whose destination contains the source are refused before the handler removes the destination, which would otherwise take the source down with it * webdav: also refuse a destination inside the source a MOVE or COPY into its own subtree recurses into the destination it just created; refuse it with the same guard instead of burning the recursion budget for a 500 * webdav: allow a shallow copy into the source subtree a COPY with Depth: 0 creates only the destination collection and never reads children, so it is safe inside the source; only deep copies and moves are refused there * webdav: refuse a copy/move whose source is unreadable * webdav: cover the missing-source refusal in the guard test * webdav: 500 on a source lookup failure, 404 only when absent --------- Co-authored-by: Chris Lu <chris.lu@gmail.com> Co-authored-by: Chris Lu <chrislusf@users.noreply.github.com>
64 lines
1.7 KiB
Go
64 lines
1.7 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 {
|
|
if path.Clean("/"+name) == "/" {
|
|
return os.ErrInvalid
|
|
}
|
|
name = w.confine(name)
|
|
return w.FileSystem.RemoveAll(ctx, name)
|
|
}
|
|
|
|
func (w wrappedFs) Rename(ctx context.Context, oldName, newName string) error {
|
|
if path.Clean("/"+oldName) == "/" || path.Clean("/"+newName) == "/" {
|
|
return os.ErrInvalid
|
|
}
|
|
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)
|
|
}
|