Android Dynamic Partitions metadata: PHP parsing library

The metadata stored by Android at the beginning of a "super" partition, which is what it calls a disk partition that holds one or more Dynamic Partitions. Dynamic Partitions do more or less the same thing that LVM does on Linux, allowing Android to map ranges of non-contiguous extents to a single logical device. This metadata holds that mapping.

Application

Android

File extension

img

KS implementation details

License: CC0-1.0
Minimal Kaitai Struct required: 0.9

This page hosts a formal specification of Android Dynamic Partitions metadata using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

PHP source code to parse Android Dynamic Partitions metadata

AndroidSuper.php

<?php
// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

/**
 * The metadata stored by Android at the beginning of a "super" partition, which
 * is what it calls a disk partition that holds one or more Dynamic Partitions.
 * Dynamic Partitions do more or less the same thing that LVM does on Linux,
 * allowing Android to map ranges of non-contiguous extents to a single logical
 * device. This metadata holds that mapping.
 */

namespace {
    class AndroidSuper extends \Kaitai\Struct\Struct {
        public function __construct(\Kaitai\Struct\Stream $_io, \Kaitai\Struct\Struct $_parent = null, \AndroidSuper $_root = null) {
            parent::__construct($_io, $_parent, $_root);
            $this->_read();
        }

        private function _read() {
        }
        protected $_m_root;
        public function root() {
            if ($this->_m_root !== null)
                return $this->_m_root;
            $_pos = $this->_io->pos();
            $this->_io->seek(4096);
            $this->_m_root = new \AndroidSuper\Root($this->_io, $this, $this->_root);
            $this->_io->seek($_pos);
            return $this->_m_root;
        }
    }
}

namespace AndroidSuper {
    class Root extends \Kaitai\Struct\Struct {
        public function __construct(\Kaitai\Struct\Stream $_io, \AndroidSuper $_parent = null, \AndroidSuper $_root = null) {
            parent::__construct($_io, $_parent, $_root);
            $this->_read();
        }

        private function _read() {
            $this->_m__raw_primaryGeometry = $this->_io->readBytes(4096);
            $_io__raw_primaryGeometry = new \Kaitai\Struct\Stream($this->_m__raw_primaryGeometry);
            $this->_m_primaryGeometry = new \AndroidSuper\Geometry($_io__raw_primaryGeometry, $this, $this->_root);
            $this->_m__raw_backupGeometry = $this->_io->readBytes(4096);
            $_io__raw_backupGeometry = new \Kaitai\Struct\Stream($this->_m__raw_backupGeometry);
            $this->_m_backupGeometry = new \AndroidSuper\Geometry($_io__raw_backupGeometry, $this, $this->_root);
            $this->_m__raw_primaryMetadata = [];
            $this->_m_primaryMetadata = [];
            $n = $this->primaryGeometry()->metadataSlotCount();
            for ($i = 0; $i < $n; $i++) {
                $this->_m__raw_primaryMetadata[] = $this->_io->readBytes($this->primaryGeometry()->metadataMaxSize());
                $_io__raw_primaryMetadata = new \Kaitai\Struct\Stream(end($this->_m__raw_primaryMetadata));
                $this->_m_primaryMetadata[] = new \AndroidSuper\Metadata($_io__raw_primaryMetadata, $this, $this->_root);
            }
            $this->_m__raw_backupMetadata = [];
            $this->_m_backupMetadata = [];
            $n = $this->primaryGeometry()->metadataSlotCount();
            for ($i = 0; $i < $n; $i++) {
                $this->_m__raw_backupMetadata[] = $this->_io->readBytes($this->primaryGeometry()->metadataMaxSize());
                $_io__raw_backupMetadata = new \Kaitai\Struct\Stream(end($this->_m__raw_backupMetadata));
                $this->_m_backupMetadata[] = new \AndroidSuper\Metadata($_io__raw_backupMetadata, $this, $this->_root);
            }
        }
        protected $_m_primaryGeometry;
        protected $_m_backupGeometry;
        protected $_m_primaryMetadata;
        protected $_m_backupMetadata;
        protected $_m__raw_primaryGeometry;
        protected $_m__raw_backupGeometry;
        protected $_m__raw_primaryMetadata;
        protected $_m__raw_backupMetadata;
        public function primaryGeometry() { return $this->_m_primaryGeometry; }
        public function backupGeometry() { return $this->_m_backupGeometry; }
        public function primaryMetadata() { return $this->_m_primaryMetadata; }
        public function backupMetadata() { return $this->_m_backupMetadata; }
        public function _raw_primaryGeometry() { return $this->_m__raw_primaryGeometry; }
        public function _raw_backupGeometry() { return $this->_m__raw_backupGeometry; }
        public function _raw_primaryMetadata() { return $this->_m__raw_primaryMetadata; }
        public function _raw_backupMetadata() { return $this->_m__raw_backupMetadata; }
    }
}

