feat: return method not allowed when uploading to read only fs

Instead of an internal server error, we should be returning
method not allowed when trying to upload to a read only
filesystem or make other modifications that are expected to
fail. This will give clearer feedback to the clients
that this is not expected to work.

Fixes #1062
This commit is contained in:
Ben McClelland
2025-03-01 10:50:30 -08:00
parent 955004377d
commit 003719cc74
4 changed files with 30 additions and 2 deletions

View File

@@ -23,6 +23,7 @@ import (
"syscall" "syscall"
"github.com/pkg/xattr" "github.com/pkg/xattr"
"github.com/versity/versitygw/s3err"
) )
const ( const (
@@ -56,10 +57,18 @@ func (x XattrMeta) RetrieveAttribute(f *os.File, bucket, object, attribute strin
// StoreAttribute stores the value of a specific attribute for an object in a bucket. // StoreAttribute stores the value of a specific attribute for an object in a bucket.
func (x XattrMeta) StoreAttribute(f *os.File, bucket, object, attribute string, value []byte) error { func (x XattrMeta) StoreAttribute(f *os.File, bucket, object, attribute string, value []byte) error {
if f != nil { if f != nil {
return xattr.FSet(f, xattrPrefix+attribute, value) err := xattr.FSet(f, xattrPrefix+attribute, value)
if errors.Is(err, syscall.EROFS) {
return s3err.GetAPIError(s3err.ErrMethodNotAllowed)
}
return err
} }
return xattr.Set(filepath.Join(bucket, object), xattrPrefix+attribute, value) err := xattr.Set(filepath.Join(bucket, object), xattrPrefix+attribute, value)
if errors.Is(err, syscall.EROFS) {
return s3err.GetAPIError(s3err.ErrMethodNotAllowed)
}
return err
} }
// DeleteAttribute removes the value of a specific attribute for an object in a bucket. // DeleteAttribute removes the value of a specific attribute for an object in a bucket.
@@ -68,6 +77,9 @@ func (x XattrMeta) DeleteAttribute(bucket, object, attribute string) error {
if errors.Is(err, xattr.ENOATTR) { if errors.Is(err, xattr.ENOATTR) {
return ErrNoSuchKey return ErrNoSuchKey
} }
if errors.Is(err, syscall.EROFS) {
return s3err.GetAPIError(s3err.ErrMethodNotAllowed)
}
return err return err
} }

View File

@@ -362,6 +362,9 @@ func (p *Posix) CreateBucket(ctx context.Context, input *s3.CreateBucketInput, a
return s3err.GetAPIError(s3err.ErrBucketAlreadyExists) return s3err.GetAPIError(s3err.ErrBucketAlreadyExists)
} }
if err != nil { if err != nil {
if errors.Is(err, syscall.EROFS) {
return s3err.GetAPIError(s3err.ErrMethodNotAllowed)
}
return fmt.Errorf("mkdir bucket: %w", err) return fmt.Errorf("mkdir bucket: %w", err)
} }

View File

@@ -29,6 +29,7 @@ import (
"github.com/versity/versitygw/auth" "github.com/versity/versitygw/auth"
"github.com/versity/versitygw/backend" "github.com/versity/versitygw/backend"
"github.com/versity/versitygw/s3err"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
@@ -62,6 +63,10 @@ func (p *Posix) openTmpFile(dir, bucket, obj string, size int64, acct auth.Accou
// this is not supported. // this is not supported.
fd, err := unix.Open(dir, unix.O_RDWR|unix.O_TMPFILE|unix.O_CLOEXEC, defaultFilePerm) fd, err := unix.Open(dir, unix.O_RDWR|unix.O_TMPFILE|unix.O_CLOEXEC, defaultFilePerm)
if err != nil { if err != nil {
if errors.Is(err, syscall.EROFS) {
return nil, s3err.GetAPIError(s3err.ErrMethodNotAllowed)
}
// O_TMPFILE not supported, try fallback // O_TMPFILE not supported, try fallback
err = backend.MkdirAll(dir, uid, gid, doChown, p.newDirPerm) err = backend.MkdirAll(dir, uid, gid, doChown, p.newDirPerm)
if err != nil { if err != nil {

View File

@@ -24,9 +24,11 @@ import (
"io/fs" "io/fs"
"os" "os"
"path/filepath" "path/filepath"
"syscall"
"github.com/versity/versitygw/auth" "github.com/versity/versitygw/auth"
"github.com/versity/versitygw/backend" "github.com/versity/versitygw/backend"
"github.com/versity/versitygw/s3err"
) )
type tmpfile struct { type tmpfile struct {
@@ -43,11 +45,17 @@ func (p *Posix) openTmpFile(dir, bucket, obj string, size int64, acct auth.Accou
var err error var err error
err = backend.MkdirAll(dir, uid, gid, doChown, p.newDirPerm) err = backend.MkdirAll(dir, uid, gid, doChown, p.newDirPerm)
if err != nil { if err != nil {
if errors.Is(err, syscall.EROFS) {
return nil, s3err.GetAPIError(s3err.ErrMethodNotAllowed)
}
return nil, fmt.Errorf("make temp dir: %w", err) return nil, fmt.Errorf("make temp dir: %w", err)
} }
f, err := os.CreateTemp(dir, f, err := os.CreateTemp(dir,
fmt.Sprintf("%x.", sha256.Sum256([]byte(obj)))) fmt.Sprintf("%x.", sha256.Sum256([]byte(obj))))
if err != nil { if err != nil {
if errors.Is(err, syscall.EROFS) {
return nil, s3err.GetAPIError(s3err.ErrMethodNotAllowed)
}
return nil, fmt.Errorf("create temp file: %w", err) return nil, fmt.Errorf("create temp file: %w", err)
} }