// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild using System; using System.Collections.Generic; namespace Kaitai { /// /// UBIFS is a file system for raw flash memory devices. It works on top of /// UBI (Unsorted Block Images), which works as an abstraction layer so UBIFS /// does not need to care about low level details. This specification only covers /// UBIFS, not UBI. /// /// A UBIFS image is divided into several Logical Erase Blocks (LEB). The first /// LEB (LEB0) contains the superblock node, which includes information about LEB /// size, amongst others. The next two LEBs (LEB1 and LEB2) comprise the so called /// "master area" and both contain a copy of the master node, which includes /// information about where to find the root index node, which is then used to /// access all the files on the file system. /// /// **Limitations of this Kaitai Struct implementation:** /// /// 1. It only supports UBIFS images created by `mkfs.ubifs` (such as those in /// firmware images built by OpenWrt), not file systems modified after /// creation, e.g. by writing files to them on a running Linux system. The /// Linux kernel appends a new copy of the master node to LEB1 and LEB2 /// whenever it mounts the file system read-write, unmounts it, or commits /// changes (updates the index on flash to include them). When mounting, Linux /// uses the /// [last copy](https://github.com/torvalds/linux/blob/8d3ae59288f1e7d58d76558a6ee96d533bc5019f/fs/ubifs/master.c#L84-L165), /// whereas this implementation reads the first one (`master_1`), which still /// describes the file system as it was created. Changes made since then are /// therefore not visible via `index_root`. /// 2. Changes that have not been committed yet are not in the index at all, so /// they are not visible via `index_root` either. Until they are committed, /// UBIFS keeps them only in the journal. This only matters for file systems /// that were not unmounted cleanly, because unmounting /// [commits](https://github.com/torvalds/linux/blob/8d3ae59288f1e7d58d76558a6ee96d533bc5019f/fs/ubifs/super.c#L486-L493) /// all changes. /// 3. Parsing doesn't advance the stream position. This implementation reads /// the image only through instances, so that parsing doesn't copy the /// whole image into memory. To find where the image ends, e.g. when /// carving it out of a larger file, use `len_image`. For the same reason, /// parsing doesn't fail if the image is truncated. To detect that, check /// that the stream is at least `len_image` bytes long /// (`_io.size >= len_image`). /// /// /// Reference: Git tag "v7.2" /// /// /// Reference: Git tag "v7.2" /// /// /// Reference: Source /// public partial class Ubifs : KaitaiStruct { public static Ubifs FromFile(string fileName) { return new Ubifs(new KaitaiStream(fileName)); } public enum Compression { NoCompression = 0, Lzo = 1, Zlib = 2, Zstd = 3, } public enum HashAlgos { Md4 = 0, Md5 = 1, Sha1 = 2, RipeMd160 = 3, Sha256 = 4, Sha384 = 5, Sha512 = 6, Sha224 = 7, RipeMd128 = 8, RipeMd256 = 9, RipeMd320 = 10, Wp256 = 11, Wp384 = 12, Wp512 = 13, Tgr128 = 14, Tgr160 = 15, Tgr192 = 16, Sm3256 = 17, Streebog256 = 18, Streebog512 = 19, Sha3256 = 20, Sha3384 = 21, Sha3512 = 22, } public enum Hashes { R5 = 0, TestHash = 1, } public enum InodeTypes { Regular = 0, Directory = 1, Link = 2, BlockDevice = 3, CharacterDevice = 4, Fifo = 5, Socket = 6, } public enum KeyFormats { Simple = 0, } public enum KeyTypes { Inode = 0, Data = 1, Directory = 2, ExtendedAttribute = 3, } public enum NodeGroupTypes { NoNodeGroup = 0, InNodeGroup = 1, LastOfNodeGroup = 2, } public enum NodeTypes { Inode = 0, Data = 1, Directory = 2, ExtendedAttribute = 3, Truncation = 4, Padding = 5, Superblock = 6, Master = 7, Reference = 8, Index = 9, CommitStart = 10, Orphan = 11, Authentication = 12, Signature = 13, } public Ubifs(KaitaiStream p__io, KaitaiStruct p__parent = null, Ubifs p__root = null) : base(p__io) { m_parent = p__parent; m_root = p__root ?? this; f_index = false; f_indexRoot = false; f_lebs = false; f_lenImage = false; f_master1 = false; f_master2 = false; f_superblock = false; _read(); } private void _read() { } /// /// Reference: Git tag "v7.2" /// public partial class AuthenticationHeader : KaitaiStruct { public static AuthenticationHeader FromFile(string fileName) { return new AuthenticationHeader(new KaitaiStream(fileName)); } public AuthenticationHeader(KaitaiStream p__io, Ubifs.Block p__parent = null, Ubifs p__root = null) : base(p__io) { m_parent = p__parent; m_root = p__root; _read(); } private void _read() { _hmac = m_io.ReadBytes(M_Root.Superblock.NodeHeader.LenHash); } private byte[] _hmac; private Ubifs m_root; private Ubifs.Block m_parent; /// /// HMAC authenticating the journal before this node, keyed with the /// file system's authentication key (which is not stored on flash). /// The kernel [uses](https://github.com/torvalds/linux/blob/8d3ae59288f1e7d58d76558a6ee96d533bc5019f/fs/ubifs/auth.c#L275-L276) /// HMAC with the hash algorithm in `<superblock_header>.hash_algo` /// ([checked when mounting](https://github.com/torvalds/linux/blob/8d3ae59288f1e7d58d76558a6ee96d533bc5019f/fs/ubifs/sb.c#L575-L580)), /// so `hmac` is as long as its digest, e.g. 32 bytes for SHA-256. /// public byte[] Hmac { get { return _hmac; } } public Ubifs M_Root { get { return m_root; } } public Ubifs.Block M_Parent { get { return m_parent; } } } public partial class Block : KaitaiStruct { public static Block FromFile(string fileName) { return new Block(new KaitaiStream(fileName)); } public Block(KaitaiStream p__io, Ubifs.Branch p__parent = null, Ubifs p__root = null) : base(p__io) { m_parent = p__parent; m_root = p__root; _read(); } private void _read() { _header = new CommonHeader(m_io, this, m_root); switch (Header.NodeType) { case Ubifs.NodeTypes.Authentication: { __raw_nodeHeader = m_io.ReadBytes(Header.LenFullNode - 24); var io___raw_nodeHeader = new KaitaiStream(__raw_nodeHeader); _nodeHeader = new AuthenticationHeader(io___raw_nodeHeader, this, m_root); break; } case Ubifs.NodeTypes.CommitStart: { __raw_nodeHeader = m_io.ReadBytes(Header.LenFullNode - 24); var io___raw_nodeHeader = new KaitaiStream(__raw_nodeHeader); _nodeHeader = new CommitStartHeader(io___raw_nodeHeader, this, m_root); break; } case Ubifs.NodeTypes.Data: { __raw_nodeHeader = m_io.ReadBytes(Header.LenFullNode - 24); var io___raw_nodeHeader = new KaitaiStream(__raw_nodeHeader); _nodeHeader = new DataHeader(io___raw_nodeHeader, this, m_root); break; } case Ubifs.NodeTypes.Directory: { __raw_nodeHeader = m_io.ReadBytes(Header.LenFullNode - 24); var io___raw_nodeHeader = new KaitaiStream(__raw_nodeHeader); _nodeHeader = new DirectoryHeader(io___raw_nodeHeader, this, m_root); break; } case Ubifs.NodeTypes.ExtendedAttribute: { __raw_nodeHeader = m_io.ReadBytes(Header.LenFullNode - 24); var io___raw_nodeHeader = new KaitaiStream(__raw_nodeHeader); _nodeHeader = new DirectoryHeader(io___raw_nodeHeader, this, m_root); break; } case Ubifs.NodeTypes.Index: { __raw_nodeHeader = m_io.ReadBytes(Header.LenFullNode - 24); var io___raw_nodeHeader = new KaitaiStream(__raw_nodeHeader); _nodeHeader = new IndexHeader(io___raw_nodeHeader, this, m_root); break; } case Ubifs.NodeTypes.Inode: { __raw_nodeHeader = m_io.ReadBytes(Header.LenFullNode - 24); var io___raw_nodeHeader = new KaitaiStream(__raw_nodeHeader); _nodeHeader = new InodeHeader(io___raw_nodeHeader, this, m_root); break; } case Ubifs.NodeTypes.Master: { __raw_nodeHeader = m_io.ReadBytes(Header.LenFullNode - 24); var io___raw_nodeHeader = new KaitaiStream(__raw_nodeHeader); _nodeHeader = new MasterHeader(io___raw_nodeHeader, this, m_root); break; } case Ubifs.NodeTypes.Orphan: { __raw_nodeHeader = m_io.ReadBytes(Header.LenFullNode - 24); var io___raw_nodeHeader = new KaitaiStream(__raw_nodeHeader); _nodeHeader = new OrphanHeader(io___raw_nodeHeader, this, m_root); break; } case Ubifs.NodeTypes.Padding: { __raw_nodeHeader = m_io.ReadBytes(Header.LenFullNode - 24); var io___raw_nodeHeader = new KaitaiStream(__raw_nodeHeader); _nodeHeader = new PaddingHeader(io___raw_nodeHeader, this, m_root); break; } case Ubifs.NodeTypes.Reference: { __raw_nodeHeader = m_io.ReadBytes(Header.LenFullNode - 24); var io___raw_nodeHeader = new KaitaiStream(__raw_nodeHeader); _nodeHeader = new ReferenceHeader(io___raw_nodeHeader, this, m_root); break; } case Ubifs.NodeTypes.Signature: { __raw_nodeHeader = m_io.ReadBytes(Header.LenFullNode - 24); var io___raw_nodeHeader = new KaitaiStream(__raw_nodeHeader); _nodeHeader = new SignatureHeader(io___raw_nodeHeader, this, m_root); break; } case Ubifs.NodeTypes.Superblock: { __raw_nodeHeader = m_io.ReadBytes(Header.LenFullNode - 24); var io___raw_nodeHeader = new KaitaiStream(__raw_nodeHeader); _nodeHeader = new SuperblockHeader(io___raw_nodeHeader, this, m_root); break; } case Ubifs.NodeTypes.Truncation: { __raw_nodeHeader = m_io.ReadBytes(Header.LenFullNode - 24); var io___raw_nodeHeader = new KaitaiStream(__raw_nodeHeader); _nodeHeader = new TruncationHeader(io___raw_nodeHeader, this, m_root); break; } default: { _nodeHeader = m_io.ReadBytes(Header.LenFullNode - 24); break; } } } private CommonHeader _header; private object _nodeHeader; private Ubifs m_root; private Ubifs.Branch m_parent; private byte[] __raw_nodeHeader; public CommonHeader Header { get { return _header; } } public object NodeHeader { get { return _nodeHeader; } } public Ubifs M_Root { get { return m_root; } } public Ubifs.Branch M_Parent { get { return m_parent; } } public byte[] M_RawNodeHeader { get { return __raw_nodeHeader; } } } /// /// Reference: Git tag "v7.2" /// public partial class Branch : KaitaiStruct { public static Branch FromFile(string fileName) { return new Branch(new KaitaiStream(fileName)); } public Branch(KaitaiStream p__io, Ubifs.IndexHeader p__parent = null, Ubifs p__root = null) : base(p__io) { m_parent = p__parent; m_root = p__root; f_target = false; _read(); } private void _read() { _targetLeb = m_io.ReadU4le(); _ofsTarget = m_io.ReadU4le(); _lenTarget = m_io.ReadU4le(); __raw_key = m_io.ReadBytes(8); var io___raw_key = new KaitaiStream(__raw_key); _key = new Key(io___raw_key, this, m_root); _hash = m_io.ReadBytes(M_Root.Superblock.NodeHeader.LenHash); } private bool f_target; private Block _target; public Block Target { get { if (f_target) return _target; f_target = true; KaitaiStream io = M_Root.M_Io; long _pos = io.Pos; io.Seek(M_Root.Superblock.NodeHeader.LebSize * TargetLeb + OfsTarget); __raw_target = io.ReadBytes(LenTarget); var io___raw_target = new KaitaiStream(__raw_target); _target = new Block(io___raw_target, this, m_root); { Block M_ = _target; if (!( ((M_.Header.LenFullNode == LenTarget) && ((M_Parent.Level != 0 ? M_.Header.NodeType == Ubifs.NodeTypes.Index : ((int) M_.Header.NodeType) == ((int) Key.Type)))) )) { throw new ValidationExprError(_target, m_io, "/types/branch/instances/target"); } } io.Seek(_pos); return _target; } } private uint _targetLeb; private uint _ofsTarget; private uint _lenTarget; private Key _key; private byte[] _hash; private Ubifs m_root; private Ubifs.IndexHeader m_parent; private byte[] __raw_key; private byte[] __raw_target; /// /// LEB number of the target node /// public uint TargetLeb { get { return _targetLeb; } } /// /// offset within the LEB `target_leb` /// public uint OfsTarget { get { return _ofsTarget; } } /// /// target node length /// public uint LenTarget { get { return _lenTarget; } } public Key Key { get { return _key; } } /// /// Hash of the target node. It is empty in a file system without /// authentication (see `<superblock_header>.len_hash`). /// /// /// Reference: Git tag "v7.2" /// public byte[] Hash { get { return _hash; } } public Ubifs M_Root { get { return m_root; } } public Ubifs.IndexHeader M_Parent { get { return m_parent; } } public byte[] M_RawKey { get { return __raw_key; } } public byte[] M_RawTarget { get { return __raw_target; } } } /// /// Reference: Git tag "v7.2" /// public partial class CommitStartHeader : KaitaiStruct { public static CommitStartHeader FromFile(string fileName) { return new CommitStartHeader(new KaitaiStream(fileName)); } public CommitStartHeader(KaitaiStream p__io, Ubifs.Block p__parent = null, Ubifs p__root = null) : base(p__io) { m_parent = p__parent; m_root = p__root; _read(); } private void _read() { _commitNumber = m_io.ReadU8le(); } private ulong _commitNumber; private Ubifs m_root; private Ubifs.Block m_parent; /// /// commit number /// public ulong CommitNumber { get { return _commitNumber; } } public Ubifs M_Root { get { return m_root; } } public Ubifs.Block M_Parent { get { return m_parent; } } } /// /// Reference: Git tag "v7.2" /// public partial class CommonHeader : KaitaiStruct { public static CommonHeader FromFile(string fileName) { return new CommonHeader(new KaitaiStream(fileName)); } public CommonHeader(KaitaiStream p__io, KaitaiStruct p__parent = null, Ubifs p__root = null) : base(p__io) { m_parent = p__parent; m_root = p__root; _read(); } private void _read() { _magic = m_io.ReadU4le(); if (!(_magic == 101718065)) { throw new ValidationNotEqualError(101718065, _magic, m_io, "/types/common_header/seq/0"); } _crc = m_io.ReadU4le(); _sequenceNumber = m_io.ReadU8le(); _lenFullNode = m_io.ReadU4le(); _nodeType = ((Ubifs.NodeTypes) m_io.ReadU1()); _groupType = ((Ubifs.NodeGroupTypes) m_io.ReadU1()); _padding = m_io.ReadBytes(2); if (!((KaitaiStream.ByteArrayCompare(_padding, new byte[] { 0, 0 }) == 0))) { throw new ValidationNotEqualError(new byte[] { 0, 0 }, _padding, m_io, "/types/common_header/seq/6"); } } private uint _magic; private uint _crc; private ulong _sequenceNumber; private uint _lenFullNode; private NodeTypes _nodeType; private NodeGroupTypes _groupType; private byte[] _padding; private Ubifs m_root; private KaitaiStruct m_parent; /// /// UBIFS node magic number (`UBIFS_NODE_MAGIC`) /// public uint Magic { get { return _magic; } } /// /// CRC-32 checksum of the whole node except `magic` and `crc` (i.e. /// starting at offset 8). It is computed with an initial register value /// of 0xFFFFFFFF and no final XOR, so it is the bitwise NOT of a standard /// CRC-32 (e.g. /// [`zlib.crc32()`](https://docs.python.org/3/library/zlib.html#zlib.crc32) /// in Python). /// public uint Crc { get { return _crc; } } /// /// sequence number /// public ulong SequenceNumber { get { return _sequenceNumber; } } /// /// full node length /// public uint LenFullNode { get { return _lenFullNode; } } /// /// node type /// public NodeTypes NodeType { get { return _nodeType; } } /// /// node group type /// public NodeGroupTypes GroupType { get { return _groupType; } } /// /// reserved for future, zeroes /// public byte[] Padding { get { return _padding; } } public Ubifs M_Root { get { return m_root; } } public KaitaiStruct M_Parent { get { return m_parent; } } } /// /// Reference: Git tag "v7.2" /// public partial class DataHeader : KaitaiStruct { public static DataHeader FromFile(string fileName) { return new DataHeader(new KaitaiStream(fileName)); } public DataHeader(KaitaiStream p__io, Ubifs.Block p__parent = null, Ubifs p__root = null) : base(p__io) { m_parent = p__parent; m_root = p__root; _read(); } private void _read() { __raw_key = m_io.ReadBytes(16); var io___raw_key = new KaitaiStream(__raw_key); _key = new Key(io___raw_key, this, m_root); _lenUncompressed = m_io.ReadU4le(); if (!(_lenUncompressed >= 1)) { throw new ValidationLessThanError(1, _lenUncompressed, m_io, "/types/data_header/seq/1"); } if (!(_lenUncompressed <= 4096)) { throw new ValidationGreaterThanError(4096, _lenUncompressed, m_io, "/types/data_header/seq/1"); } _compression = ((Ubifs.Compression) m_io.ReadU2le()); if (!Enum.IsDefined(typeof(Compression), _compression)) { throw new ValidationNotInEnumError(_compression, m_io, "/types/data_header/seq/2"); } _lenCompressedData = m_io.ReadU2le(); _data = m_io.ReadBytesFull(); } private Key _key; private uint _lenUncompressed; private Compression _compression; private ushort _lenCompressedData; private byte[] _data; private Ubifs m_root; private Ubifs.Block m_parent; private byte[] __raw_key; /// /// node key /// public Key Key { get { return _key; } } /// /// uncompressed data size in bytes /// public uint LenUncompressed { get { return _lenUncompressed; } } /// /// compression type (`compression::no_compression`, `compression::lzo`, /// etc.) /// public Compression Compression { get { return _compression; } } /// /// compressed data size in bytes, only valid when data is encrypted /// public ushort LenCompressedData { get { return _lenCompressedData; } } /// /// One block (up to 4096 bytes) of the file, block number `key.value`, /// compressed as given by `compression`. Blocks missing from the index /// are holes (zeros). In an encrypted inode, the compressed data is /// padded /// and [encrypted](https://github.com/torvalds/linux/blob/8d3ae59288f1e7d58d76558a6ee96d533bc5019f/fs/ubifs/crypto.c#L38-L59), /// and `len_compressed_data` is the length of the compressed data /// before padding. /// public byte[] Data { get { return _data; } } public Ubifs M_Root { get { return m_root; } } public Ubifs.Block M_Parent { get { return m_parent; } } public byte[] M_RawKey { get { return __raw_key; } } } /// /// Reference: Git tag "v7.2" /// public partial class DirectoryHeader : KaitaiStruct { public static DirectoryHeader FromFile(string fileName) { return new DirectoryHeader(new KaitaiStream(fileName)); } public DirectoryHeader(KaitaiStream p__io, Ubifs.Block p__parent = null, Ubifs p__root = null) : base(p__io) { m_parent = p__parent; m_root = p__root; _read(); } private void _read() { __raw_key = m_io.ReadBytes(16); var io___raw_key = new KaitaiStream(__raw_key); _key = new Key(io___raw_key, this, m_root); { Key M_ = _key; if (!( ((M_.Type == Ubifs.KeyTypes.Directory) || (M_.Type == Ubifs.KeyTypes.ExtendedAttribute)) )) { throw new ValidationExprError(_key, m_io, "/types/directory_header/seq/0"); } } _inodeNumber = m_io.ReadU8le(); if (!(_inodeNumber <= 4294967295)) { throw new ValidationGreaterThanError(4294967295, _inodeNumber, m_io, "/types/directory_header/seq/1"); } _padding1 = m_io.ReadBytes(1); if (!((KaitaiStream.ByteArrayCompare(_padding1, new byte[] { 0 }) == 0))) { throw new ValidationNotEqualError(new byte[] { 0 }, _padding1, m_io, "/types/directory_header/seq/2"); } _inodeType = ((Ubifs.InodeTypes) m_io.ReadU1()); if (!Enum.IsDefined(typeof(InodeTypes), _inodeType)) { throw new ValidationNotInEnumError(_inodeType, m_io, "/types/directory_header/seq/3"); } _lenName = m_io.ReadU2le(); _cookie = m_io.ReadU4le(); _name = m_io.ReadBytes(LenName); _nameTerminator = m_io.ReadBytes(1); if (!((KaitaiStream.ByteArrayCompare(_nameTerminator, new byte[] { 0 }) == 0))) { throw new ValidationNotEqualError(new byte[] { 0 }, _nameTerminator, m_io, "/types/directory_header/seq/7"); } } private Key _key; private ulong _inodeNumber; private byte[] _padding1; private InodeTypes _inodeType; private ushort _lenName; private uint _cookie; private byte[] _name; private byte[] _nameTerminator; private Ubifs m_root; private Ubifs.Block m_parent; private byte[] __raw_key; /// /// node key /// public Key Key { get { return _key; } } /// /// target inode number /// public ulong InodeNumber { get { return _inodeNumber; } } /// /// reserved for future, zeroes /// public byte[] Padding1 { get { return _padding1; } } /// /// type of the target inode (`inode_types::regular`, /// `inode_types::directory`, etc.) /// public InodeTypes InodeType { get { return _inodeType; } } /// /// name length /// public ushort LenName { get { return _lenName; } } /// /// A 32bits random number, used to construct a 64bits identifier. /// public uint Cookie { get { return _cookie; } } /// /// Name of the entry. /// /// It is kept as bytes rather than decoded as a string: names in a /// directory whose inode has `<inode_header>.encrypted` set are /// ciphertext, which may contain any bytes, including zeros. /// /// Unencrypted names, including all names of extended attributes, contain /// no zero bytes, because Linux can't create such names. They aren't /// necessarily valid UTF-8, though, as UBIFS doesn't enforce any encoding /// of file names. A zero byte in such a name means that the image was /// crafted, since an accidental corruption would most likely fail the /// node's /// [CRC check](https://github.com/torvalds/linux/blob/8d3ae59288f1e7d58d76558a6ee96d533bc5019f/fs/ubifs/io.c#L287-L293). /// public byte[] Name { get { return _name; } } /// /// Zero byte after `name`, [not counted in /// `len_name`](https://github.com/torvalds/linux/blob/8d3ae59288f1e7d58d76558a6ee96d533bc5019f/fs/ubifs/replay.c#L487-L489). /// public byte[] NameTerminator { get { return _nameTerminator; } } public Ubifs M_Root { get { return m_root; } } public Ubifs.Block M_Parent { get { return m_parent; } } public byte[] M_RawKey { get { return __raw_key; } } } public partial class Dummy : KaitaiStruct { public static Dummy FromFile(string fileName) { return new Dummy(new KaitaiStream(fileName)); } public Dummy(KaitaiStream p__io, Ubifs p__parent = null, Ubifs p__root = null) : base(p__io) { m_parent = p__parent; m_root = p__root; _read(); } private void _read() { } private Ubifs m_root; private Ubifs m_parent; public Ubifs M_Root { get { return m_root; } } public Ubifs M_Parent { get { return m_parent; } } } public partial class IndexBlock : KaitaiStruct { public static IndexBlock FromFile(string fileName) { return new IndexBlock(new KaitaiStream(fileName)); } public IndexBlock(KaitaiStream p__io, Ubifs p__parent = null, Ubifs p__root = null) : base(p__io) { m_parent = p__parent; m_root = p__root; _read(); } private void _read() { _header = new CommonHeader(m_io, this, m_root); { CommonHeader M_ = _header; if (!(M_.NodeType == Ubifs.NodeTypes.Index)) { throw new ValidationExprError(_header, m_io, "/types/index_block/seq/0"); } } __raw_nodeHeader = m_io.ReadBytes(Header.LenFullNode - 24); var io___raw_nodeHeader = new KaitaiStream(__raw_nodeHeader); _nodeHeader = new IndexHeader(io___raw_nodeHeader, this, m_root); } private CommonHeader _header; private IndexHeader _nodeHeader; private Ubifs m_root; private Ubifs m_parent; private byte[] __raw_nodeHeader; public CommonHeader Header { get { return _header; } } public IndexHeader NodeHeader { get { return _nodeHeader; } } public Ubifs M_Root { get { return m_root; } } public Ubifs M_Parent { get { return m_parent; } } public byte[] M_RawNodeHeader { get { return __raw_nodeHeader; } } } /// /// Reference: Git tag "v7.2" /// public partial class IndexHeader : KaitaiStruct { public static IndexHeader FromFile(string fileName) { return new IndexHeader(new KaitaiStream(fileName)); } public IndexHeader(KaitaiStream p__io, KaitaiStruct p__parent = null, Ubifs p__root = null) : base(p__io) { m_parent = p__parent; m_root = p__root; _read(); } private void _read() { _numBranches = m_io.ReadU2le(); _level = m_io.ReadU2le(); _branches = new List(); for (var i = 0; i < NumBranches; i++) { _branches.Add(new Branch(m_io, this, m_root)); } } private ushort _numBranches; private ushort _level; private List _branches; private Ubifs m_root; private KaitaiStruct m_parent; /// /// number of child index nodes /// public ushort NumBranches { get { return _numBranches; } } /// /// tree level /// public ushort Level { get { return _level; } } /// /// LEB number / offset / length / key branches /// /// All branches have the same size, which is 20 bytes (with the simple /// key format) plus the hash length in an authenticated file system. /// /// /// Reference: Git tag "v7.2" /// public List Branches { get { return _branches; } } public Ubifs M_Root { get { return m_root; } } public KaitaiStruct M_Parent { get { return m_parent; } } } /// /// Reference: Git tag "v7.2" /// public partial class InodeHeader : KaitaiStruct { public static InodeHeader FromFile(string fileName) { return new InodeHeader(new KaitaiStream(fileName)); } public InodeHeader(KaitaiStream p__io, Ubifs.Block p__parent = null, Ubifs p__root = null) : base(p__io) { m_parent = p__parent; m_root = p__root; f_append = false; f_compressed = false; f_dirsync = false; f_encrypted = false; f_immutable = false; f_isBlockDevice = false; f_isCharacterDevice = false; f_isDir = false; f_isFifo = false; f_isLink = false; f_isRegular = false; f_isSocket = false; f_synchronous = false; f_xattr = false; _read(); } private void _read() { __raw_key = m_io.ReadBytes(16); var io___raw_key = new KaitaiStream(__raw_key); _key = new Key(io___raw_key, this, m_root); _creationSequenceNumber = m_io.ReadU8le(); _lenUncompressed = m_io.ReadU8le(); _atimeSec = m_io.ReadU8le(); _ctimeSec = m_io.ReadU8le(); _mtimeSec = m_io.ReadU8le(); _atimeNsec = m_io.ReadU4le(); _ctimeNsec = m_io.ReadU4le(); _mtimeNsec = m_io.ReadU4le(); _numLinks = m_io.ReadU4le(); _uid = m_io.ReadU4le(); _gid = m_io.ReadU4le(); _mode = m_io.ReadU4le(); _flags = m_io.ReadU4le(); _lenData = m_io.ReadU4le(); _numXattrs = m_io.ReadU4le(); _xattrSize = m_io.ReadU4le(); _padding1 = m_io.ReadBytes(4); if (!((KaitaiStream.ByteArrayCompare(_padding1, new byte[] { 0, 0, 0, 0 }) == 0))) { throw new ValidationNotEqualError(new byte[] { 0, 0, 0, 0 }, _padding1, m_io, "/types/inode_header/seq/17"); } _xattrNames = m_io.ReadU4le(); _compression = ((Ubifs.Compression) m_io.ReadU2le()); if (!Enum.IsDefined(typeof(Compression), _compression)) { throw new ValidationNotInEnumError(_compression, m_io, "/types/inode_header/seq/19"); } _padding2 = m_io.ReadBytes(26); _data = m_io.ReadBytes(LenData); } private bool f_append; private bool _append; /// /// writes to the inode may only append data /// public bool Append { get { if (f_append) return _append; f_append = true; _append = (bool) ((Flags & 8) != 0); return _append; } } private bool f_compressed; private bool _compressed; /// /// use compression for this inode /// public bool Compressed { get { if (f_compressed) return _compressed; f_compressed = true; _compressed = (bool) ((Flags & 1) != 0); return _compressed; } } private bool f_dirsync; private bool _dirsync; /// /// I/O on this directory inode has to be synchronous /// public bool Dirsync { get { if (f_dirsync) return _dirsync; f_dirsync = true; _dirsync = (bool) ((Flags & 16) != 0); return _dirsync; } } private bool f_encrypted; private bool _encrypted; /// /// use encryption for this inode /// public bool Encrypted { get { if (f_encrypted) return _encrypted; f_encrypted = true; _encrypted = (bool) ((Flags & 64) != 0); return _encrypted; } } private bool f_immutable; private bool _immutable; /// /// inode is immutable /// public bool Immutable { get { if (f_immutable) return _immutable; f_immutable = true; _immutable = (bool) ((Flags & 4) != 0); return _immutable; } } private bool f_isBlockDevice; private bool _isBlockDevice; public bool IsBlockDevice { get { if (f_isBlockDevice) return _isBlockDevice; f_isBlockDevice = true; _isBlockDevice = (bool) ((Mode & 61440) == 24576); return _isBlockDevice; } } private bool f_isCharacterDevice; private bool _isCharacterDevice; public bool IsCharacterDevice { get { if (f_isCharacterDevice) return _isCharacterDevice; f_isCharacterDevice = true; _isCharacterDevice = (bool) ((Mode & 61440) == 8192); return _isCharacterDevice; } } private bool f_isDir; private bool _isDir; public bool IsDir { get { if (f_isDir) return _isDir; f_isDir = true; _isDir = (bool) ((Mode & 61440) == 16384); return _isDir; } } private bool f_isFifo; private bool _isFifo; public bool IsFifo { get { if (f_isFifo) return _isFifo; f_isFifo = true; _isFifo = (bool) ((Mode & 61440) == 4096); return _isFifo; } } private bool f_isLink; private bool _isLink; public bool IsLink { get { if (f_isLink) return _isLink; f_isLink = true; _isLink = (bool) ((Mode & 61440) == 40960); return _isLink; } } private bool f_isRegular; private bool _isRegular; public bool IsRegular { get { if (f_isRegular) return _isRegular; f_isRegular = true; _isRegular = (bool) ((Mode & 61440) == 32768); return _isRegular; } } private bool f_isSocket; private bool _isSocket; public bool IsSocket { get { if (f_isSocket) return _isSocket; f_isSocket = true; _isSocket = (bool) ((Mode & 61440) == 49152); return _isSocket; } } private bool f_synchronous; private bool _synchronous; /// /// I/O on this inode has to be synchronous /// public bool Synchronous { get { if (f_synchronous) return _synchronous; f_synchronous = true; _synchronous = (bool) ((Flags & 2) != 0); return _synchronous; } } private bool f_xattr; private bool _xattr; /// /// this inode is the inode for an extended attribute value /// public bool Xattr { get { if (f_xattr) return _xattr; f_xattr = true; _xattr = (bool) ((Flags & 32) != 0); return _xattr; } } private Key _key; private ulong _creationSequenceNumber; private ulong _lenUncompressed; private ulong _atimeSec; private ulong _ctimeSec; private ulong _mtimeSec; private uint _atimeNsec; private uint _ctimeNsec; private uint _mtimeNsec; private uint _numLinks; private uint _uid; private uint _gid; private uint _mode; private uint _flags; private uint _lenData; private uint _numXattrs; private uint _xattrSize; private byte[] _padding1; private uint _xattrNames; private Compression _compression; private byte[] _padding2; private byte[] _data; private Ubifs m_root; private Ubifs.Block m_parent; private byte[] __raw_key; /// /// node key /// public Key Key { get { return _key; } } /// /// sequence number at time of creation /// public ulong CreationSequenceNumber { get { return _creationSequenceNumber; } } /// /// inode size in bytes (amount of uncompressed data) /// public ulong LenUncompressed { get { return _lenUncompressed; } } /// /// access time seconds /// public ulong AtimeSec { get { return _atimeSec; } } /// /// status change time seconds /// public ulong CtimeSec { get { return _ctimeSec; } } /// /// modification time seconds /// public ulong MtimeSec { get { return _mtimeSec; } } /// /// access time nanoseconds /// public uint AtimeNsec { get { return _atimeNsec; } } /// /// status change time nanoseconds /// public uint CtimeNsec { get { return _ctimeNsec; } } /// /// modification time nanoseconds /// public uint MtimeNsec { get { return _mtimeNsec; } } /// /// number of hard links /// public uint NumLinks { get { return _numLinks; } } /// /// owner ID /// public uint Uid { get { return _uid; } } /// /// group ID /// public uint Gid { get { return _gid; } } /// /// file type and permissions, as in `st_mode` (see the `is_*` /// instances for the file type) /// public uint Mode { get { return _mode; } } /// /// per-inode flags (see the instances `compressed`, `synchronous`, /// etc.) /// public uint Flags { get { return _flags; } } /// /// inode data length /// public uint LenData { get { return _lenData; } } /// /// count of extended attributes this inode has /// public uint NumXattrs { get { return _numXattrs; } } /// /// summarized size of all extended attributes in bytes /// public uint XattrSize { get { return _xattrSize; } } public byte[] Padding1 { get { return _padding1; } } /// /// sum of lengths of all extended attribute names belonging /// to this inode /// public uint XattrNames { get { return _xattrNames; } } /// /// compression type used for this inode /// public Compression Compression { get { return _compression; } } /// /// reserved for future, zeroes /// public byte[] Padding2 { get { return _padding2; } } /// /// Target of a [symbolic link](https://github.com/torvalds/linux/blob/8d3ae59288f1e7d58d76558a6ee96d533bc5019f/fs/ubifs/dir.c#L1220-L1241), /// (encrypted if the inode is encrypted), device number of a device node /// ([`union ubifs_dev_desc`](https://github.com/torvalds/linux/blob/8d3ae59288f1e7d58d76558a6ee96d533bc5019f/fs/ubifs/ubifs-media.h#L466-L478): /// 4 bytes from Linux, 8 from `mkfs.ubifs`), or value of an /// [extended attribute](https://github.com/torvalds/linux/blob/8d3ae59288f1e7d58d76558a6ee96d533bc5019f/fs/ubifs/xattr.c#L115-L121) /// (in its own inode, see `xattr`). Empty otherwise. /// public byte[] Data { get { return _data; } } public Ubifs M_Root { get { return m_root; } } public Ubifs.Block M_Parent { get { return m_parent; } } public byte[] M_RawKey { get { return __raw_key; } } } /// /// Inode nodes (`inode_header`), directory and extended attribute entry /// nodes (`directory_header`) and data nodes (`data_header`) store keys in a /// 16-byte field (`UBIFS_MAX_KEY_LEN`). The simple key format, the only one /// there is (see `<superblock_header>.key_fmt`), uses only the first 8 /// bytes, and Linux (v7.2) /// [zeroes](https://github.com/torvalds/linux/blob/8d3ae59288f1e7d58d76558a6ee96d533bc5019f/fs/ubifs/key.h#L432-L440) /// the rest. Branches of index nodes (`branch`) /// [store only these 8 bytes](https://github.com/torvalds/linux/blob/8d3ae59288f1e7d58d76558a6ee96d533bc5019f/fs/ubifs/key.h#L448-L455). /// /// /// Reference: Git tag "v7.2" /// public partial class Key : KaitaiStruct { public static Key FromFile(string fileName) { return new Key(new KaitaiStream(fileName)); } public Key(KaitaiStream p__io, KaitaiStruct p__parent = null, Ubifs p__root = null) : base(p__io) { m_parent = p__parent; m_root = p__root; _read(); } private void _read() { _inodeNumber = m_io.ReadU4le(); _value = m_io.ReadBitsIntLe(29); _type = ((Ubifs.KeyTypes) m_io.ReadBitsIntLe(3)); if (!Enum.IsDefined(typeof(KeyTypes), _type)) { throw new ValidationNotInEnumError(_type, m_io, "/types/key/seq/2"); } m_io.AlignToByte(); _unused = m_io.ReadBytesFull(); } private uint _inodeNumber; private ulong _value; private KeyTypes _type; private byte[] _unused; private Ubifs m_root; private KaitaiStruct m_parent; /// /// Number of the inode that the node belongs to: the parent directory /// for a directory entry, the inode that the attribute belongs to for an /// extended attribute entry. /// public uint InodeNumber { get { return _inodeNumber; } } /// /// Block number for a data node, hash of the name (see /// `<superblock_header>.key_hash`) for a directory or extended /// attribute entry, 0 for an inode node. /// public ulong Value { get { return _value; } } /// /// Type of the key, which matches the type of the node. /// public KeyTypes Type { get { return _type; } } /// /// Rest of the field holding the key: 8 zero bytes in `inode_header`, /// `directory_header` and `data_header`, empty in `branch`. /// public byte[] Unused { get { return _unused; } } public Ubifs M_Root { get { return m_root; } } public KaitaiStruct M_Parent { get { return m_parent; } } } /// /// Reference: Git tag "v7.2" /// public partial class MasterHeader : KaitaiStruct { public static MasterHeader FromFile(string fileName) { return new MasterHeader(new KaitaiStream(fileName)); } public MasterHeader(KaitaiStream p__io, KaitaiStruct p__parent = null, Ubifs p__root = null) : base(p__io) { m_parent = p__parent; m_root = p__root; f_dirty = false; f_noOrphans = false; f_recovery = false; _read(); } private void _read() { _highestInum = m_io.ReadU8le(); _commitNumber = m_io.ReadU8le(); _flags = m_io.ReadU4le(); _lebLog = m_io.ReadU4le(); _lebRoot = m_io.ReadU4le(); _ofsRoot = m_io.ReadU4le(); _lenRoot = m_io.ReadU4le(); _lebGarbageCollect = m_io.ReadU4le(); _lebIndexHead = m_io.ReadU4le(); _ofsIndexHead = m_io.ReadU4le(); _lenIndex = m_io.ReadU8le(); _totalFree = m_io.ReadU8le(); _totalDirty = m_io.ReadU8le(); _totalUsed = m_io.ReadU8le(); _totalDead = m_io.ReadU8le(); _totalDark = m_io.ReadU8le(); _lebLpt = m_io.ReadU4le(); _ofsLpt = m_io.ReadU4le(); _lebNhead = m_io.ReadU4le(); _ofsNhead = m_io.ReadU4le(); _lebLpropsTable = m_io.ReadU4le(); _ofsLpropsTable = m_io.ReadU4le(); _lebSaveTable = m_io.ReadU4le(); _ofsSaveTable = m_io.ReadU4le(); _lebLastLptScan = m_io.ReadU4le(); _numEmptyLebs = m_io.ReadU4le(); _numIndexLebs = m_io.ReadU4le(); _numLebs = m_io.ReadU4le(); _hashRootIndex = m_io.ReadBytes(64); _hashLpt = m_io.ReadBytes(64); _hmac = m_io.ReadBytes(64); _padding = m_io.ReadBytes(152); } private bool f_dirty; private bool _dirty; /// /// master node is dirty /// public bool Dirty { get { if (f_dirty) return _dirty; f_dirty = true; _dirty = (bool) ((Flags & 1) != 0); return _dirty; } } private bool f_noOrphans; private bool _noOrphans; /// /// no orphan inodes present /// public bool NoOrphans { get { if (f_noOrphans) return _noOrphans; f_noOrphans = true; _noOrphans = (bool) ((Flags & 2) != 0); return _noOrphans; } } private bool f_recovery; private bool _recovery; /// /// written by recovery /// public bool Recovery { get { if (f_recovery) return _recovery; f_recovery = true; _recovery = (bool) ((Flags & 4) != 0); return _recovery; } } private ulong _highestInum; private ulong _commitNumber; private uint _flags; private uint _lebLog; private uint _lebRoot; private uint _ofsRoot; private uint _lenRoot; private uint _lebGarbageCollect; private uint _lebIndexHead; private uint _ofsIndexHead; private ulong _lenIndex; private ulong _totalFree; private ulong _totalDirty; private ulong _totalUsed; private ulong _totalDead; private ulong _totalDark; private uint _lebLpt; private uint _ofsLpt; private uint _lebNhead; private uint _ofsNhead; private uint _lebLpropsTable; private uint _ofsLpropsTable; private uint _lebSaveTable; private uint _ofsSaveTable; private uint _lebLastLptScan; private uint _numEmptyLebs; private uint _numIndexLebs; private uint _numLebs; private byte[] _hashRootIndex; private byte[] _hashLpt; private byte[] _hmac; private byte[] _padding; private Ubifs m_root; private KaitaiStruct m_parent; /// /// highest inode number in the committed index /// public ulong HighestInum { get { return _highestInum; } } /// /// commit number /// public ulong CommitNumber { get { return _commitNumber; } } /// /// various flags (see the instances `dirty`, `no_orphans` and /// `recovery`) /// public uint Flags { get { return _flags; } } /// /// start of the log /// public uint LebLog { get { return _lebLog; } } /// /// LEB number of the root indexing node /// public uint LebRoot { get { return _lebRoot; } } /// /// offset within the LEB `leb_root` /// public uint OfsRoot { get { return _ofsRoot; } } /// /// root indexing node length /// public uint LenRoot { get { return _lenRoot; } } /// /// LEB reserved for garbage collection, which must be in the main area. /// /// The [comment above `struct ubifs_mst_node`](https://github.com/torvalds/linux/blob/8d3ae59288f1e7d58d76558a6ee96d533bc5019f/fs/ubifs/ubifs-media.h#L695-L696) /// says that -1 (`0xffff_ffff`) means the LEB was not reserved and should /// be reserved on mount. However, Linux refuses to mount a file system /// with -1. Linux /// [stores](https://github.com/torvalds/linux/blob/8d3ae59288f1e7d58d76558a6ee96d533bc5019f/fs/ubifs/master.c#L362) /// the field in an /// [`int`](https://github.com/torvalds/linux/blob/8d3ae59288f1e7d58d76558a6ee96d533bc5019f/fs/ubifs/ubifs.h#L1424), /// so `0xffff_ffff` becomes -1. Then -1 is rejected by the condition /// `c->gc_lnum < c->main_first`, because the main area /// [starts](https://github.com/torvalds/linux/blob/8d3ae59288f1e7d58d76558a6ee96d533bc5019f/fs/ubifs/sb.c#L775-L777) /// after the superblock, master, log, LPT and orphan areas. /// /// /// Reference: Git tag "v7.2" /// public uint LebGarbageCollect { get { return _lebGarbageCollect; } } /// /// LEB number of index head /// public uint LebIndexHead { get { return _lebIndexHead; } } /// /// offset of index head /// public uint OfsIndexHead { get { return _ofsIndexHead; } } /// /// size of index on flash /// public ulong LenIndex { get { return _lenIndex; } } /// /// total free space in bytes /// public ulong TotalFree { get { return _totalFree; } } /// /// total dirty space in bytes /// public ulong TotalDirty { get { return _totalDirty; } } /// /// total used space in bytes (includes only data LEBs) /// public ulong TotalUsed { get { return _totalUsed; } } /// /// total dead space in bytes (includes only data LEBs) /// public ulong TotalDead { get { return _totalDead; } } /// /// total dark space in bytes (includes only data LEBs) /// public ulong TotalDark { get { return _totalDark; } } /// /// LEB number of LPT root nnode /// public uint LebLpt { get { return _lebLpt; } } /// /// offset of LPT root nnode /// public uint OfsLpt { get { return _ofsLpt; } } /// /// LEB number of LPT head /// public uint LebNhead { get { return _lebNhead; } } /// /// offset of LPT head /// public uint OfsNhead { get { return _ofsNhead; } } /// /// LEB number of LPT's own lprops table /// public uint LebLpropsTable { get { return _lebLpropsTable; } } /// /// offset of LPT's own lprops table /// public uint OfsLpropsTable { get { return _ofsLpropsTable; } } /// /// LEB number of LPT's save table. Present in every master node, but /// only meaningful if `<superblock_header>.biglpt` is set: only then /// does Linux [check the location of the save table](https://github.com/torvalds/linux/blob/8d3ae59288f1e7d58d76558a6ee96d533bc5019f/fs/ubifs/master.c#L252-L256). /// public uint LebSaveTable { get { return _lebSaveTable; } } /// /// offset of LPT's save table (only meaningful if /// `<superblock_header>.biglpt` is set, see `leb_save_table`) /// public uint OfsSaveTable { get { return _ofsSaveTable; } } /// /// LEB number of last LPT scan /// public uint LebLastLptScan { get { return _lebLastLptScan; } } /// /// number of empty logical eraseblocks /// public uint NumEmptyLebs { get { return _numEmptyLebs; } } /// /// number of indexing logical eraseblocks /// public uint NumIndexLebs { get { return _numIndexLebs; } } /// /// count of LEBs used by file-system /// public uint NumLebs { get { return _numLebs; } } /// /// the hash of the root index node /// public byte[] HashRootIndex { get { return _hashRootIndex; } } /// /// the hash of the LPT /// public byte[] HashLpt { get { return _hashLpt; } } /// /// HMAC to authenticate the master node /// public byte[] Hmac { get { return _hmac; } } /// /// reserved for future, zeroes /// public byte[] Padding { get { return _padding; } } public Ubifs M_Root { get { return m_root; } } public KaitaiStruct M_Parent { get { return m_parent; } } } public partial class MasterblockNode : KaitaiStruct { public static MasterblockNode FromFile(string fileName) { return new MasterblockNode(new KaitaiStream(fileName)); } public MasterblockNode(KaitaiStream p__io, Ubifs p__parent = null, Ubifs p__root = null) : base(p__io) { m_parent = p__parent; m_root = p__root; _read(); } private void _read() { _header = new CommonHeader(m_io, this, m_root); { CommonHeader M_ = _header; if (!(M_.NodeType == Ubifs.NodeTypes.Master)) { throw new ValidationExprError(_header, m_io, "/types/masterblock_node/seq/0"); } } __raw_nodeHeader = m_io.ReadBytes(Header.LenFullNode - 24); var io___raw_nodeHeader = new KaitaiStream(__raw_nodeHeader); _nodeHeader = new MasterHeader(io___raw_nodeHeader, this, m_root); } private CommonHeader _header; private MasterHeader _nodeHeader; private Ubifs m_root; private Ubifs m_parent; private byte[] __raw_nodeHeader; public CommonHeader Header { get { return _header; } } public MasterHeader NodeHeader { get { return _nodeHeader; } } public Ubifs M_Root { get { return m_root; } } public Ubifs M_Parent { get { return m_parent; } } public byte[] M_RawNodeHeader { get { return __raw_nodeHeader; } } } /// /// Reference: Git tag "v7.2" /// public partial class OrphanHeader : KaitaiStruct { public static OrphanHeader FromFile(string fileName) { return new OrphanHeader(new KaitaiStream(fileName)); } public OrphanHeader(KaitaiStream p__io, Ubifs.Block p__parent = null, Ubifs p__root = null) : base(p__io) { m_parent = p__parent; m_root = p__root; _read(); } private void _read() { _commitNumber = m_io.ReadU8le(); _inodeNumbers = new List(); { var i = 0; while (!m_io.IsEof) { _inodeNumbers.Add(m_io.ReadU8le()); i++; } } } private ulong _commitNumber; private List _inodeNumbers; private Ubifs m_root; private Ubifs.Block m_parent; /// /// commit number (also top bit is set on the last node of the commit) /// public ulong CommitNumber { get { return _commitNumber; } } /// /// inode numbers of orphans /// public List InodeNumbers { get { return _inodeNumbers; } } public Ubifs M_Root { get { return m_root; } } public Ubifs.Block M_Parent { get { return m_parent; } } } /// /// Reference: Git tag "v7.2" /// public partial class PaddingHeader : KaitaiStruct { public static PaddingHeader FromFile(string fileName) { return new PaddingHeader(new KaitaiStream(fileName)); } public PaddingHeader(KaitaiStream p__io, Ubifs.Block p__parent = null, Ubifs p__root = null) : base(p__io) { m_parent = p__parent; m_root = p__root; _read(); } private void _read() { _lenPadding = m_io.ReadU4le(); } private uint _lenPadding; private Ubifs m_root; private Ubifs.Block m_parent; /// /// how many bytes after this node are unused (because padded) /// public uint LenPadding { get { return _lenPadding; } } public Ubifs M_Root { get { return m_root; } } public Ubifs.Block M_Parent { get { return m_parent; } } } /// /// logical eraseblock reference node. /// /// /// Reference: Git tag "v7.2" /// public partial class ReferenceHeader : KaitaiStruct { public static ReferenceHeader FromFile(string fileName) { return new ReferenceHeader(new KaitaiStream(fileName)); } public ReferenceHeader(KaitaiStream p__io, Ubifs.Block p__parent = null, Ubifs p__root = null) : base(p__io) { m_parent = p__parent; m_root = p__root; _read(); } private void _read() { _lebNumber = m_io.ReadU4le(); _ofsLeb = m_io.ReadU4le(); _journalHeadNumber = m_io.ReadU4le(); _padding = m_io.ReadBytes(28); } private uint _lebNumber; private uint _ofsLeb; private uint _journalHeadNumber; private byte[] _padding; private Ubifs m_root; private Ubifs.Block m_parent; /// /// the referred logical eraseblock number /// public uint LebNumber { get { return _lebNumber; } } /// /// start offset in the referred LEB /// public uint OfsLeb { get { return _ofsLeb; } } /// /// journal head number /// public uint JournalHeadNumber { get { return _journalHeadNumber; } } /// /// reserved for future, zeroes /// public byte[] Padding { get { return _padding; } } public Ubifs M_Root { get { return m_root; } } public Ubifs.Block M_Parent { get { return m_parent; } } } /// /// Signature of the superblock node of a file system signed when it was /// created (`mkfs.ubifs` with a key). Linux /// [expects it](https://github.com/torvalds/linux/blob/8d3ae59288f1e7d58d76558a6ee96d533bc5019f/fs/ubifs/auth.c#L198-L216) /// as the first node after the superblock node in LEB0. /// /// /// Reference: Git tag "v7.2" /// public partial class SignatureHeader : KaitaiStruct { public static SignatureHeader FromFile(string fileName) { return new SignatureHeader(new KaitaiStream(fileName)); } public SignatureHeader(KaitaiStream p__io, Ubifs.Block p__parent = null, Ubifs p__root = null) : base(p__io) { m_parent = p__parent; m_root = p__root; _read(); } private void _read() { _type = m_io.ReadU4le(); if (!(_type == 1)) { throw new ValidationNotEqualError(1, _type, m_io, "/types/signature_header/seq/0"); } _lenSignature = m_io.ReadU4le(); _padding = m_io.ReadBytes(32); _signature = m_io.ReadBytes(LenSignature); } private uint _type; private uint _lenSignature; private byte[] _padding; private byte[] _signature; private Ubifs m_root; private Ubifs.Block m_parent; /// /// type of the signature /// public uint Type { get { return _type; } } /// /// length of `signature` in bytes /// public uint LenSignature { get { return _lenSignature; } } /// /// reserved for future, zeroes /// public byte[] Padding { get { return _padding; } } /// /// [PKCS #7 signature](https://github.com/torvalds/linux/blob/8d3ae59288f1e7d58d76558a6ee96d533bc5019f/fs/ubifs/auth.c#L233-L236) /// of the whole superblock node (4096 bytes, including the common /// header) /// public byte[] Signature { get { return _signature; } } public Ubifs M_Root { get { return m_root; } } public Ubifs.Block M_Parent { get { return m_parent; } } } /// /// Reference: Git tag "v7.2" /// public partial class SuperblockHeader : KaitaiStruct { public static SuperblockHeader FromFile(string fileName) { return new SuperblockHeader(new KaitaiStream(fileName)); } public SuperblockHeader(KaitaiStream p__io, KaitaiStruct p__parent = null, Ubifs p__root = null) : base(p__io) { m_parent = p__parent; m_root = p__root; f_authenticated = false; f_biglpt = false; f_doubleHash = false; f_encrypted = false; f_lenHash = false; f_spaceFixup = false; _read(); } private void _read() { _padding0 = m_io.ReadBytes(2); if (!((KaitaiStream.ByteArrayCompare(_padding0, new byte[] { 0, 0 }) == 0))) { throw new ValidationNotEqualError(new byte[] { 0, 0 }, _padding0, m_io, "/types/superblock_header/seq/0"); } _keyHash = ((Ubifs.Hashes) m_io.ReadU1()); if (!Enum.IsDefined(typeof(Hashes), _keyHash)) { throw new ValidationNotInEnumError(_keyHash, m_io, "/types/superblock_header/seq/1"); } _keyFmt = ((Ubifs.KeyFormats) m_io.ReadU1()); if (!(_keyFmt == Ubifs.KeyFormats.Simple)) { throw new ValidationNotEqualError(Ubifs.KeyFormats.Simple, _keyFmt, m_io, "/types/superblock_header/seq/2"); } _flags = m_io.ReadU4le(); _minIoSize = m_io.ReadU4le(); _lebSize = m_io.ReadU4le(); _numLebs = m_io.ReadU4le(); _maxNumLebs = m_io.ReadU4le(); if (!(_maxNumLebs >= NumLebs)) { throw new ValidationLessThanError(NumLebs, _maxNumLebs, m_io, "/types/superblock_header/seq/7"); } _maxBudBytes = m_io.ReadU8le(); _numLogLebs = m_io.ReadU4le(); if (!(_numLogLebs >= 2)) { throw new ValidationLessThanError(2, _numLogLebs, m_io, "/types/superblock_header/seq/9"); } _numLptLebs = m_io.ReadU4le(); if (!(_numLptLebs >= 2)) { throw new ValidationLessThanError(2, _numLptLebs, m_io, "/types/superblock_header/seq/10"); } _numOrphanLebs = m_io.ReadU4le(); if (!(_numOrphanLebs >= 1)) { throw new ValidationLessThanError(1, _numOrphanLebs, m_io, "/types/superblock_header/seq/11"); } _numJournalHeads = m_io.ReadU4le(); _fanout = m_io.ReadU4le(); _numSaveTableEntries = m_io.ReadU4le(); _fmtVersion = m_io.ReadU4le(); if (!(_fmtVersion >= 4)) { throw new ValidationLessThanError(4, _fmtVersion, m_io, "/types/superblock_header/seq/15"); } _defaultCompression = ((Ubifs.Compression) m_io.ReadU2le()); if (!Enum.IsDefined(typeof(Compression), _defaultCompression)) { throw new ValidationNotInEnumError(_defaultCompression, m_io, "/types/superblock_header/seq/16"); } _padding1 = m_io.ReadBytes(2); if (!((KaitaiStream.ByteArrayCompare(_padding1, new byte[] { 0, 0 }) == 0))) { throw new ValidationNotEqualError(new byte[] { 0, 0 }, _padding1, m_io, "/types/superblock_header/seq/17"); } _reservePoolUid = m_io.ReadU4le(); _reservePoolGid = m_io.ReadU4le(); _reservePoolSize = m_io.ReadU8le(); _timeGranularity = m_io.ReadU4le(); if (!(_timeGranularity >= 1)) { throw new ValidationLessThanError(1, _timeGranularity, m_io, "/types/superblock_header/seq/21"); } if (!(_timeGranularity <= 1000000000)) { throw new ValidationGreaterThanError(1000000000, _timeGranularity, m_io, "/types/superblock_header/seq/21"); } _uuid = m_io.ReadBytes(16); _roCompatVersion = m_io.ReadU4le(); _hmac = m_io.ReadBytes(64); _hmacWkm = m_io.ReadBytes(64); if (Authenticated) { _hashAlgo = ((Ubifs.HashAlgos) m_io.ReadU2le()); if (!Enum.IsDefined(typeof(HashAlgos), _hashAlgo)) { throw new ValidationNotInEnumError(_hashAlgo, m_io, "/types/superblock_header/seq/26"); } } if (!(Authenticated)) { _hashAlgoUnused = m_io.ReadU2le(); } _hashMst = m_io.ReadBytes(64); _padding2 = m_io.ReadBytes(3774); } private bool f_authenticated; private bool _authenticated; /// /// this filesystem contains hashes for authentication /// public bool Authenticated { get { if (f_authenticated) return _authenticated; f_authenticated = true; _authenticated = (bool) ((Flags & 32) != 0); return _authenticated; } } private bool f_biglpt; private bool _biglpt; /// /// if "big" LPT model is used if set /// public bool Biglpt { get { if (f_biglpt) return _biglpt; f_biglpt = true; _biglpt = (bool) ((Flags & 2) != 0); return _biglpt; } } private bool f_doubleHash; private bool _doubleHash; /// /// store a 32bit cookie in directory entry nodes to /// support 64bit cookies for lookups by hash /// public bool DoubleHash { get { if (f_doubleHash) return _doubleHash; f_doubleHash = true; _doubleHash = (bool) ((Flags & 8) != 0); return _doubleHash; } } private bool f_encrypted; private bool _encrypted; /// /// filesystem contains encrypted files /// public bool Encrypted { get { if (f_encrypted) return _encrypted; f_encrypted = true; _encrypted = (bool) ((Flags & 16) != 0); return _encrypted; } } private bool f_lenHash; private int _lenHash; /// /// Length in bytes of the hash of a node (`c->hash_len` in Linux), e.g. /// in each branch of an index node. In an authenticated file system, it /// is determined by `hash_algo`. Otherwise, it is 0. /// public int LenHash { get { if (f_lenHash) return _lenHash; f_lenHash = true; _lenHash = (int) ((!(Authenticated) ? 0 : ( ((HashAlgo == Ubifs.HashAlgos.Md4) || (HashAlgo == Ubifs.HashAlgos.Md5) || (HashAlgo == Ubifs.HashAlgos.RipeMd128) || (HashAlgo == Ubifs.HashAlgos.Tgr128)) ? 16 : ( ((HashAlgo == Ubifs.HashAlgos.Sha1) || (HashAlgo == Ubifs.HashAlgos.RipeMd160) || (HashAlgo == Ubifs.HashAlgos.Tgr160)) ? 20 : (HashAlgo == Ubifs.HashAlgos.Tgr192 ? 24 : (HashAlgo == Ubifs.HashAlgos.Sha224 ? 28 : ( ((HashAlgo == Ubifs.HashAlgos.Sha256) || (HashAlgo == Ubifs.HashAlgos.RipeMd256) || (HashAlgo == Ubifs.HashAlgos.Wp256) || (HashAlgo == Ubifs.HashAlgos.Sm3256) || (HashAlgo == Ubifs.HashAlgos.Streebog256) || (HashAlgo == Ubifs.HashAlgos.Sha3256)) ? 32 : (HashAlgo == Ubifs.HashAlgos.RipeMd320 ? 40 : ( ((HashAlgo == Ubifs.HashAlgos.Sha384) || (HashAlgo == Ubifs.HashAlgos.Wp384) || (HashAlgo == Ubifs.HashAlgos.Sha3384)) ? 48 : ( ((HashAlgo == Ubifs.HashAlgos.Sha512) || (HashAlgo == Ubifs.HashAlgos.Wp512) || (HashAlgo == Ubifs.HashAlgos.Streebog512) || (HashAlgo == Ubifs.HashAlgos.Sha3512)) ? 64 : -1)))))))))); return _lenHash; } } private bool f_spaceFixup; private bool _spaceFixup; /// /// first-mount "fixup" of free space within LEBs needed /// public bool SpaceFixup { get { if (f_spaceFixup) return _spaceFixup; f_spaceFixup = true; _spaceFixup = (bool) ((Flags & 4) != 0); return _spaceFixup; } } private byte[] _padding0; private Hashes _keyHash; private KeyFormats _keyFmt; private uint _flags; private uint _minIoSize; private uint _lebSize; private uint _numLebs; private uint _maxNumLebs; private ulong _maxBudBytes; private uint _numLogLebs; private uint _numLptLebs; private uint _numOrphanLebs; private uint _numJournalHeads; private uint _fanout; private uint _numSaveTableEntries; private uint _fmtVersion; private Compression _defaultCompression; private byte[] _padding1; private uint _reservePoolUid; private uint _reservePoolGid; private ulong _reservePoolSize; private uint _timeGranularity; private byte[] _uuid; private uint _roCompatVersion; private byte[] _hmac; private byte[] _hmacWkm; private HashAlgos _hashAlgo; private ushort? _hashAlgoUnused; private byte[] _hashMst; private byte[] _padding2; private Ubifs m_root; private KaitaiStruct m_parent; /// /// reserved for future, zeroes /// public byte[] Padding0 { get { return _padding0; } } /// /// type of hash function used in keys /// public Hashes KeyHash { get { return _keyHash; } } /// /// format of the key /// public KeyFormats KeyFmt { get { return _keyFmt; } } /// /// file-system flags (see the instances `biglpt`, `space_fixup`, etc.) /// public uint Flags { get { return _flags; } } /// /// minimal input/output unit size /// public uint MinIoSize { get { return _minIoSize; } } /// /// logical eraseblock size in bytes /// public uint LebSize { get { return _lebSize; } } /// /// count of LEBs used by file-system /// public uint NumLebs { get { return _numLebs; } } /// /// maximum count of LEBs used by file-system /// public uint MaxNumLebs { get { return _maxNumLebs; } } /// /// maximum amount of data stored in buds /// public ulong MaxBudBytes { get { return _maxBudBytes; } } /// /// log size in logical eraseblocks /// public uint NumLogLebs { get { return _numLogLebs; } } /// /// number of LEBs used for lprops table /// public uint NumLptLebs { get { return _numLptLebs; } } /// /// number of LEBs used for recording orphans /// public uint NumOrphanLebs { get { return _numOrphanLebs; } } /// /// count of journal heads /// public uint NumJournalHeads { get { return _numJournalHeads; } } /// /// tree fanout (max. number of links per indexing node) /// public uint Fanout { get { return _fanout; } } /// /// number of LEB numbers in LPT's save table /// public uint NumSaveTableEntries { get { return _numSaveTableEntries; } } /// /// UBIFS on-flash format version /// public uint FmtVersion { get { return _fmtVersion; } } /// /// default compression algorithm (`compression::lzo`, etc.) /// public Compression DefaultCompression { get { return _defaultCompression; } } /// /// reserved for future, zeroes /// public byte[] Padding1 { get { return _padding1; } } /// /// reserve pool UID /// public uint ReservePoolUid { get { return _reservePoolUid; } } /// /// reserve pool GID /// public uint ReservePoolGid { get { return _reservePoolGid; } } /// /// size of the reserved pool in bytes /// public ulong ReservePoolSize { get { return _reservePoolSize; } } /// /// time granularity in nanoseconds /// public uint TimeGranularity { get { return _timeGranularity; } } /// /// UUID generated when the file system image was created /// public byte[] Uuid { get { return _uuid; } } /// /// UBIFS R/O compatibility version /// public uint RoCompatVersion { get { return _roCompatVersion; } } /// /// HMAC to authenticate the superblock node /// public byte[] Hmac { get { return _hmac; } } /// /// HMAC of a well known message (the string "UBIFS") as a convenience /// to the user to check if the correct key is passed /// public byte[] HmacWkm { get { return _hmacWkm; } } /// /// The hash algo used for this filesystem /// /// /// Reference: Git tag "v7.2" /// public HashAlgos HashAlgo { get { return _hashAlgo; } } /// /// Unused field (would only be meaningful in an authenticated file /// system, see `hash_algo`). The kernel writes it as 0xffff, `mkfs.ubifs` /// writes it as 0. /// /// /// Reference: Git tag "v7.2" /// public ushort? HashAlgoUnused { get { return _hashAlgoUnused; } } /// /// hash of the master node, only valid for signed images in which the /// master node does not contain a hmac /// public byte[] HashMst { get { return _hashMst; } } /// /// reserved for future, zeroes /// public byte[] Padding2 { get { return _padding2; } } public Ubifs M_Root { get { return m_root; } } public KaitaiStruct M_Parent { get { return m_parent; } } } public partial class SuperblockNode : KaitaiStruct { public static SuperblockNode FromFile(string fileName) { return new SuperblockNode(new KaitaiStream(fileName)); } public SuperblockNode(KaitaiStream p__io, Ubifs p__parent = null, Ubifs p__root = null) : base(p__io) { m_parent = p__parent; m_root = p__root; _read(); } private void _read() { _header = new CommonHeader(m_io, this, m_root); { CommonHeader M_ = _header; if (!(M_.NodeType == Ubifs.NodeTypes.Superblock)) { throw new ValidationExprError(_header, m_io, "/types/superblock_node/seq/0"); } } __raw_nodeHeader = m_io.ReadBytes(Header.LenFullNode - 24); var io___raw_nodeHeader = new KaitaiStream(__raw_nodeHeader); _nodeHeader = new SuperblockHeader(io___raw_nodeHeader, this, m_root); } private CommonHeader _header; private SuperblockHeader _nodeHeader; private Ubifs m_root; private Ubifs m_parent; private byte[] __raw_nodeHeader; public CommonHeader Header { get { return _header; } } public SuperblockHeader NodeHeader { get { return _nodeHeader; } } public Ubifs M_Root { get { return m_root; } } public Ubifs M_Parent { get { return m_parent; } } public byte[] M_RawNodeHeader { get { return __raw_nodeHeader; } } } /// /// This node exists only in the journal and never goes to the main area. /// /// /// Reference: Git tag "v7.2" /// public partial class TruncationHeader : KaitaiStruct { public static TruncationHeader FromFile(string fileName) { return new TruncationHeader(new KaitaiStream(fileName)); } public TruncationHeader(KaitaiStream p__io, Ubifs.Block p__parent = null, Ubifs p__root = null) : base(p__io) { m_parent = p__parent; m_root = p__root; _read(); } private void _read() { _inodeNumber = m_io.ReadU4le(); _padding = m_io.ReadBytes(12); _oldSize = m_io.ReadU8le(); if (!(_oldSize >= 1)) { throw new ValidationLessThanError(1, _oldSize, m_io, "/types/truncation_header/seq/2"); } _newSize = m_io.ReadU8le(); if (!(_newSize <= OldSize - 1)) { throw new ValidationGreaterThanError(OldSize - 1, _newSize, m_io, "/types/truncation_header/seq/3"); } } private uint _inodeNumber; private byte[] _padding; private ulong _oldSize; private ulong _newSize; private Ubifs m_root; private Ubifs.Block m_parent; /// /// truncated inode number /// public uint InodeNumber { get { return _inodeNumber; } } /// /// reserved for future, zeroes /// public byte[] Padding { get { return _padding; } } /// /// size before truncation /// public ulong OldSize { get { return _oldSize; } } /// /// size after truncation /// public ulong NewSize { get { return _newSize; } } public Ubifs M_Root { get { return m_root; } } public Ubifs.Block M_Parent { get { return m_parent; } } } private bool f_index; private Dummy _index; /// /// The LEB containing the root index node, as given by `leb_root` in /// `master_1`. /// /// This instance only provides the substream for `index_root`, which can then /// use `ofs_root` (an offset within the LEB) as its `pos` directly. /// public Dummy Index { get { if (f_index) return _index; f_index = true; long _pos = m_io.Pos; m_io.Seek(Superblock.NodeHeader.LebSize * Master1.NodeHeader.LebRoot); __raw_index = m_io.ReadBytes(Superblock.NodeHeader.LebSize); var io___raw_index = new KaitaiStream(__raw_index); _index = new Dummy(io___raw_index, this, m_root); m_io.Seek(_pos); return _index; } } private bool f_indexRoot; private IndexBlock _indexRoot; /// /// The root index node, in the `index` LEB at the offset given by /// `ofs_root` in `master_1`. All other index nodes, and through them all /// the files, are reached from it by recursively following the `target` of /// each of the `branches`. /// public IndexBlock IndexRoot { get { if (f_indexRoot) return _indexRoot; f_indexRoot = true; KaitaiStream io = Index.M_Io; long _pos = io.Pos; io.Seek(Master1.NodeHeader.OfsRoot); _indexRoot = new IndexBlock(io, this, m_root); { IndexBlock M_ = _indexRoot; if (!(M_.Header.LenFullNode == Master1.NodeHeader.LenRoot)) { throw new ValidationExprError(_indexRoot, m_io, "/instances/index_root"); } } io.Seek(_pos); return _indexRoot; } } private bool f_lebs; private List _lebs; /// /// All LEBs of the file system: `lebs[n]` is LEB number `n`, which starts /// at offset `n * <superblock_header>.leb_size`. /// /// Accessing this instance reads the whole image into memory. To get a /// single LEB, it's cheaper to read `<superblock_header>.leb_size` bytes /// at the offset of that LEB. /// public List Lebs { get { if (f_lebs) return _lebs; f_lebs = true; long _pos = m_io.Pos; m_io.Seek(0); _lebs = new List(); for (var i = 0; i < Superblock.NodeHeader.NumLebs; i++) { _lebs.Add(m_io.ReadBytes(Superblock.NodeHeader.LebSize)); } m_io.Seek(_pos); return _lebs; } } private bool f_lenImage; private int _lenImage; /// /// Length of the file system image in bytes: /// `<superblock_header>.num_lebs` LEBs of `<superblock_header>.leb_size` /// bytes each. Use it to find where the image ends and to detect a /// truncated image (see the top-level `doc`). /// public int LenImage { get { if (f_lenImage) return _lenImage; f_lenImage = true; _lenImage = (int) (Superblock.NodeHeader.LebSize * Superblock.NodeHeader.NumLebs); return _lenImage; } } private bool f_master1; private MasterblockNode _master1; /// /// The first copy of the master node in LEB1. This Kaitai Struct /// implementation uses it to locate `index_root`. In an image created by /// `mkfs.ubifs`, it is the only copy in LEB1. /// /// /// Reference: Git tag "v2.3.1" /// public MasterblockNode Master1 { get { if (f_master1) return _master1; f_master1 = true; long _pos = m_io.Pos; m_io.Seek(Superblock.NodeHeader.LebSize); __raw_master1 = m_io.ReadBytes(Superblock.NodeHeader.LebSize); var io___raw_master1 = new KaitaiStream(__raw_master1); _master1 = new MasterblockNode(io___raw_master1, this, m_root); m_io.Seek(_pos); return _master1; } } private bool f_master2; private MasterblockNode _master2; /// /// The first copy of the master node in LEB2. In an image created by /// `mkfs.ubifs`, it is the same as `master_1` except for the sequence /// number and CRC in the common header. Linux keeps the master node in both /// LEB1 and LEB2 for redundancy, and when mounting, it checks that the /// copies in both match. /// /// /// Reference: Git tag "v2.3.1" /// /// /// Reference: Git tag "v7.2" /// public MasterblockNode Master2 { get { if (f_master2) return _master2; f_master2 = true; long _pos = m_io.Pos; m_io.Seek(Superblock.NodeHeader.LebSize * 2); __raw_master2 = m_io.ReadBytes(Superblock.NodeHeader.LebSize); var io___raw_master2 = new KaitaiStream(__raw_master2); _master2 = new MasterblockNode(io___raw_master2, this, m_root); m_io.Seek(_pos); return _master2; } } private bool f_superblock; private SuperblockNode _superblock; /// /// The superblock node will always be at the start of /// the first LEB. /// public SuperblockNode Superblock { get { if (f_superblock) return _superblock; f_superblock = true; long _pos = m_io.Pos; m_io.Seek(0); _superblock = new SuperblockNode(m_io, this, m_root); m_io.Seek(_pos); return _superblock; } } private Ubifs m_root; private KaitaiStruct m_parent; private byte[] __raw_index; private byte[] __raw_master1; private byte[] __raw_master2; public Ubifs M_Root { get { return m_root; } } public KaitaiStruct M_Parent { get { return m_parent; } } public byte[] M_RawIndex { get { return __raw_index; } } public byte[] M_RawMaster1 { get { return __raw_master1; } } public byte[] M_RawMaster2 { get { return __raw_master2; } } } }