namespace AndroidSuper {
    class Geometry extends \Kaitai\Struct\Struct {
        public function __construct(\Kaitai\Struct\Stream $_io, \AndroidSuper\Root $_parent = null, \AndroidSuper $_root = null) {
            parent::__construct($_io, $_parent, $_root);
            $this->_read();
        }

        private function _read() {
            $this->_m_magic = $this->_io->readBytes(4);
            if (!($this->magic() == "\x67\x44\x6C\x61")) {
                throw new \Kaitai\Struct\Error\ValidationNotEqualError("\x67\x44\x6C\x61", $this->magic(), $this->_io(), "/types/geometry/seq/0");
            }
            $this->_m_structSize = $this->_io->readU4le();
            $this->_m_checksum = $this->_io->readBytes(32);
            $this->_m_metadataMaxSize = $this->_io->readU4le();
            $this->_m_metadataSlotCount = $this->_io->readU4le();
            $this->_m_logicalBlockSize = $this->_io->readU4le();
        }
        protected $_m_magic;
        protected $_m_structSize;
        protected $_m_checksum;
        protected $_m_metadataMaxSize;
        protected $_m_metadataSlotCount;
        protected $_m_logicalBlockSize;
        public function magic() { return $this->_m_magic; }
        public function structSize() { return $this->_m_structSize; }

        /**
         * SHA-256 hash of struct_size bytes from beginning of geometry,
         * calculated as if checksum were zeroed out
         */
        public function checksum() { return $this->_m_checksum; }
        public function metadataMaxSize() { return $this->_m_metadataMaxSize; }
        public function metadataSlotCount() { return $this->_m_metadataSlotCount; }
        public function logicalBlockSize() { return $this->_m_logicalBlockSize; }
    }
}

namespace AndroidSuper {
    class Metadata extends \Kaitai\Struct\Struct {
        public function __construct(\Kaitai\Struct\Stream $_io, \AndroidSuper\Root $_parent = null, \AndroidSuper $_root = null) {
            parent::__construct($_io, $_parent, $_root);
            $this->_read();
        }

