Merge ~mwhudson/curtin:v2-sector-size into curtin:master

Proposed by Michael Hudson-Doyle
Status: Merged
Approved by: Michael Hudson-Doyle
Approved revision: c7b3a9bdc7dacbdfd8226373fdcd39c6ee3b6b73
Merge reported by: Server Team CI bot
Merged at revision: not available
Proposed branch: ~mwhudson/curtin:v2-sector-size
Merge into: curtin:master
Diff against target: 248 lines (+61/-35)
4 files modified
curtin/block/__init__.py (+6/-9)
curtin/commands/block_meta.py (+2/-1)
curtin/commands/block_meta_v2.py (+32/-18)
tests/integration/test_block_meta.py (+21/-7)
Reviewer Review Type Date Requested Status
Server Team CI bot continuous-integration Approve
Dan Bungert Approve
Review via email: mp+415999@code.launchpad.net

Commit message

block_meta_v2: fix partitioning a device with sector size != 512

It turns out sfdisk takes input in the sector size of the device.

Also includes a fix for sysfs_partition_data in this case.

To post a comment you must log in.
Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
~mwhudson/curtin:v2-sector-size updated
c7b3a9b... by Michael Hudson-Doyle

fix v2 partitioning with 4k sectors

sfdisk *does* take input in the sector size of the device

250f5c5... by Michael Hudson-Doyle

add integration tests for device with 4096 sectors

these all fail

