@@ -38,6 +38,7 @@ import (
3838 goceph "github.com/ceph/go-ceph/cephfs"
3939 provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
4040 typepb "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
41+ typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
4142 "github.com/cs3org/reva/pkg/appctx"
4243 "github.com/cs3org/reva/pkg/errtypes"
4344 "github.com/cs3org/reva/pkg/storage"
@@ -151,6 +152,21 @@ func (fs *cephfs) CreateDir(ctx context.Context, ref *provider.Reference) error
151152 return getRevaError (ctx , err )
152153}
153154
155+ func getRecycleTargetFromPath (path string , recyclePath string , recyclePathDepth int ) (string , error ) {
156+ // Tokenize the given (absolute) path
157+ components := strings .Split (filepath .Clean (string (filepath .Separator )+ path ), string (filepath .Separator ))
158+ if recyclePathDepth > len (components )- 1 {
159+ return "" , errors .New ("path is too short" )
160+ }
161+
162+ // And construct the target by injecting the recyclePath at the required depth
163+ var target []string = []string {string (filepath .Separator )}
164+ target = append (target , components [:recyclePathDepth + 1 ]... )
165+ target = append (target , recyclePath , time .Now ().Format ("2006/01/02" ))
166+ target = append (target , components [recyclePathDepth + 1 :]... )
167+ return filepath .Join (target ... ), nil
168+ }
169+
154170func (fs * cephfs ) Delete (ctx context.Context , ref * provider.Reference ) (err error ) {
155171 var path string
156172 user := fs .makeUser (ctx )
@@ -161,8 +177,16 @@ func (fs *cephfs) Delete(ctx context.Context, ref *provider.Reference) (err erro
161177
162178 log := appctx .GetLogger (ctx )
163179 user .op (func (cv * cacheVal ) {
164- if err = cv .mount .Unlink (path ); err != nil && err .Error () == errIsADirectory {
165- err = cv .mount .RemoveDir (path )
180+ if fs .conf .RecyclePath != "" {
181+ // Recycle bin is configured, move to recycle as opposed to unlink
182+ targetPath , err := getRecycleTargetFromPath (path , fs .conf .RecyclePath , fs .conf .RecyclePathDepth )
183+ if err == nil {
184+ err = cv .mount .Rename (path , targetPath )
185+ }
186+ } else {
187+ if err = cv .mount .Unlink (path ); err != nil && err .Error () == errIsADirectory {
188+ err = cv .mount .RemoveDir (path )
189+ }
166190 }
167191 })
168192
@@ -502,24 +526,113 @@ func (fs *cephfs) TouchFile(ctx context.Context, ref *provider.Reference) error
502526 return getRevaError (ctx , err )
503527}
504528
505- func (fs * cephfs ) EmptyRecycle (ctx context.Context ) error {
506- return errtypes .NotSupported ("unimplemented" )
507- }
529+ func (fs * cephfs ) listDeletedEntries (ctx context.Context , maxentries int , basePath string , from , to time.Time ) (res []* provider.RecycleItem , err error ) {
530+ res = []* provider.RecycleItem {}
531+ user := fs .makeUser (ctx )
532+ count := 0
533+ rootRecyclePath := filepath .Join (basePath , fs .conf .RecyclePath )
534+ for d := to ; ! d .Before (from ); d = d .AddDate (0 , 0 , - 1 ) {
508535
509- func (fs * cephfs ) CreateStorageSpace (ctx context.Context , req * provider.CreateStorageSpaceRequest ) (r * provider.CreateStorageSpaceResponse , err error ) {
510- return nil , errtypes .NotSupported ("unimplemented" )
536+ user .op (func (cv * cacheVal ) {
537+ var dir * goceph.Directory
538+ if dir , err = cv .mount .OpenDir (filepath .Join (rootRecyclePath , d .Format ("2006/01/02" ))); err != nil {
539+ return
540+ }
541+ defer closeDir (dir )
542+
543+ var entry * goceph.DirEntryPlus
544+ for entry , err = dir .ReadDirPlus (goceph .StatxBasicStats , 0 ); entry != nil && err == nil ; entry , err = dir .ReadDirPlus (goceph .StatxBasicStats , 0 ) {
545+ //TODO(lopresti) validate content of entry.Name() here.
546+ targetPath := filepath .Join (basePath , entry .Name ())
547+ stat := entry .Statx ()
548+ res = append (res , & provider.RecycleItem {
549+ Ref : & provider.Reference {Path : targetPath },
550+ Key : filepath .Join (rootRecyclePath , targetPath ),
551+ Size : stat .Size ,
552+ DeletionTime : & typesv1beta1.Timestamp {
553+ Seconds : uint64 (stat .Mtime .Sec ),
554+ Nanos : uint32 (stat .Mtime .Nsec ),
555+ },
556+ })
557+
558+ count += 1
559+ if count > maxentries {
560+ err = errtypes .BadRequest ("list too long" )
561+ return
562+ }
563+ }
564+ })
565+ }
566+ return res , err
511567}
512568
513569func (fs * cephfs ) ListRecycle (ctx context.Context , basePath , key , relativePath string , from , to * typepb.Timestamp ) ([]* provider.RecycleItem , error ) {
514- return nil , errtypes .NotSupported ("unimplemented" )
570+ md , err := fs .GetMD (ctx , & provider.Reference {Path : basePath }, nil )
571+ if err != nil {
572+ return nil , err
573+ }
574+ if ! md .PermissionSet .ListRecycle {
575+ return nil , errtypes .PermissionDenied ("cephfs: user doesn't have permissions to restore recycled items" )
576+ }
577+
578+ var dateFrom , dateTo time.Time
579+ if from != nil && to != nil {
580+ dateFrom = time .Unix (int64 (from .Seconds ), 0 )
581+ dateTo = time .Unix (int64 (to .Seconds ), 0 )
582+ if dateFrom .AddDate (0 , 0 , fs .conf .MaxDaysInRecycleList ).Before (dateTo ) {
583+ return nil , errtypes .BadRequest ("cephfs: too many days requested in listing the recycle bin" )
584+ }
585+ } else {
586+ // if no date range was given, list up to two days ago
587+ dateTo = time .Now ()
588+ dateFrom = dateTo .AddDate (0 , 0 , - 2 )
589+ }
590+
591+ sublog := appctx .GetLogger (ctx ).With ().Logger ()
592+ sublog .Debug ().Time ("from" , dateFrom ).Time ("to" , dateTo ).Msg ("executing ListDeletedEntries" )
593+ recycleEntries , err := fs .listDeletedEntries (ctx , fs .conf .MaxRecycleEntries , basePath , dateFrom , dateTo )
594+ if err != nil {
595+ switch err .(type ) {
596+ case errtypes.IsBadRequest :
597+ return nil , errtypes .BadRequest ("cephfs: too many entries found in listing the recycle bin" )
598+ default :
599+ return nil , errors .Wrap (err , "cephfs: error listing deleted entries" )
600+ }
601+ }
602+ return recycleEntries , nil
515603}
516604
517605func (fs * cephfs ) RestoreRecycleItem (ctx context.Context , basePath , key , relativePath string , restoreRef * provider.Reference ) error {
518- return errtypes .NotSupported ("unimplemented" )
606+ user := fs .makeUser (ctx )
607+ md , err := fs .GetMD (ctx , & provider.Reference {Path : basePath }, nil )
608+ if err != nil {
609+ return err
610+ }
611+ if ! md .PermissionSet .RestoreRecycleItem {
612+ return errtypes .PermissionDenied ("cephfs: user doesn't have permissions to restore recycled items" )
613+ }
614+
615+ user .op (func (cv * cacheVal ) {
616+ //TODO(lopresti) validate content of basePath and relativePath. Key is expected to contain the recycled path
617+ if err = cv .mount .Rename (key , filepath .Join (basePath , relativePath )); err != nil {
618+ return
619+ }
620+ //TODO(tmourati): Add entry id logic, handle already moved file error
621+ })
622+
623+ return getRevaError (err )
519624}
520625
521626func (fs * cephfs ) PurgeRecycleItem (ctx context.Context , basePath , key , relativePath string ) error {
522- return errtypes .NotSupported ("unimplemented" )
627+ return errtypes .NotSupported ("cephfs: operation not supported" )
628+ }
629+
630+ func (fs * cephfs ) EmptyRecycle (ctx context.Context ) error {
631+ return errtypes .NotSupported ("cephfs: operation not supported" )
632+ }
633+
634+ func (fs * cephfs ) CreateStorageSpace (ctx context.Context , req * provider.CreateStorageSpaceRequest ) (r * provider.CreateStorageSpaceResponse , err error ) {
635+ return nil , errtypes .NotSupported ("unimplemented" )
523636}
524637
525638func (fs * cephfs ) ListStorageSpaces (ctx context.Context , filter []* provider.ListStorageSpacesRequest_Filter ) ([]* provider.StorageSpace , error ) {
0 commit comments