mirror of
https://github.com/versity/versitygw.git
synced 2025-12-23 05:05:16 +00:00
feat: add option to configure mode permissions on new directories
We had 0755 hard coded for newly created directories before. This adds a user option to configure what the default mode permissions should be for newly created directories. Fixes #878
This commit is contained in:
@@ -15,11 +15,6 @@ import (
|
|||||||
"github.com/versity/versitygw/s3err"
|
"github.com/versity/versitygw/s3err"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
// TODO: make this configurable
|
|
||||||
defaultDirPerm fs.FileMode = 0755
|
|
||||||
)
|
|
||||||
|
|
||||||
// MkdirAll is similar to os.MkdirAll but it will return
|
// MkdirAll is similar to os.MkdirAll but it will return
|
||||||
// ErrObjectParentIsFile when appropriate
|
// ErrObjectParentIsFile when appropriate
|
||||||
// MkdirAll creates a directory named path,
|
// MkdirAll creates a directory named path,
|
||||||
@@ -32,7 +27,7 @@ var (
|
|||||||
// and returns nil.
|
// and returns nil.
|
||||||
// Any directory created will be set to provided uid/gid ownership
|
// Any directory created will be set to provided uid/gid ownership
|
||||||
// if doChown is true.
|
// if doChown is true.
|
||||||
func MkdirAll(path string, uid, gid int, doChown bool) error {
|
func MkdirAll(path string, uid, gid int, doChown bool, dirPerm fs.FileMode) error {
|
||||||
// Fast path: if we can tell whether path is a directory or file, stop with success or error.
|
// Fast path: if we can tell whether path is a directory or file, stop with success or error.
|
||||||
dir, err := os.Stat(path)
|
dir, err := os.Stat(path)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -55,14 +50,14 @@ func MkdirAll(path string, uid, gid int, doChown bool) error {
|
|||||||
|
|
||||||
if j > 1 {
|
if j > 1 {
|
||||||
// Create parent.
|
// Create parent.
|
||||||
err = MkdirAll(path[:j-1], uid, gid, doChown)
|
err = MkdirAll(path[:j-1], uid, gid, doChown, dirPerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parent now exists; invoke Mkdir and use its result.
|
// Parent now exists; invoke Mkdir and use its result.
|
||||||
err = os.Mkdir(path, defaultDirPerm)
|
err = os.Mkdir(path, dirPerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Handle arguments like "foo/." by
|
// Handle arguments like "foo/." by
|
||||||
// double-checking that directory doesn't exist.
|
// double-checking that directory doesn't exist.
|
||||||
|
|||||||
@@ -68,6 +68,9 @@ type Posix struct {
|
|||||||
|
|
||||||
// bucket versioning directory path
|
// bucket versioning directory path
|
||||||
versioningDir string
|
versioningDir string
|
||||||
|
|
||||||
|
// newDirPerm is the permission to set on newly created directories
|
||||||
|
newDirPerm fs.FileMode
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ backend.Backend = &Posix{}
|
var _ backend.Backend = &Posix{}
|
||||||
@@ -103,6 +106,7 @@ type PosixOpts struct {
|
|||||||
ChownGID bool
|
ChownGID bool
|
||||||
BucketLinks bool
|
BucketLinks bool
|
||||||
VersioningDir string
|
VersioningDir string
|
||||||
|
NewDirPerm fs.FileMode
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(rootdir string, meta meta.MetadataStorer, opts PosixOpts) (*Posix, error) {
|
func New(rootdir string, meta meta.MetadataStorer, opts PosixOpts) (*Posix, error) {
|
||||||
@@ -167,6 +171,7 @@ func New(rootdir string, meta meta.MetadataStorer, opts PosixOpts) (*Posix, erro
|
|||||||
chowngid: opts.ChownGID,
|
chowngid: opts.ChownGID,
|
||||||
bucketlinks: opts.BucketLinks,
|
bucketlinks: opts.BucketLinks,
|
||||||
versioningDir: verioningdirAbs,
|
versioningDir: verioningdirAbs,
|
||||||
|
newDirPerm: opts.NewDirPerm,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,11 +296,6 @@ func (p *Posix) HeadBucket(_ context.Context, input *s3.HeadBucketInput) (*s3.He
|
|||||||
return &s3.HeadBucketOutput{}, nil
|
return &s3.HeadBucketOutput{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
// TODO: make this configurable
|
|
||||||
defaultDirPerm fs.FileMode = 0755
|
|
||||||
)
|
|
||||||
|
|
||||||
func (p *Posix) CreateBucket(ctx context.Context, input *s3.CreateBucketInput, acl []byte) error {
|
func (p *Posix) CreateBucket(ctx context.Context, input *s3.CreateBucketInput, acl []byte) error {
|
||||||
if input.Bucket == nil {
|
if input.Bucket == nil {
|
||||||
return s3err.GetAPIError(s3err.ErrInvalidBucketName)
|
return s3err.GetAPIError(s3err.ErrInvalidBucketName)
|
||||||
@@ -310,7 +310,7 @@ func (p *Posix) CreateBucket(ctx context.Context, input *s3.CreateBucketInput, a
|
|||||||
|
|
||||||
bucket := *input.Bucket
|
bucket := *input.Bucket
|
||||||
|
|
||||||
err := os.Mkdir(bucket, defaultDirPerm)
|
err := os.Mkdir(bucket, p.newDirPerm)
|
||||||
if err != nil && os.IsExist(err) {
|
if err != nil && os.IsExist(err) {
|
||||||
aclJSON, err := p.meta.RetrieveAttribute(nil, bucket, "", aclkey)
|
aclJSON, err := p.meta.RetrieveAttribute(nil, bucket, "", aclkey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -642,7 +642,7 @@ func (p *Posix) createObjVersion(bucket, key string, size int64, acc auth.Accoun
|
|||||||
|
|
||||||
versionPath = filepath.Join(versionBucketPath, versioningKey)
|
versionPath = filepath.Join(versionBucketPath, versioningKey)
|
||||||
|
|
||||||
err = os.MkdirAll(filepath.Join(versionBucketPath, genObjVersionKey(key)), defaultDirPerm)
|
err = os.MkdirAll(filepath.Join(versionBucketPath, genObjVersionKey(key)), p.newDirPerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return versionPath, err
|
return versionPath, err
|
||||||
}
|
}
|
||||||
@@ -1375,7 +1375,7 @@ func (p *Posix) CompleteMultipartUpload(ctx context.Context, input *s3.CompleteM
|
|||||||
dir := filepath.Dir(objname)
|
dir := filepath.Dir(objname)
|
||||||
if dir != "" {
|
if dir != "" {
|
||||||
uid, gid, doChown := p.getChownIDs(acct)
|
uid, gid, doChown := p.getChownIDs(acct)
|
||||||
err = backend.MkdirAll(dir, uid, gid, doChown)
|
err = backend.MkdirAll(dir, uid, gid, doChown, p.newDirPerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -2158,7 +2158,7 @@ func (p *Posix) PutObject(ctx context.Context, po *s3.PutObjectInput) (s3respons
|
|||||||
return s3response.PutObjectOutput{}, s3err.GetAPIError(s3err.ErrDirectoryObjectContainsData)
|
return s3response.PutObjectOutput{}, s3err.GetAPIError(s3err.ErrDirectoryObjectContainsData)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = backend.MkdirAll(name, uid, gid, doChown)
|
err = backend.MkdirAll(name, uid, gid, doChown, p.newDirPerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, syscall.EDQUOT) {
|
if errors.Is(err, syscall.EDQUOT) {
|
||||||
return s3response.PutObjectOutput{}, s3err.GetAPIError(s3err.ErrQuotaExceeded)
|
return s3response.PutObjectOutput{}, s3err.GetAPIError(s3err.ErrQuotaExceeded)
|
||||||
@@ -2248,7 +2248,7 @@ func (p *Posix) PutObject(ctx context.Context, po *s3.PutObjectInput) (s3respons
|
|||||||
|
|
||||||
dir := filepath.Dir(name)
|
dir := filepath.Dir(name)
|
||||||
if dir != "" {
|
if dir != "" {
|
||||||
err = backend.MkdirAll(dir, uid, gid, doChown)
|
err = backend.MkdirAll(dir, uid, gid, doChown, p.newDirPerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return s3response.PutObjectOutput{}, s3err.GetAPIError(s3err.ErrExistingObjectIsDirectory)
|
return s3response.PutObjectOutput{}, s3err.GetAPIError(s3err.ErrExistingObjectIsDirectory)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ type tmpfile struct {
|
|||||||
needsChown bool
|
needsChown bool
|
||||||
uid int
|
uid int
|
||||||
gid int
|
gid int
|
||||||
|
newDirPerm fs.FileMode
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -62,7 +63,7 @@ func (p *Posix) openTmpFile(dir, bucket, obj string, size int64, acct auth.Accou
|
|||||||
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 {
|
||||||
// O_TMPFILE not supported, try fallback
|
// O_TMPFILE not supported, try fallback
|
||||||
err = backend.MkdirAll(dir, uid, gid, doChown)
|
err = backend.MkdirAll(dir, uid, gid, doChown, p.newDirPerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("make temp dir: %w", err)
|
return nil, fmt.Errorf("make temp dir: %w", err)
|
||||||
}
|
}
|
||||||
@@ -108,6 +109,7 @@ func (p *Posix) openTmpFile(dir, bucket, obj string, size int64, acct auth.Accou
|
|||||||
needsChown: doChown,
|
needsChown: doChown,
|
||||||
uid: uid,
|
uid: uid,
|
||||||
gid: gid,
|
gid: gid,
|
||||||
|
newDirPerm: p.newDirPerm,
|
||||||
}
|
}
|
||||||
|
|
||||||
// falloc is best effort, its fine if this fails
|
// falloc is best effort, its fine if this fails
|
||||||
@@ -151,7 +153,7 @@ func (tmp *tmpfile) link() error {
|
|||||||
|
|
||||||
dir := filepath.Dir(objPath)
|
dir := filepath.Dir(objPath)
|
||||||
|
|
||||||
err = backend.MkdirAll(dir, tmp.uid, tmp.gid, tmp.needsChown)
|
err = backend.MkdirAll(dir, tmp.uid, tmp.gid, tmp.needsChown, tmp.newDirPerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("make parent dir: %w", err)
|
return fmt.Errorf("make parent dir: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ func (p *Posix) openTmpFile(dir, bucket, obj string, size int64, acct auth.Accou
|
|||||||
|
|
||||||
// Create a temp file for upload while in progress (see link comments below).
|
// Create a temp file for upload while in progress (see link comments below).
|
||||||
var err error
|
var err error
|
||||||
err = backend.MkdirAll(dir, uid, gid, doChown)
|
err = backend.MkdirAll(dir, uid, gid, doChown, p.newDirPerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("make temp dir: %w", err)
|
return nil, fmt.Errorf("make temp dir: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ type ScoutfsOpts struct {
|
|||||||
ChownGID bool
|
ChownGID bool
|
||||||
GlacierMode bool
|
GlacierMode bool
|
||||||
BucketLinks bool
|
BucketLinks bool
|
||||||
|
NewDirPerm fs.FileMode
|
||||||
}
|
}
|
||||||
|
|
||||||
type ScoutFS struct {
|
type ScoutFS struct {
|
||||||
@@ -74,6 +75,9 @@ type ScoutFS struct {
|
|||||||
// used to determine if chowning is needed
|
// used to determine if chowning is needed
|
||||||
euid int
|
euid int
|
||||||
egid int
|
egid int
|
||||||
|
|
||||||
|
// newDirPerm is the permissions to use when creating new directories
|
||||||
|
newDirPerm fs.FileMode
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ backend.Backend = &ScoutFS{}
|
var _ backend.Backend = &ScoutFS{}
|
||||||
@@ -277,7 +281,7 @@ func (s *ScoutFS) CompleteMultipartUpload(ctx context.Context, input *s3.Complet
|
|||||||
dir := filepath.Dir(objname)
|
dir := filepath.Dir(objname)
|
||||||
if dir != "" {
|
if dir != "" {
|
||||||
uid, gid, doChown := s.getChownIDs(acct)
|
uid, gid, doChown := s.getChownIDs(acct)
|
||||||
err = backend.MkdirAll(dir, uid, gid, doChown)
|
err = backend.MkdirAll(dir, uid, gid, doChown, s.newDirPerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ func New(rootdir string, opts ScoutfsOpts) (*ScoutFS, error) {
|
|||||||
ChownUID: opts.ChownUID,
|
ChownUID: opts.ChownUID,
|
||||||
ChownGID: opts.ChownGID,
|
ChownGID: opts.ChownGID,
|
||||||
BucketLinks: opts.BucketLinks,
|
BucketLinks: opts.BucketLinks,
|
||||||
|
NewDirPerm: opts.NewDirPerm,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -58,6 +59,7 @@ func New(rootdir string, opts ScoutfsOpts) (*ScoutFS, error) {
|
|||||||
chownuid: opts.ChownUID,
|
chownuid: opts.ChownUID,
|
||||||
chowngid: opts.ChownGID,
|
chowngid: opts.ChownGID,
|
||||||
glaciermode: opts.GlacierMode,
|
glaciermode: opts.GlacierMode,
|
||||||
|
newDirPerm: opts.NewDirPerm,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,10 +73,10 @@ type tmpfile struct {
|
|||||||
needsChown bool
|
needsChown bool
|
||||||
uid int
|
uid int
|
||||||
gid int
|
gid int
|
||||||
|
newDirPerm fs.FileMode
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// TODO: make this configurable
|
|
||||||
defaultFilePerm uint32 = 0644
|
defaultFilePerm uint32 = 0644
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -102,6 +104,7 @@ func (s *ScoutFS) openTmpFile(dir, bucket, obj string, size int64, acct auth.Acc
|
|||||||
needsChown: doChown,
|
needsChown: doChown,
|
||||||
uid: uid,
|
uid: uid,
|
||||||
gid: gid,
|
gid: gid,
|
||||||
|
newDirPerm: s.newDirPerm,
|
||||||
}
|
}
|
||||||
|
|
||||||
if doChown {
|
if doChown {
|
||||||
@@ -129,7 +132,7 @@ func (tmp *tmpfile) link() error {
|
|||||||
|
|
||||||
dir := filepath.Dir(objPath)
|
dir := filepath.Dir(objPath)
|
||||||
|
|
||||||
err = backend.MkdirAll(dir, tmp.uid, tmp.gid, tmp.needsChown)
|
err = backend.MkdirAll(dir, tmp.uid, tmp.gid, tmp.needsChown, tmp.newDirPerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("make parent dir: %w", err)
|
return fmt.Errorf("make parent dir: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,7 +58,9 @@ func initPosix(ctx context.Context) {
|
|||||||
log.Fatalf("make temp directory: %v", err)
|
log.Fatalf("make temp directory: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
be, err := posix.New(tempdir, meta.XattrMeta{}, posix.PosixOpts{})
|
be, err := posix.New(tempdir, meta.XattrMeta{}, posix.PosixOpts{
|
||||||
|
NewDirPerm: 0755,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("init posix: %v", err)
|
log.Fatalf("init posix: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
|
"math"
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
"github.com/versity/versitygw/backend/meta"
|
"github.com/versity/versitygw/backend/meta"
|
||||||
@@ -26,6 +28,7 @@ var (
|
|||||||
chownuid, chowngid bool
|
chownuid, chowngid bool
|
||||||
bucketlinks bool
|
bucketlinks bool
|
||||||
versioningDir string
|
versioningDir string
|
||||||
|
dirPerms uint
|
||||||
)
|
)
|
||||||
|
|
||||||
func posixCommand() *cli.Command {
|
func posixCommand() *cli.Command {
|
||||||
@@ -68,6 +71,14 @@ will be translated into the file /mnt/fs/gwroot/mybucket/a/b/c/myobject`,
|
|||||||
EnvVars: []string{"VGW_VERSIONING_DIR"},
|
EnvVars: []string{"VGW_VERSIONING_DIR"},
|
||||||
Destination: &versioningDir,
|
Destination: &versioningDir,
|
||||||
},
|
},
|
||||||
|
&cli.UintFlag{
|
||||||
|
Name: "dir-perms",
|
||||||
|
Usage: "default directory permissions for new directories",
|
||||||
|
EnvVars: []string{"VGW_DIR_PERMS"},
|
||||||
|
Destination: &dirPerms,
|
||||||
|
DefaultText: "0755",
|
||||||
|
Value: 0755,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -83,11 +94,16 @@ func runPosix(ctx *cli.Context) error {
|
|||||||
return fmt.Errorf("posix xattr check: %v", err)
|
return fmt.Errorf("posix xattr check: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if dirPerms > math.MaxUint32 {
|
||||||
|
return fmt.Errorf("invalid directory permissions: %d", dirPerms)
|
||||||
|
}
|
||||||
|
|
||||||
be, err := posix.New(gwroot, meta.XattrMeta{}, posix.PosixOpts{
|
be, err := posix.New(gwroot, meta.XattrMeta{}, posix.PosixOpts{
|
||||||
ChownUID: chownuid,
|
ChownUID: chownuid,
|
||||||
ChownGID: chowngid,
|
ChownGID: chowngid,
|
||||||
BucketLinks: bucketlinks,
|
BucketLinks: bucketlinks,
|
||||||
VersioningDir: versioningDir,
|
VersioningDir: versioningDir,
|
||||||
|
NewDirPerm: fs.FileMode(dirPerms),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("init posix: %v", err)
|
return fmt.Errorf("init posix: %v", err)
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
|
"math"
|
||||||
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
"github.com/versity/versitygw/backend/scoutfs"
|
"github.com/versity/versitygw/backend/scoutfs"
|
||||||
@@ -69,6 +71,14 @@ move interfaces as well as support for tiered filesystems.`,
|
|||||||
EnvVars: []string{"VGW_BUCKET_LINKS"},
|
EnvVars: []string{"VGW_BUCKET_LINKS"},
|
||||||
Destination: &bucketlinks,
|
Destination: &bucketlinks,
|
||||||
},
|
},
|
||||||
|
&cli.UintFlag{
|
||||||
|
Name: "dir-perms",
|
||||||
|
Usage: "default directory permissions for new directories",
|
||||||
|
EnvVars: []string{"VGW_DIR_PERMS"},
|
||||||
|
Destination: &dirPerms,
|
||||||
|
DefaultText: "0755",
|
||||||
|
Value: 0755,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -78,11 +88,16 @@ func runScoutfs(ctx *cli.Context) error {
|
|||||||
return fmt.Errorf("no directory provided for operation")
|
return fmt.Errorf("no directory provided for operation")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if dirPerms > math.MaxUint32 {
|
||||||
|
return fmt.Errorf("invalid directory permissions: %d", dirPerms)
|
||||||
|
}
|
||||||
|
|
||||||
var opts scoutfs.ScoutfsOpts
|
var opts scoutfs.ScoutfsOpts
|
||||||
opts.GlacierMode = glacier
|
opts.GlacierMode = glacier
|
||||||
opts.ChownUID = chownuid
|
opts.ChownUID = chownuid
|
||||||
opts.ChownGID = chowngid
|
opts.ChownGID = chowngid
|
||||||
opts.BucketLinks = bucketlinks
|
opts.BucketLinks = bucketlinks
|
||||||
|
opts.NewDirPerm = fs.FileMode(dirPerms)
|
||||||
|
|
||||||
be, err := scoutfs.New(ctx.Args().Get(0), opts)
|
be, err := scoutfs.New(ctx.Args().Get(0), opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user