Revision 34fd52a7

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