@@ -38,6 +38,7 @@ import (
38
38
goceph "github.com/ceph/go-ceph/cephfs"
39
39
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
40
40
typepb "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
41
+ typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
41
42
"github.com/cs3org/reva/pkg/appctx"
42
43
"github.com/cs3org/reva/pkg/errtypes"
43
44
"github.com/cs3org/reva/pkg/storage"
@@ -151,6 +152,21 @@ func (fs *cephfs) CreateDir(ctx context.Context, ref *provider.Reference) error
151
152
return getRevaError (ctx , err )
152
153
}
153
154
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
+
154
170
func (fs * cephfs ) Delete (ctx context.Context , ref * provider.Reference ) (err error ) {
155
171
var path string
156
172
user := fs .makeUser (ctx )
@@ -161,8 +177,16 @@ func (fs *cephfs) Delete(ctx context.Context, ref *provider.Reference) (err erro
161
177
162
178
log := appctx .GetLogger (ctx )
163
179
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
+ }
166
190
}
167
191
})
168
192
@@ -502,24 +526,113 @@ func (fs *cephfs) TouchFile(ctx context.Context, ref *provider.Reference) error
502
526
return getRevaError (ctx , err )
503
527
}
504
528
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 ) {
508
535
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
511
567
}
512
568
513
569
func (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
515
603
}
516
604
517
605
func (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 )
519
624
}
520
625
521
626
func (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" )
523
636
}
524
637
525
638
func (fs * cephfs ) ListStorageSpaces (ctx context.Context , filter []* provider.ListStorageSpacesRequest_Filter ) ([]* provider.StorageSpace , error ) {
0 commit comments