diff --git a/backend/mkdir.go b/backend/mkdir.go index 18afeb07..753c7015 100644 --- a/backend/mkdir.go +++ b/backend/mkdir.go @@ -15,11 +15,6 @@ import ( "github.com/versity/versitygw/s3err" ) -var ( - // TODO: make this configurable - defaultDirPerm fs.FileMode = 0755 -) - // MkdirAll is similar to os.MkdirAll but it will return // ErrObjectParentIsFile when appropriate // MkdirAll creates a directory named path, @@ -32,7 +27,7 @@ var ( // and returns nil. // Any directory created will be set to provided uid/gid ownership // 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. dir, err := os.Stat(path) if err == nil { @@ -55,14 +50,14 @@ func MkdirAll(path string, uid, gid int, doChown bool) error { if j > 1 { // Create parent. - err = MkdirAll(path[:j-1], uid, gid, doChown) + err = MkdirAll(path[:j-1], uid, gid, doChown, dirPerm) if err != nil { return err } } // Parent now exists; invoke Mkdir and use its result. - err = os.Mkdir(path, defaultDirPerm) + err = os.Mkdir(path, dirPerm) if err != nil { // Handle arguments like "foo/." by // double-checking that directory doesn't exist. diff --git a/backend/posix/posix.go b/backend/posix/posix.go index 2b55eb2b..9a73f04d 100644 --- a/backend/posix/posix.go +++ b/backend/posix/posix.go @@ -68,6 +68,9 @@ type Posix struct { // bucket versioning directory path versioningDir string + + // newDirPerm is the permission to set on newly created directories + newDirPerm fs.FileMode } var _ backend.Backend = &Posix{} @@ -103,6 +106,7 @@ type PosixOpts struct { ChownGID bool BucketLinks bool VersioningDir string + NewDirPerm fs.FileMode } 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, bucketlinks: opts.BucketLinks, versioningDir: verioningdirAbs, + newDirPerm: opts.NewDirPerm, }, nil } @@ -271,11 +276,6 @@ func (p *Posix) HeadBucket(_ context.Context, input *s3.HeadBucketInput) (*s3.He 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 { if input.Bucket == nil { return s3err.GetAPIError(s3err.ErrInvalidBucketName) @@ -290,7 +290,7 @@ func (p *Posix) CreateBucket(ctx context.Context, input *s3.CreateBucketInput, a bucket := *input.Bucket - err := os.Mkdir(bucket, defaultDirPerm) + err := os.Mkdir(bucket, p.newDirPerm) if err != nil && os.IsExist(err) { aclJSON, err := p.meta.RetrieveAttribute(nil, bucket, "", aclkey) if err != nil { @@ -597,7 +597,7 @@ func (p *Posix) createObjVersion(bucket, key string, size int64, acc auth.Accoun 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 { return versionPath, err } @@ -1209,7 +1209,7 @@ func (p *Posix) CompleteMultipartUpload(ctx context.Context, input *s3.CompleteM dir := filepath.Dir(objname) if dir != "" { 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 { return nil, err } @@ -1990,7 +1990,7 @@ func (p *Posix) PutObject(ctx context.Context, po *s3.PutObjectInput) (s3respons 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 errors.Is(err, syscall.EDQUOT) { return s3response.PutObjectOutput{}, s3err.GetAPIError(s3err.ErrQuotaExceeded) @@ -2069,7 +2069,7 @@ func (p *Posix) PutObject(ctx context.Context, po *s3.PutObjectInput) (s3respons dir := filepath.Dir(name) if dir != "" { - err = backend.MkdirAll(dir, uid, gid, doChown) + err = backend.MkdirAll(dir, uid, gid, doChown, p.newDirPerm) if err != nil { return s3response.PutObjectOutput{}, s3err.GetAPIError(s3err.ErrExistingObjectIsDirectory) } diff --git a/backend/posix/with_otmpfile.go b/backend/posix/with_otmpfile.go index cc41da26..ac0281e1 100644 --- a/backend/posix/with_otmpfile.go +++ b/backend/posix/with_otmpfile.go @@ -43,6 +43,7 @@ type tmpfile struct { needsChown bool uid int gid int + newDirPerm fs.FileMode } 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) if err != nil { // 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 { 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, uid: uid, gid: gid, + newDirPerm: p.newDirPerm, } // falloc is best effort, its fine if this fails @@ -151,7 +153,7 @@ func (tmp *tmpfile) link() error { 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 { return fmt.Errorf("make parent dir: %w", err) } diff --git a/backend/posix/without_otmpfile.go b/backend/posix/without_otmpfile.go index 717f3c6c..ed742377 100644 --- a/backend/posix/without_otmpfile.go +++ b/backend/posix/without_otmpfile.go @@ -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). var err error - err = backend.MkdirAll(dir, uid, gid, doChown) + err = backend.MkdirAll(dir, uid, gid, doChown, p.newDirPerm) if err != nil { return nil, fmt.Errorf("make temp dir: %w", err) } diff --git a/backend/scoutfs/scoutfs.go b/backend/scoutfs/scoutfs.go index c57651de..6f17c755 100644 --- a/backend/scoutfs/scoutfs.go +++ b/backend/scoutfs/scoutfs.go @@ -44,6 +44,7 @@ type ScoutfsOpts struct { ChownGID bool GlacierMode bool BucketLinks bool + NewDirPerm fs.FileMode } type ScoutFS struct { @@ -74,6 +75,9 @@ type ScoutFS struct { // used to determine if chowning is needed euid int egid int + + // newDirPerm is the permissions to use when creating new directories + newDirPerm fs.FileMode } var _ backend.Backend = &ScoutFS{} @@ -277,7 +281,7 @@ func (s *ScoutFS) CompleteMultipartUpload(ctx context.Context, input *s3.Complet dir := filepath.Dir(objname) if dir != "" { 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 { return nil, err } diff --git a/backend/scoutfs/scoutfs_compat.go b/backend/scoutfs/scoutfs_compat.go index 9482cc07..97577102 100644 --- a/backend/scoutfs/scoutfs_compat.go +++ b/backend/scoutfs/scoutfs_compat.go @@ -40,6 +40,7 @@ func New(rootdir string, opts ScoutfsOpts) (*ScoutFS, error) { ChownUID: opts.ChownUID, ChownGID: opts.ChownGID, BucketLinks: opts.BucketLinks, + NewDirPerm: opts.NewDirPerm, }) if err != nil { return nil, err @@ -58,6 +59,7 @@ func New(rootdir string, opts ScoutfsOpts) (*ScoutFS, error) { chownuid: opts.ChownUID, chowngid: opts.ChownGID, glaciermode: opts.GlacierMode, + newDirPerm: opts.NewDirPerm, }, nil } @@ -71,10 +73,10 @@ type tmpfile struct { needsChown bool uid int gid int + newDirPerm fs.FileMode } var ( - // TODO: make this configurable defaultFilePerm uint32 = 0644 ) @@ -102,6 +104,7 @@ func (s *ScoutFS) openTmpFile(dir, bucket, obj string, size int64, acct auth.Acc needsChown: doChown, uid: uid, gid: gid, + newDirPerm: s.newDirPerm, } if doChown { @@ -129,7 +132,7 @@ func (tmp *tmpfile) link() error { 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 { return fmt.Errorf("make parent dir: %w", err) } diff --git a/cmd/versitygw/posix.go b/cmd/versitygw/posix.go index 3c8421de..0fb1e7fc 100644 --- a/cmd/versitygw/posix.go +++ b/cmd/versitygw/posix.go @@ -16,6 +16,8 @@ package main import ( "fmt" + "io/fs" + "math" "github.com/urfave/cli/v2" "github.com/versity/versitygw/backend/meta" @@ -26,6 +28,7 @@ var ( chownuid, chowngid bool bucketlinks bool versioningDir string + dirPerms uint ) func posixCommand() *cli.Command { @@ -68,11 +71,20 @@ will be translated into the file /mnt/fs/gwroot/mybucket/a/b/c/myobject`, EnvVars: []string{"VGW_VERSIONING_DIR"}, 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, + }, }, } } func runPosix(ctx *cli.Context) error { + fmt.Println("posix backend perms:", dirPerms) if ctx.NArg() == 0 { return fmt.Errorf("no directory provided for operation") } @@ -83,11 +95,16 @@ func runPosix(ctx *cli.Context) error { 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{ ChownUID: chownuid, ChownGID: chowngid, BucketLinks: bucketlinks, VersioningDir: versioningDir, + NewDirPerm: fs.FileMode(dirPerms), }) if err != nil { return fmt.Errorf("init posix: %v", err) diff --git a/cmd/versitygw/scoutfs.go b/cmd/versitygw/scoutfs.go index d7a3f792..93274a55 100644 --- a/cmd/versitygw/scoutfs.go +++ b/cmd/versitygw/scoutfs.go @@ -16,6 +16,8 @@ package main import ( "fmt" + "io/fs" + "math" "github.com/urfave/cli/v2" "github.com/versity/versitygw/backend/scoutfs" @@ -69,6 +71,14 @@ move interfaces as well as support for tiered filesystems.`, EnvVars: []string{"VGW_BUCKET_LINKS"}, 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") } + if dirPerms > math.MaxUint32 { + return fmt.Errorf("invalid directory permissions: %d", dirPerms) + } + var opts scoutfs.ScoutfsOpts opts.GlacierMode = glacier opts.ChownUID = chownuid opts.ChownGID = chowngid opts.BucketLinks = bucketlinks + opts.NewDirPerm = fs.FileMode(dirPerms) be, err := scoutfs.New(ctx.Args().Get(0), opts) if err != nil {