Revision history for this message
Dan Bungert (dbungert) :
review: Approve
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/block/__init__.py b/curtin/block/__init__.py
2index ca0bc10..f37ebee 100644
3--- a/curtin/block/__init__.py
4+++ b/curtin/block/__init__.py
5@@ -993,19 +993,12 @@ def sysfs_partition_data(blockdev=None, sysfs_path=None):
6 else:
7 raise ValueError("Blockdev and sysfs_path cannot both be None")
8
9- # queue property is only on parent devices, ie, we can't read
10- # /sys/class/block/vda/vda1/queue/* as queue is only on the
11- # parent device
12 sysfs_prefix = sysfs_path
13 (parent, partnum) = get_blockdev_for_partition(blockdev)
14 if partnum:
15 sysfs_prefix = sys_block_path(parent)
16 partnum = int(partnum)
17
18- block_size = int(util.load_file(os.path.join(
19- sysfs_prefix, 'queue/logical_block_size')))
20- unit = block_size
21-
22 ptdata = []
23 for part_sysfs in get_sysfs_partitions(sysfs_prefix):
24 data = {}
25@@ -1015,8 +1008,12 @@ def sysfs_partition_data(blockdev=None, sysfs_path=None):
26 continue
27 data[sfile] = int(util.load_file(dfile))
28 if partnum is None or data['partition'] == partnum:
29- ptdata.append((path_to_kname(part_sysfs), data['partition'],
30- data['start'] * unit, data['size'] * unit,))
31+ ptdata.append((
32+ path_to_kname(part_sysfs),
33+ data['partition'],
34+ data['start'] * SECTOR_SIZE_BYTES,
35+ data['size'] * SECTOR_SIZE_BYTES,
36+ ))
37
38 return ptdata
39
40diff --git a/curtin/commands/block_meta.py b/curtin/commands/block_meta.py
41index fbbfeeb..cdf30c5 100644
42--- a/curtin/commands/block_meta.py
43+++ b/curtin/commands/block_meta.py
44@@ -553,6 +553,7 @@ DEVS = set()
45 def image_handler(info, storage_config, handlers):
46 path = info['path']
47 size = int(util.human2bytes(info['size']))
48+ sector_size = str(int(util.human2bytes(info.get('sector_size', 512))))
49 if info.get('preserve', False):
50 actual_size = os.stat(path).st_size
51 if size != actual_size:
52@@ -571,7 +572,7 @@ def image_handler(info, storage_config, handlers):
53 raise
54 try:
55 dev = util.subp([
56- 'losetup', '--show', '--find', path],
57+ 'losetup', '--show', '--sector-size', sector_size, '--find', path],
58 capture=True)[0].strip()
59 except BaseException:
60 if os.path.exists(path) and not info.get('preserve'):
61diff --git a/curtin/commands/block_meta_v2.py b/curtin/commands/block_meta_v2.py
62index 4d000f0..90e5f7a 100644
63--- a/curtin/commands/block_meta_v2.py
64+++ b/curtin/commands/block_meta_v2.py
65@@ -39,8 +39,6 @@ class PartTableEntry:
66
67
68 ONE_MIB_BYTES = 1 << 20
69-SECTOR_BYTES = 512
70-ONE_MIB_SECTORS = ONE_MIB_BYTES // SECTOR_BYTES
71
72
73 def align_up(size, block_size):
74@@ -65,8 +63,20 @@ class SFDiskPartTable:
75
76 label = None
77
78- def __init__(self):
79+ def __init__(self, sector_bytes):
80 self.entries = []
81+ self._sector_bytes = sector_bytes
82+ if ONE_MIB_BYTES % sector_bytes != 0:
83+ raise Exception(
84+ f"sector_bytes {sector_bytes} does not divide 1MiB, cannot "
85+ "continue!")
86+ self.one_mib_sectors = ONE_MIB_BYTES // sector_bytes
87+
88+ def bytes2sectors(self, amount):
89+ return int(util.human2bytes(amount)) // self._sector_bytes
90+
91+ def sectors2bytes(self, amount):
92+ return amount * self._sector_bytes
93
94 def render(self):
95 r = ['label: ' + self.label, ''] + [e.render() for e in self.entries]
96@@ -94,14 +104,14 @@ class GPTPartTable(SFDiskPartTable):
97 def add(self, action):
98 number = action.get('number', len(self.entries) + 1)
99 if 'offset' in action:
100- start = int(util.human2bytes(action['offset'])) // SECTOR_BYTES
101+ start = self.bytes2sectors(action['offset'])
102 else:
103 if self.entries:
104 prev = self.entries[-1]
105- start = align_up(prev.start + prev.size, ONE_MIB_SECTORS)
106+ start = align_up(prev.start + prev.size, self.one_mib_sectors)
107 else:
108- start = ONE_MIB_SECTORS
109- size = int(util.human2bytes(action['size'])) // SECTOR_BYTES
110+ start = self.one_mib_sectors
111+ size = self.bytes2sectors(action['size'])
112 uuid = action.get('uuid')
113 type = FLAG_TO_GUID.get(action.get('flag'))
114 entry = PartTableEntry(number, start, size, type, uuid)
115@@ -118,7 +128,7 @@ class DOSPartTable(SFDiskPartTable):
116 flag = action.get('flag', None)
117 start = action.get('offset', None)
118 if start is not None:
119- start = int(util.human2bytes(start)) // SECTOR_BYTES
120+ start = self.bytes2sectors(start)
121 if flag == 'logical':
122 if self._extended is None:
123 raise Exception("logical partition without extended partition")
124@@ -138,14 +148,14 @@ class DOSPartTable(SFDiskPartTable):
125 number = 5
126 if start is None:
127 start = align_up(
128- self._extended.start + ONE_MIB_SECTORS,
129- ONE_MIB_SECTORS)
130+ self._extended.start + self.one_mib_sectors,
131+ self.one_mib_sectors)
132 else:
133 number = prev.number + 1
134 if start is None:
135 start = align_up(
136- prev.start + prev.size + ONE_MIB_SECTORS,
137- ONE_MIB_SECTORS)
138+ prev.start + prev.size + self.one_mib_sectors,
139+ self.one_mib_sectors)
140 else:
141 number = action.get('number', len(self.entries) + 1)
142 if number > 4:
143@@ -157,10 +167,12 @@ class DOSPartTable(SFDiskPartTable):
144 if entry.number <= 4:
145 prev = entry
146 if prev is None:
147- start = ONE_MIB_SECTORS
148+ start = self.one_mib_sectors
149 else:
150- start = align_up(prev.start + prev.size, ONE_MIB_SECTORS)
151- size = int(util.human2bytes(action['size'])) // SECTOR_BYTES
152+ start = align_up(
153+ prev.start + prev.size,
154+ self.one_mib_sectors)
155+ size = self.bytes2sectors(action['size'])
156 type = FLAG_TO_MBR_TYPE.get(flag)
157 if flag == 'boot':
158 bootable = True
159@@ -217,7 +229,9 @@ def disk_handler_v2(info, storage_config, handlers):
160 return
161
162 disk = get_path_to_storage_volume(info.get('id'), storage_config)
163- table = table_cls()
164+ (sector_size, _) = block.get_blockdev_sector_size(disk)
165+
166+ table = table_cls(sector_size)
167 preserved_offsets = set()
168 wipes = {}
169
170@@ -238,7 +252,7 @@ def disk_handler_v2(info, storage_config, handlers):
171
172 # Do a superblock wipe of any partitions that are being deleted.
173 for kname, nr, offset, sz in block.sysfs_partition_data(disk):
174- offset_sectors = offset // SECTOR_BYTES
175+ offset_sectors = table.bytes2sectors(offset)
176 if offset_sectors not in preserved_offsets:
177 block.wipe_volume(block.kname_to_path(kname), 'superblock')
178
179@@ -246,7 +260,7 @@ def disk_handler_v2(info, storage_config, handlers):
180
181 # Wipe the new partitions as needed.
182 for kname, number, offset, size in block.sysfs_partition_data(disk):
183- offset_sectors = offset // SECTOR_BYTES
184+ offset_sectors = table.bytes2sectors(offset)
185 mode = wipes[offset_sectors]
186 if mode is not None:
187 block.wipe_volume(block.kname_to_path(kname), mode)
188diff --git a/tests/integration/test_block_meta.py b/tests/integration/test_block_meta.py
189index a415c20..0c74cd6 100644
190--- a/tests/integration/test_block_meta.py
191+++ b/tests/integration/test_block_meta.py
192@@ -19,10 +19,11 @@ class IntegrationTestCase(CiTestCase):
193
194
195 @contextlib.contextmanager
196-def loop_dev(image):
197- dev = util.subp(
198- ['losetup', '--show', '--find', '--partscan', image],
199- capture=True, decode='ignore')[0].strip()
200+def loop_dev(image, sector_size=512):
201+ dev = util.subp([
202+ 'losetup', '--show', '--find', '--partscan',
203+ '--sector-size', str(sector_size), image,
204+ ], capture=True, decode='ignore')[0].strip()
205 try:
206 udev.udevadm_trigger([dev])
207 yield dev
208@@ -112,17 +113,18 @@ class TestBlockMeta(IntegrationTestCase):
209 ]
210 util.subp(cmd, env=cmd_env, **kwargs)
211
212- def _test_default_offsets(self, ptable, version):
213+ def _test_default_offsets(self, ptable, version, sector_size=512):
214 psize = 40 << 20
215 img = self.tmp_path('image.img')
216 config = StorageConfigBuilder(version=version)
217- config.add_image(path=img, size='200M', ptable=ptable)
218+ config.add_image(
219+ path=img, size='200M', ptable=ptable, sector_size=sector_size)
220 p1 = config.add_part(size=psize, number=1)
221 p2 = config.add_part(size=psize, number=2)
222 p3 = config.add_part(size=psize, number=3)
223 self.run_bm(config.render())
224
225- with loop_dev(img) as dev:
226+ with loop_dev(img, sector_size) as dev:
227 self.assertEqual(
228 summarize_partitions(dev), [
229 PartData(number=1, offset=1 << 20, size=psize),
230@@ -147,6 +149,18 @@ class TestBlockMeta(IntegrationTestCase):
231 def test_default_offsets_msdos_v2(self):
232 self._test_default_offsets('msdos', 2)
233
234+ def test_default_offsets_gpt_v1_4k(self):
235+ self._test_default_offsets('gpt', 1, 4096)
236+
237+ def test_default_offsets_msdos_v1_4k(self):
238+ self._test_default_offsets('msdos', 1, 4096)
239+
240+ def test_default_offsets_gpt_v2_4k(self):
241+ self._test_default_offsets('gpt', 2, 4096)
242+
243+ def test_default_offsets_msdos_v2_4k(self):
244+ self._test_default_offsets('msdos', 2, 4096)
245+
246 def _test_specified_offsets(self, ptable, version):
247 psize = 20 << 20
248 img = self.tmp_path('image.img')

Subscribers

People subscribed via source and target branches