diff --git a/backend/posix/posix.go b/backend/posix/posix.go index 834d3145..e80aa1f4 100644 --- a/backend/posix/posix.go +++ b/backend/posix/posix.go @@ -61,6 +61,10 @@ type Posix struct { // used to determine if chowning is needed euid int egid int + + // bucketlinks is a flag to enable symlinks to directories at the top + // level gateway directory to be treated as buckets the same as directories + bucketlinks bool } var _ backend.Backend = &Posix{} @@ -87,8 +91,9 @@ const ( ) type PosixOpts struct { - ChownUID bool - ChownGID bool + ChownUID bool + ChownGID bool + BucketLinks bool } func New(rootdir string, meta meta.MetadataStorer, opts PosixOpts) (*Posix, error) { @@ -103,13 +108,14 @@ func New(rootdir string, meta meta.MetadataStorer, opts PosixOpts) (*Posix, erro } return &Posix{ - meta: meta, - rootfd: f, - rootdir: rootdir, - euid: os.Geteuid(), - egid: os.Getegid(), - chownuid: opts.ChownUID, - chowngid: opts.ChownGID, + meta: meta, + rootfd: f, + rootdir: rootdir, + euid: os.Geteuid(), + egid: os.Getegid(), + chownuid: opts.ChownUID, + chowngid: opts.ChownGID, + bucketlinks: opts.BucketLinks, }, nil } @@ -130,17 +136,25 @@ func (p *Posix) ListBuckets(_ context.Context, owner string, isAdmin bool) (s3re var buckets []s3response.ListAllMyBucketsEntry for _, entry := range entries { - if !entry.IsDir() { - // buckets must be a directory - continue - } - fi, err := entry.Info() if err != nil { // skip entries returning errors continue } + if p.bucketlinks && entry.Type() == fs.ModeSymlink { + fi, err = os.Stat(entry.Name()) + if err != nil { + // skip entries returning errors + continue + } + } + + if !fi.IsDir() { + // buckets must be a directory + continue + } + // return all the buckets for admin users if isAdmin { buckets = append(buckets, s3response.ListAllMyBucketsEntry{ diff --git a/backend/scoutfs/scoutfs.go b/backend/scoutfs/scoutfs.go index 5ef1979c..b3a3a8a6 100644 --- a/backend/scoutfs/scoutfs.go +++ b/backend/scoutfs/scoutfs.go @@ -42,6 +42,7 @@ type ScoutfsOpts struct { ChownUID bool ChownGID bool GlacierMode bool + BucketLinks bool } type ScoutFS struct { diff --git a/backend/scoutfs/scoutfs_compat.go b/backend/scoutfs/scoutfs_compat.go index fdd15c29..fe937636 100644 --- a/backend/scoutfs/scoutfs_compat.go +++ b/backend/scoutfs/scoutfs_compat.go @@ -37,8 +37,9 @@ func New(rootdir string, opts ScoutfsOpts) (*ScoutFS, error) { metastore := meta.XattrMeta{} p, err := posix.New(rootdir, metastore, posix.PosixOpts{ - ChownUID: opts.ChownUID, - ChownGID: opts.ChownGID, + ChownUID: opts.ChownUID, + ChownGID: opts.ChownGID, + BucketLinks: opts.BucketLinks, }) if err != nil { return nil, err diff --git a/cmd/versitygw/posix.go b/cmd/versitygw/posix.go index 9d234c80..b91fde59 100644 --- a/cmd/versitygw/posix.go +++ b/cmd/versitygw/posix.go @@ -24,6 +24,7 @@ import ( var ( chownuid, chowngid bool + bucketlinks bool ) func posixCommand() *cli.Command { @@ -54,6 +55,12 @@ will be translated into the file /mnt/fs/gwroot/mybucket/a/b/c/myobject`, EnvVars: []string{"VGW_CHOWN_GID"}, Destination: &chowngid, }, + &cli.BoolFlag{ + Name: "bucketlinks", + Usage: "allow symlinked directories at bucket level to be treated as buckets", + EnvVars: []string{"VGW_BUCKET_LINKS"}, + Destination: &bucketlinks, + }, }, } } @@ -70,8 +77,9 @@ func runPosix(ctx *cli.Context) error { } be, err := posix.New(gwroot, meta.XattrMeta{}, posix.PosixOpts{ - ChownUID: chownuid, - ChownGID: chowngid, + ChownUID: chownuid, + ChownGID: chowngid, + BucketLinks: bucketlinks, }) if err != nil { return fmt.Errorf("init posix: %v", err) diff --git a/cmd/versitygw/scoutfs.go b/cmd/versitygw/scoutfs.go index 8ab1470e..d7a3f792 100644 --- a/cmd/versitygw/scoutfs.go +++ b/cmd/versitygw/scoutfs.go @@ -63,6 +63,12 @@ move interfaces as well as support for tiered filesystems.`, EnvVars: []string{"VGW_CHOWN_GID"}, Destination: &chowngid, }, + &cli.BoolFlag{ + Name: "bucketlinks", + Usage: "allow symlinked directories at bucket level to be treated as buckets", + EnvVars: []string{"VGW_BUCKET_LINKS"}, + Destination: &bucketlinks, + }, }, } } @@ -76,6 +82,7 @@ func runScoutfs(ctx *cli.Context) error { opts.GlacierMode = glacier opts.ChownUID = chownuid opts.ChownGID = chowngid + opts.BucketLinks = bucketlinks be, err := scoutfs.New(ctx.Args().Get(0), opts) if err != nil { diff --git a/extra/example.conf b/extra/example.conf index f84213cc..09687df0 100644 --- a/extra/example.conf +++ b/extra/example.conf @@ -305,6 +305,10 @@ ROOT_SECRET_ACCESS_KEY= #VGW_CHOWN_UID=false #VGW_CHOWN_GID=false +# The VGW_BUCKET_LINKS option will enable the gateway to treat symbolic links +# to directories at the top level gateway directory as buckets. +#VGW_BUCKET_LINKS=false + ########### # scoutfs # ########### @@ -336,6 +340,10 @@ ROOT_SECRET_ACCESS_KEY= #VGW_CHOWN_UID=false #VGW_CHOWN_GID=false +# The VGW_BUCKET_LINKS option will enable the gateway to treat symbolic links +# to directories at the top level gateway directory as buckets. +#VGW_BUCKET_LINKS=false + ###### # s3 # ######