@@ -26,6 +26,7 @@ import (
26
26
"github.com/pkg/errors"
27
27
"github.com/stretchr/testify/require"
28
28
bolt "go.etcd.io/bbolt"
29
+ libcap "kernel.org/pub/linux/libs/security/libcap/cap"
29
30
)
30
31
31
32
func newSnapshotter (ctx context.Context , t * testing.T , snapshotterName string ) (_ context.Context , _ * mergeSnapshotter , rerr error ) {
@@ -372,6 +373,37 @@ func TestHardlinks(t *testing.T) {
372
373
}
373
374
}
374
375
376
+ func TestMergeFileCapabilities (t * testing.T ) {
377
+ requireRoot (t )
378
+ for _ , snName := range []string {"overlayfs" , "native" , "native-nohardlink" } {
379
+ snName := snName
380
+ t .Run (snName , func (t * testing.T ) {
381
+ t .Parallel ()
382
+
383
+ ctx , sn , err := newSnapshotter (context .Background (), t , snName )
384
+ require .NoError (t , err )
385
+
386
+ setCaps := "cap_net_bind_service=+ep"
387
+ base1Snap := committedKey (ctx , t , sn , identity .NewID (), "" ,
388
+ fstest .CreateFile ("hasCaps" , []byte ("capable" ), 0700 ),
389
+ fstest .Chown ("hasCaps" , 1000 , 1000 ),
390
+ setFileCap ("hasCaps" , setCaps ),
391
+ )
392
+ base2Snap := committedKey (ctx , t , sn , identity .NewID (), "" ,
393
+ fstest .CreateFile ("foo" , []byte ("bar" ), 0600 ),
394
+ )
395
+
396
+ mergeSnap := mergeKey (ctx , t , sn , identity .NewID (), []Diff {
397
+ {"" , base1Snap .Name },
398
+ {"" , base2Snap .Name },
399
+ })
400
+
401
+ actualCaps := getFileCap (ctx , t , sn , mergeSnap .Name , "hasCaps" )
402
+ require .Equal (t , "cap_net_bind_service=ep" , actualCaps )
403
+ })
404
+ }
405
+ }
406
+
375
407
func TestUsage (t * testing.T ) {
376
408
for _ , snName := range []string {"overlayfs" , "native" , "native-nohardlink" } {
377
409
snName := snName
@@ -559,7 +591,7 @@ func requireMtime(t *testing.T, path string, mtime time.Time) {
559
591
require .Equal (t , mtime .UnixNano (), stat .Mtim .Nano ())
560
592
}
561
593
562
- func tryStatPath (ctx context.Context , t * testing.T , sn * mergeSnapshotter , key , path string ) ( st * syscall. Stat_t ) {
594
+ func pathCallback [ T any ] (ctx context.Context , t * testing.T , sn * mergeSnapshotter , key , path string , cb func ( t * testing. T , path string ) * T ) * T {
563
595
t .Helper ()
564
596
mounts , cleanup := getMounts (ctx , t , sn , key )
565
597
defer cleanup ()
@@ -577,24 +609,32 @@ func tryStatPath(ctx context.Context, t *testing.T, sn *mergeSnapshotter, key, p
577
609
}
578
610
}
579
611
if upperdir != "" {
580
- st = trySyscallStat (t , filepath .Join (upperdir , path ))
581
- if st != nil {
582
- return st
612
+ r := cb (t , filepath .Join (upperdir , path ))
613
+ if r != nil {
614
+ return r
583
615
}
584
616
}
585
617
for _ , lowerdir := range lowerdirs {
586
- st = trySyscallStat (t , filepath .Join (lowerdir , path ))
587
- if st != nil {
588
- return st
618
+ r := cb (t , filepath .Join (lowerdir , path ))
619
+ if r != nil {
620
+ return r
589
621
}
590
622
}
591
623
return nil
592
624
}
593
625
626
+ var r * T
594
627
withMount (ctx , t , sn , key , func (root string ) {
595
- st = trySyscallStat (t , filepath .Join (root , path ))
628
+ r = cb (t , filepath .Join (root , path ))
629
+ })
630
+ return r
631
+ }
632
+
633
+ func tryStatPath (ctx context.Context , t * testing.T , sn * mergeSnapshotter , key , path string ) * syscall.Stat_t {
634
+ t .Helper ()
635
+ return pathCallback (ctx , t , sn , key , path , func (t * testing.T , path string ) * syscall.Stat_t {
636
+ return trySyscallStat (t , path )
596
637
})
597
- return st
598
638
}
599
639
600
640
func statPath (ctx context.Context , t * testing.T , sn * mergeSnapshotter , key , path string ) (st * syscall.Stat_t ) {
@@ -610,3 +650,36 @@ func requireRoot(t *testing.T) {
610
650
t .Skip ("test requires root" )
611
651
}
612
652
}
653
+
654
+ func setFileCap (path string , caps string ) fstest.Applier {
655
+ return applyFn (func (root string ) error {
656
+ path := filepath .Join (root , path )
657
+ capSet , err := libcap .FromText (caps )
658
+ if err != nil {
659
+ return err
660
+ }
661
+ return capSet .SetFile (path )
662
+ })
663
+ }
664
+
665
+ func getFileCap (ctx context.Context , t * testing.T , sn * mergeSnapshotter , key , path string ) string {
666
+ t .Helper ()
667
+ caps := pathCallback (ctx , t , sn , key , path , func (t * testing.T , path string ) * string {
668
+ t .Helper ()
669
+ capSet , err := libcap .GetFile (path )
670
+ if err != nil {
671
+ require .ErrorIs (t , err , os .ErrNotExist )
672
+ return nil
673
+ }
674
+ caps := capSet .String ()
675
+ return & caps
676
+ })
677
+ require .NotNil (t , caps )
678
+ return * caps
679
+ }
680
+
681
+ type applyFn func (root string ) error
682
+
683
+ func (a applyFn ) Apply (root string ) error {
684
+ return a (root )
685
+ }
0 commit comments