""" Master Boot Record. """ # cfdisk uses the following algorithm to compute the geometry: # 0. Use the values given by the user. # 1. Try to guess the geometry from the partition table: # if all the used partitions end at the same head H and the # same sector S, then there are (H+1) heads and S sectors/cylinder. # 2. Ask the system (ioctl/HDIO_GETGEO). # 3. 255 heads and 63 sectors/cylinder. from hachoir.parser import Parser from hachoir.field import (FieldSet, Enum, Bits, UInt8, UInt16, UInt32, RawBytes) from hachoir.core.endian import LITTLE_ENDIAN from hachoir.core.tools import humanFilesize from hachoir.core.text_handler import textHandler, hexadecimal BLOCK_SIZE = 512 # bytes class CylinderNumber(Bits): def __init__(self, parent, name, description=None): Bits.__init__(self, parent, name, 10, description) def createValue(self): i = self.parent.stream.readInteger( self.absolute_address, False, self._size, self.parent.endian) return i >> 2 | i % 4 << 8 class PartitionHeader(FieldSet): static_size = 16 * 8 # taken from the source of cfdisk: # sed -n 's/.*{\(.*\), N_(\(.*\))}.*/ \1: \2,/p' i386_sys_types.c system_name = { 0x00: "Empty", 0x01: "FAT12", 0x02: "XENIX root", 0x03: "XENIX usr", 0x04: "FAT16 <32M", 0x05: "Extended", 0x06: "FAT16", 0x07: "HPFS/NTFS", 0x08: "AIX", 0x09: "AIX bootable", 0x0a: "OS/2 Boot Manager", 0x0b: "W95 FAT32", 0x0c: "W95 FAT32 (LBA)", 0x0e: "W95 FAT16 (LBA)", 0x0f: "W95 Ext'd (LBA)", 0x10: "OPUS", 0x11: "Hidden FAT12", 0x12: "Compaq diagnostics", 0x14: "Hidden FAT16 <32M", 0x16: "Hidden FAT16", 0x17: "Hidden HPFS/NTFS", 0x18: "AST SmartSleep", 0x1b: "Hidden W95 FAT32", 0x1c: "Hidden W95 FAT32 (LBA)", 0x1e: "Hidden W95 FAT16 (LBA)", 0x24: "NEC DOS", 0x39: "Plan 9", 0x3c: "PartitionMagic recovery", 0x40: "Venix 80286", 0x41: "PPC PReP Boot", 0x42: "SFS", 0x4d: "QNX4.x", 0x4e: "QNX4.x 2nd part", 0x4f: "QNX4.x 3rd part", 0x50: "OnTrack DM", 0x51: "OnTrack DM6 Aux1", 0x52: "CP/M", 0x53: "OnTrack DM6 Aux3", 0x54: "OnTrackDM6", 0x55: "EZ-Drive", 0x56: "Golden Bow", 0x5c: "Priam Edisk", 0x61: "SpeedStor", 0x63: "GNU HURD or SysV", 0x64: "Novell Netware 286", 0x65: "Novell Netware 386", 0x70: "DiskSecure Multi-Boot", 0x75: "PC/IX", 0x80: "Old Minix", 0x81: "Minix / old Linux", 0x82: "Linux swap / Solaris", 0x83: "Linux (ext2/ext3)", 0x84: "OS/2 hidden C: drive", 0x85: "Linux extended", 0x86: "NTFS volume set", 0x87: "NTFS volume set", 0x88: "Linux plaintext", 0x8e: "Linux LVM", 0x93: "Amoeba", 0x94: "Amoeba BBT", 0x9f: "BSD/OS", 0xa0: "IBM Thinkpad hibernation", 0xa5: "FreeBSD", 0xa6: "OpenBSD", 0xa7: "NeXTSTEP", 0xa8: "Darwin UFS", 0xa9: "NetBSD", 0xab: "Darwin boot", 0xb7: "BSDI fs", 0xb8: "BSDI swap", 0xbb: "Boot Wizard hidden", 0xbe: "Solaris boot", 0xbf: "Solaris", 0xc1: "DRDOS/sec (FAT-12)", 0xc4: "DRDOS/sec (FAT-16 < 32M)", 0xc6: "DRDOS/sec (FAT-16)", 0xc7: "Syrinx", 0xda: "Non-FS data", 0xdb: "CP/M / CTOS / ...", 0xde: "Dell Utility", 0xdf: "BootIt", 0xe1: "DOS access", 0xe3: "DOS R/O", 0xe4: "SpeedStor", 0xeb: "BeOS fs", 0xee: "EFI GPT", 0xef: "EFI (FAT-12/16/32)", 0xf0: "Linux/PA-RISC boot", 0xf1: "SpeedStor", 0xf4: "SpeedStor", 0xf2: "DOS secondary", 0xfd: "Linux raid autodetect", 0xfe: "LANstep", 0xff: "BBT" } def createFields(self): yield UInt8(self, "bootable", "Bootable flag (true if equals to 0x80)") if self["bootable"].value not in (0x00, 0x80): self.warning( "Stream doesn't look like master boot record (partition bootable error)!") yield UInt8(self, "start_head", "Starting head number of the partition") yield Bits(self, "start_sector", 6, "Starting sector number of the partition") yield CylinderNumber(self, "start_cylinder", "Starting cylinder number of the partition") yield Enum(UInt8(self, "system", "System indicator"), self.system_name) yield UInt8(self, "end_head", "Ending head number of the partition") yield Bits(self, "end_sector", 6, "Ending sector number of the partition") yield CylinderNumber(self, "end_cylinder", "Ending cylinder number of the partition") yield UInt32(self, "LBA", "LBA (number of sectors before this partition)") yield UInt32(self, "size", "Size (block count)") def isUsed(self): return self["system"].value != 0 def createDescription(self): desc = "Partition header: " if self.isUsed(): system = self["system"].display size = self["size"].value * BLOCK_SIZE desc += "%s, %s" % (system, humanFilesize(size)) else: desc += "(unused)" return desc class MasterBootRecord(FieldSet): static_size = 512 * 8 def createFields(self): yield RawBytes(self, "program", 446, "Boot program (Intel x86 machine code)") yield PartitionHeader(self, "header[0]") yield PartitionHeader(self, "header[1]") yield PartitionHeader(self, "header[2]") yield PartitionHeader(self, "header[3]") yield textHandler(UInt16(self, "signature", "Signature (0xAA55)"), hexadecimal) def _getPartitions(self): return (self[index] for index in range(1, 5)) headers = property(_getPartitions) class Partition(FieldSet): def createFields(self): mbr = MasterBootRecord(self, "mbr") yield mbr # No error if we only want to analyse a backup of a mbr if self.eof: return for start, index, header in sorted((hdr["LBA"].value, index, hdr) for index, hdr in enumerate(mbr.headers) if hdr.isUsed()): # Seek to the beginning of the partition padding = self.seekByte(start * BLOCK_SIZE, "padding[]") if padding: yield padding # Content of the partition name = "partition[%u]" % index size = BLOCK_SIZE * header["size"].value desc = header["system"].display if header["system"].value == 5: yield Partition(self, name, desc, size * 8) else: yield RawBytes(self, name, size, desc) # Padding at the end if self.current_size < self._size: yield self.seekBit(self._size, "end") class MSDos_HardDrive(Parser, Partition): endian = LITTLE_ENDIAN MAGIC = b"\x55\xAA" PARSER_TAGS = { "id": "msdos_harddrive", "category": "file_system", "description": "MS-DOS hard drive with Master Boot Record (MBR)", "min_size": 512 * 8, "file_ext": ("",), # "magic": ((MAGIC, 510*8),), } def validate(self): if self.stream.readBytes(510 * 8, 2) != self.MAGIC: return "Invalid signature" used = False for hdr in self["mbr"].headers: if hdr["bootable"].value not in (0x00, 0x80): return "Wrong boot flag" used |= hdr.isUsed() return used or "No partition found"