@@ -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,8 +196,16 @@ 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 }
186210
187211 //TODO(tmourati): Add entry id logic
@@ -597,24 +621,113 @@ func (fs *cephfs) TouchFile(ctx context.Context, ref *provider.Reference) error
597621 return getRevaError (err )
598622}
599623
600- func (fs * cephfs ) EmptyRecycle (ctx context.Context ) error {
601- return errtypes .NotSupported ("unimplemented" )
602- }
624+ func (fs * cephfs ) listDeletedEntries (ctx context.Context , maxentries int , basePath string , from , to time.Time ) (res []* provider.RecycleItem , err error ) {
625+ res = []* provider.RecycleItem {}
626+ user := fs .makeUser (ctx )
627+ count := 0
628+ rootRecyclePath := filepath .Join (basePath , fs .conf .RecyclePath )
629+ for d := to ; ! d .Before (from ); d = d .AddDate (0 , 0 , - 1 ) {
603630
604- func (fs * cephfs ) CreateStorageSpace (ctx context.Context , req * provider.CreateStorageSpaceRequest ) (r * provider.CreateStorageSpaceResponse , err error ) {
605- return nil , errtypes .NotSupported ("unimplemented" )
631+ user .op (func (cv * cacheVal ) {
632+ var dir * goceph.Directory
633+ if dir , err = cv .mount .OpenDir (filepath .Join (rootRecyclePath , d .Format ("2006/01/02" ))); err != nil {
634+ return
635+ }
636+ defer closeDir (dir )
637+
638+ var entry * goceph.DirEntryPlus
639+ for entry , err = dir .ReadDirPlus (goceph .StatxBasicStats , 0 ); entry != nil && err == nil ; entry , err = dir .ReadDirPlus (goceph .StatxBasicStats , 0 ) {
640+ //TODO(lopresti) validate content of entry.Name() here.
641+ targetPath := filepath .Join (basePath , entry .Name ())
642+ stat := entry .Statx ()
643+ res = append (res , & provider.RecycleItem {
644+ Ref : & provider.Reference {Path : targetPath },
645+ Key : filepath .Join (rootRecyclePath , targetPath ),
646+ Size : stat .Size ,
647+ DeletionTime : & typesv1beta1.Timestamp {
648+ Seconds : uint64 (stat .Mtime .Sec ),
649+ Nanos : uint32 (stat .Mtime .Nsec ),
650+ },
651+ })
652+
653+ count += 1
654+ if count > maxentries {
655+ err = errtypes .BadRequest ("list too long" )
656+ return
657+ }
658+ }
659+ })
660+ }
661+ return res , err
606662}
607663
608664func (fs * cephfs ) ListRecycle (ctx context.Context , basePath , key , relativePath string , from , to * typepb.Timestamp ) ([]* provider.RecycleItem , error ) {
609- return nil , errtypes .NotSupported ("unimplemented" )
665+ md , err := fs .GetMD (ctx , & provider.Reference {Path : basePath }, nil )
666+ if err != nil {
667+ return nil , err
668+ }
669+ if ! md .PermissionSet .ListRecycle {
670+ return nil , errtypes .PermissionDenied ("cephfs: user doesn't have permissions to restore recycled items" )
671+ }
672+
673+ var dateFrom , dateTo time.Time
674+ if from != nil && to != nil {
675+ dateFrom = time .Unix (int64 (from .Seconds ), 0 )
676+ dateTo = time .Unix (int64 (to .Seconds ), 0 )
677+ if dateFrom .AddDate (0 , 0 , fs .conf .MaxDaysInRecycleList ).Before (dateTo ) {
678+ return nil , errtypes .BadRequest ("cephfs: too many days requested in listing the recycle bin" )
679+ }
680+ } else {
681+ // if no date range was given, list up to two days ago
682+ dateTo = time .Now ()
683+ dateFrom = dateTo .AddDate (0 , 0 , - 2 )
684+ }
685+
686+ sublog := appctx .GetLogger (ctx ).With ().Logger ()
687+ sublog .Debug ().Time ("from" , dateFrom ).Time ("to" , dateTo ).Msg ("executing ListDeletedEntries" )
688+ recycleEntries , err := fs .listDeletedEntries (ctx , fs .conf .MaxRecycleEntries , basePath , dateFrom , dateTo )
689+ if err != nil {
690+ switch err .(type ) {
691+ case errtypes.IsBadRequest :
692+ return nil , errtypes .BadRequest ("cephfs: too many entries found in listing the recycle bin" )
693+ default :
694+ return nil , errors .Wrap (err , "cephfs: error listing deleted entries" )
695+ }
696+ }
697+ return recycleEntries , nil
610698}
611699
612700func (fs * cephfs ) RestoreRecycleItem (ctx context.Context , basePath , key , relativePath string , restoreRef * provider.Reference ) error {
613- return errtypes .NotSupported ("unimplemented" )
701+ user := fs .makeUser (ctx )
702+ md , err := fs .GetMD (ctx , & provider.Reference {Path : basePath }, nil )
703+ if err != nil {
704+ return err
705+ }
706+ if ! md .PermissionSet .RestoreRecycleItem {
707+ return errtypes .PermissionDenied ("cephfs: user doesn't have permissions to restore recycled items" )
708+ }
709+
710+ user .op (func (cv * cacheVal ) {
711+ //TODO(lopresti) validate content of basePath and relativePath. Key is expected to contain the recycled path
712+ if err = cv .mount .Rename (key , filepath .Join (basePath , relativePath )); err != nil {
713+ return
714+ }
715+ //TODO(tmourati): Add entry id logic, handle already moved file error
716+ })
717+
718+ return getRevaError (err )
614719}
615720
616721func (fs * cephfs ) PurgeRecycleItem (ctx context.Context , basePath , key , relativePath string ) error {
617- return errtypes .NotSupported ("unimplemented" )
722+ return errtypes .NotSupported ("cephfs: operation not supported" )
723+ }
724+
725+ func (fs * cephfs ) EmptyRecycle (ctx context.Context ) error {
726+ return errtypes .NotSupported ("cephfs: operation not supported" )
727+ }
728+
729+ func (fs * cephfs ) CreateStorageSpace (ctx context.Context , req * provider.CreateStorageSpaceRequest ) (r * provider.CreateStorageSpaceResponse , err error ) {
730+ return nil , errtypes .NotSupported ("unimplemented" )
618731}
619732
620733func (fs * cephfs ) ListStorageSpaces (ctx context.Context , filter []* provider.ListStorageSpacesRequest_Filter ) ([]* provider.StorageSpace , error ) {
0 commit comments