Merge ~dbungert/curtin:resize-vs-wipe into curtin:master

Proposed by Dan Bungert
Status: Merged
Approved by: Dan Bungert
Approved revision: 84000f3fe8d6c926ff20013662c43a7f2286533f
Merge reported by: Server Team CI bot
Merged at revision: not available
Proposed branch: ~dbungert/curtin:resize-vs-wipe
Merge into: curtin:master
Diff against target: 110 lines (+61/-17)
2 files modified
curtin/commands/block_meta_v2.py (+19/-17)
tests/integration/test_block_meta.py (+42/-0)
Reviewer Review Type Date Requested Status
Server Team CI bot continuous-integration Approve
Michael Hudson-Doyle Approve
Review via email: mp+420511@code.launchpad.net

Commit message

block/v2: resize-friendly ordering of wipe

Description of the change

Split from https://code.launchpad.net/~dbungert/curtin/+git/curtin/+merge/419640 and is expected to have resolved the comments there.

To post a comment you must log in.
Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michael Hudson-Doyle (mwhudson) wrote :

LGTM too. This is going to conflict with that previous branch I think? Which do you plan to land first?

review: Approve
Revision history for this message
Dan Bungert (dbungert) wrote :

Yes, all 3 branches will conflict with each other, sadly. I guess I should do
1) ntfs
2) handle no format action
3) wipe

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/curtin/commands/block_meta_v2.py b/curtin/commands/block_meta_v2.py
2index cc2639c..a095c8f 100644
3--- a/curtin/commands/block_meta_v2.py
4+++ b/curtin/commands/block_meta_v2.py
5@@ -362,8 +362,20 @@ def disk_handler_v2(info, storage_config, handlers):
6 resizes[entry.start] = _prepare_resize(storage_config, action,
7 table, part_info)
8 preserved_offsets.add(entry.start)
9- wipe = wipes[entry.start] = _wipe_for_action(action)
10- if wipe is not None:
11+ wipes[entry.start] = _wipe_for_action(action)
12+
13+ for kname, nr, offset, size in block.sysfs_partition_data(disk):
14+ offset_sectors = table.bytes2sectors(offset)
15+ resize = resizes.get(offset_sectors)
16+ if resize and resize['direction'] == 'down':
17+ perform_resize(kname, resize)
18+
19+ for kname, nr, offset, size in block.sysfs_partition_data(disk):
20+ offset_sectors = table.bytes2sectors(offset)
21+ if offset_sectors not in preserved_offsets:
22+ # Do a superblock wipe of any partitions that are being deleted.
23+ block.wipe_volume(block.kname_to_path(kname), 'superblock')
24+ elif wipes.get(offset_sectors) is not None:
25 # We do a quick wipe of where any new partitions will be,
26 # because if there is bcache or other metadata there, this
27 # can cause the partition to be used by a storage
28@@ -371,27 +383,17 @@ def disk_handler_v2(info, storage_config, handlers):
29 # wipe_volume call below. See
30 # https://bugs.launchpad.net/curtin/+bug/1718699 for all
31 # the gory details.
32- wipe_offset = table.sectors2bytes(entry.start)
33- LOG.debug('Wiping 1M on %s at offset %s', disk, wipe_offset)
34- block.zero_file_at_offsets(disk, [wipe_offset], exclusive=False)
35-
36- for kname, nr, offset, size in block.sysfs_partition_data(disk):
37- offset_sectors = table.bytes2sectors(offset)
38- if offset_sectors not in preserved_offsets:
39- # Do a superblock wipe of any partitions that are being deleted.
40- block.wipe_volume(block.kname_to_path(kname), 'superblock')
41- resize = resizes.get(offset_sectors)
42- if resize and resize['direction'] == 'down':
43- perform_resize(kname, resize)
44+ LOG.debug('Wiping 1M on %s at offset %s', disk, offset)
45+ block.zero_file_at_offsets(disk, [offset], exclusive=False)
46
47 table.apply(disk)
48
49 for kname, number, offset, size in block.sysfs_partition_data(disk):
50 offset_sectors = table.bytes2sectors(offset)
51- mode = wipes[offset_sectors]
52- if mode is not None:
53+ wipe = wipes[offset_sectors]
54+ if wipe is not None:
55 # Wipe the new partitions as needed.
56- block.wipe_volume(block.kname_to_path(kname), mode)
57+ block.wipe_volume(block.kname_to_path(kname), wipe)
58 resize = resizes.get(offset_sectors)
59 if resize and resize['direction'] == 'up':
60 perform_resize(kname, resize)
61diff --git a/tests/integration/test_block_meta.py b/tests/integration/test_block_meta.py
62index 76a974b..8322062 100644
63--- a/tests/integration/test_block_meta.py
64+++ b/tests/integration/test_block_meta.py
65@@ -820,3 +820,45 @@ class TestBlockMeta(IntegrationTestCase):
66 self.assertEqual(9 << 20, _get_filesystem_size(dev, p1))
67 self.assertEqual(136 << 20, _get_filesystem_size(dev, p5))
68 self.assertEqual(50 << 20, _get_filesystem_size(dev, p6))
69+
70+ def test_split_and_wiping(self):
71+ # regression test for a bug where a partition wipe would happen before
72+ # a resize was performed, resulting in data loss.
73+ img = self.tmp_path('image.img')
74+ config = StorageConfigBuilder(version=2)
75+ config.add_image(path=img, size='100M', ptable='gpt')
76+ p1 = config.add_part(size=98 << 20, offset=1 << 20, number=1,
77+ fstype='ext4')
78+ self.run_bm(config.render())
79+ with loop_dev(img) as dev:
80+ self.assertEqual(
81+ summarize_partitions(dev), [
82+ PartData(number=1, offset=1 << 20, size=98 << 20),
83+ ])
84+ with self.mount(dev, p1) as mnt_point:
85+ # Attempt to create files across the partition with gaps
86+ for i in range(1, 41):
87+ with open(f'{mnt_point}/{str(i)}', 'wb') as fp:
88+ fp.write(bytes([i]) * (2 << 20))
89+ for i in range(1, 41):
90+ if i % 5 != 0:
91+ os.remove(f'{mnt_point}/{str(i)}')
92+
93+ config = StorageConfigBuilder(version=2)
94+ config.add_image(path=img, size='100M', ptable='gpt')
95+ p1 = config.add_part(size=49 << 20, offset=1 << 20, number=1,
96+ fstype='ext4', resize=True)
97+ config.set_preserve()
98+ config.add_part(size=49 << 20, offset=50 << 20, number=2,
99+ fstype='ext4')
100+ self.run_bm(config.render())
101+ with loop_dev(img) as dev:
102+ self.assertEqual(
103+ summarize_partitions(dev), [
104+ PartData(number=1, offset=1 << 20, size=49 << 20),
105+ PartData(number=2, offset=50 << 20, size=49 << 20),
106+ ])
107+ with self.mount(dev, p1) as mnt_point:
108+ for i in range(5, 41, 5):
109+ with open(f'{mnt_point}/{str(i)}', 'rb') as fp:
110+ self.assertEqual(bytes([i]) * (2 << 20), fp.read())

Subscribers

People subscribed via source and target branches