@@ -314,6 +314,161 @@ func TestLoopbackOverlay(t *testing.T) {
314314 }
315315}
316316
317+ // TestTemporaryMountActivation tests the WithTemporary option used by
318+ // `ctr images mount`. This verifies the bind mount returned in info.System
319+ // points to a valid, mounted directory.
320+ func TestTemporaryMountActivation (t * testing.T ) {
321+ testutil .RequiresRoot (t )
322+ ctx := logtest .WithT (context .Background (), t )
323+ ctx = namespaces .WithNamespace (ctx , "test" )
324+ td := t .TempDir ()
325+ metadb := filepath .Join (td , "mounts.db" )
326+ targetdir := filepath .Join (td , "m" )
327+ db , err := bolt .Open (metadb , 0600 , nil )
328+ require .NoError (t , err )
329+ defer db .Close ()
330+
331+ sourcedir := filepath .Join (td , "source" )
332+ require .NoError (t , os .MkdirAll (sourcedir , 0755 ))
333+
334+ a := fstest .Apply (
335+ fstest .CreateFile ("/testfile.txt" , []byte ("test content\n " ), 0644 ),
336+ fstest .CreateDir ("/testdir" , 0755 ),
337+ )
338+ require .NoError (t , a .Apply (sourcedir ))
339+
340+ mounts := []mount.Mount {
341+ {
342+ Type : "bind" ,
343+ Source : sourcedir ,
344+ Options : []string {"rbind" , "ro" },
345+ },
346+ }
347+
348+ m , err := NewManager (db , targetdir )
349+ require .NoError (t , err )
350+
351+ ainfo , err := m .Activate (ctx , "temp-mount-test" , mounts , mount .WithTemporary )
352+ require .NoError (t , err )
353+ defer func () {
354+ assert .NoError (t , m .Deactivate (ctx , "temp-mount-test" ))
355+ }()
356+
357+ require .NotEmpty (t , ainfo .System , "Expected system mounts to be returned" )
358+
359+ require .Len (t , ainfo .System , 1 , "Expected exactly one system mount" )
360+ systemMount := ainfo .System [0 ]
361+ assert .Equal (t , "bind" , systemMount .Type , "Expected bind mount type" )
362+
363+ sourceInfo , err := os .Stat (systemMount .Source )
364+ require .NoError (t , err , "Bind mount source %q should exist" , systemMount .Source )
365+ assert .True (t , sourceInfo .IsDir (), "Bind mount source should be a directory" )
366+
367+ testFile := filepath .Join (systemMount .Source , "testfile.txt" )
368+ content , err := os .ReadFile (testFile )
369+ require .NoError (t , err , "Should be able to read test file from bind mount source" )
370+ assert .Equal (t , "test content\n " , string (content ), "Test file content should match" )
371+
372+ targetMount := filepath .Join (td , "target" )
373+ require .NoError (t , os .MkdirAll (targetMount , 0755 ))
374+
375+ err = mount .All (ainfo .System , targetMount )
376+ require .NoError (t , err , "Should be able to mount system mounts to target" )
377+ defer testutil .Unmount (t , targetMount )
378+
379+ targetTestFile := filepath .Join (targetMount , "testfile.txt" )
380+ targetContent , err := os .ReadFile (targetTestFile )
381+ require .NoError (t , err , "Should be able to read test file from target mount" )
382+ assert .Equal (t , "test content\n " , string (targetContent ), "Target test file content should match" )
383+ }
384+
385+ // TestTemporaryOverlayMountActivation tests WithTemporary with overlay mounts,
386+ // which is the more common case for `ctr images mount` with overlay snapshotter.
387+ func TestTemporaryOverlayMountActivation (t * testing.T ) {
388+ testutil .RequiresRoot (t )
389+ ctx := logtest .WithT (context .Background (), t )
390+ ctx = namespaces .WithNamespace (ctx , "test" )
391+ td := t .TempDir ()
392+ metadb := filepath .Join (td , "mounts.db" )
393+ targetdir := filepath .Join (td , "m" )
394+ db , err := bolt .Open (metadb , 0600 , nil )
395+ require .NoError (t , err )
396+ defer db .Close ()
397+
398+ lower1 := filepath .Join (td , "lower1" )
399+ lower2 := filepath .Join (td , "lower2" )
400+ upper := filepath .Join (td , "upper" )
401+ work := filepath .Join (td , "work" )
402+
403+ require .NoError (t , os .MkdirAll (lower1 , 0755 ))
404+ require .NoError (t , os .MkdirAll (lower2 , 0755 ))
405+ require .NoError (t , os .MkdirAll (upper , 0755 ))
406+ require .NoError (t , os .MkdirAll (work , 0755 ))
407+
408+ require .NoError (t , os .WriteFile (filepath .Join (lower1 , "file1.txt" ), []byte ("layer1\n " ), 0644 ))
409+ require .NoError (t , os .WriteFile (filepath .Join (lower2 , "file2.txt" ), []byte ("layer2\n " ), 0644 ))
410+
411+ mounts := []mount.Mount {
412+ {
413+ Type : "overlay" ,
414+ Source : "overlay" ,
415+ Options : []string {
416+ fmt .Sprintf ("lowerdir=%s:%s" , lower2 , lower1 ),
417+ fmt .Sprintf ("upperdir=%s" , upper ),
418+ fmt .Sprintf ("workdir=%s" , work ),
419+ },
420+ },
421+ }
422+
423+ m , err := NewManager (db , targetdir )
424+ require .NoError (t , err )
425+
426+ ainfo , err := m .Activate (ctx , "temp-overlay-test" , mounts , mount .WithTemporary )
427+ require .NoError (t , err )
428+ defer func () {
429+ assert .NoError (t , m .Deactivate (ctx , "temp-overlay-test" ))
430+ }()
431+
432+ require .NotEmpty (t , ainfo .System , "Expected system mounts to be returned" )
433+
434+ require .Len (t , ainfo .System , 1 , "Expected exactly one system mount" )
435+ systemMount := ainfo .System [0 ]
436+ assert .Equal (t , "bind" , systemMount .Type , "Expected bind mount type" )
437+
438+ sourceInfo , err := os .Stat (systemMount .Source )
439+ require .NoError (t , err , "Bind mount source %q should exist" , systemMount .Source )
440+ assert .True (t , sourceInfo .IsDir (), "Bind mount source should be a directory" )
441+
442+ file1 := filepath .Join (systemMount .Source , "file1.txt" )
443+ file2 := filepath .Join (systemMount .Source , "file2.txt" )
444+
445+ content1 , err := os .ReadFile (file1 )
446+ require .NoError (t , err , "Should be able to read file1 from overlay via bind source" )
447+ assert .Equal (t , "layer1\n " , string (content1 ))
448+
449+ content2 , err := os .ReadFile (file2 )
450+ require .NoError (t , err , "Should be able to read file2 from overlay via bind source" )
451+ assert .Equal (t , "layer2\n " , string (content2 ))
452+
453+ targetMount := filepath .Join (td , "target" )
454+ require .NoError (t , os .MkdirAll (targetMount , 0755 ))
455+
456+ err = mount .All (ainfo .System , targetMount )
457+ require .NoError (t , err , "Should be able to mount system mounts to target" )
458+ defer testutil .Unmount (t , targetMount )
459+
460+ targetFile1 := filepath .Join (targetMount , "file1.txt" )
461+ targetFile2 := filepath .Join (targetMount , "file2.txt" )
462+
463+ targetContent1 , err := os .ReadFile (targetFile1 )
464+ require .NoError (t , err )
465+ assert .Equal (t , "layer1\n " , string (targetContent1 ))
466+
467+ targetContent2 , err := os .ReadFile (targetFile2 )
468+ require .NoError (t , err )
469+ assert .Equal (t , "layer2\n " , string (targetContent2 ))
470+ }
471+
317472func initalizeBlockDevice (td string , a fstest.Applier ) (string , error ) {
318473 file , err := os .CreateTemp (td , "fs-" )
319474 if err != nil {
@@ -342,7 +497,7 @@ func initalizeBlockDevice(td string, a fstest.Applier) (string, error) {
342497
343498 m := mount.Mount {
344499 Type : "ext4" ,
345- Source : dpath , // previous mount
500+ Source : dpath ,
346501 Options : []string {"loop" },
347502 }
348503 target , err := os .MkdirTemp (td , "mount-" )
0 commit comments