From c43df968d568f5738745dd299b7e210d40ab6955 Mon Sep 17 00:00:00 2001 From: Rusty Bird Date: Sat, 15 Jun 2019 16:03:41 +0000 Subject: [PATCH 1/5] storage/reflink: update snap_on_start volume size in start() --- qubes/storage/reflink.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qubes/storage/reflink.py b/qubes/storage/reflink.py index fd4d4ee5..2fc4c03d 100644 --- a/qubes/storage/reflink.py +++ b/qubes/storage/reflink.py @@ -198,6 +198,7 @@ class ReflinkVolume(qubes.storage.Volume): if self.snap_on_start: # pylint: disable=protected-access _copy_file(self.source._path_clean, self._path_clean) + self.size = os.path.getsize(self._path_clean) if self.snap_on_start or self.save_on_stop: _copy_file(self._path_clean, self._path_dirty) else: From 9f5d05bfde04746e8d47972b306c924bad080c60 Mon Sep 17 00:00:00 2001 From: Rusty Bird Date: Sat, 15 Jun 2019 16:03:42 +0000 Subject: [PATCH 2/5] storage/reflink: update volume size in __init__() --- qubes/storage/reflink.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/qubes/storage/reflink.py b/qubes/storage/reflink.py index 2fc4c03d..21ca8317 100644 --- a/qubes/storage/reflink.py +++ b/qubes/storage/reflink.py @@ -141,6 +141,13 @@ class ReflinkVolume(qubes.storage.Volume): self._path_import = self._path_vid + '-import.img' self.path = self._path_dirty + # In case the volume was previously resized, but then a crash + # prevented qubesd from serializing the new size to qubes.xml: + for path in (self._path_dirty, self._path_clean): + with suppress(FileNotFoundError): + self.size = os.path.getsize(path) + break + @_unblock def create(self): if self.save_on_stop and not self.snap_on_start: From 30b92f8845fd2a102a5f65de74a338d104384799 Mon Sep 17 00:00:00 2001 From: Rusty Bird Date: Sat, 15 Jun 2019 16:03:43 +0000 Subject: [PATCH 3/5] storage/reflink: simplify volume.usage() --- qubes/storage/reflink.py | 12 +++--------- qubes/tests/storage_reflink.py | 10 ++++++---- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/qubes/storage/reflink.py b/qubes/storage/reflink.py index 21ca8317..8ad4857b 100644 --- a/qubes/storage/reflink.py +++ b/qubes/storage/reflink.py @@ -36,7 +36,6 @@ from contextlib import contextmanager, suppress import qubes.storage -BLKSIZE = 512 FICLONE = 1074041865 # defined in LOOP_SET_CAPACITY = 0x4C07 # defined in LOGGER = logging.getLogger('qubes.storage.reflink') @@ -342,10 +341,9 @@ class ReflinkVolume(qubes.storage.Volume): ''' Return volume disk usage from the VM's perspective. It is usually much lower from the host's perspective due to CoW. ''' - with suppress(FileNotFoundError): - return _get_file_disk_usage(self._path_dirty) - with suppress(FileNotFoundError): - return _get_file_disk_usage(self._path_clean) + for path in (self._path_dirty, self._path_clean): + with suppress(FileNotFoundError): + return os.stat(path).st_blocks * 512 return 0 @@ -369,10 +367,6 @@ def _replace_file(dst): _remove_file(tmp.name) raise -def _get_file_disk_usage(path): - ''' Return real disk usage (not logical file size) of a file. ''' - return os.stat(path).st_blocks * BLKSIZE - def _fsync_dir(path): dir_fd = os.open(path, os.O_RDONLY | os.O_DIRECTORY) try: diff --git a/qubes/tests/storage_reflink.py b/qubes/tests/storage_reflink.py index 65decde1..ec2cadf9 100644 --- a/qubes/tests/storage_reflink.py +++ b/qubes/tests/storage_reflink.py @@ -62,15 +62,17 @@ class ReflinkMixin: os.symlink(img_real, img_sym) reflink._create_sparse_file(img_real, size_initial) - self.assertEqual(reflink._get_file_disk_usage(img_real), 0) - self.assertEqual(os.stat(img_real).st_size, size_initial) + stat = os.stat(img_real) + self.assertEqual(stat.st_blocks, 0) + self.assertEqual(stat.st_size, size_initial) dev_from_real = setup_loopdev(img_real, cleanup_via=self.addCleanup) dev_from_sym = setup_loopdev(img_sym, cleanup_via=self.addCleanup) reflink._resize_file(img_real, size_resized) - self.assertEqual(reflink._get_file_disk_usage(img_real), 0) - self.assertEqual(os.stat(img_real).st_size, size_resized) + stat = os.stat(img_real) + self.assertEqual(stat.st_blocks, 0) + self.assertEqual(stat.st_size, size_resized) reflink_update_loopdev_sizes(os.path.join(self.test_dir, 'unrelated')) From 25e92bd19bc7f3b236a725716b16b08bd1c39608 Mon Sep 17 00:00:00 2001 From: Rusty Bird Date: Sat, 15 Jun 2019 16:03:45 +0000 Subject: [PATCH 4/5] storage/reflink: volume.resize(): remove safety check It is handled by 'qvm-volume resize', which has a '--force' option that can't work if the check is duplicated in the storage driver. --- qubes/storage/reflink.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/qubes/storage/reflink.py b/qubes/storage/reflink.py index 8ad4857b..687f74ea 100644 --- a/qubes/storage/reflink.py +++ b/qubes/storage/reflink.py @@ -256,19 +256,13 @@ class ReflinkVolume(qubes.storage.Volume): @_unblock def resize(self, size): - ''' Expand a read-write volume image; notify any corresponding + ''' Resize a read-write volume image; notify any corresponding loop devices of the size change. ''' if not self.rw: raise qubes.storage.StoragePoolException( 'Cannot resize: {} is read-only'.format(self.vid)) - if size < self.size: - raise qubes.storage.StoragePoolException( - 'For your own safety, shrinking of {} is disabled' - ' ({} < {}). If you really know what you are doing,' - ' use "truncate" manually.'.format(self.vid, size, self.size)) - try: # assume volume is not (cleanly) stopped ... _resize_file(self._path_dirty, size) update = True From ef128156a33e82ffdf0495ddb736fbd1d945bf04 Mon Sep 17 00:00:00 2001 From: Rusty Bird Date: Sat, 15 Jun 2019 16:03:46 +0000 Subject: [PATCH 5/5] storage/reflink: volume.resize(): succeed without image Successfully resize volumes without any currently existing image file, e.g. cleanly stopped volatile volumes: Just update the nominal size in this case. --- qubes/storage/reflink.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/qubes/storage/reflink.py b/qubes/storage/reflink.py index 687f74ea..e9269392 100644 --- a/qubes/storage/reflink.py +++ b/qubes/storage/reflink.py @@ -263,15 +263,13 @@ class ReflinkVolume(qubes.storage.Volume): raise qubes.storage.StoragePoolException( 'Cannot resize: {} is read-only'.format(self.vid)) - try: # assume volume is not (cleanly) stopped ... - _resize_file(self._path_dirty, size) - update = True - except FileNotFoundError: # ... but it actually is. - _resize_file(self._path_clean, size) - update = False + for path in (self._path_dirty, self._path_clean): + with suppress(FileNotFoundError): + _resize_file(path, size) + break self.size = size - if update: + if path == self._path_dirty: _update_loopdev_sizes(self._path_dirty) return self