Merge remote-tracking branch 'origin/pr/266'
* origin/pr/266: storage/reflink: volume.resize(): succeed without image storage/reflink: volume.resize(): remove safety check storage/reflink: simplify volume.usage() storage/reflink: update volume size in __init__() storage/reflink: update snap_on_start volume size in start()
This commit is contained in:
commit
ad8c2d4b09
@ -36,7 +36,6 @@ from contextlib import contextmanager, suppress
|
|||||||
|
|
||||||
import qubes.storage
|
import qubes.storage
|
||||||
|
|
||||||
BLKSIZE = 512
|
|
||||||
FICLONE = 1074041865 # defined in <linux/fs.h>
|
FICLONE = 1074041865 # defined in <linux/fs.h>
|
||||||
LOOP_SET_CAPACITY = 0x4C07 # defined in <linux/loop.h>
|
LOOP_SET_CAPACITY = 0x4C07 # defined in <linux/loop.h>
|
||||||
LOGGER = logging.getLogger('qubes.storage.reflink')
|
LOGGER = logging.getLogger('qubes.storage.reflink')
|
||||||
@ -141,6 +140,13 @@ class ReflinkVolume(qubes.storage.Volume):
|
|||||||
self._path_import = self._path_vid + '-import.img'
|
self._path_import = self._path_vid + '-import.img'
|
||||||
self.path = self._path_dirty
|
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
|
@_unblock
|
||||||
def create(self):
|
def create(self):
|
||||||
if self.save_on_stop and not self.snap_on_start:
|
if self.save_on_stop and not self.snap_on_start:
|
||||||
@ -198,6 +204,7 @@ class ReflinkVolume(qubes.storage.Volume):
|
|||||||
if self.snap_on_start:
|
if self.snap_on_start:
|
||||||
# pylint: disable=protected-access
|
# pylint: disable=protected-access
|
||||||
_copy_file(self.source._path_clean, self._path_clean)
|
_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:
|
if self.snap_on_start or self.save_on_stop:
|
||||||
_copy_file(self._path_clean, self._path_dirty)
|
_copy_file(self._path_clean, self._path_dirty)
|
||||||
else:
|
else:
|
||||||
@ -249,28 +256,20 @@ class ReflinkVolume(qubes.storage.Volume):
|
|||||||
|
|
||||||
@_unblock
|
@_unblock
|
||||||
def resize(self, size):
|
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.
|
loop devices of the size change.
|
||||||
'''
|
'''
|
||||||
if not self.rw:
|
if not self.rw:
|
||||||
raise qubes.storage.StoragePoolException(
|
raise qubes.storage.StoragePoolException(
|
||||||
'Cannot resize: {} is read-only'.format(self.vid))
|
'Cannot resize: {} is read-only'.format(self.vid))
|
||||||
|
|
||||||
if size < self.size:
|
for path in (self._path_dirty, self._path_clean):
|
||||||
raise qubes.storage.StoragePoolException(
|
with suppress(FileNotFoundError):
|
||||||
'For your own safety, shrinking of {} is disabled'
|
_resize_file(path, size)
|
||||||
' ({} < {}). If you really know what you are doing,'
|
break
|
||||||
' use "truncate" manually.'.format(self.vid, size, self.size))
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
self.size = size
|
self.size = size
|
||||||
if update:
|
if path == self._path_dirty:
|
||||||
_update_loopdev_sizes(self._path_dirty)
|
_update_loopdev_sizes(self._path_dirty)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
@ -334,10 +333,9 @@ class ReflinkVolume(qubes.storage.Volume):
|
|||||||
''' Return volume disk usage from the VM's perspective. It is
|
''' Return volume disk usage from the VM's perspective. It is
|
||||||
usually much lower from the host's perspective due to CoW.
|
usually much lower from the host's perspective due to CoW.
|
||||||
'''
|
'''
|
||||||
with suppress(FileNotFoundError):
|
for path in (self._path_dirty, self._path_clean):
|
||||||
return _get_file_disk_usage(self._path_dirty)
|
with suppress(FileNotFoundError):
|
||||||
with suppress(FileNotFoundError):
|
return os.stat(path).st_blocks * 512
|
||||||
return _get_file_disk_usage(self._path_clean)
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
@ -361,10 +359,6 @@ def _replace_file(dst):
|
|||||||
_remove_file(tmp.name)
|
_remove_file(tmp.name)
|
||||||
raise
|
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):
|
def _fsync_dir(path):
|
||||||
dir_fd = os.open(path, os.O_RDONLY | os.O_DIRECTORY)
|
dir_fd = os.open(path, os.O_RDONLY | os.O_DIRECTORY)
|
||||||
try:
|
try:
|
||||||
|
@ -62,15 +62,17 @@ class ReflinkMixin:
|
|||||||
|
|
||||||
os.symlink(img_real, img_sym)
|
os.symlink(img_real, img_sym)
|
||||||
reflink._create_sparse_file(img_real, size_initial)
|
reflink._create_sparse_file(img_real, size_initial)
|
||||||
self.assertEqual(reflink._get_file_disk_usage(img_real), 0)
|
stat = os.stat(img_real)
|
||||||
self.assertEqual(os.stat(img_real).st_size, size_initial)
|
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_real = setup_loopdev(img_real, cleanup_via=self.addCleanup)
|
||||||
dev_from_sym = setup_loopdev(img_sym, cleanup_via=self.addCleanup)
|
dev_from_sym = setup_loopdev(img_sym, cleanup_via=self.addCleanup)
|
||||||
|
|
||||||
reflink._resize_file(img_real, size_resized)
|
reflink._resize_file(img_real, size_resized)
|
||||||
self.assertEqual(reflink._get_file_disk_usage(img_real), 0)
|
stat = os.stat(img_real)
|
||||||
self.assertEqual(os.stat(img_real).st_size, size_resized)
|
self.assertEqual(stat.st_blocks, 0)
|
||||||
|
self.assertEqual(stat.st_size, size_resized)
|
||||||
|
|
||||||
reflink_update_loopdev_sizes(os.path.join(self.test_dir, 'unrelated'))
|
reflink_update_loopdev_sizes(os.path.join(self.test_dir, 'unrelated'))
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user