# nobodd.disk

The `nobodd.disk` module contains the `DiskImage` class which is the primary entry point for handling disk images. Constructed with a filename (or file-like object which provides a valid `fileno()` method), the class will attempt to determine if MBR or GPT style partitioning is in use. The `DiskImage.partitions` attribute can then be queried to enumerate, or access the data of, individual partitions:

    >>> from nobodd.disk import DiskImage
    >>> img = DiskImage('gpt_disk.img')
    >>> img
    <DiskImage file=<_io.BufferedReader name='gpt_disk.img'> style='gpt' signature=UUID('733b49a8-6918-4e44-8d3d-47ed9b481335')>
    >>> img.style
    'gpt'
    >>> len(img.partitions)
    4
    >>> img.partitions
    DiskPartitionsGPT({
    1: <DiskPartition size=8388608 label='big-part' type=UUID('ebd0a0a2-b9e5-4433-87c0-68b6b72699c7')>,
    2: <DiskPartition size=204800 label='little-part1' type=UUID('ebd0a0a2-b9e5-4433-87c0-68b6b72699c7')>,
    5: <DiskPartition size=4194304 label='medium-part' type=UUID('ebd0a0a2-b9e5-4433-87c0-68b6b72699c7')>,
    6: <DiskPartition size=204800 label='little-part2' type=UUID('ebd0a0a2-b9e5-4433-87c0-68b6b72699c7')>,
    })

Note that partitions are numbered from 1 and that, especially in the case of MBR, partition numbers may not be contiguous: primary partitions are numbered 1 through 4, but logical partitions may only exist in one primary partition, and are numbered from 5. Hence it is entirely valid to have partitions 1, 5, and 6:

    >>> from nobodd.disk import DiskImage
    >>> img = DiskImage('test-ebr.img')
    >>> img.style
    'mbr'
    >>> len(img.partitions)
    3
    >>> list(img.partitions.keys())
    [1, 5, 6]
    >>> img.partitions[1]
    <DiskPartition size=536870912 label='Partition 1' type=12>
    >>> img.partitions[5]
    <DiskPartition size=536870912 label='Partition 5' type=131>
    >>> img.partitions[6]
    <DiskPartition size=1070596096 label='Partition 6' type=131>

GPT partition tables may also have non-contiguous numbering, although this is less common in practice.

It is also worth noting that partitions need not be contiguous on disk (it is valid for gaps to be present, and this is commonly the case where partitions are aligned on various boundaries). Furthermore, partitions may not be physically laid out on disk in the same order as their numbering suggests. That is, partition 2 may have a lower starting sector than partition 1, and so on.

The `DiskPartition.data` attribute can be used to access the content of the partition as a buffer object (see `memoryview`).

## DiskImage

class nobodd.disk.DiskImage(filename_or_obj, sector_size=512, access=1)

  Represents a disk image, specified by filename_or_obj which must be a `str` or `Path` naming the file, or a file-like object.

  If a file-like object is provided, it must have a `fileno` method which returns a valid file-descriptor number (the class uses `mmap` internally which requires a “real” file).

  The disk image is expected to be partitioned with either an MBR partition table or a GPT. The partitions within the image can be enumerated with the `partitions` attribute. The instance can (and should) be used as a context manager; exiting the context will call the `close()` method implicitly.

  If specified, sector_size is the size of sectors (in bytes) within the disk image. This defaults to 512 bytes, and should almost always be left alone. The access parameter controls the access used when constructing the memory mapping. This defaults to `mmap.ACCESS_READ` for read-only access. If you wish to write to file-systems within the disk image, change this to `mmap.ACCESS_WRITE`. You may also use `mmap.ACCESS_COPY` for read-write mappings that don’t actually affect the underlying disk image.

  Note

  Please note that this library provides no means to re-partition disk images, just the ability to re-write files within FAT partitions.

  close()

    Destroys the memory mapping used on the file provided. If the file was opened by this class, it will also be closed. This method is idempotent and is implicitly called when the instance is used as a context manager.

    Note

    All mappings derived from this one must be closed before calling this method. By far the easiest means of arranging this is to consistently use context managers with all instances derived from this.

  property partitions

    Provides access to the partitions in the image as a `Mapping` of partition number to `DiskPartition` instances.

    Warning

    Disk partition numbers start from 1 and need not be contiguous, or ordered.

    For example, it is perfectly valid to have partition 1 occur later on disk than partition 2, for partition 3 to be undefined, and partition 4 to be defined between partition 1 and 2. The partition number is essentially little more than an arbitrary key.

    In the case of MBR partition tables, it is particularly common to have missing partition numbers as the primary layout only permits 4 partitions. Hence, the “extended partitions” scheme numbers partitions from 5. However, if not all primary partitions are defined, there will be a “jump” from, say, partition 2 to partition 5.

  property signature

    The identifying signature of the disk. In the case of a GPT partitioned disk, this is a `UUID`. In the case of MBR, this is a 32-bit integer number.

  property style

    The style of partition table in use on the disk image. Will be one of the strings, ‘gpt’ or ‘mbr’.

## DiskPartition

class nobodd.disk.DiskPartition(mem, label, type)

  Represents an individual disk partition within a `DiskImage`.

  Instances of this class are returned as the values of the mapping provided by `DiskImage.partitions`. Instances can (and should) be used as a context manager to implicitly close references upon exiting the context.

  close()

    Release the internal `memoryview` reference. This method is idempotent and is implicitly called when the instance is used as a context manager.

  property data

    Returns a buffer (specifically, a `memoryview`) covering the contents of the partition in the owning `DiskImage`.

  property label

    The label of the partition. GPT partitions may have a 36 character unicode label. MBR partitions do not have a label, so the string “Partition {num}” will be used instead (where {num} is the partition number).

  property type

    The type of the partition. For GPT partitions, this will be a `uuid.UUID` instance. For MBR partitions, this will be an `int`.

## Support classes

You should not need to use these classes directly; they will be instantiated automatically when querying the `DiskImage.partitions` attribute according to the detected table format.

class nobodd.disk.DiskPartitionsGPT(mem, sector_size=512)

  Provides a `Mapping` from partition number to `DiskPartition` instances for a GPT.

  mem is the buffer covering the whole disk image. sector_size specifies the sector size of the disk image, which should almost always be left at the default of 512 bytes.

class nobodd.disk.DiskPartitionsMBR(mem, sector_size=512)

  Provides a `Mapping` from partition number to `DiskPartition` instances for a MBR.

  mem is the buffer covering the whole disk image. sector_size specifies the sector size of the disk image, which should almost always be left at the default of 512 bytes.