""" Linux swap file. Documentation: Linux kernel source code, files: - mm/swapfile.c - include/linux/swap.h Author: Victor Stinner Creation date: 25 december 2006 (christmas ;-)) """ from hachoir.parser import Parser from hachoir.field import (ParserError, GenericVector, UInt32, String, Bytes, NullBytes, RawBytes) from hachoir.core.endian import LITTLE_ENDIAN from hachoir.core.tools import humanFilesize from hachoir.core.bits import str2hex PAGE_SIZE = 4096 # Definition of MAX_SWAP_BADPAGES in Linux kernel: # (__swapoffset(magic.magic) - __swapoffset(info.badpages)) / sizeof(int) MAX_SWAP_BADPAGES = ((PAGE_SIZE - 10) - 1536) // 4 class Page(RawBytes): static_size = PAGE_SIZE * 8 def __init__(self, parent, name): RawBytes.__init__(self, parent, name, PAGE_SIZE) class UUID(Bytes): static_size = 16 * 8 def __init__(self, parent, name, *args, **kwargs): Bytes.__init__(self, parent, name, 16, *args, **kwargs) def createDisplay(self): text = str2hex(self.value, format=r"%02x") return "%s-%s-%s-%s-%s" % ( text[:8], text[8:12], text[12:16], text[16:20], text[20:]) class LinuxSwapFile(Parser): PARSER_TAGS = { "id": "linux_swap", "file_ext": ("",), "category": "file_system", "min_size": PAGE_SIZE * 8, "description": "Linux swap file", "magic": ( (b"SWAP-SPACE", (PAGE_SIZE - 10) * 8), (b"SWAPSPACE2", (PAGE_SIZE - 10) * 8), (b"S1SUSPEND\0", (PAGE_SIZE - 10) * 8), ), } endian = LITTLE_ENDIAN def validate(self): magic = self.stream.readBytes((PAGE_SIZE - 10) * 8, 10) if magic not in (b"SWAP-SPACE", b"SWAPSPACE2", b"S1SUSPEND\0"): return "Unknown magic string" if MAX_SWAP_BADPAGES < self["nb_badpage"].value: return "Invalid number of bad page (%u)" % self["nb_badpage"].value return True def getPageCount(self): """ Number of pages which can really be used for swapping: number of page minus bad pages minus one page (used for the header) """ # -1 because first page is used for the header return self["last_page"].value - self["nb_badpage"].value - 1 def createDescription(self): if self["magic"].value == "S1SUSPEND\0": text = "Suspend swap file version 1" elif self["magic"].value == "SWAPSPACE2": text = "Linux swap file version 2" else: text = "Linux swap file version 1" nb_page = self.getPageCount() return "%s, page size: %s, %s pages" % ( text, humanFilesize(PAGE_SIZE), nb_page) def createFields(self): # First kilobyte: boot sectors yield RawBytes(self, "boot", 1024, "Space for disklabel etc.") # Header yield UInt32(self, "version") yield UInt32(self, "last_page") yield UInt32(self, "nb_badpage") yield UUID(self, "sws_uuid") yield UUID(self, "sws_volume") yield NullBytes(self, "reserved", 117 * 4) # Read bad pages (if any) count = self["nb_badpage"].value if count: if MAX_SWAP_BADPAGES < count: raise ParserError("Invalid number of bad page (%u)" % count) yield GenericVector(self, "badpages", count, UInt32, "badpage") # Read magic padding = self.seekByte(PAGE_SIZE - 10, "padding", null=True) if padding: yield padding yield String(self, "magic", 10, charset="ASCII") # Read all pages yield GenericVector(self, "pages", self["last_page"].value, Page, "page") # Padding at the end padding = self.seekBit(self.size, "end_padding", null=True) if padding: yield padding