This page hosts a formal specification of cramfs using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.
digraph {
rankdir=LR;
node [shape=plaintext];
subgraph cluster__cramfs {
label="Cramfs";
graph[style=dotted];
cramfs__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="super_block_pos">0</TD><TD PORT="super_block_size">...</TD><TD>SuperBlockStruct</TD><TD PORT="super_block_type">super_block</TD></TR>
</TABLE>>];
cramfs__inst__page_size [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
<TR><TD>page_size</TD><TD>4096</TD></TR>
</TABLE>>];
subgraph cluster__super_block_struct {
label="Cramfs::SuperBlockStruct";
graph[style=dotted];
super_block_struct__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="magic_pos">0</TD><TD PORT="magic_size">4</TD><TD></TD><TD PORT="magic_type">magic</TD></TR>
<TR><TD PORT="size_pos">4</TD><TD PORT="size_size">4</TD><TD>u4le</TD><TD PORT="size_type">size</TD></TR>
<TR><TD PORT="flags_pos">8</TD><TD PORT="flags_size">4</TD><TD>u4le</TD><TD PORT="flags_type">flags</TD></TR>
<TR><TD PORT="future_pos">12</TD><TD PORT="future_size">4</TD><TD>u4le</TD><TD PORT="future_type">future</TD></TR>
<TR><TD PORT="signature_pos">16</TD><TD PORT="signature_size">16</TD><TD></TD><TD PORT="signature_type">signature</TD></TR>
<TR><TD PORT="fsid_pos">32</TD><TD PORT="fsid_size">16</TD><TD>Info</TD><TD PORT="fsid_type">fsid</TD></TR>
<TR><TD PORT="name_pos">48</TD><TD PORT="name_size">16</TD><TD>str(ASCII)</TD><TD PORT="name_type">name</TD></TR>
<TR><TD PORT="root_pos">64</TD><TD PORT="root_size">...</TD><TD>Inode</TD><TD PORT="root_type">root</TD></TR>
</TABLE>>];
super_block_struct__inst__flag_fsid_v2 [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
<TR><TD>flag_fsid_v2</TD><TD>((flags >> 0) & 1)</TD></TR>
</TABLE>>];
super_block_struct__inst__flag_holes [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
<TR><TD>flag_holes</TD><TD>((flags >> 8) & 1)</TD></TR>
</TABLE>>];
super_block_struct__inst__flag_wrong_signature [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
<TR><TD>flag_wrong_signature</TD><TD>((flags >> 9) & 1)</TD></TR>
</TABLE>>];
super_block_struct__inst__flag_sorted_dirs [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
<TR><TD>flag_sorted_dirs</TD><TD>((flags >> 1) & 1)</TD></TR>
</TABLE>>];
super_block_struct__inst__flag_shifted_root_offset [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
<TR><TD>flag_shifted_root_offset</TD><TD>((flags >> 10) & 1)</TD></TR>
</TABLE>>];
}
subgraph cluster__chunked_data_inode {
label="Cramfs::ChunkedDataInode";
graph[style=dotted];
chunked_data_inode__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="block_end_index_pos">0</TD><TD PORT="block_end_index_size">4</TD><TD>u4le</TD><TD PORT="block_end_index_type">block_end_index</TD></TR>
<TR><TD COLSPAN="4" PORT="block_end_index__repeat">repeat (((_parent.size + _root.page_size) - 1) / _root.page_size) times</TD></TR>
<TR><TD PORT="raw_blocks_pos">...</TD><TD PORT="raw_blocks_size">⇲</TD><TD></TD><TD PORT="raw_blocks_type">raw_blocks</TD></TR>
</TABLE>>];
}
subgraph cluster__inode {
label="Cramfs::Inode";
graph[style=dotted];
inode__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="mode_pos">0</TD><TD PORT="mode_size">2</TD><TD>u2le</TD><TD PORT="mode_type">mode</TD></TR>
<TR><TD PORT="uid_pos">2</TD><TD PORT="uid_size">2</TD><TD>u2le</TD><TD PORT="uid_type">uid</TD></TR>
<TR><TD PORT="size_gid_pos">4</TD><TD PORT="size_gid_size">4</TD><TD>u4le</TD><TD PORT="size_gid_type">size_gid</TD></TR>
<TR><TD PORT="namelen_offset_pos">8</TD><TD PORT="namelen_offset_size">4</TD><TD>u4le</TD><TD PORT="namelen_offset_type">namelen_offset</TD></TR>
<TR><TD PORT="name_pos">12</TD><TD PORT="name_size">namelen</TD><TD>str(utf-8)</TD><TD PORT="name_type">name</TD></TR>
</TABLE>>];
inode__inst__attr [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
<TR><TD>attr</TD><TD>((mode >> 9) & 7)</TD></TR>
</TABLE>>];
inode__inst__as_reg_file [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="as_reg_file_pos">offset</TD><TD PORT="as_reg_file_size">...</TD><TD>ChunkedDataInode</TD><TD PORT="as_reg_file_type">as_reg_file</TD></TR>
</TABLE>>];
inode__inst__perm_u [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
<TR><TD>perm_u</TD><TD>((mode >> 6) & 7)</TD></TR>
</TABLE>>];
inode__inst__as_symlink [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="as_symlink_pos">offset</TD><TD PORT="as_symlink_size">...</TD><TD>ChunkedDataInode</TD><TD PORT="as_symlink_type">as_symlink</TD></TR>
</TABLE>>];
inode__inst__perm_o [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
<TR><TD>perm_o</TD><TD>(mode & 7)</TD></TR>
</TABLE>>];
inode__inst__size [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
<TR><TD>size</TD><TD>(size_gid & 16777215)</TD></TR>
</TABLE>>];
inode__inst__gid [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
<TR><TD>gid</TD><TD>(size_gid >> 24)</TD></TR>
</TABLE>>];
inode__inst__perm_g [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
<TR><TD>perm_g</TD><TD>((mode >> 3) & 7)</TD></TR>
</TABLE>>];
inode__inst__namelen [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
<TR><TD>namelen</TD><TD>((namelen_offset & 63) << 2)</TD></TR>
</TABLE>>];
inode__inst__as_dir [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="as_dir_pos">offset</TD><TD PORT="as_dir_size">size</TD><TD>DirInode</TD><TD PORT="as_dir_type">as_dir</TD></TR>
</TABLE>>];
inode__inst__type [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
<TR><TD>type</TD><TD>Kaitai::Struct::Stream::resolve_enum(FILE_TYPE, ((mode >> 12) & 15))</TD></TR>
</TABLE>>];
inode__inst__offset [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
<TR><TD>offset</TD><TD>(((namelen_offset >> 6) & 67108863) << 2)</TD></TR>
</TABLE>>];
}
subgraph cluster__dir_inode {
label="Cramfs::DirInode";
graph[style=dotted];
dir_inode__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="children_pos">0</TD><TD PORT="children_size">...</TD><TD>Inode</TD><TD PORT="children_type">children</TD></TR>
<TR><TD COLSPAN="4" PORT="children__repeat">repeat to end of stream</TD></TR>
</TABLE>>];
}
subgraph cluster__info {
label="Cramfs::Info";
graph[style=dotted];
info__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="crc_pos">0</TD><TD PORT="crc_size">4</TD><TD>u4le</TD><TD PORT="crc_type">crc</TD></TR>
<TR><TD PORT="edition_pos">4</TD><TD PORT="edition_size">4</TD><TD>u4le</TD><TD PORT="edition_type">edition</TD></TR>
<TR><TD PORT="blocks_pos">8</TD><TD PORT="blocks_size">4</TD><TD>u4le</TD><TD PORT="blocks_type">blocks</TD></TR>
<TR><TD PORT="files_pos">12</TD><TD PORT="files_size">4</TD><TD>u4le</TD><TD PORT="files_type">files</TD></TR>
</TABLE>>];
}
}
cramfs__seq:super_block_type -> super_block_struct__seq [style=bold];
super_block_struct__seq:fsid_type -> info__seq [style=bold];
super_block_struct__seq:root_type -> inode__seq [style=bold];
super_block_struct__seq:flags_type -> super_block_struct__inst__flag_fsid_v2 [color="#404040"];
super_block_struct__seq:flags_type -> super_block_struct__inst__flag_holes [color="#404040"];
super_block_struct__seq:flags_type -> super_block_struct__inst__flag_wrong_signature [color="#404040"];
super_block_struct__seq:flags_type -> super_block_struct__inst__flag_sorted_dirs [color="#404040"];
super_block_struct__seq:flags_type -> super_block_struct__inst__flag_shifted_root_offset [color="#404040"];
inode__inst__size:size_type -> chunked_data_inode__seq:block_end_index__repeat [color="#404040"];
cramfs__inst__page_size:page_size_type -> chunked_data_inode__seq:block_end_index__repeat [color="#404040"];
cramfs__inst__page_size:page_size_type -> chunked_data_inode__seq:block_end_index__repeat [color="#404040"];
inode__inst__namelen:namelen_type -> inode__seq:name_size [color="#404040"];
inode__seq:mode_type -> inode__inst__attr [color="#404040"];
inode__inst__offset:offset_type -> inode__inst__as_reg_file:as_reg_file_pos [color="#404040"];
inode__inst__as_reg_file:as_reg_file_type -> chunked_data_inode__seq [style=bold];
inode__seq:mode_type -> inode__inst__perm_u [color="#404040"];
inode__inst__offset:offset_type -> inode__inst__as_symlink:as_symlink_pos [color="#404040"];
inode__inst__as_symlink:as_symlink_type -> chunked_data_inode__seq [style=bold];
inode__seq:mode_type -> inode__inst__perm_o [color="#404040"];
inode__seq:size_gid_type -> inode__inst__size [color="#404040"];
inode__seq:size_gid_type -> inode__inst__gid [color="#404040"];
inode__seq:mode_type -> inode__inst__perm_g [color="#404040"];
inode__seq:namelen_offset_type -> inode__inst__namelen [color="#404040"];
inode__inst__offset:offset_type -> inode__inst__as_dir:as_dir_pos [color="#404040"];
inode__inst__size:size_type -> inode__inst__as_dir:as_dir_size [color="#404040"];
inode__inst__as_dir:as_dir_type -> dir_inode__seq [style=bold];
inode__seq:mode_type -> inode__inst__type [color="#404040"];
inode__seq:namelen_offset_type -> inode__inst__offset [color="#404040"];
dir_inode__seq:children_type -> inode__seq [style=bold];
}