Revision 3b2674b3
b/snf-image-helper/disklabel.py | ||
---|---|---|
112 | 112 |
|
113 | 113 |
return struct.pack('<BBB', byte0, byte1, byte2) |
114 | 114 |
|
115 |
format = "<444s2x16s16s16s16s2s" |
|
116 |
""" |
|
117 |
Offset Length Contents |
|
118 |
0 440(max. 446) code area |
|
119 |
440 2(optional) disk signature |
|
120 |
444 2 Usually nulls |
|
121 |
446 16 Partition 0 |
|
122 |
462 16 Partition 1 |
|
123 |
478 16 Partition 2 |
|
124 |
494 16 Partition 3 |
|
125 |
510 2 MBR signature |
|
126 |
""" |
|
127 | 115 |
def __init__(self, block): |
128 | 116 |
"""Create an MBR instance""" |
129 |
raw_part = {} |
|
130 |
(self.code_area, |
|
131 |
raw_part[0], |
|
132 |
raw_part[1], |
|
133 |
raw_part[2], |
|
134 |
raw_part[3], |
|
135 |
self.signature) = struct.unpack(self.format, block) |
|
117 |
|
|
118 |
self.format = "<444s2x16s16s16s16s2s" |
|
119 |
raw_part = {} # Offset Length Contents |
|
120 |
(self.code_area, # 0 440(max. 446) code area |
|
121 |
# 440 2(optional) disk signature |
|
122 |
# 444 2 Usually nulls |
|
123 |
raw_part[0], # 446 16 Partition 0 |
|
124 |
raw_part[1], # 462 16 Partition 1 |
|
125 |
raw_part[2], # 478 16 Partition 2 |
|
126 |
raw_part[3], # 494 16 Partition 3 |
|
127 |
self.signature # 510 2 MBR signature |
|
128 |
) = struct.unpack(self.format, block) |
|
136 | 129 |
|
137 | 130 |
self.part = {} |
138 | 131 |
for i in range(4): |
139 | 132 |
self.part[i] = self.Partition(raw_part[i]) |
140 | 133 |
|
141 |
@staticmethod |
|
142 |
def size(): |
|
134 |
def size(self): |
|
143 | 135 |
"""Return the size of a Master Boot Record.""" |
144 |
return struct.calcsize(MBR.format)
|
|
136 |
return struct.calcsize(self.format)
|
|
145 | 137 |
|
146 | 138 |
def pack(self): |
147 | 139 |
"""Pack an MBR to a binary string.""" |
... | ... | |
154 | 146 |
self.signature) |
155 | 147 |
|
156 | 148 |
def __str__(self): |
149 |
"""Print the MBR""" |
|
157 | 150 |
ret = "" |
158 | 151 |
for i in range(4): |
159 | 152 |
ret += "Partition %d: %s\n" % (i, self.part[i]) |
... | ... | |
164 | 157 |
|
165 | 158 |
|
166 | 159 |
class Disk(object): |
167 |
"""Represents an BSD Disk"""
|
|
160 |
"""Represents a BSD Disk""" |
|
168 | 161 |
|
169 | 162 |
def __init__(self, device): |
170 | 163 |
"""Create a Disk instance""" |
... | ... | |
200 | 193 |
self.disklabel.write_to(d) |
201 | 194 |
|
202 | 195 |
def __str__(self): |
196 |
"""Print the partitioning info of the Disk""" |
|
203 | 197 |
return str(self.mbr) + str(self.disklabel) |
204 | 198 |
|
205 | 199 |
def enlarge(self, new_size): |
... | ... | |
230 | 224 |
return self.disklabel.get_last_partition_id() |
231 | 225 |
|
232 | 226 |
def get_duid(self): |
233 |
"""Get the Disk UID (work only on OpenBSD)"""
|
|
227 |
"""Get the Disklabel Unique Identifier (works only for OpenBSD)"""
|
|
234 | 228 |
if 'uid' in self.disklabel.field: |
235 | 229 |
return self.disklabel.field['uid'] |
236 | 230 |
|
... | ... | |
242 | 236 |
|
243 | 237 |
def __init__(self, device): |
244 | 238 |
"""Create a Disklabel instance""" |
239 |
pass |
|
240 |
|
|
241 |
@property |
|
242 |
def format(self): |
|
243 |
"""Fields format string""" |
|
244 |
raise NotImplementedError |
|
245 |
|
|
246 |
@property |
|
247 |
def field(self): |
|
248 |
"""Diskalabel Fields data structure""" |
|
249 |
raise NotImplementedError |
|
250 |
|
|
251 |
@property |
|
252 |
def ptable(self): |
|
253 |
"""Partition Table data structure""" |
|
245 | 254 |
raise NotImplementedError |
246 | 255 |
|
247 | 256 |
def pack(self, checksum=None): |
248 | 257 |
"""Return a binary copy of the Disklabel block""" |
249 | 258 |
|
250 | 259 |
out = OrderedDict() |
251 |
for k, v in self.field.items(): |
|
260 |
for k, v in self.field.iteritems():
|
|
252 | 261 |
out[k] = v |
253 | 262 |
|
254 | 263 |
if checksum is not None: |
... | ... | |
313 | 322 |
|
314 | 323 |
raw = cStringIO.StringIO(ptable) |
315 | 324 |
try: |
316 |
for i in range(pnumber):
|
|
317 |
p = self.Partition(
|
|
318 |
*struct.unpack(self.format, raw.read(size))) |
|
319 |
self.part.append(p)
|
|
325 |
for _ in xrange(pnumber):
|
|
326 |
self.part.append(
|
|
327 |
self.Partition(*struct.unpack(self.format, raw.read(size)))
|
|
328 |
)
|
|
320 | 329 |
finally: |
321 | 330 |
raw.close() |
322 | 331 |
|
323 | 332 |
def __str__(self): |
324 | 333 |
"""Print the Partition table""" |
325 | 334 |
val = "" |
326 |
for i in range(len(self.part)): |
|
335 |
for i in xrange(len(self.part)):
|
|
327 | 336 |
val += "%c: %s\n" % (chr(ord('a') + i), str(self.part[i])) |
328 | 337 |
return val |
329 | 338 |
|
330 | 339 |
def pack(self): |
331 | 340 |
"""Packs the partition table into a binary string.""" |
332 | 341 |
ret = "" |
333 |
for i in range(len(self.part)): |
|
342 |
for i in xrange(len(self.part)):
|
|
334 | 343 |
ret += struct.pack(self.format, *self.part[i]) |
335 | 344 |
return ret + ((364 - len(self.part) * 16) * '\x00') |
336 | 345 |
|
... | ... | |
340 | 349 |
|
341 | 350 |
class PartitionTable(PartitionTableBase): |
342 | 351 |
"""Represents a BSD Partition Table""" |
343 |
format = "<IIIBBH" |
|
344 |
""" |
|
345 |
Partition Entry: |
|
346 |
Offset Length Contents |
|
347 |
0 4 Number of sectors in partition |
|
348 |
4 4 Starting sector |
|
349 |
8 4 Filesystem basic fragment size |
|
350 |
12 1 Filesystem type |
|
351 |
13 1 Filesystem fragments per block |
|
352 |
14 2 Filesystem cylinders per group |
|
353 |
""" |
|
354 | 352 |
|
353 |
format = "<IIIBBH" |
|
355 | 354 |
Partition = namedtuple( |
356 |
'Partition', 'size, offset, fsize, fstype, frag, cpg') |
|
357 |
|
|
358 |
format = "<IHH16s16sIIIIIIHHIHHHHIII20s20sIHHII364s" |
|
355 |
'Partition', # Offset Length Contents |
|
356 |
['size', # 0 4 Number of sectors in partition |
|
357 |
'offset', # 4 4 Starting sector |
|
358 |
'fsize', # 8 4 Filesystem basic fragment size |
|
359 |
'fstype', # 12 1 Filesystem type |
|
360 |
'frag', # 13 1 Filesystem fragments per block |
|
361 |
'cpg' # 14 2 Filesystem cylinders per group |
|
362 |
]) |
|
359 | 363 |
|
360 | 364 |
def __init__(self, device): |
361 | 365 |
"""Create a BSD DiskLabel instance""" |
366 |
super(BSD_Disklabel, self).__init__(device) |
|
362 | 367 |
|
368 |
# Disklabel starts at offset one |
|
363 | 369 |
device.seek(BLOCKSIZE, os.SEEK_CUR) |
364 |
# The offset of the disklabel from the beginning of the partition is |
|
365 |
# one sector |
|
366 | 370 |
sector1 = device.read(BLOCKSIZE) |
367 | 371 |
|
372 |
self.format = "<IHH16s16sIIIIIIHHIHHHHIII20s20sIHHII364s" |
|
368 | 373 |
d_ = OrderedDict() # Off Len Content |
369 | 374 |
(d_["magic"], # 0 4 Magic |
370 | 375 |
d_["dtype"], # 4 2 Drive Type |
... | ... | |
405 | 410 |
"""Print the Disklabel""" |
406 | 411 |
|
407 | 412 |
title = "Disklabel" |
413 |
|
|
414 |
# Those fields may contain null bytes |
|
408 | 415 |
typename = self.field['typename'].strip('\x00').strip() |
409 | 416 |
packname = self.field['packname'].strip('\x00').strip() |
410 | 417 |
|
... | ... | |
447 | 454 |
class PartitionTable(PartitionTableBase): |
448 | 455 |
"""Reprepsents an OpenBSD Partition Table""" |
449 | 456 |
format = "<IIHHBBH" |
450 |
""" |
|
451 |
Partition Entry: |
|
452 |
Offset Length Contents |
|
453 |
0 4 Number of sectors in the partition |
|
454 |
4 4 Starting sector |
|
455 |
8 2 Starting sector (high part) |
|
456 |
10 2 Number of sectors (high part) |
|
457 |
12 1 Filesystem type |
|
458 |
13 1 Filesystem Fragment per block |
|
459 |
14 2 FS cylinders per group |
|
460 |
""" |
|
461 | 457 |
Partition = namedtuple( |
462 |
'Partition', 'size, offset, offseth, sizeh, fstype, frag, cpg') |
|
458 |
'Partition', # Offset Length Contents |
|
459 |
['size', # 0 4 Number of sectors in the partition |
|
460 |
'offset', # 4 4 Starting sector |
|
461 |
'offseth', # 8 2 Starting sector (high part) |
|
462 |
'sizeh', # 10 2 Number of sectors (high part) |
|
463 |
'fstype', # 12 1 Filesystem type |
|
464 |
'frag', # 13 1 Filesystem Fragments per block |
|
465 |
'cpg' # 14 2 FS cylinders per group |
|
466 |
]) |
|
463 | 467 |
|
464 | 468 |
def setpsize(self, i, size): |
465 | 469 |
"""Set size for partition i""" |
... | ... | |
483 | 487 |
"""Get offset for partition i""" |
484 | 488 |
return (self.part[i].offseth << 32) + self.part[i].offset |
485 | 489 |
|
486 |
format = "<IHH16s16sIIIIII8sIHHIII20sHH16sIHHII364s" |
|
487 |
|
|
488 | 490 |
def __init__(self, device): |
489 | 491 |
"""Create a DiskLabel instance""" |
492 |
super(OpenBSD_Disklabel, self).__init__(device) |
|
490 | 493 |
|
494 |
# Disklabel starts at offset one |
|
491 | 495 |
device.seek(BLOCKSIZE, os.SEEK_CUR) |
492 |
# The offset of the disklabel from the beginning of the partition is |
|
493 |
# one sector |
|
494 | 496 |
sector1 = device.read(BLOCKSIZE) |
495 | 497 |
|
498 |
self.format = "<IHH16s16sIIIIII8sIHHIII20sHH16sIHHII364s" |
|
496 | 499 |
d_ = OrderedDict() # Off Len Content |
497 | 500 |
(d_["magic"], # 0 4 Magic |
498 | 501 |
d_["dtype"], # 4 2 Drive Type |
... | ... | |
548 | 551 |
"""Get start of usable region""" |
549 | 552 |
return (self.field['bstarth'] << 32) + self.field['bstart'] |
550 | 553 |
|
551 |
bstart = property(getbstart, setbstart, None, "usable region start")
|
|
554 |
bstart = property(getbstart, setbstart, None, "start of usable region")
|
|
552 | 555 |
|
553 | 556 |
def setbend(self, bend): |
554 |
"""Set size of useable region"""
|
|
557 |
"""Set end of useable region"""
|
|
555 | 558 |
self.field['bendh'] = bend >> 32 |
556 | 559 |
self.field['bend'] = bend & 0xffffffff |
557 | 560 |
|
558 | 561 |
def getbend(self): |
559 |
"""Get size of usable region"""
|
|
562 |
"""Get end of usable region"""
|
|
560 | 563 |
return (self.field['bendh'] << 32) + self.field['bend'] |
561 | 564 |
|
562 |
bend = property(getbend, setbend, None, "usable region size")
|
|
565 |
bend = property(getbend, setbend, None, "end of usable region")
|
|
563 | 566 |
|
564 | 567 |
def enlarge(self, new_size): |
565 | 568 |
"""Enlarge the disk and return the last usable sector""" |
... | ... | |
580 | 583 |
# Update the checksum |
581 | 584 |
self.field['checksum'] = self.compute_checksum() |
582 | 585 |
|
583 |
# bend is the size and not the end of the usable region. I named it |
|
584 |
# like this because this is how it is named in OpenBSD. To get the last |
|
585 |
# usable sector you need to reduce this value by one. |
|
586 |
# The last usable sector is the end of the usable region minus one |
|
586 | 587 |
return self.bend - 1 |
587 | 588 |
|
588 | 589 |
def get_last_partition_id(self): |
... | ... | |
624 | 625 |
def __str__(self): |
625 | 626 |
"""Print the Disklabel""" |
626 | 627 |
|
627 |
title = "Disklabel"
|
|
628 |
# Those values may contain null bytes
|
|
628 | 629 |
typename = self.field['typename'].strip('\x00').strip() |
629 | 630 |
packname = self.field['packname'].strip('\x00').strip() |
631 |
|
|
630 | 632 |
duid = "".join(x.encode('hex') for x in self.field['uid']) |
633 |
|
|
634 |
title = "Disklabel" |
|
631 | 635 |
return \ |
632 | 636 |
"%s\n%s\n" % (title, len(title) * "=") + \ |
633 | 637 |
"Magic Number: 0x%(magic)x\n" \ |
... | ... | |
660 | 664 |
"%s" % self.ptable |
661 | 665 |
|
662 | 666 |
|
663 |
if __name__ == '__main__':
|
|
664 |
|
|
667 |
def main():
|
|
668 |
"""Main entry point""" |
|
665 | 669 |
usage = "Usage: %prog [options] <input_media>" |
666 | 670 |
parser = optparse.OptionParser(usage=usage) |
667 | 671 |
|
... | ... | |
671 | 675 |
parser.add_option("--get-last-partition", action="store_true", |
672 | 676 |
dest="last_part", default=False, |
673 | 677 |
help="print the label of the last partition") |
674 |
parser.add_option("--get-duid", action="store_true", dest="duid",
|
|
675 |
default=False,
|
|
676 |
help="print the disklabel unique identifier")
|
|
678 |
parser.add_option( |
|
679 |
"--get-duid", action="store_true", dest="duid", default=False,
|
|
680 |
help="print the Disklabel Unique Identifier (OpenBSD only)")
|
|
677 | 681 |
parser.add_option("-d", "--enlarge-disk", type="int", dest="disk_size", |
678 | 682 |
default=None, metavar="SIZE", |
679 | 683 |
help="Enlarge the disk to this SIZE (in sectors)") |
... | ... | |
691 | 695 |
|
692 | 696 |
if options.list: |
693 | 697 |
print disk |
694 |
sys.exit(0)
|
|
698 |
return 0
|
|
695 | 699 |
|
696 | 700 |
if options.duid: |
697 | 701 |
print "%s" % "".join(x.encode('hex') for x in disk.get_duid()) |
698 |
sys.exit(0)
|
|
702 |
return 0
|
|
699 | 703 |
|
700 | 704 |
if options.last_part: |
701 | 705 |
print "%c" % chr(ord('a') + disk.get_last_partition_id()) |
... | ... | |
707 | 711 |
disk.enlarge_last_partition() |
708 | 712 |
|
709 | 713 |
disk.write() |
714 |
return 0 |
|
715 |
|
|
710 | 716 |
|
711 |
sys.exit(0) |
|
717 |
if __name__ == '__main__': |
|
718 |
sys.exit(main()) |
|
712 | 719 |
|
713 | 720 |
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai : |
Also available in: Unified diff