github.com/cs3org/reva/v2@v2.27.7/pkg/storage/fs/posix/posix.go (about)

     1  //go:build linux
     2  // +build linux
     3  
     4  // Copyright 2018-2021 CERN
     5  //
     6  // Licensed under the Apache License, Version 2.0 (the "License");
     7  // you may not use this file except in compliance with the License.
     8  // You may obtain a copy of the License at
     9  //
    10  //     http://www.apache.org/licenses/LICENSE-2.0
    11  //
    12  // Unless required by applicable law or agreed to in writing, software
    13  // distributed under the License is distributed on an "AS IS" BASIS,
    14  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15  // See the License for the specific language governing permissions and
    16  // limitations under the License.
    17  //
    18  // In applying this license, CERN does not waive the privileges and immunities
    19  // granted to it by virtue of its status as an Intergovernmental Organization
    20  // or submit itself to any jurisdiction.
    21  
    22  package posix
    23  
    24  import (
    25  	"context"
    26  	"fmt"
    27  	"os"
    28  	"syscall"
    29  
    30  	"github.com/rs/zerolog"
    31  	tusd "github.com/tus/tusd/v2/pkg/handler"
    32  	microstore "go-micro.dev/v4/store"
    33  
    34  	"github.com/cs3org/reva/v2/pkg/events"
    35  	"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
    36  	"github.com/cs3org/reva/v2/pkg/storage"
    37  	"github.com/cs3org/reva/v2/pkg/storage/fs/posix/blobstore"
    38  	"github.com/cs3org/reva/v2/pkg/storage/fs/posix/lookup"
    39  	"github.com/cs3org/reva/v2/pkg/storage/fs/posix/options"
    40  	"github.com/cs3org/reva/v2/pkg/storage/fs/posix/timemanager"
    41  	"github.com/cs3org/reva/v2/pkg/storage/fs/posix/trashbin"
    42  	"github.com/cs3org/reva/v2/pkg/storage/fs/posix/tree"
    43  	"github.com/cs3org/reva/v2/pkg/storage/fs/registry"
    44  	"github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs"
    45  	"github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/aspects"
    46  	"github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/metadata"
    47  	"github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node"
    48  	"github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/permissions"
    49  	"github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/upload"
    50  	"github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/usermapper"
    51  	"github.com/cs3org/reva/v2/pkg/storage/utils/middleware"
    52  	"github.com/cs3org/reva/v2/pkg/store"
    53  	"github.com/pkg/errors"
    54  )
    55  
    56  func init() {
    57  	registry.Register("posix", New)
    58  }
    59  
    60  type posixFS struct {
    61  	storage.FS
    62  
    63  	um usermapper.Mapper
    64  }
    65  
    66  // New returns an implementation to of the storage.FS interface that talk to
    67  // a local filesystem.
    68  func New(m map[string]interface{}, stream events.Stream, log *zerolog.Logger) (storage.FS, error) {
    69  	o, err := options.New(m)
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  
    74  	fs := &posixFS{}
    75  	um := usermapper.NewUnixMapper()
    76  
    77  	var lu *lookup.Lookup
    78  	switch o.MetadataBackend {
    79  	case "xattrs":
    80  		lu = lookup.New(metadata.NewXattrsBackend(o.Root, o.FileMetadataCache), um, o, &timemanager.Manager{})
    81  	case "messagepack":
    82  		lu = lookup.New(metadata.NewMessagePackBackend(o.Root, o.FileMetadataCache), um, o, &timemanager.Manager{})
    83  	default:
    84  		return nil, fmt.Errorf("unknown metadata backend %s, only 'messagepack' or 'xattrs' (default) supported", o.MetadataBackend)
    85  	}
    86  
    87  	trashbin, err := trashbin.New(o, lu, log)
    88  	if err != nil {
    89  		return nil, err
    90  	}
    91  	err = trashbin.Setup(fs)
    92  	if err != nil {
    93  		return nil, err
    94  	}
    95  
    96  	bs, err := blobstore.New(o.Root)
    97  	if err != nil {
    98  		return nil, err
    99  	}
   100  
   101  	switch o.IDCache.Store {
   102  	case "", "memory", "noop":
   103  		return nil, fmt.Errorf("the posix driver requires a shared id cache, e.g. nats-js-kv or redis")
   104  	}
   105  
   106  	tp, err := tree.New(lu, bs, um, trashbin, o, stream, store.Create(
   107  		store.Store(o.IDCache.Store),
   108  		store.TTL(o.IDCache.TTL),
   109  		store.Size(o.IDCache.Size),
   110  		microstore.Nodes(o.IDCache.Nodes...),
   111  		microstore.Database(o.IDCache.Database),
   112  		microstore.Table(o.IDCache.Table),
   113  		store.DisablePersistence(o.IDCache.DisablePersistence),
   114  		store.Authentication(o.IDCache.AuthUsername, o.IDCache.AuthPassword),
   115  	), log)
   116  	if err != nil {
   117  		return nil, err
   118  	}
   119  
   120  	permissionsSelector, err := pool.PermissionsSelector(o.PermissionsSVC, pool.WithTLSMode(o.PermTLSMode))
   121  	if err != nil {
   122  		return nil, err
   123  	}
   124  
   125  	p := permissions.NewPermissions(node.NewPermissions(lu), permissionsSelector)
   126  
   127  	aspects := aspects.Aspects{
   128  		Lookup:            lu,
   129  		Tree:              tp,
   130  		Permissions:       p,
   131  		EventStream:       stream,
   132  		UserMapper:        um,
   133  		DisableVersioning: true,
   134  		Trashbin:          trashbin,
   135  	}
   136  
   137  	dfs, err := decomposedfs.New(&o.Options, aspects, log)
   138  	if err != nil {
   139  		return nil, err
   140  	}
   141  
   142  	hooks := []middleware.Hook{}
   143  	if o.UseSpaceGroups {
   144  		resolveSpaceHook := func(methodName string, ctx context.Context, spaceID string) (context.Context, middleware.UnHook, error) {
   145  			if spaceID == "" {
   146  				return ctx, nil, nil
   147  			}
   148  
   149  			spaceRoot := lu.InternalPath(spaceID, spaceID)
   150  			fi, err := os.Stat(spaceRoot)
   151  			if err != nil {
   152  				return ctx, nil, err
   153  			}
   154  
   155  			ctx = context.WithValue(ctx, decomposedfs.CtxKeySpaceGID, fi.Sys().(*syscall.Stat_t).Gid)
   156  
   157  			return ctx, nil, err
   158  		}
   159  		scopeSpaceGroupHook := func(methodName string, ctx context.Context, spaceID string) (context.Context, middleware.UnHook, error) {
   160  			spaceGID, ok := ctx.Value(decomposedfs.CtxKeySpaceGID).(uint32)
   161  			if !ok {
   162  				return ctx, nil, nil
   163  			}
   164  
   165  			unscope, err := um.ScopeUserByIds(-1, int(spaceGID))
   166  			if err != nil {
   167  				return ctx, nil, errors.Wrap(err, "failed to scope user")
   168  			}
   169  
   170  			return ctx, unscope, nil
   171  		}
   172  		hooks = append(hooks, resolveSpaceHook, scopeSpaceGroupHook)
   173  	}
   174  
   175  	mw := middleware.NewFS(dfs, hooks...)
   176  	fs.FS = mw
   177  	fs.um = um
   178  
   179  	return fs, nil
   180  }
   181  
   182  // ListUploadSessions returns the upload sessions matching the given filter
   183  func (fs *posixFS) ListUploadSessions(ctx context.Context, filter storage.UploadSessionFilter) ([]storage.UploadSession, error) {
   184  	return fs.FS.(storage.UploadSessionLister).ListUploadSessions(ctx, filter)
   185  }
   186  
   187  // UseIn tells the tus upload middleware which extensions it supports.
   188  func (fs *posixFS) UseIn(composer *tusd.StoreComposer) {
   189  	fs.FS.(storage.ComposableFS).UseIn(composer)
   190  }
   191  
   192  // NewUpload returns a new tus Upload instance
   193  func (fs *posixFS) NewUpload(ctx context.Context, info tusd.FileInfo) (upload tusd.Upload, err error) {
   194  	return fs.FS.(tusd.DataStore).NewUpload(ctx, info)
   195  }
   196  
   197  // NewUpload returns a new tus Upload instance
   198  func (fs *posixFS) GetUpload(ctx context.Context, id string) (upload tusd.Upload, err error) {
   199  	return fs.FS.(tusd.DataStore).GetUpload(ctx, id)
   200  }
   201  
   202  // AsTerminatableUpload returns a TerminatableUpload
   203  // To implement the termination extension as specified in https://tus.io/protocols/resumable-upload.html#termination
   204  // the storage needs to implement AsTerminatableUpload
   205  func (fs *posixFS) AsTerminatableUpload(up tusd.Upload) tusd.TerminatableUpload {
   206  	return up.(*upload.OcisSession)
   207  }
   208  
   209  // AsLengthDeclarableUpload returns a LengthDeclarableUpload
   210  // To implement the creation-defer-length extension as specified in https://tus.io/protocols/resumable-upload.html#creation
   211  // the storage needs to implement AsLengthDeclarableUpload
   212  func (fs *posixFS) AsLengthDeclarableUpload(up tusd.Upload) tusd.LengthDeclarableUpload {
   213  	return up.(*upload.OcisSession)
   214  }
   215  
   216  // AsConcatableUpload returns a ConcatableUpload
   217  // To implement the concatenation extension as specified in https://tus.io/protocols/resumable-upload.html#concatenation
   218  // the storage needs to implement AsConcatableUpload
   219  func (fs *posixFS) AsConcatableUpload(up tusd.Upload) tusd.ConcatableUpload {
   220  	return up.(*upload.OcisSession)
   221  }