        private function _read() {
            $this->_m_magic = $this->_io->readBytes(4);
            if (!($this->magic() == "\x30\x50\x4C\x41")) {
                throw new \Kaitai\Struct\Error\ValidationNotEqualError("\x30\x50\x4C\x41", $this->magic(), $this->_io(), "/types/metadata/seq/0");
            }
            $this->_m_majorVersion = $this->_io->readU2le();
            $this->_m_minorVersion = $this->_io->readU2le();
            $this->_m_headerSize = $this->_io->readU4le();
            $this->_m_headerChecksum = $this->_io->readBytes(32);
            $this->_m_tablesSize = $this->_io->readU4le();
            $this->_m_tablesChecksum = $this->_io->readBytes(32);
            $this->_m_partitions = new \AndroidSuper\Metadata\TableDescriptor(\AndroidSuper\Metadata\TableKind::PARTITIONS, $this->_io, $this, $this->_root);
            $this->_m_extents = new \AndroidSuper\Metadata\TableDescriptor(\AndroidSuper\Metadata\TableKind::EXTENTS, $this->_io, $this, $this->_root);
            $this->_m_groups = new \AndroidSuper\Metadata\TableDescriptor(\AndroidSuper\Metadata\TableKind::GROUPS, $this->_io, $this, $this->_root);
            $this->_m_blockDevices = new \AndroidSuper\Metadata\TableDescriptor(\AndroidSuper\Metadata\TableKind::BLOCK_DEVICES, $this->_io, $this, $this->_root);
        }
        protected $_m_magic;
        protected $_m_majorVersion;
        protected $_m_minorVersion;
        protected $_m_headerSize;
        protected $_m_headerChecksum;
        protected $_m_tablesSize;
        protected $_m_tablesChecksum;
        protected $_m_partitions;
        protected $_m_extents;
        protected $_m_groups;
        protected $_m_blockDevices;
        public function magic() { return $this->_m_magic; }
        public function majorVersion() { return $this->_m_majorVersion; }
        public function minorVersion() { return $this->_m_minorVersion; }
        public function headerSize() { return $this->_m_headerSize; }

        /**
         * SHA-256 hash of header_size bytes from beginning of metadata,
         * calculated as if header_checksum were zeroed out
         */
        public function headerChecksum() { return $this->_m_headerChecksum; }
        public function tablesSize() { return $this->_m_tablesSize; }

        /**
         * SHA-256 hash of tables_size bytes from end of header
         */
        public function tablesChecksum() { return $this->_m_tablesChecksum; }
        public function partitions() { return $this->_m_partitions; }
        public function extents() { return $this->_m_extents; }
        public function groups() { return $this->_m_groups; }
        public function blockDevices() { return $this->_m_blockDevices; }
    }
}

namespace AndroidSuper\Metadata {
    class BlockDevice extends \Kaitai\Struct\Struct {
        public function __construct(\Kaitai\Struct\Stream $_io, \AndroidSuper\Metadata\TableDescriptor $_parent = null, \AndroidSuper $_root = null) {
            parent::__construct($_io, $_parent, $_root);
            $this->_read();
        }

        private function _read() {
            $this->_m_firstLogicalSector = $this->_io->readU8le();
            $this->_m_alignment = $this->_io->readU4le();
            $this->_m_alignmentOffset = $this->_io->readU4le();
            $this->_m_size = $this->_io->readU8le();
            $this->_m_partitionName = \Kaitai\Struct\Stream::bytesToStr(\Kaitai\Struct\Stream::bytesTerminate($this->_io->readBytes(36), 0, false), "UTF-8");
            $this->_m_flagSlotSuffixed = $this->_io->readBitsIntLe(1) != 0;
            $this->_m_flagsReserved = $this->_io->readBitsIntLe(31);
        }
        protected $_m_firstLogicalSector;
        protected $_m_alignment;
        protected $_m_alignmentOffset;
        protected $_m_size;
        protected $_m_partitionName;
        protected $_m_flagSlotSuffixed;
        protected $_m_flagsReserved;
        public function firstLogicalSector() { return $this->_m_firstLogicalSector; }
        public function alignment() { return $this->_m_alignment; }
        public function alignmentOffset() { return $this->_m_alignmentOffset; }
        public function size() { return $this->_m_size; }
        public function partitionName() { return $this->_m_partitionName; }
        public function flagSlotSuffixed() { return $this->_m_flagSlotSuffixed; }
        public function flagsReserved() { return $this->_m_flagsReserved; }
    }
}

namespace AndroidSuper\Metadata {
    class Extent extends \Kaitai\Struct\Struct {
        public function __construct(\Kaitai\Struct\Stream $_io, \AndroidSuper\Metadata\TableDescriptor $_parent = null, \AndroidSuper $_root = null) {
            parent::__construct($_io, $_parent, $_root);
            $this->_read();
        }

