Revision 34fd52a7 snf-image-helper/disklabel.py
b/snf-image-helper/disklabel.py | ||
---|---|---|
19 | 19 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
20 | 20 |
# 02110-1301, USA. |
21 | 21 |
|
22 |
"""This module provides the code for handling OpenBSD disklabels"""
|
|
22 |
"""This module provides the code for handling BSD disklabels""" |
|
23 | 23 |
|
24 | 24 |
import struct |
25 | 25 |
import sys |
26 |
import os |
|
26 | 27 |
import cStringIO |
27 | 28 |
import optparse |
28 | 29 |
|
... | ... | |
36 | 37 |
BBSIZE = 8192 # size of boot area with label |
37 | 38 |
SBSIZE = 8192 # max size of fs superblock |
38 | 39 |
|
39 |
DISKMAGIC = 0x82564557 |
|
40 |
|
|
41 | 40 |
|
42 | 41 |
class MBR(object): |
43 | 42 |
"""Represents a Master Boot Record.""" |
... | ... | |
158 | 157 |
ret += "Partition %d: %s\n" % (i, self.part[i]) |
159 | 158 |
ret += "Signature: %s %s\n" % (hex(ord(self.signature[0])), |
160 | 159 |
hex(ord(self.signature[1]))) |
161 |
return ret |
|
160 |
title = "Master Boot Record" |
|
161 |
return "%s\n%s\n%s\n" % (title, len(title) * "=", ret) |
|
162 | 162 |
|
163 | 163 |
|
164 | 164 |
class Disklabel: |
165 |
"""Represents an BSD Disklabel""" |
|
166 |
|
|
167 |
def __init__(self, disk): |
|
168 |
"""Create a DiskLabel instance""" |
|
169 |
self.disk = disk |
|
170 |
self.part_num = None |
|
171 |
self.disklabel = None |
|
172 |
|
|
173 |
with open(disk, "rb") as d: |
|
174 |
sector0 = d.read(BLOCKSIZE) |
|
175 |
self.mbr = MBR(sector0) |
|
176 |
|
|
177 |
for i in range(4): |
|
178 |
ptype = self.mbr.part[i].type |
|
179 |
if ptype in (0xa5, 0xa6, 0xa9): |
|
180 |
d.seek(BLOCKSIZE * self.mbr.part[i].first_sector) |
|
181 |
self.part_num = i |
|
182 |
if ptype == 0xa5: # FreeBSD |
|
183 |
self.disklabel = BSD_Disklabel(d) |
|
184 |
elif ptype == 0xa6: # OpenBSD |
|
185 |
self.disklabel = OpenBSD_Disklabel(d) |
|
186 |
else: # NetBSD |
|
187 |
self.disklabel = BSD_Disklabel(d) |
|
188 |
break |
|
189 |
|
|
190 |
assert self.disklabel is not None, "No *BSD partition found" |
|
191 |
|
|
192 |
def write(self): |
|
193 |
"""Write the disklabel back to the media""" |
|
194 |
with open(self.disk, 'rw+b') as d: |
|
195 |
d.write(self.mbr.pack()) |
|
196 |
|
|
197 |
d.seek(self.mbr.part[self.part_num].first_sector * BLOCKSIZE) |
|
198 |
self.disklabel.write_to(d) |
|
199 |
|
|
200 |
def __str__(self): |
|
201 |
return str(self.mbr) + str(self.disklabel) |
|
202 |
|
|
203 |
def enlarge_disk(self, new_size): |
|
204 |
"""Enlarge the size of the disk and return the last usable sector""" |
|
205 |
|
|
206 |
# Fix the disklabel |
|
207 |
end = self.disklabel.enlarge_disk(new_size) |
|
208 |
|
|
209 |
# Fix the MBR |
|
210 |
start = self.mbr.part[self.part_num].first_sector |
|
211 |
self.mbr.part[self.part_num].sector_count = end - start + 1 |
|
212 |
|
|
213 |
cylinder = end // (self.disklabel.ntracks * self.disklabel.nsectors) |
|
214 |
header = (end // self.disklabel.nsectors) % self.disklabel.ntracks |
|
215 |
sector = (end % self.disklabel.nsectors) + 1 |
|
216 |
chs = MBR.Partition.pack_chs(cylinder, header, sector) |
|
217 |
self.mbr.part[self.part_num].end = chs |
|
218 |
|
|
219 |
def enlarge_last_partition(self): |
|
220 |
self.disklabel.enlarge_last_partition() |
|
221 |
|
|
222 |
|
|
223 |
class BSD_Disklabel: |
|
224 |
pass |
|
225 |
|
|
226 |
|
|
227 |
class OpenBSD_Disklabel: |
|
165 | 228 |
"""Represents an OpenBSD Disklabel""" |
166 | 229 |
format = "<IHH16s16sIIIIII8sIHHIII20sHH16sIHHII364s" |
167 | 230 |
""" |
... | ... | |
258 | 321 |
tmp.frag, tmp.cpg) |
259 | 322 |
|
260 | 323 |
def getpsize(self, i): |
324 |
"""Get size for partition i""" |
|
261 | 325 |
return (self.part[i].sizeh << 32) + self.part[i].size |
262 | 326 |
|
263 | 327 |
def setpoffset(self, i, offset): |
264 |
"""Set offset for partition i"""
|
|
328 |
"""Set offset for partition i""" |
|
265 | 329 |
tmp = self.part[i] |
266 | 330 |
self.part[i] = self.Partition(tmp.size, offset & 0xffffffff, |
267 | 331 |
offset >> 32, tmp.sizeh, tmp.frag, |
268 | 332 |
tmp.cpg) |
269 | 333 |
|
270 | 334 |
def getpoffset(self, i): |
335 |
"""Get offset for partition i""" |
|
271 | 336 |
return (self.part[i].offseth << 32) + self.part[i].offset |
272 | 337 |
|
273 |
def __init__(self, disk): |
|
274 |
"""Create a DiskLabel instance""" |
|
275 |
self.disk = disk |
|
276 |
self.part_num = None |
|
277 |
|
|
278 |
with open(disk, "rb") as d: |
|
279 |
sector0 = d.read(BLOCKSIZE) |
|
280 |
self.mbr = MBR(sector0) |
|
281 |
|
|
282 |
for i in range(4): |
|
283 |
if self.mbr.part[i].type == 0xa6: # OpenBSD type |
|
284 |
self.part_num = i |
|
285 |
break |
|
338 |
DISKMAGIC = 0x82564557 |
|
286 | 339 |
|
287 |
assert self.part_num is not None, "No OpenBSD partition found" |
|
340 |
def __init__(self, device): |
|
341 |
"""Create a DiskLabel instance""" |
|
288 | 342 |
|
289 |
d.seek(BLOCKSIZE * self.mbr.part[self.part_num].first_sector) |
|
290 |
part_sector0 = d.read(BLOCKSIZE) |
|
291 |
# The offset of the disklabel from the begining of the |
|
292 |
# partition is one sector |
|
293 |
part_sector1 = d.read(BLOCKSIZE) |
|
343 |
device.seek(BLOCKSIZE, os.SEEK_CUR) |
|
344 |
# The offset of the disklabel from the beginning of the partition is |
|
345 |
# one sector |
|
346 |
sector1 = device.read(BLOCKSIZE) |
|
294 | 347 |
|
295 | 348 |
(self.magic, |
296 | 349 |
self.dtype, |
... | ... | |
319 | 372 |
self.npartitions, |
320 | 373 |
self.bbsize, |
321 | 374 |
self.sbsize, |
322 |
ptable_raw) = struct.unpack(self.format, part_sector1)
|
|
375 |
ptable_raw) = struct.unpack(self.format, sector1) |
|
323 | 376 |
|
324 |
assert self.magic == DISKMAGIC, "Disklabel is not valid" |
|
377 |
assert self.magic == self.DISKMAGIC, "Disklabel is not valid"
|
|
325 | 378 |
|
326 | 379 |
self.ptable = self.PartitionTable(ptable_raw, self.npartitions) |
327 | 380 |
|
... | ... | |
391 | 444 |
return (self.bstarth << 32) + self.bstart |
392 | 445 |
|
393 | 446 |
def setbend(self, bend): |
394 |
"""Set end of useable region"""
|
|
447 |
"""Set size of useable region"""
|
|
395 | 448 |
self.bendh = bend >> 32 |
396 | 449 |
self.bend = bend & 0xffffffff |
397 | 450 |
|
398 | 451 |
def getbend(self): |
452 |
"""Get size of usable region""" |
|
399 | 453 |
return (self.bendh << 32) + self.bend |
400 | 454 |
|
401 | 455 |
def enlarge_disk(self, new_size): |
402 |
"""Enlarge the size of the disk""" |
|
456 |
"""Enlarge the size of the disk and return the last usable sector"""
|
|
403 | 457 |
|
404 |
assert new_size >= self.secperunit, \
|
|
405 |
"New size cannot be smaller that %s" % self.secperunit
|
|
458 |
assert new_size >= self.getdsize(), \
|
|
459 |
"New size cannot be smaller that %s" % self.getdsize()
|
|
406 | 460 |
|
407 | 461 |
# Fix the disklabel |
408 | 462 |
self.setdsize(new_size) |
... | ... | |
412 | 466 |
# Partition 'c' descriptes the entire disk |
413 | 467 |
self.ptable.setpsize(2, new_size) |
414 | 468 |
|
415 |
# Fix the MBR table |
|
416 |
start = self.mbr.part[self.part_num].first_sector |
|
417 |
self.mbr.part[self.part_num].sector_count = self.getbend() - start |
|
418 |
|
|
419 |
lba = self.getbend() - 1 |
|
420 |
cylinder = lba // (self.ntracks * self.nsectors) |
|
421 |
header = (lba // self.nsectors) % self.ntracks |
|
422 |
sector = (lba % self.nsectors) + 1 |
|
423 |
chs = MBR.Partition.pack_chs(cylinder, header, sector) |
|
424 |
self.mbr.part[self.part_num].end = chs |
|
425 |
|
|
469 |
# Update the checksum |
|
426 | 470 |
self.checksum = self.compute_checksum() |
427 | 471 |
|
428 |
def write(self): |
|
429 |
"""Write the disklabel back to the media""" |
|
430 |
with open(self.disk, 'rw+b') as d: |
|
431 |
d.write(self.mbr.pack()) |
|
472 |
# getbend() gives back the size of the usable region and not the end of |
|
473 |
# the usable region. I named it like this because this is how it is |
|
474 |
# named in OpenBSD. To get the last usable sector you need to reduce |
|
475 |
# this value by one. |
|
476 |
return self.getbend() - 1 |
|
477 |
|
|
478 |
def write_to(self, device): |
|
479 |
"""Write the disklabel to a device""" |
|
432 | 480 |
|
433 |
d.seek((self.mbr.part[self.part_num].first_sector + 1) * BLOCKSIZE) |
|
434 |
d.write(self.pack()) |
|
481 |
# The disklabel starts at sector 1 |
|
482 |
device.seek(BLOCKSIZE, os.SEEK_CUR) |
|
483 |
device.write(self.pack()) |
|
435 | 484 |
|
436 | 485 |
def get_last_partition_id(self): |
437 | 486 |
"""Returns the id of the last partition""" |
... | ... | |
471 | 520 |
|
472 | 521 |
def __str__(self): |
473 | 522 |
"""Print the Disklabel""" |
474 |
title1 = "Master Boot Record" |
|
475 |
title2 = "Disklabel" |
|
476 | 523 |
|
524 |
title = "Disklabel" |
|
477 | 525 |
return \ |
478 |
"%s\n%s\n%s\n" % (title1, len(title1) * "=", str(self.mbr)) + \ |
|
479 |
"%s\n%s\n" % (title2, len(title2) * "=") + \ |
|
526 |
"%s\n%s\n" % (title, len(title) * "=") + \ |
|
480 | 527 |
"Magic Number: 0x%x\n" % self.magic + \ |
481 | 528 |
"Drive type: %d\n" % self.dtype + \ |
482 | 529 |
"Subtype: %d\n" % self.subtype + \ |
Also available in: Unified diff