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