        private function _read() {
            $this->_m_numSectors = $this->_io->readU8le();
            $this->_m_targetType = $this->_io->readU4le();
            $this->_m_targetData = $this->_io->readU8le();
            $this->_m_targetSource = $this->_io->readU4le();
        }
        protected $_m_numSectors;
        protected $_m_targetType;
        protected $_m_targetData;
        protected $_m_targetSource;
        public function numSectors() { return $this->_m_numSectors; }
        public function targetType() { return $this->_m_targetType; }
        public function targetData() { return $this->_m_targetData; }
        public function targetSource() { return $this->_m_targetSource; }
    }
}

namespace AndroidSuper\Metadata\Extent {
    class TargetType {
        const LINEAR = 0;
        const ZERO = 1;
    }
}

namespace AndroidSuper\Metadata {
    class TableDescriptor extends \Kaitai\Struct\Struct {
        public function __construct(int $kind, \Kaitai\Struct\Stream $_io, \AndroidSuper\Metadata $_parent = null, \AndroidSuper $_root = null) {
            parent::__construct($_io, $_parent, $_root);
            $this->_m_kind = $kind;
            $this->_read();
        }

        private function _read() {
            $this->_m_offset = $this->_io->readU4le();
            $this->_m_numEntries = $this->_io->readU4le();
            $this->_m_entrySize = $this->_io->readU4le();
        }
        protected $_m_table;
        public function table() {
            if ($this->_m_table !== null)
                return $this->_m_table;
            $_pos = $this->_io->pos();
            $this->_io->seek(($this->_parent()->headerSize() + $this->offset()));
            $this->_m__raw_table = [];
            $this->_m_table = [];
            $n = $this->numEntries();
            for ($i = 0; $i < $n; $i++) {
                switch ($this->kind()) {
                    case \AndroidSuper\Metadata\TableKind::PARTITIONS:
                        $this->_m__raw_table[] = $this->_io->readBytes($this->entrySize());
                        $_io__raw_table = new \Kaitai\Struct\Stream(end($this->_m__raw_table));
                        $this->_m_table[] = new \AndroidSuper\Metadata\Partition($_io__raw_table, $this, $this->_root);
                        break;
                    case \AndroidSuper\Metadata\TableKind::EXTENTS:
                        $this->_m__raw_table[] = $this->_io->readBytes($this->entrySize());
                        $_io__raw_table = new \Kaitai\Struct\Stream(end($this->_m__raw_table));
                        $this->_m_table[] = new \AndroidSuper\Metadata\Extent($_io__raw_table, $this, $this->_root);
                        break;
                    case \AndroidSuper\Metadata\TableKind::GROUPS:
                        $this->_m__raw_table[] = $this->_io->readBytes($this->entrySize());
                        $_io__raw_table = new \Kaitai\Struct\Stream(end($this->_m__raw_table));
                        $this->_m_table[] = new \AndroidSuper\Metadata\Group($_io__raw_table, $this, $this->_root);
                        break;
                    case \AndroidSuper\Metadata\TableKind::BLOCK_DEVICES:
                        $this->_m__raw_table[] = $this->_io->readBytes($this->entrySize());
                        $_io__raw_table = new \Kaitai\Struct\Stream(end($this->_m__raw_table));
                        $this->_m_table[] = new \AndroidSuper\Metadata\BlockDevice($_io__raw_table, $this, $this->_root);
                        break;
                    default:
                        $this->_m_table[] = $this->_io->readBytes($this->entrySize());
                        break;
                }
            }
            $this->_io->seek($_pos);
            return $this->_m_table;
        }
        protected $_m_offset;
        protected $_m_numEntries;
        protected $_m_entrySize;
        protected $_m_kind;
        protected $_m__raw_table;
        public function offset() { return $this->_m_offset; }
        public function numEntries() { return $this->_m_numEntries; }
        public function entrySize() { return $this->_m_entrySize; }
        public function kind() { return $this->_m_kind; }
        public function _raw_table() { return $this->_m__raw_table; }
    }
}

