@@ -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"
@@ -171,6 +172,21 @@ func (fs *cephfs) CreateDir(ctx context.Context, ref *provider.Reference) error
171172 return getRevaError (err )
172173}
173174
175+ func getRecycleTargetFromPath (path string , recyclePath string , recyclePathDepth int ) (string , error ) {
176+ // Tokenize the given (absolute) path
177+ components := strings .Split (filepath .Clean (string (filepath .Separator )+ path ), string (filepath .Separator ))
178+ if recyclePathDepth > len (components )- 1 {
179+ return "" , errors .New ("path is too short" )
180+ }
181+
182+ // And construct the target by injecting the recyclePath at the required depth
183+ var target []string = []string {string (filepath .Separator )}
184+ target = append (target , components [:recyclePathDepth + 1 ]... )
185+ target = append (target , recyclePath , time .Now ().Format ("2006/01/02" ))
186+ target = append (target , components [recyclePathDepth + 1 :]... )
187+ return filepath .Join (target ... ), nil
188+ }
189+
174190func (fs * cephfs ) Delete (ctx context.Context , ref * provider.Reference ) (err error ) {
175191 var path string
176192 user := fs .makeUser (ctx )
@@ -180,10 +196,17 @@ func (fs *cephfs) Delete(ctx context.Context, ref *provider.Reference) (err erro
180196 }
181197
182198 user .op (func (cv * cacheVal ) {
183- if err = cv .mount .Unlink (path ); err != nil && err .Error () == errIsADirectory {
184- err = cv .mount .RemoveDir (path )
199+ if fs .conf .RecyclePath != "" {
200+ // Recycle bin is configured, move to recycle as opposed to unlink
201+ targetPath , err := getRecycleTargetFromPath (path , fs .conf .RecyclePath , fs .conf .RecyclePathDepth )
202+ if err == nil {
203+ err = cv .mount .Rename (path , targetPath )
204+ }
205+ } else {
206+ if err = cv .mount .Unlink (path ); err != nil && err .Error () == errIsADirectory {
207+ err = cv .mount .RemoveDir (path )
208+ }
185209 }
186-
187210 //TODO(tmourati): Add entry id logic
188211 })
189212
@@ -597,24 +620,113 @@ func (fs *cephfs) TouchFile(ctx context.Context, ref *provider.Reference) error
597620 return getRevaError (err )
598621}
599622
600- func (fs * cephfs ) EmptyRecycle (ctx context.Context ) error {
601- return errtypes .NotSupported ("unimplemented" )
602- }
623+ func (fs * cephfs ) listDeletedEntries (ctx context.Context , maxentries int , basePath string , from , to time.Time ) (res []* provider.RecycleItem , err error ) {
624+ res = []* provider.RecycleItem {}
625+ user := fs .makeUser (ctx )
626+ count := 0
627+ rootRecyclePath := filepath .Join (basePath , fs .conf .RecyclePath )
628+ for d := to ; ! d .Before (from ); d = d .AddDate (0 , 0 , - 1 ) {
603629
604- func (fs * cephfs ) CreateStorageSpace (ctx context.Context , req * provider.CreateStorageSpaceRequest ) (r * provider.CreateStorageSpaceResponse , err error ) {
605- return nil , errtypes .NotSupported ("unimplemented" )
630+ user .op (func (cv * cacheVal ) {
631+ var dir * goceph.Directory
632+ if dir , err = cv .mount .OpenDir (filepath .Join (rootRecyclePath , d .Format ("2006/01/02" ))); err != nil {
633+ return
634+ }
635+ defer closeDir (dir )
636+
637+ var entry * goceph.DirEntryPlus
638+ for entry , err = dir .ReadDirPlus (goceph .StatxBasicStats , 0 ); entry != nil && err == nil ; entry , err = dir .ReadDirPlus (goceph .StatxBasicStats , 0 ) {
639+ //TODO(lopresti) validate content of entry.Name() here.
640+ targetPath := filepath .Join (basePath , entry .Name ())
641+ stat := entry .Statx ()
642+ res = append (res , & provider.RecycleItem {
643+ Ref : & provider.Reference {Path : targetPath },
644+ Key : filepath .Join (rootRecyclePath , targetPath ),
645+ Size : stat .Size ,
646+ DeletionTime : & typesv1beta1.Timestamp {
647+ Seconds : uint64 (stat .Mtime .Sec ),
648+ Nanos : uint32 (stat .Mtime .Nsec ),
649+ },
650+ })
651+
652+ count += 1
653+ if count > maxentries {
654+ err = errtypes .BadRequest ("list too long" )
655+ return
656+ }
657+ }
658+ })
659+ }
660+ return res , err
606661}
607662
608663func (fs * cephfs ) ListRecycle (ctx context.Context , basePath , key , relativePath string , from , to * typepb.Timestamp ) ([]* provider.RecycleItem , error ) {
609- return nil , errtypes .NotSupported ("unimplemented" )
664+ md , err := fs .GetMD (ctx , & provider.Reference {Path : basePath }, nil )
665+ if err != nil {
666+ return nil , err
667+ }
668+ if ! md .PermissionSet .ListRecycle {
669+ return nil , errtypes .PermissionDenied ("cephfs: user doesn't have permissions to restore recycled items" )
670+ }
671+
672+ var dateFrom , dateTo time.Time
673+ if from != nil && to != nil {
674+ dateFrom = time .Unix (int64 (from .Seconds ), 0 )
675+ dateTo = time .Unix (int64 (to .Seconds ), 0 )
676+ if dateFrom .AddDate (0 , 0 , fs .conf .MaxDaysInRecycleList ).Before (dateTo ) {
677+ return nil , errtypes .BadRequest ("cephfs: too many days requested in listing the recycle bin" )
678+ }
679+ } else {
680+ // if no date range was given, list up to two days ago
681+ dateTo = time .Now ()
682+ dateFrom = dateTo .AddDate (0 , 0 , - 2 )
683+ }
684+
685+ sublog := appctx .GetLogger (ctx ).With ().Logger ()
686+ sublog .Debug ().Time ("from" , dateFrom ).Time ("to" , dateTo ).Msg ("executing ListDeletedEntries" )
687+ recycleEntries , err := fs .listDeletedEntries (ctx , fs .conf .MaxRecycleEntries , basePath , dateFrom , dateTo )
688+ if err != nil {
689+ switch err .(type ) {
690+ case errtypes.IsBadRequest :
691+ return nil , errtypes .BadRequest ("cephfs: too many entries found in listing the recycle bin" )
692+ default :
693+ return nil , errors .Wrap (err , "cephfs: error listing deleted entries" )
694+ }
695+ }
696+ return recycleEntries , nil
610697}
611698
612699func (fs * cephfs ) RestoreRecycleItem (ctx context.Context , basePath , key , relativePath string , restoreRef * provider.Reference ) error {
613- return errtypes .NotSupported ("unimplemented" )
700+ user := fs .makeUser (ctx )
701+ md , err := fs .GetMD (ctx , & provider.Reference {Path : basePath }, nil )
702+ if err != nil {
703+ return err
704+ }
705+ if ! md .PermissionSet .RestoreRecycleItem {
706+ return errtypes .PermissionDenied ("cephfs: user doesn't have permissions to restore recycled items" )
707+ }
708+
709+ user .op (func (cv * cacheVal ) {
710+ //TODO(lopresti) validate content of basePath and relativePath. Key is expected to contain the recycled path
711+ if err = cv .mount .Rename (key , filepath .Join (basePath , relativePath )); err != nil {
712+ return
713+ }
714+ //TODO(tmourati): Add entry id logic, handle already moved file error
715+ })
716+
717+ return getRevaError (err )
614718}
615719
616720func (fs * cephfs ) PurgeRecycleItem (ctx context.Context , basePath , key , relativePath string ) error {
617- return errtypes .NotSupported ("unimplemented" )
721+ return errtypes .NotSupported ("cephfs: operation not supported" )
722+ }
723+
724+ func (fs * cephfs ) EmptyRecycle (ctx context.Context ) error {
725+ return errtypes .NotSupported ("cephfs: operation not supported" )
726+ }
727+
728+ func (fs * cephfs ) CreateStorageSpace (ctx context.Context , req * provider.CreateStorageSpaceRequest ) (r * provider.CreateStorageSpaceResponse , err error ) {
729+ return nil , errtypes .NotSupported ("unimplemented" )
618730}
619731
620732func (fs * cephfs ) ListStorageSpaces (ctx context.Context , filter []* provider.ListStorageSpacesRequest_Filter ) ([]* provider.StorageSpace , error ) {
0 commit comments