@@ -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"
@@ -149,6 +150,21 @@ func (fs *cephfs) CreateDir(ctx context.Context, ref *provider.Reference) error
149150 return getRevaError (err )
150151}
151152
153+ func getRecycleTargetFromPath (path string , recyclePath string , recyclePathDepth int ) (string , error ) {
154+ // Tokenize the given (absolute) path
155+ components := strings .Split (filepath .Clean (string (filepath .Separator )+ path ), string (filepath .Separator ))
156+ if recyclePathDepth > len (components )- 1 {
157+ return "" , errors .New ("path is too short" )
158+ }
159+
160+ // And construct the target by injecting the recyclePath at the required depth
161+ var target []string = []string {string (filepath .Separator )}
162+ target = append (target , components [:recyclePathDepth + 1 ]... )
163+ target = append (target , recyclePath , time .Now ().Format ("2006/01/02" ))
164+ target = append (target , components [recyclePathDepth + 1 :]... )
165+ return filepath .Join (target ... ), nil
166+ }
167+
152168func (fs * cephfs ) Delete (ctx context.Context , ref * provider.Reference ) (err error ) {
153169 var path string
154170 user := fs .makeUser (ctx )
@@ -158,8 +174,16 @@ func (fs *cephfs) Delete(ctx context.Context, ref *provider.Reference) (err erro
158174 }
159175
160176 user .op (func (cv * cacheVal ) {
161- if err = cv .mount .Unlink (path ); err != nil && err .Error () == errIsADirectory {
162- err = cv .mount .RemoveDir (path )
177+ if fs .conf .RecyclePath != "" {
178+ // Recycle bin is configured, move to recycle as opposed to unlink
179+ targetPath , err := getRecycleTargetFromPath (path , fs .conf .RecyclePath , fs .conf .RecyclePathDepth )
180+ if err == nil {
181+ err = cv .mount .Rename (path , targetPath )
182+ }
183+ } else {
184+ if err = cv .mount .Unlink (path ); err != nil && err .Error () == errIsADirectory {
185+ err = cv .mount .RemoveDir (path )
186+ }
163187 }
164188 })
165189
@@ -477,24 +501,113 @@ func (fs *cephfs) TouchFile(ctx context.Context, ref *provider.Reference) error
477501 return getRevaError (err )
478502}
479503
480- func (fs * cephfs ) EmptyRecycle (ctx context.Context ) error {
481- return errtypes .NotSupported ("unimplemented" )
482- }
504+ func (fs * cephfs ) listDeletedEntries (ctx context.Context , maxentries int , basePath string , from , to time.Time ) (res []* provider.RecycleItem , err error ) {
505+ res = []* provider.RecycleItem {}
506+ user := fs .makeUser (ctx )
507+ count := 0
508+ rootRecyclePath := filepath .Join (basePath , fs .conf .RecyclePath )
509+ for d := to ; ! d .Before (from ); d = d .AddDate (0 , 0 , - 1 ) {
483510
484- func (fs * cephfs ) CreateStorageSpace (ctx context.Context , req * provider.CreateStorageSpaceRequest ) (r * provider.CreateStorageSpaceResponse , err error ) {
485- return nil , errtypes .NotSupported ("unimplemented" )
511+ user .op (func (cv * cacheVal ) {
512+ var dir * goceph.Directory
513+ if dir , err = cv .mount .OpenDir (filepath .Join (rootRecyclePath , d .Format ("2006/01/02" ))); err != nil {
514+ return
515+ }
516+ defer closeDir (dir )
517+
518+ var entry * goceph.DirEntryPlus
519+ for entry , err = dir .ReadDirPlus (goceph .StatxBasicStats , 0 ); entry != nil && err == nil ; entry , err = dir .ReadDirPlus (goceph .StatxBasicStats , 0 ) {
520+ //TODO(lopresti) validate content of entry.Name() here.
521+ targetPath := filepath .Join (basePath , entry .Name ())
522+ stat := entry .Statx ()
523+ res = append (res , & provider.RecycleItem {
524+ Ref : & provider.Reference {Path : targetPath },
525+ Key : filepath .Join (rootRecyclePath , targetPath ),
526+ Size : stat .Size ,
527+ DeletionTime : & typesv1beta1.Timestamp {
528+ Seconds : uint64 (stat .Mtime .Sec ),
529+ Nanos : uint32 (stat .Mtime .Nsec ),
530+ },
531+ })
532+
533+ count += 1
534+ if count > maxentries {
535+ err = errtypes .BadRequest ("list too long" )
536+ return
537+ }
538+ }
539+ })
540+ }
541+ return res , err
486542}
487543
488544func (fs * cephfs ) ListRecycle (ctx context.Context , basePath , key , relativePath string , from , to * typepb.Timestamp ) ([]* provider.RecycleItem , error ) {
489- return nil , errtypes .NotSupported ("unimplemented" )
545+ md , err := fs .GetMD (ctx , & provider.Reference {Path : basePath }, nil )
546+ if err != nil {
547+ return nil , err
548+ }
549+ if ! md .PermissionSet .ListRecycle {
550+ return nil , errtypes .PermissionDenied ("cephfs: user doesn't have permissions to restore recycled items" )
551+ }
552+
553+ var dateFrom , dateTo time.Time
554+ if from != nil && to != nil {
555+ dateFrom = time .Unix (int64 (from .Seconds ), 0 )
556+ dateTo = time .Unix (int64 (to .Seconds ), 0 )
557+ if dateFrom .AddDate (0 , 0 , fs .conf .MaxDaysInRecycleList ).Before (dateTo ) {
558+ return nil , errtypes .BadRequest ("cephfs: too many days requested in listing the recycle bin" )
559+ }
560+ } else {
561+ // if no date range was given, list up to two days ago
562+ dateTo = time .Now ()
563+ dateFrom = dateTo .AddDate (0 , 0 , - 2 )
564+ }
565+
566+ sublog := appctx .GetLogger (ctx ).With ().Logger ()
567+ sublog .Debug ().Time ("from" , dateFrom ).Time ("to" , dateTo ).Msg ("executing ListDeletedEntries" )
568+ recycleEntries , err := fs .listDeletedEntries (ctx , fs .conf .MaxRecycleEntries , basePath , dateFrom , dateTo )
569+ if err != nil {
570+ switch err .(type ) {
571+ case errtypes.IsBadRequest :
572+ return nil , errtypes .BadRequest ("cephfs: too many entries found in listing the recycle bin" )
573+ default :
574+ return nil , errors .Wrap (err , "cephfs: error listing deleted entries" )
575+ }
576+ }
577+ return recycleEntries , nil
490578}
491579
492580func (fs * cephfs ) RestoreRecycleItem (ctx context.Context , basePath , key , relativePath string , restoreRef * provider.Reference ) error {
493- return errtypes .NotSupported ("unimplemented" )
581+ user := fs .makeUser (ctx )
582+ md , err := fs .GetMD (ctx , & provider.Reference {Path : basePath }, nil )
583+ if err != nil {
584+ return err
585+ }
586+ if ! md .PermissionSet .RestoreRecycleItem {
587+ return errtypes .PermissionDenied ("cephfs: user doesn't have permissions to restore recycled items" )
588+ }
589+
590+ user .op (func (cv * cacheVal ) {
591+ //TODO(lopresti) validate content of basePath and relativePath. Key is expected to contain the recycled path
592+ if err = cv .mount .Rename (key , filepath .Join (basePath , relativePath )); err != nil {
593+ return
594+ }
595+ //TODO(tmourati): Add entry id logic, handle already moved file error
596+ })
597+
598+ return getRevaError (err )
494599}
495600
496601func (fs * cephfs ) PurgeRecycleItem (ctx context.Context , basePath , key , relativePath string ) error {
497- return errtypes .NotSupported ("unimplemented" )
602+ return errtypes .NotSupported ("cephfs: operation not supported" )
603+ }
604+
605+ func (fs * cephfs ) EmptyRecycle (ctx context.Context ) error {
606+ return errtypes .NotSupported ("cephfs: operation not supported" )
607+ }
608+
609+ func (fs * cephfs ) CreateStorageSpace (ctx context.Context , req * provider.CreateStorageSpaceRequest ) (r * provider.CreateStorageSpaceResponse , err error ) {
610+ return nil , errtypes .NotSupported ("unimplemented" )
498611}
499612
500613func (fs * cephfs ) ListStorageSpaces (ctx context.Context , filter []* provider.ListStorageSpacesRequest_Filter ) ([]* provider.StorageSpace , error ) {
0 commit comments