storage/file: fix is_dirty() false positive
is_dirty() returned a false positive if the volume was merely the source of a currently running volume. For example, if fedora-33:root was the source volume for myappvm:root and myappvm was running - then is_dirty() returned True for fedora-33:root, because fedora-33/root-cow.img contains some allocated blocks (one 256 KiB chunk containing only the header) in this scenario, even though fedora-33 is shut down. Fixes QubesOS/qubes-issues#6371
This commit is contained in:
parent
796e6f5096
commit
05eb051193
@ -4,6 +4,8 @@
|
|||||||
#
|
#
|
||||||
# This creates dm-snapshot device on given arguments
|
# This creates dm-snapshot device on given arguments
|
||||||
|
|
||||||
|
SNAPSHOT_CHUNKSIZE=256 # same as in file.py
|
||||||
|
|
||||||
dir=$(dirname "$0")
|
dir=$(dirname "$0")
|
||||||
if [ "$1" = "prepare" ] || [ "$1" = "cleanup" ]; then
|
if [ "$1" = "prepare" ] || [ "$1" = "cleanup" ]; then
|
||||||
# shellcheck disable=SC1090,SC1091
|
# shellcheck disable=SC1090,SC1091
|
||||||
@ -80,7 +82,7 @@ create_dm_snapshot() {
|
|||||||
base_dev=$(get_dev "$base")
|
base_dev=$(get_dev "$base")
|
||||||
cow_dev=$(get_dev "$cow")
|
cow_dev=$(get_dev "$cow")
|
||||||
base_sz=$(blockdev --getsz "$base_dev")
|
base_sz=$(blockdev --getsz "$base_dev")
|
||||||
do_or_die dmsetup create "$dm_devname" --table "0 $base_sz snapshot $base_dev $cow_dev P 256"
|
do_or_die dmsetup create "$dm_devname" --table "0 $base_sz snapshot $base_dev $cow_dev P $SNAPSHOT_CHUNKSIZE"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,12 @@ import qubes.utils
|
|||||||
|
|
||||||
BLKSIZE = 512
|
BLKSIZE = 512
|
||||||
|
|
||||||
|
# 256 KiB chunk, same as in block-snapshot script. Header created by
|
||||||
|
# struct.pack('<4I', 0x70416e53, 1, 1, 256) mimicking write_header()
|
||||||
|
# in linux/drivers/md/dm-snap-persistent.c
|
||||||
|
EMPTY_SNAPSHOT = b'SnAp\x01\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00' \
|
||||||
|
+ bytes(262128)
|
||||||
|
|
||||||
|
|
||||||
class FilePool(qubes.storage.Pool):
|
class FilePool(qubes.storage.Pool):
|
||||||
''' File based 'original' disk implementation
|
''' File based 'original' disk implementation
|
||||||
@ -217,7 +223,10 @@ class FileVolume(qubes.storage.Volume):
|
|||||||
if self.save_on_stop:
|
if self.save_on_stop:
|
||||||
with suppress(FileNotFoundError), open(self.path_cow, 'rb') as cow:
|
with suppress(FileNotFoundError), open(self.path_cow, 'rb') as cow:
|
||||||
cow_used = os.fstat(cow.fileno()).st_blocks * BLKSIZE
|
cow_used = os.fstat(cow.fileno()).st_blocks * BLKSIZE
|
||||||
return cow_used > 0
|
return (cow_used > 0 and
|
||||||
|
(cow_used > len(EMPTY_SNAPSHOT) or
|
||||||
|
cow.read(len(EMPTY_SNAPSHOT)) != EMPTY_SNAPSHOT or
|
||||||
|
cow_used > cow.seek(0, os.SEEK_HOLE)))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def resize(self, size):
|
def resize(self, size):
|
||||||
|
Loading…
Reference in New Issue
Block a user