@@ -379,6 +379,112 @@ async def _test_005_size_after_clone(self):
379379 await qubes .utils .coro_maybe (testvol2 .import_volume (testvol ))
380380 self .assertEqual (testvol2 .size , size )
381381
382+ def test_006_no_revisions (self ):
383+ """Test if no-revisions volume persists data, and blocks parallel
384+ access"""
385+ return self .loop .run_until_complete (self ._test_006_no_revisions ())
386+
387+ async def _test_006_no_revisions (self ):
388+ self .vm1 .storage .set_revisions_to_keep ("private" , - 1 )
389+ dir_path = "/var/tmp/test-pool2"
390+ pool2 = await self .app .add_pool (
391+ dir_path = dir_path , name = "test-pool2" , driver = "file"
392+ )
393+ self .addCleanup (shutil .rmtree , dir_path )
394+
395+ size = 32 * 1024 * 1024
396+ volume_config = {
397+ "pool" : self .pool .name ,
398+ "size" : size ,
399+ "save_on_stop" : True ,
400+ "rw" : True ,
401+ "revisions_to_keep" : - 1 ,
402+ }
403+ testvol = self .vm1 .storage .init_volume ("testvol" , volume_config )
404+ await qubes .utils .coro_maybe (testvol .create ())
405+ volume2_config = {
406+ "pool" : self .pool .name ,
407+ "size" : size ,
408+ "snap_on_start" : True ,
409+ "source" : testvol .vid ,
410+ "rw" : True ,
411+ }
412+ testvol2 = self .vm2 .storage .init_volume ("testvol2" , volume2_config )
413+ await qubes .utils .coro_maybe (testvol2 .create ())
414+ del testvol
415+ del testvol2
416+
417+ volume3_config = {
418+ "pool" : pool2 .name ,
419+ "size" : size ,
420+ "save_on_stop" : True ,
421+ "rw" : True ,
422+ }
423+ testvol3 = self .vm2 .storage .init_volume ("testvol3" , volume3_config )
424+ await qubes .utils .coro_maybe (testvol3 .create ())
425+
426+ self .app .save ()
427+ await self .vm1 .start ()
428+ await self .wait_for_session (self .vm1 )
429+ # non-volatile image not clean
430+ await self .vm1 .run_for_stdio (
431+ "head -c {} /dev/zero 2>&1 | diff -q /dev/xvde - 2>&1" .format (size ),
432+ user = "root" ,
433+ )
434+
435+ await self .vm1 .run_for_stdio ("echo test123 > /dev/xvde" , user = "root" )
436+ await self .vm1 .shutdown (wait = True )
437+ await self .vm1 .start ()
438+ # non-volatile image volatile
439+ with self .assertRaises (subprocess .CalledProcessError ):
440+ await self .vm1 .run_for_stdio (
441+ "head -c {} /dev/zero 2>&1 | diff -q /dev/xvde - 2>&1" .format (
442+ size
443+ ),
444+ user = "root" ,
445+ )
446+
447+ # should not work
448+ with self .assertRaises (qubes .exc .QubesException ):
449+ await self .vm2 .start ()
450+ # clone within the same pool
451+ with self .assertRaises (qubes .exc .QubesException ):
452+ await self .vm2 .storage .import_volume (
453+ self .vm2 .storage .get_volume ("private" ),
454+ self .vm1 .storage .get_volume ("private" ),
455+ )
456+ # clone to a different pool
457+ with self .assertRaises (qubes .exc .QubesException ):
458+ await self .vm2 .storage .import_volume (
459+ self .vm2 .storage .get_volume ("testvol3" ),
460+ self .vm1 .storage .get_volume ("testvol" ),
461+ )
462+ # export is not blocked explicitly now, but its uses are (cross-pool
463+ # clone, backup)
464+ # with self.assertRaises(qubes.exc.QubesException):
465+ # path = await self.vm1.storage.export("testvol")
466+ # # just in case it was allowed...
467+ # await self.vm1.storage.export_end("testvol", path)
468+
469+ # now shutdown vm1 and the above operations should work
470+ await self .vm1 .shutdown (wait = True )
471+ await self .vm2 .storage .import_volume (
472+ self .vm2 .storage .get_volume ("private" ),
473+ self .vm1 .storage .get_volume ("private" ),
474+ )
475+ await self .vm2 .storage .import_volume (
476+ self .vm2 .storage .get_volume ("testvol3" ),
477+ self .vm1 .storage .get_volume ("testvol" ),
478+ )
479+ path = await self .vm1 .storage .export ("testvol" )
480+ await self .vm1 .storage .export_end ("testvol" , path )
481+
482+ await self .vm2 .start ()
483+ stdout , stderr = await self .vm2 .run_for_stdio (
484+ "head -c 7 /dev/xvde" , user = "root"
485+ )
486+ self .assertEqual (stdout , b"test123" )
487+
382488
383489class StorageFile (StorageTestMixin , qubes .tests .SystemTestCase ):
384490 def init_pool (self ):
0 commit comments