mirror of
https://github.com/SickGear/SickGear.git
synced 2024-11-30 08:23:37 +00:00
236 lines
7.7 KiB
Python
236 lines
7.7 KiB
Python
|
"""
|
||
|
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"
|