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 }