diff --git a/chfs-ng/internal/api/server.go b/chfs-ng/internal/api/server.go index 25d672a..b7b1773 100644 --- a/chfs-ng/internal/api/server.go +++ b/chfs-ng/internal/api/server.go @@ -981,6 +981,9 @@ func (f *webdavStorageFS) Create(ctx context.Context, name string, body io.ReadC if err != nil { return nil, false, err } + if isVirtualStorageRoot(f.store, p) { + return nil, false, virtualRootMutationError() + } _, statErr := f.store.Stat(p) if statErr != nil && !os.IsNotExist(statErr) { return nil, false, statErr @@ -1002,13 +1005,26 @@ func (f *webdavStorageFS) Create(ctx context.Context, name string, body io.ReadC return fi, true, err } +func isVirtualStorageRoot(store storage.Storage, p string) bool { + m, ok := store.(storage.Multi) + if !ok { + return false + } + _, rel, found := m.FindRoot(p) + return found && rel == "" +} + +func virtualRootMutationError() error { + return webdav.NewHTTPError(http.StatusForbidden, fmt.Errorf("cannot modify virtual root")) +} + func (f *webdavStorageFS) RemoveAll(ctx context.Context, name string, opts *webdav.RemoveAllOptions) error { p, err := davSafePath(name) if err != nil { return err } - if p == "" { - return fmt.Errorf("cannot remove storage root") + if p == "" || isVirtualStorageRoot(f.store, p) { + return virtualRootMutationError() } if _, err := f.store.Stat(p); err != nil { if os.IsNotExist(err) { @@ -1038,8 +1054,8 @@ func (f *webdavStorageFS) Mkdir(ctx context.Context, name string) error { if err != nil { return err } - if p == "" { - return fmt.Errorf("cannot create storage root") + if p == "" || isVirtualStorageRoot(f.store, p) { + return virtualRootMutationError() } if _, err := f.store.Stat(p); err == nil { return webdav.NewHTTPError(http.StatusMethodNotAllowed, fmt.Errorf("resource already exists")) @@ -1061,6 +1077,9 @@ 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 isVirtualStorageRoot(f.store, src) || isVirtualStorageRoot(f.store, dst) { + return false, virtualRootMutationError() + } _, dstErr := f.store.Stat(dst) dstExists := dstErr == nil if dstErr != nil && !os.IsNotExist(dstErr) { @@ -1130,6 +1149,9 @@ 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 isVirtualStorageRoot(f.store, src) || isVirtualStorageRoot(f.store, dst) { + return false, virtualRootMutationError() + } 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.