namespace AndroidSuper\Metadata {
    class Partition extends \Kaitai\Struct\Struct {
        public function __construct(\Kaitai\Struct\Stream $_io, \AndroidSuper\Metadata\TableDescriptor $_parent = null, \AndroidSuper $_root = null) {
            parent::__construct($_io, $_parent, $_root);
            $this->_read();
        }

        private function _read() {
            $this->_m_name = \Kaitai\Struct\Stream::bytesToStr(\Kaitai\Struct\Stream::bytesTerminate($this->_io->readBytes(36), 0, false), "UTF-8");
            $this->_m_attrReadonly = $this->_io->readBitsIntLe(1) != 0;
            $this->_m_attrSlotSuffixed = $this->_io->readBitsIntLe(1) != 0;
            $this->_m_attrUpdated = $this->_io->readBitsIntLe(1) != 0;
            $this->_m_attrDisabled = $this->_io->readBitsIntLe(1) != 0;
            $this->_m_attrsReserved = $this->_io->readBitsIntLe(28);
            $this->_io->alignToByte();
            $this->_m_firstExtentIndex = $this->_io->readU4le();
            $this->_m_numExtents = $this->_io->readU4le();
            $this->_m_groupIndex = $this->_io->readU4le();
        }
        protected $_m_name;
        protected $_m_attrReadonly;
        protected $_m_attrSlotSuffixed;
        protected $_m_attrUpdated;
        protected $_m_attrDisabled;
        protected $_m_attrsReserved;
        protected $_m_firstExtentIndex;
        protected $_m_numExtents;
        protected $_m_groupIndex;
        public function name() { return $this->_m_name; }
        public function attrReadonly() { return $this->_m_attrReadonly; }
        public function attrSlotSuffixed() { return $this->_m_attrSlotSuffixed; }
        public function attrUpdated() { return $this->_m_attrUpdated; }
        public function attrDisabled() { return $this->_m_attrDisabled; }
        public function attrsReserved() { return $this->_m_attrsReserved; }
        public function firstExtentIndex() { return $this->_m_firstExtentIndex; }
        public function numExtents() { return $this->_m_numExtents; }
        public function groupIndex() { return $this->_m_groupIndex; }
    }
}

namespace AndroidSuper\Metadata {
    class Group extends \Kaitai\Struct\Struct {
        public function __construct(\Kaitai\Struct\Stream $_io, \AndroidSuper\Metadata\TableDescriptor $_parent = null, \AndroidSuper $_root = null) {
            parent::__construct($_io, $_parent, $_root);
            $this->_read();
        }

        private function _read() {
            $this->_m_name = \Kaitai\Struct\Stream::bytesToStr(\Kaitai\Struct\Stream::bytesTerminate($this->_io->readBytes(36), 0, false), "UTF-8");
            $this->_m_flagSlotSuffixed = $this->_io->readBitsIntLe(1) != 0;
            $this->_m_flagsReserved = $this->_io->readBitsIntLe(31);
            $this->_io->alignToByte();
            $this->_m_maximumSize = $this->_io->readU8le();
        }
        protected $_m_name;
        protected $_m_flagSlotSuffixed;
        protected $_m_flagsReserved;
        protected $_m_maximumSize;
        public function name() { return $this->_m_name; }
        public function flagSlotSuffixed() { return $this->_m_flagSlotSuffixed; }
        public function flagsReserved() { return $this->_m_flagsReserved; }
        public function maximumSize() { return $this->_m_maximumSize; }
    }
}

namespace AndroidSuper\Metadata {
    class TableKind {
        const PARTITIONS = 0;
        const EXTENTS = 1;
        const GROUPS = 2;
        const BLOCK_DEVICES = 3;
    }
}