The contents of the files lie between the superblock (or the compressor options) and the inode table, in no index: each file’s inode says where its data starts. A file is cut into blocks of the superblock’s block size, each compressed on its own, so a reader can decode just the part of a file it wants. A file’s last, shorter piece is either a short block of its own or a tail packed with the tails of other files into a fragment block.
Data blocks and their size words
The blocks of one file are consecutive in the image, beginning at the inode’s start block. The inode’s block list has one 32-bit word for each, giving its size in the image; adding them up gives where the next block starts.
| Bits | Meaning |
|---|---|
0 to 23 | The size of the block as stored, in bytes: at most the block size. Zero is a sparse block: nothing is stored, and the block reads as zeros. |
24 | Set: the block is stored uncompressed (the compressor made it no smaller, or -noD). Clear: it is compressed. |
25 to 31 | Reserved, zero. |
Block i holds bytes i × block size onward of the file. It decompresses to the block size, except the last block of a file that has no tail in a fragment, which gives what remains.
How many blocks a file has: with a tail in a fragment, the file size divided by the block size rounded down; without, rounded up. The inode’s fragment field is 0xFFFFFFFF when the file has no fragment tail.
Sparse blocks
mksquashfs finds a block of a file that is all zeros and stores no data for it: its size word is 0. A reader gives zeros for it. The extended file inode can count the bytes that are holes in its sparse field; the field is informational.
Fragments
A file that does not end on a block boundary leaves a tail shorter than a block. By default
mksquashfs puts the tail of a small file (or of any file, with -tailends) into a
fragment block shared with other tails, so that a thousand small files do not each waste a
block. The inode names the fragment (an index into the fragment table)
and the offset of its tail inside the decompressed fragment block; the tail’s length is the file
size modulo the block size. A fragment block is a data block like any other: compressed or stored,
at most the block size, found through the fragment table.
With -no-fragments (flag NO_FRAGMENTS) there are none, and every file’s
last block is short.
Duplicates
Unless -no-duplicates is given (flag DUPLICATES), mksquashfs stores one copy
of files with identical contents: their inodes have the same start block and block list. A viewer
therefore sees the same bytes of the image belong to several files.
Reading a file
- Read the inode; take its start block, block list, fragment index, fragment offset and size.
- For the part of the file wanted, find its blocks: block i starts at the start block plus the sum of the first i sizes. Read that many bytes; decompress unless bit 24 is set; a size of 0 is a block of zeros.
- For a tail, read the fragment table entry, read and decompress the fragment block (or use it as stored) and take the tail from the offset.
The explorer does this block by block, so a file of many gigabytes opens without reading it whole: a window of it is made from the blocks it touches, and nothing else is decoded.