summaryrefslogtreecommitdiff
path: root/external/construct/formats/filesystem
diff options
context:
space:
mode:
Diffstat (limited to 'external/construct/formats/filesystem')
-rw-r--r--external/construct/formats/filesystem/__init__.py4
-rw-r--r--external/construct/formats/filesystem/ext2.py157
-rw-r--r--external/construct/formats/filesystem/fat16.py226
-rw-r--r--external/construct/formats/filesystem/mbr.py77
4 files changed, 464 insertions, 0 deletions
diff --git a/external/construct/formats/filesystem/__init__.py b/external/construct/formats/filesystem/__init__.py
new file mode 100644
index 0000000..217ec83
--- /dev/null
+++ b/external/construct/formats/filesystem/__init__.py
@@ -0,0 +1,4 @@
+"""
+file systems on-disk formats (ext2, fat32, ntfs, ...)
+and related disk formats (mbr, ...)
+"""
diff --git a/external/construct/formats/filesystem/ext2.py b/external/construct/formats/filesystem/ext2.py
new file mode 100644
index 0000000..954049e
--- /dev/null
+++ b/external/construct/formats/filesystem/ext2.py
@@ -0,0 +1,157 @@
+"""
+Extension 2 (ext2)
+Used in Linux systems
+"""
+from construct import *
+
+
+Char = SLInt8
+UChar = ULInt8
+Short = SLInt16
+UShort = ULInt16
+Long = SLInt32
+ULong = ULInt32
+
+def BlockPointer(name):
+ return Struct(name,
+ ULong("block_number"),
+ OnDemandPointer(lambda ctx: ctx["block_number"]),
+ )
+
+superblock = Struct("superblock",
+ ULong('inodes_count'),
+ ULong('blocks_count'),
+ ULong('reserved_blocks_count'),
+ ULong('free_blocks_count'),
+ ULong('free_inodes_count'),
+ ULong('first_data_block'),
+ Enum(ULong('log_block_size'),
+ OneKB = 0,
+ TwoKB = 1,
+ FourKB = 2,
+ ),
+ Long('log_frag_size'),
+ ULong('blocks_per_group'),
+ ULong('frags_per_group'),
+ ULong('inodes_per_group'),
+ ULong('mtime'),
+ ULong('wtime'),
+ UShort('mnt_count'),
+ Short('max_mnt_count'),
+ Const(UShort('magic'), 0xEF53),
+ UShort('state'),
+ UShort('errors'),
+ Padding(2),
+ ULong('lastcheck'),
+ ULong('checkinterval'),
+ ULong('creator_os'),
+ ULong('rev_level'),
+ Padding(235 * 4),
+)
+
+group_descriptor = Struct("group_descriptor",
+ ULong('block_bitmap'),
+ ULong('inode_bitmap'),
+ ULong('inode_table'),
+ UShort('free_blocks_count'),
+ UShort('free_inodes_count'),
+ UShort('used_dirs_count'),
+ Padding(14),
+)
+
+inode = Struct("inode",
+ FlagsEnum(UShort('mode'),
+ IXOTH = 0x0001,
+ IWOTH = 0x0002,
+ IROTH = 0x0004,
+ IRWXO = 0x0007,
+ IXGRP = 0x0008,
+ IWGRP = 0x0010,
+ IRGRP = 0x0020,
+ IRWXG = 0x0038,
+ IXUSR = 0x0040,
+ IWUSR = 0x0080,
+ IRUSR = 0x0100,
+ IRWXU = 0x01C0,
+ ISVTX = 0x0200,
+ ISGID = 0x0400,
+ ISUID = 0x0800,
+ IFIFO = 0x1000,
+ IFCHR = 0x2000,
+ IFDIR = 0x4000,
+ IFBLK = 0x6000,
+ IFREG = 0x8000,
+ IFLNK = 0xC000,
+ IFSOCK = 0xA000,
+ IFMT = 0xF000,
+ ),
+ UShort('uid'),
+ ULong('size'),
+ ULong('atime'),
+ ULong('ctime'),
+ ULong('mtime'),
+ ULong('dtime'),
+ UShort('gid'),
+ UShort('links_count'),
+ ULong('blocks'),
+ FlagsEnum(ULong('flags'),
+ SecureDelete = 0x0001,
+ AllowUndelete = 0x0002,
+ Compressed = 0x0004,
+ Synchronous = 0x0008,
+ ),
+ Padding(4),
+ Array(12, ULong('blocks')),
+ ULong("indirect1_block"),
+ ULong("indirect2_block"),
+ ULong("indirect3_block"),
+ ULong('version'),
+ ULong('file_acl'),
+ ULong('dir_acl'),
+ ULong('faddr'),
+ UChar('frag'),
+ Byte('fsize'),
+ Padding(10) ,
+)
+
+# special inodes
+EXT2_BAD_INO = 1
+EXT2_ROOT_INO = 2
+EXT2_ACL_IDX_INO = 3
+EXT2_ACL_DATA_INO = 4
+EXT2_BOOT_LOADER_INO = 5
+EXT2_UNDEL_DIR_INO = 6
+EXT2_FIRST_INO = 11
+
+directory_record = Struct("directory_entry",
+ ULong("inode"),
+ UShort("rec_length"),
+ UShort("name_length"),
+ Field("name", lambda ctx: ctx["name_length"]),
+ Padding(lambda ctx: ctx["rec_length"] - ctx["name_length"])
+)
+
+if __name__ == "__main__":
+ print (superblock.sizeof())
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/external/construct/formats/filesystem/fat16.py b/external/construct/formats/filesystem/fat16.py
new file mode 100644
index 0000000..5d6caf1
--- /dev/null
+++ b/external/construct/formats/filesystem/fat16.py
@@ -0,0 +1,226 @@
+# fat.py; ad-hoc fat16 reader
+# by Bram Westerbaan <bram@westerbaan.name>
+#
+# references:
+# http://en.wikipedia.org/wiki/File_Allocation_Table
+# http://www.ecma-international.org/publications/standards/Ecma-107.htm
+#
+# example:
+# with open("/dev/sdc1") as file:
+# fs = FatFs(file)
+# for rootdir in fs:
+# print rootdir
+import numbers
+from io import BytesIO, BufferedReader
+from construct import Struct, Byte, Bytes, ULInt16, ULInt32, Enum, \
+ Array, Padding, Embed, Pass, BitStruct, Flag, Const
+
+
+def Fat16Header(name):
+ return Struct(name,
+ Bytes("jumpInstruction", 3),
+ Bytes("creatingSystemId", 8),
+ ULInt16("sectorSize"),
+ Byte("sectorsPerCluster"),
+ ULInt16("reservedSectorCount"),
+ Byte("fatCount"),
+ ULInt16("rootdirEntryCount"),
+ ULInt16("sectorCount_small"),
+ Byte("mediaId"),
+ ULInt16("sectorsPerFat"),
+ ULInt16("sectorsPerTrack"),
+ ULInt16("sideCount"),
+ ULInt32("hiddenSectorCount"),
+ ULInt32("sectorCount_large"),
+ Byte("physicalDriveNumber"),
+ Byte("currentHead"),
+ Byte("extendedBootSignature"),
+ Bytes("volumeId", 4),
+ Bytes("volumeLabel", 11),
+ Const(Bytes("fsType", 8), "FAT16 "),
+ Bytes("bootCode", 448),
+ Const(Bytes("bootSectorSignature", 2), "\x55\xaa"))
+
+def BootSector(name):
+ header = Fat16Header("header")
+ return Struct(name,
+ Embed(header),
+ Padding(lambda ctx: ctx.sectorSize - header.sizeof()))
+
+def FatEntry(name):
+ return Enum(ULInt16(name),
+ free_cluster = 0x0000,
+ bad_cluster = 0xfff7,
+ last_cluster = 0xffff,
+ _default_ = Pass)
+
+def DirEntry(name):
+ return Struct(name,
+ Bytes("name", 8),
+ Bytes("extension", 3),
+ BitStruct("attributes",
+ Flag("unused"),
+ Flag("device"),
+ Flag("archive"),
+ Flag("subDirectory"),
+ Flag("volumeLabel"),
+ Flag("system"),
+ Flag("hidden"),
+ Flag("readonly")),
+ # reserved
+ Padding(10),
+ ULInt16("timeRecorded"),
+ ULInt16("dateRecorded"),
+ ULInt16("firstCluster"),
+ ULInt32("fileSize"))
+
+def PreDataRegion(name):
+ rde = DirEntry("rootdirs")
+ fe = FatEntry("fats")
+ return Struct(name,
+ Embed(BootSector("bootSector")),
+ # the remaining reserved sectors
+ Padding(lambda ctx: (ctx.reservedSectorCount - 1)
+ * ctx.sectorSize),
+ # file allocation tables
+ Array(lambda ctx: (ctx.fatCount),
+ Array(lambda ctx: ctx.sectorsPerFat *
+ ctx.sectorSize / fe.sizeof(), fe)),
+ # root directories
+ Array(lambda ctx: (ctx.rootdirEntryCount*rde.sizeof())
+ / ctx.sectorSize, rde))
+
+class File(object):
+ def __init__(self, dirEntry, fs):
+ self.fs = fs
+ self.dirEntry = dirEntry
+
+ @classmethod
+ def fromDirEntry(cls, dirEntry, fs):
+ if dirEntry.name[0] in "\x00\xe5\x2e":
+ return None
+ a = dirEntry.attributes
+ #Long file name directory entry
+ if a.volumeLabel and a.system and a.hidden and a.readonly:
+ return None
+ if a.subDirectory:
+ return Directory(dirEntry, fs)
+ return File(dirEntry, fs)
+
+ @classmethod
+ def fromDirEntries(cls, dirEntries, fs):
+ return filter(None, [cls.fromDirEntry(de, fs)
+ for de in dirEntries])
+
+ def toStream(self, stream):
+ self.fs.fileToStream(self.dirEntry.firstCluster, stream)
+
+ @property
+ def name(self):
+ return "%s.%s" % (self.dirEntry.name.rstrip(),
+ self.dirEntry.extension)
+
+ def __str__(self):
+ return "&%s %s" % (self.dirEntry.firstCluster, self.name)
+
+class Directory(File):
+ def __init__(self, dirEntry, fs, children=None):
+ File.__init__(self, dirEntry, fs)
+ self.children = children
+ if not self.children:
+ self.children = File.fromDirEntries(\
+ self.fs.getDirEntries(\
+ self.dirEntry.firstCluster), fs)
+
+ @property
+ def name(self):
+ return self.dirEntry.name.rstrip()
+
+ def __str__(self):
+ return "&%s %s/" % (self.dirEntry.firstCluster, self.name)
+
+ def __getitem__(self, name):
+ for file in self.children:
+ if file.name == name:
+ return file
+
+ def __iter__(self):
+ return iter(self.children)
+
+class FatFs(Directory):
+ def __init__(self, stream):
+ self.stream = stream
+ self.pdr = PreDataRegion("pdr").parse_stream(stream)
+ Directory.__init__(self, dirEntry = None,
+ fs = self, children = File.fromDirEntries(
+ self.pdr.rootdirs, self))
+
+ def fileToStream(self, clidx, stream):
+ for clidx in self.getLinkedClusters(clidx):
+ self.clusterToStream(clidx, stream)
+
+ def clusterToStream(self, clidx, stream):
+ start, todo = self.getClusterSlice(clidx)
+ self.stream.seek(start)
+ while todo > 0:
+ read = self.stream.read(todo)
+ if not len(read):
+ print("failed to read %s bytes at %s" % (todo, self.stream.tell()))
+ raise EOFError()
+ todo -= len(read)
+ stream.write(read)
+
+ def getClusterSlice(self, clidx):
+ startSector = self.pdr.reservedSectorCount \
+ + self.pdr.fatCount * self.pdr.sectorsPerFat \
+ + (self.pdr.rootdirEntryCount * 32) \
+ / self.pdr.sectorSize \
+ + (clidx-2) * self.pdr.sectorsPerCluster
+ start = startSector * self.pdr.sectorSize
+ length = self.pdr.sectorSize * self.pdr.sectorsPerCluster
+ return (start, length)
+
+ def getLinkedClusters(self, clidx):
+ res = []
+ while clidx != "last_cluster":
+ if not isinstance(clidx, numbers.Real):
+ print(clidx)
+ assert False
+ assert 2 <= clidx <= 0xffef
+ res.append(clidx)
+ clidx = self.getNextCluster(clidx)
+ assert clidx not in res
+ return res
+
+ def getNextCluster(self, clidx):
+ ress = set([fat[clidx] for fat in self.pdr.fats])
+ if len(ress)==1:
+ return ress.pop()
+ print("inconsistencie between FATs: %s points to" % clidx)
+ for i,fat in enumerate(self.pdr.fats):
+ print("\t%s according to fat #%s" % (fat[clidx], i))
+ res = ress.pop()
+ print ("assuming %s" % res)
+ return res
+
+ def getDirEntries(self, clidx):
+ try:
+ for de in self._getDirEntries(clidx):
+ yield de
+ except IOError:
+ print("failed to read directory entries at %s" % clidx)
+
+ def _getDirEntries(self, clidx):
+ de = DirEntry("dirEntry")
+ with BytesIO() as mem:
+ self.fileToStream(clidx, mem)
+ mem.seek(0)
+ with BufferedReader(mem) as reader:
+ while reader.peek(1):
+ yield de.parse_stream(reader)
+ def __str__(self):
+ return "/"
+
+ @property
+ def name(self):
+ return ""
diff --git a/external/construct/formats/filesystem/mbr.py b/external/construct/formats/filesystem/mbr.py
new file mode 100644
index 0000000..1fd5a62
--- /dev/null
+++ b/external/construct/formats/filesystem/mbr.py
@@ -0,0 +1,77 @@
+"""
+Master Boot Record
+The first sector on disk, contains the partition table, bootloader, et al.
+
+http://www.win.tue.nl/~aeb/partitions/partition_types-1.html
+"""
+from construct import *
+from binascii import unhexlify
+import six
+
+
+mbr = Struct("mbr",
+ HexDumpAdapter(Bytes("bootloader_code", 446)),
+ Array(4,
+ Struct("partitions",
+ Enum(Byte("state"),
+ INACTIVE = 0x00,
+ ACTIVE = 0x80,
+ ),
+ BitStruct("beginning",
+ Octet("head"),
+ Bits("sect", 6),
+ Bits("cyl", 10),
+ ),
+ Enum(UBInt8("type"),
+ Nothing = 0x00,
+ FAT12 = 0x01,
+ XENIX_ROOT = 0x02,
+ XENIX_USR = 0x03,
+ FAT16_old = 0x04,
+ Extended_DOS = 0x05,
+ FAT16 = 0x06,
+ FAT32 = 0x0b,
+ FAT32_LBA = 0x0c,
+ NTFS = 0x07,
+ LINUX_SWAP = 0x82,
+ LINUX_NATIVE = 0x83,
+ _default_ = Pass,
+ ),
+ BitStruct("ending",
+ Octet("head"),
+ Bits("sect", 6),
+ Bits("cyl", 10),
+ ),
+ UBInt32("sector_offset"), # offset from MBR in sectors
+ UBInt32("size"), # in sectors
+ )
+ ),
+ Const(Bytes("signature", 2), six.b("\x55\xAA")),
+)
+
+
+
+if __name__ == "__main__":
+ cap1 = unhexlify(six.b(
+ "33C08ED0BC007CFB5007501FFCBE1B7CBF1B065057B9E501F3A4CBBDBE07B104386E00"
+ "7C09751383C510E2F4CD188BF583C610497419382C74F6A0B507B4078BF0AC3C0074FC"
+ "BB0700B40ECD10EBF2884E10E84600732AFE4610807E040B740B807E040C7405A0B607"
+ "75D2804602068346080683560A00E821007305A0B607EBBC813EFE7D55AA740B807E10"
+ "0074C8A0B707EBA98BFC1E578BF5CBBF05008A5600B408CD1372238AC1243F988ADE8A"
+ "FC43F7E38BD186D6B106D2EE42F7E239560A77237205394608731CB80102BB007C8B4E"
+ "028B5600CD1373514F744E32E48A5600CD13EBE48A560060BBAA55B441CD13723681FB"
+ "55AA7530F6C101742B61606A006A00FF760AFF76086A0068007C6A016A10B4428BF4CD"
+ "136161730E4F740B32E48A5600CD13EBD661F9C3496E76616C69642070617274697469"
+ "6F6E207461626C65004572726F72206C6F6164696E67206F7065726174696E67207379"
+ "7374656D004D697373696E67206F7065726174696E672073797374656D000000000000"
+ "0000000000000000000000000000000000000000000000000000000000000000000000"
+ "00000000000000000000000000000000002C4463B7BDB7BD00008001010007FEFFFF3F"
+ "000000371671020000C1FF0FFEFFFF761671028A8FDF06000000000000000000000000"
+ "000000000000000000000000000000000000000055AA"))
+
+ print(mbr.parse(cap1))
+
+
+
+
+