feat: add configurable file permissions for new objects

Replace the hardcoded 0644 defaultFilePerm with a NewFilePerm option on
the posix and scoutfs backends, exposed as the --file-perms flag and
VGW_FILE_PERMS env var alongside the existing dir-perms option.

The mode passed to open() is masked by the process umask, so the
O_TMPFILE path now chmods explicitly to match the CreateTemp fallback
path and give new objects the configured mode regardless of umask.
This commit is contained in:
Kyd Cao
2026-08-28 08:51:03 -07:00
committed by Ben McClelland
parent f04bd6a068
commit 45a532e6a6
8 changed files with 191 additions and 76 deletions
+20 -1
View File
@@ -24,11 +24,17 @@ import (
"github.com/versity/versitygw/backend/posix"
)
// maxFilePerms is the highest accepted value for the file-perms option. The
// posix backend only applies permission bits to new objects, so anything
// above this would be silently dropped.
const maxFilePerms = 0777
var (
chownuid, chowngid bool
bucketlinks bool
versioningDir string
dirPerms uint
filePerms uint
sidecar string
nometa bool
forceNoTmpFile bool
@@ -90,6 +96,14 @@ will be translated into the file /mnt/fs/gwroot/mybucket/a/b/c/myobject`,
DefaultText: "0755",
Value: 0755,
},
&cli.UintFlag{
Name: "file-perms",
Usage: "default file permissions for new objects",
EnvVars: []string{"VGW_FILE_PERMS"},
Destination: &filePerms,
DefaultText: "0644",
Value: 0644,
},
&cli.StringFlag{
Name: "sidecar",
Usage: "use provided sidecar directory to store metadata",
@@ -161,6 +175,10 @@ func runPosix(ctx *cli.Context) error {
return fmt.Errorf("invalid directory permissions: %d", dirPerms)
}
if filePerms > maxFilePerms {
return fmt.Errorf("invalid file permissions: %o, must be within 0000-0777", filePerms)
}
if nometa && sidecar != "" {
return fmt.Errorf("cannot use both nometa and sidecar metadata")
}
@@ -174,7 +192,6 @@ func runPosix(ctx *cli.Context) error {
ChownGID: chowngid,
BucketLinks: bucketlinks,
VersioningDir: versioningDir,
NewDirPerm: fs.FileMode(dirPerms),
ForceNoTmpFile: forceNoTmpFile,
ForceNoCopyFileRange: forceNoCopyFileRange,
EnableODirect: enableODirect,
@@ -185,6 +202,8 @@ func runPosix(ctx *cli.Context) error {
DefaultEtag: defaultEtag,
DataIntegrityEtag: dataIntegrityEtag,
}
opts.SetNewDirPerm(fs.FileMode(dirPerms))
opts.SetNewFilePerm(fs.FileMode(filePerms))
var ms meta.MetadataStorer
switch {
+14 -1
View File
@@ -95,6 +95,14 @@ move interfaces as well as support for tiered filesystems.`,
DefaultText: "0755",
Value: 0755,
},
&cli.UintFlag{
Name: "file-perms",
Usage: "default file permissions for new objects",
EnvVars: []string{"VGW_FILE_PERMS"},
Destination: &filePerms,
DefaultText: "0644",
Value: 0644,
},
&cli.BoolFlag{
Name: "disable-noarchive",
Usage: "disable setting noarchive for multipart part uploads",
@@ -133,6 +141,10 @@ func runScoutfs(ctx *cli.Context) error {
return fmt.Errorf("invalid directory permissions: %d", dirPerms)
}
if filePerms > maxFilePerms {
return fmt.Errorf("invalid file permissions: %o, must be within 0000-0777", filePerms)
}
if actionsConcurrency <= 0 {
return fmt.Errorf("concurrency must be positive, got %d", actionsConcurrency)
}
@@ -142,7 +154,6 @@ func runScoutfs(ctx *cli.Context) error {
opts.ChownUID = chownuid
opts.ChownGID = chowngid
opts.BucketLinks = bucketlinks
opts.NewDirPerm = fs.FileMode(dirPerms)
opts.DisableNoArchive = disableNoArchive
opts.VersioningDir = versioningDir
opts.ValidateBucketNames = DisableStrictBucketNames
@@ -151,6 +162,8 @@ func runScoutfs(ctx *cli.Context) error {
opts.CopyObjectThreshold = CopyObjectThreshold
opts.DefaultEtag = defaultEtag
opts.DataIntegrityEtag = dataIntegrityEtag
opts.SetNewDirPerm(fs.FileMode(dirPerms))
opts.SetNewFilePerm(fs.FileMode(filePerms))
be, err := scoutfs.New(ctx.Args().Get(0), opts)
if err != nil {