From 003244059faba9d9c21e135d523d350b0d9a47d2 Mon Sep 17 00:00:00 2001 From: OpenClaw Agent Date: Mon, 21 Sep 2026 22:12:44 +0800 Subject: [PATCH] fix: align WebDAV overwrite authorization --- chfs-ng/internal/api/server.go | 38 +++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/chfs-ng/internal/api/server.go b/chfs-ng/internal/api/server.go index b03fac0..7a78dc1 100644 --- a/chfs-ng/internal/api/server.go +++ b/chfs-ng/internal/api/server.go @@ -1061,12 +1061,13 @@ func (f *webdavStorageFS) Copy(ctx context.Context, name, dest string, opts *web if sameOrDescendantPath(src, dst) { return false, webdav.NewHTTPError(http.StatusConflict, fmt.Errorf("destination is inside source")) } - if opts != nil && opts.NoOverwrite { - if _, err := f.store.Stat(dst); err == nil { - return false, webdav.NewHTTPError(http.StatusPreconditionFailed, fmt.Errorf("destination exists")) - } else if !os.IsNotExist(err) { - return false, err - } + _, dstErr := f.store.Stat(dst) + dstExists := dstErr == nil + if dstErr != nil && !os.IsNotExist(dstErr) { + return false, dstErr + } + if opts != nil && opts.NoOverwrite && dstExists { + return false, webdav.NewHTTPError(http.StatusPreconditionFailed, fmt.Errorf("destination exists")) } if m, ok := f.store.(storage.Multi); ok { srcRoot, _, srcOK := m.FindRoot(src) @@ -1094,7 +1095,7 @@ func (f *webdavStorageFS) Copy(ctx context.Context, name, dest string, opts *web } _ = created } - return true, nil + return !dstExists, nil } in, err := f.store.Open(src) if err != nil { @@ -1114,7 +1115,7 @@ func (f *webdavStorageFS) Copy(ctx context.Context, name, dest string, opts *web if closeErr != nil { return false, closeErr } - return true, nil + return !dstExists, nil } func (f *webdavStorageFS) Move(ctx context.Context, name, dest string, opts *webdav.MoveOptions) (bool, error) { @@ -1129,12 +1130,12 @@ func (f *webdavStorageFS) Move(ctx context.Context, name, dest string, opts *web if sameOrDescendantPath(src, dst) { return false, webdav.NewHTTPError(http.StatusConflict, fmt.Errorf("destination is inside source")) } - if opts == nil || opts.NoOverwrite { - if _, err := f.store.Stat(dst); err == nil { - return false, webdav.NewHTTPError(http.StatusPreconditionFailed, fmt.Errorf("destination exists")) - } else if !os.IsNotExist(err) { - return false, err - } + if _, err := f.store.Stat(dst); err == nil { + // CHFS v4 MOVE does not overwrite an existing destination, even when + // Overwrite is omitted/T and the caller has delete permission. + return false, webdav.NewHTTPError(http.StatusPreconditionFailed, fmt.Errorf("destination exists")) + } else if !os.IsNotExist(err) { + return false, err } if m, ok := f.store.(storage.Multi); ok { srcRoot, _, srcOK := m.FindRoot(src) @@ -1370,6 +1371,15 @@ func (f *webdavAuthFS) Create(ctx context.Context, name string, body io.ReadClos if err := f.allow(parent, auth.Write); err != nil { return nil, false, err } + // CHFS permits PUT to overwrite an existing file only when the target + // itself is writable. A writable parent alone is enough for creation. + if _, err := f.inner.Stat(ctx, name); err == nil { + if err := f.allow(name, auth.Write); err != nil { + return nil, false, err + } + } else if !os.IsNotExist(err) { + return nil, false, err + } return f.inner.Create(ctx, name, body, opts) } func (f *webdavAuthFS) RemoveAll(ctx context.Context, name string, opts *webdav.RemoveAllOptions) error {