Revision ee788eff snf-image-helper/disklabel.py
b/snf-image-helper/disklabel.py | ||
---|---|---|
26 | 26 |
import os |
27 | 27 |
import cStringIO |
28 | 28 |
import optparse |
29 |
import abc |
|
29 | 30 |
|
30 | 31 |
from collections import namedtuple |
31 | 32 |
from collections import OrderedDict |
... | ... | |
45 | 46 |
"""Represents a Master Boot Record.""" |
46 | 47 |
class Partition(object): |
47 | 48 |
"""Represents a partition entry in MBR""" |
48 |
format = "<B3sB3sLL"
|
|
49 |
fmt = "<B3sB3sLL"
|
|
49 | 50 |
|
50 | 51 |
def __init__(self, raw_part): |
51 | 52 |
"""Create a Partition instance""" |
... | ... | |
55 | 56 |
self.end, |
56 | 57 |
self.first_sector, |
57 | 58 |
self.sector_count |
58 |
) = struct.unpack(self.format, raw_part)
|
|
59 |
) = struct.unpack(self.fmt, raw_part)
|
|
59 | 60 |
|
60 | 61 |
def pack(self): |
61 | 62 |
"""Pack the partition values into a binary string""" |
62 |
return struct.pack(self.format,
|
|
63 |
return struct.pack(self.fmt,
|
|
63 | 64 |
self.status, |
64 | 65 |
self.start, |
65 | 66 |
self.type, |
... | ... | |
70 | 71 |
@staticmethod |
71 | 72 |
def size(): |
72 | 73 |
"""Returns the size of an MBR partition entry""" |
73 |
return struct.calcsize(MBR.Partition.format)
|
|
74 |
return struct.calcsize(MBR.Partition.fmt)
|
|
74 | 75 |
|
75 | 76 |
def __str__(self): |
76 | 77 |
start = self.unpack_chs(self.start) |
... | ... | |
115 | 116 |
def __init__(self, block): |
116 | 117 |
"""Create an MBR instance""" |
117 | 118 |
|
118 |
self.format = "<444s2x16s16s16s16s2s"
|
|
119 |
self.fmt = "<444s2x16s16s16s16s2s"
|
|
119 | 120 |
raw_part = {} # Offset Length Contents |
120 | 121 |
(self.code_area, # 0 440(max. 446) code area |
121 | 122 |
# 440 2(optional) disk signature |
... | ... | |
125 | 126 |
raw_part[2], # 478 16 Partition 2 |
126 | 127 |
raw_part[3], # 494 16 Partition 3 |
127 | 128 |
self.signature # 510 2 MBR signature |
128 |
) = struct.unpack(self.format, block)
|
|
129 |
) = struct.unpack(self.fmt, block)
|
|
129 | 130 |
|
130 | 131 |
self.part = {} |
131 | 132 |
for i in range(4): |
... | ... | |
133 | 134 |
|
134 | 135 |
def size(self): |
135 | 136 |
"""Return the size of a Master Boot Record.""" |
136 |
return struct.calcsize(self.format)
|
|
137 |
return struct.calcsize(self.fmt)
|
|
137 | 138 |
|
138 | 139 |
def pack(self): |
139 | 140 |
"""Pack an MBR to a binary string.""" |
140 |
return struct.pack(self.format,
|
|
141 |
return struct.pack(self.fmt,
|
|
141 | 142 |
self.code_area, |
142 | 143 |
self.part[0].pack(), |
143 | 144 |
self.part[1].pack(), |
... | ... | |
233 | 234 |
|
234 | 235 |
class DisklabelBase(object): |
235 | 236 |
"""Disklabel base class""" |
237 |
__metaclass__ = abc.ABCMeta |
|
236 | 238 |
|
237 | 239 |
def __init__(self, device): |
238 | 240 |
"""Create a Disklabel instance""" |
239 |
pass |
|
240 | 241 |
|
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 |
|
242 |
# Subclasses need to overwrite this |
|
243 |
self.field = None |
|
244 |
self.ptable = None |
|
250 | 245 |
|
251 |
@property |
|
252 |
def ptable(self):
|
|
253 |
"""Partition Table data structure"""
|
|
254 |
raise NotImplementedError
|
|
246 |
@abc.abstractproperty
|
|
247 |
def fmt(self):
|
|
248 |
"""Fields format string for the disklabel fields"""
|
|
249 |
pass
|
|
255 | 250 |
|
256 | 251 |
def pack(self, checksum=None): |
257 | 252 |
"""Return a binary copy of the Disklabel block""" |
... | ... | |
263 | 258 |
if checksum is not None: |
264 | 259 |
out['checksum'] = checksum |
265 | 260 |
|
266 |
return struct.pack(self.format, * out.values() + [self.ptable.pack()])
|
|
261 |
return struct.pack(self.fmt, * out.values() + [self.ptable.pack()])
|
|
267 | 262 |
|
268 | 263 |
def compute_checksum(self): |
269 | 264 |
"""Compute the checksum of the disklabel""" |
... | ... | |
280 | 275 |
|
281 | 276 |
return checksum |
282 | 277 |
|
278 |
@abc.abstractmethod |
|
283 | 279 |
def enlarge(self, new_size): |
284 | 280 |
"""Enlarge the disk and return the last usable sector""" |
285 |
raise NotImplementedError
|
|
281 |
pass
|
|
286 | 282 |
|
287 | 283 |
def write_to(self, device): |
288 | 284 |
"""Write the disklabel to a device""" |
... | ... | |
291 | 287 |
device.seek(BLOCKSIZE, os.SEEK_CUR) |
292 | 288 |
device.write(self.pack()) |
293 | 289 |
|
290 |
@abc.abstractmethod |
|
294 | 291 |
def enlarge_last_partition(self): |
295 | 292 |
"""Enlarge the last partition to consume all the usable space""" |
296 |
raise NotImplementedError
|
|
293 |
pass
|
|
297 | 294 |
|
295 |
@abc.abstractmethod |
|
298 | 296 |
def get_last_partition_id(self): |
299 | 297 |
"""Get the ID of the last partition""" |
300 |
raise NotImplementedError
|
|
298 |
pass
|
|
301 | 299 |
|
300 |
@abc.abstractmethod |
|
302 | 301 |
def __str__(self): |
303 | 302 |
"""Print the Disklabel""" |
304 |
raise NotImplementedError
|
|
303 |
pass
|
|
305 | 304 |
|
306 | 305 |
|
307 | 306 |
class PartitionTableBase(object): |
308 | 307 |
"""Base Class for disklabel partition tables""" |
308 |
__metaclass__ = abc.ABCMeta |
|
309 | 309 |
|
310 |
@property |
|
311 |
def format(self):
|
|
312 |
"""Partition table format string"""
|
|
313 |
raise NotImplementedError
|
|
310 |
@abc.abstractproperty
|
|
311 |
def fmt(self):
|
|
312 |
"""Partition fields format string"""
|
|
313 |
pass
|
|
314 | 314 |
|
315 |
Partition = namedtuple('Partition', '') |
|
315 |
@abc.abstractproperty |
|
316 |
def fields(self): |
|
317 |
"""The partition fields""" |
|
318 |
pass |
|
316 | 319 |
|
317 | 320 |
def __init__(self, ptable, pnumber): |
318 | 321 |
"""Create a Partition Table instance""" |
319 |
self.part = [] |
|
320 | 322 |
|
321 |
size = struct.calcsize(self.format) |
|
323 |
self.Partition = namedtuple('Partition', self.fields) |
|
324 |
self.part = [] |
|
322 | 325 |
|
326 |
size = struct.calcsize(self.fmt) |
|
323 | 327 |
raw = cStringIO.StringIO(ptable) |
324 | 328 |
try: |
325 | 329 |
for _ in xrange(pnumber): |
326 | 330 |
self.part.append( |
327 |
self.Partition(*struct.unpack(self.format, raw.read(size)))
|
|
331 |
self.Partition(*struct.unpack(self.fmt, raw.read(size)))
|
|
328 | 332 |
) |
329 | 333 |
finally: |
330 | 334 |
raw.close() |
... | ... | |
340 | 344 |
"""Packs the partition table into a binary string.""" |
341 | 345 |
ret = "" |
342 | 346 |
for i in xrange(len(self.part)): |
343 |
ret += struct.pack(self.format, *self.part[i])
|
|
347 |
ret += struct.pack(self.fmt, *self.part[i])
|
|
344 | 348 |
return ret + ((364 - len(self.part) * 16) * '\x00') |
345 | 349 |
|
346 | 350 |
|
... | ... | |
350 | 354 |
class PartitionTable(PartitionTableBase): |
351 | 355 |
"""Represents a BSD Partition Table""" |
352 | 356 |
|
353 |
format = "<IIIBBH" |
|
354 |
Partition = namedtuple( |
|
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 |
]) |
|
357 |
@property |
|
358 |
def fmt(self): |
|
359 |
"""Partition fields format string""" |
|
360 |
return "<IIIBBH" |
|
361 |
|
|
362 |
@property |
|
363 |
def fields(self): |
|
364 |
"""The partition fields""" |
|
365 |
return [ # Offset Length Contents |
|
366 |
'size', # 0 4 Number of sectors in partition |
|
367 |
'offset', # 4 4 Starting sector of the partition |
|
368 |
'fsize', # 8 4 File system basic fragment size |
|
369 |
'fstype', # 12 1 File system type |
|
370 |
'frag', # 13 1 File system fragments per block |
|
371 |
'cpg' # 14 2 File system cylinders per group |
|
372 |
] |
|
373 |
|
|
374 |
@property |
|
375 |
def fmt(self): |
|
376 |
return "<IHH16s16sIIIIIIHHIHHHHIII20s20sIHHII364s" |
|
363 | 377 |
|
364 | 378 |
def __init__(self, device): |
365 | 379 |
"""Create a BSD DiskLabel instance""" |
... | ... | |
369 | 383 |
device.seek(BLOCKSIZE, os.SEEK_CUR) |
370 | 384 |
sector1 = device.read(BLOCKSIZE) |
371 | 385 |
|
372 |
self.format = "<IHH16s16sIIIIIIHHIHHHHIII20s20sIHHII364s" |
|
373 | 386 |
d_ = OrderedDict() # Off Len Content |
374 | 387 |
(d_["magic"], # 0 4 Magic |
375 | 388 |
d_["dtype"], # 4 2 Drive Type |
... | ... | |
400 | 413 |
d_["bbsize"], # 132 4 size of boot area at sn0, bytes |
401 | 414 |
d_["sbsize"], # 136 4 Max size of fs superblock, bytes |
402 | 415 |
ptable_raw # 140 16*16 Partition Table |
403 |
) = struct.unpack(self.format, sector1)
|
|
416 |
) = struct.unpack(self.fmt, sector1)
|
|
404 | 417 |
|
405 | 418 |
assert d_['magic'] == d_['magic2'] == DISKMAGIC, "Disklabel not valid" |
406 | 419 |
self.ptable = self.PartitionTable(ptable_raw, d_['npartitions']) |
407 | 420 |
self.field = d_ |
408 | 421 |
|
422 |
def enlarge(self, new_size): |
|
423 |
raise NotImplementedError |
|
424 |
|
|
425 |
def enlarge_last_partition(self): |
|
426 |
raise NotImplementedError |
|
427 |
|
|
428 |
def get_last_partition_id(self): |
|
429 |
raise NotImplementedError |
|
430 |
|
|
409 | 431 |
def __str__(self): |
410 | 432 |
"""Print the Disklabel""" |
411 | 433 |
|
... | ... | |
453 | 475 |
|
454 | 476 |
class PartitionTable(PartitionTableBase): |
455 | 477 |
"""Reprepsents an OpenBSD Partition Table""" |
456 |
format = "<IIHHBBH" |
|
457 |
Partition = namedtuple( |
|
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 |
]) |
|
478 |
|
|
479 |
@property |
|
480 |
def fmt(self): |
|
481 |
return "<IIHHBBH" |
|
482 |
|
|
483 |
@property |
|
484 |
def fields(self): |
|
485 |
return [ # Offset Length Contents |
|
486 |
'size', # 0 4 Number of sectors in the partition |
|
487 |
'offset', # 4 4 Starting sector of the partition |
|
488 |
'offseth', # 8 2 Starting sector (high part) |
|
489 |
'sizeh', # 10 2 Number of sectors (high part) |
|
490 |
'fstype', # 12 1 Filesystem type |
|
491 |
'frag', # 13 1 Filesystem Fragments per block |
|
492 |
'cpg' # 14 2 FS cylinders per group |
|
493 |
] |
|
467 | 494 |
|
468 | 495 |
def setpsize(self, i, size): |
469 | 496 |
"""Set size for partition i""" |
... | ... | |
487 | 514 |
"""Get offset for partition i""" |
488 | 515 |
return (self.part[i].offseth << 32) + self.part[i].offset |
489 | 516 |
|
517 |
@property |
|
518 |
def fmt(self): |
|
519 |
return "<IHH16s16sIIIIII8sIHHIII20sHH16sIHHII364s" |
|
520 |
|
|
490 | 521 |
def __init__(self, device): |
491 | 522 |
"""Create a DiskLabel instance""" |
523 |
|
|
492 | 524 |
super(OpenBSD_Disklabel, self).__init__(device) |
493 | 525 |
|
494 | 526 |
# Disklabel starts at offset one |
495 | 527 |
device.seek(BLOCKSIZE, os.SEEK_CUR) |
496 | 528 |
sector1 = device.read(BLOCKSIZE) |
497 | 529 |
|
498 |
self.format = "<IHH16s16sIIIIII8sIHHIII20sHH16sIHHII364s" |
|
499 | 530 |
d_ = OrderedDict() # Off Len Content |
500 | 531 |
(d_["magic"], # 0 4 Magic |
501 | 532 |
d_["dtype"], # 4 2 Drive Type |
... | ... | |
525 | 556 |
d_["bbsize"], # 132 4 size of boot area at sn0, bytes |
526 | 557 |
d_["sbsize"], # 136 4 Max size of fs superblock, bytes |
527 | 558 |
ptable_raw # 140 16*16 Partition Table |
528 |
) = struct.unpack(self.format, sector1)
|
|
559 |
) = struct.unpack(self.fmt, sector1)
|
|
529 | 560 |
|
530 | 561 |
assert d_['magic'] == d_['magic2'] == DISKMAGIC, "Disklabel not valid" |
531 | 562 |
self.ptable = self.PartitionTable(ptable_raw, d_['npartitions']) |
Also available in: Unified diff