123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198 |
- Notes on Filesystem Layout
- --------------------------
- These notes describe what mkcramfs generates. Kernel requirements are
- a bit looser, e.g. it doesn't care if the <file_data> items are
- swapped around (though it does care that directory entries (inodes) in
- a given directory are contiguous, as this is used by readdir).
- All data is currently in host-endian format; neither mkcramfs nor the
- kernel ever do swabbing. (See section `Block Size' below.)
- <filesystem>:
- <superblock>
- <directory_structure>
- <data>
- <superblock>: struct cramfs_super (see cramfs_fs.h).
- <directory_structure>:
- For each file:
- struct cramfs_inode (see cramfs_fs.h).
- Filename. Not generally null-terminated, but it is
- null-padded to a multiple of 4 bytes.
- The order of inode traversal is described as "width-first" (not to be
- confused with breadth-first); i.e. like depth-first but listing all of
- a directory's entries before recursing down its subdirectories: the
- same order as `ls -AUR' (but without the /^\..*:$/ directory header
- lines); put another way, the same order as `find -type d -exec
- ls -AU1 {} \;'.
- Beginning in 2.4.7, directory entries are sorted. This optimization
- allows cramfs_lookup to return more quickly when a filename does not
- exist, speeds up user-space directory sorts, etc.
- <data>:
- One <file_data> for each file that's either a symlink or a
- regular file of non-zero st_size.
- <file_data>:
- nblocks * <block_pointer>
- (where nblocks = (st_size - 1) / blksize + 1)
- nblocks * <block>
- padding to multiple of 4 bytes
- The i'th <block_pointer> for a file stores the byte offset of the
- *end* of the i'th <block> (i.e. one past the last byte, which is the
- same as the start of the (i+1)'th <block> if there is one). The first
- <block> immediately follows the last <block_pointer> for the file.
- <block_pointer>s are each 32 bits long.
- When the CRAMFS_FLAG_EXT_BLOCK_POINTERS capability bit is set, each
- <block_pointer>'s top bits may contain special flags as follows:
- CRAMFS_BLK_FLAG_UNCOMPRESSED (bit 31):
- The block data is not compressed and should be copied verbatim.
- CRAMFS_BLK_FLAG_DIRECT_PTR (bit 30):
- The <block_pointer> stores the actual block start offset and not
- its end, shifted right by 2 bits. The block must therefore be
- aligned to a 4-byte boundary. The block size is either blksize
- if CRAMFS_BLK_FLAG_UNCOMPRESSED is also specified, otherwise
- the compressed data length is included in the first 2 bytes of
- the block data. This is used to allow discontiguous data layout
- and specific data block alignments e.g. for XIP applications.
- The order of <file_data>'s is a depth-first descent of the directory
- tree, i.e. the same order as `find -size +0 \( -type f -o -type l \)
- -print'.
- <block>: The i'th <block> is the output of zlib's compress function
- applied to the i'th blksize-sized chunk of the input data if the
- corresponding CRAMFS_BLK_FLAG_UNCOMPRESSED <block_ptr> bit is not set,
- otherwise it is the input data directly.
- (For the last <block> of the file, the input may of course be smaller.)
- Each <block> may be a different size. (See <block_pointer> above.)
- <block>s are merely byte-aligned, not generally u32-aligned.
- When CRAMFS_BLK_FLAG_DIRECT_PTR is specified then the corresponding
- <block> may be located anywhere and not necessarily contiguous with
- the previous/next blocks. In that case it is minimally u32-aligned.
- If CRAMFS_BLK_FLAG_UNCOMPRESSED is also specified then the size is always
- blksize except for the last block which is limited by the file length.
- If CRAMFS_BLK_FLAG_DIRECT_PTR is set and CRAMFS_BLK_FLAG_UNCOMPRESSED
- is not set then the first 2 bytes of the block contains the size of the
- remaining block data as this cannot be determined from the placement of
- logically adjacent blocks.
- Holes
- -----
- This kernel supports cramfs holes (i.e. [efficient representation of]
- blocks in uncompressed data consisting entirely of NUL bytes), but by
- default mkcramfs doesn't test for & create holes, since cramfs in
- kernels up to at least 2.3.39 didn't support holes. Run mkcramfs
- with -z if you want it to create files that can have holes in them.
- Tools
- -----
- The cramfs user-space tools, including mkcramfs and cramfsck, are
- located at <http://sourceforge.net/projects/cramfs/>.
- Future Development
- ==================
- Block Size
- ----------
- (Block size in cramfs refers to the size of input data that is
- compressed at a time. It's intended to be somewhere around
- PAGE_SIZE for cramfs_readpage's convenience.)
- The superblock ought to indicate the block size that the fs was
- written for, since comments in <linux/pagemap.h> indicate that
- PAGE_SIZE may grow in future (if I interpret the comment
- correctly).
- Currently, mkcramfs #define's PAGE_SIZE as 4096 and uses that
- for blksize, whereas Linux-2.3.39 uses its PAGE_SIZE, which in
- turn is defined as PAGE_SIZE (which can be as large as 32KB on arm).
- This discrepancy is a bug, though it's not clear which should be
- changed.
- One option is to change mkcramfs to take its PAGE_SIZE from
- <asm/page.h>. Personally I don't like this option, but it does
- require the least amount of change: just change `#define
- PAGE_SIZE (4096)' to `#include <asm/page.h>'. The disadvantage
- is that the generated cramfs cannot always be shared between different
- kernels, not even necessarily kernels of the same architecture if
- PAGE_SIZE is subject to change between kernel versions
- (currently possible with arm and ia64).
- The remaining options try to make cramfs more sharable.
- One part of that is addressing endianness. The two options here are
- `always use little-endian' (like ext2fs) or `writer chooses
- endianness; kernel adapts at runtime'. Little-endian wins because of
- code simplicity and little CPU overhead even on big-endian machines.
- The cost of swabbing is changing the code to use the le32_to_cpu
- etc. macros as used by ext2fs. We don't need to swab the compressed
- data, only the superblock, inodes and block pointers.
- The other part of making cramfs more sharable is choosing a block
- size. The options are:
- 1. Always 4096 bytes.
- 2. Writer chooses blocksize; kernel adapts but rejects blocksize >
- PAGE_SIZE.
- 3. Writer chooses blocksize; kernel adapts even to blocksize >
- PAGE_SIZE.
- It's easy enough to change the kernel to use a smaller value than
- PAGE_SIZE: just make cramfs_readpage read multiple blocks.
- The cost of option 1 is that kernels with a larger PAGE_SIZE
- value don't get as good compression as they can.
- The cost of option 2 relative to option 1 is that the code uses
- variables instead of #define'd constants. The gain is that people
- with kernels having larger PAGE_SIZE can make use of that if
- they don't mind their cramfs being inaccessible to kernels with
- smaller PAGE_SIZE values.
- Option 3 is easy to implement if we don't mind being CPU-inefficient:
- e.g. get readpage to decompress to a buffer of size MAX_BLKSIZE (which
- must be no larger than 32KB) and discard what it doesn't need.
- Getting readpage to read into all the covered pages is harder.
- The main advantage of option 3 over 1, 2, is better compression. The
- cost is greater complexity. Probably not worth it, but I hope someone
- will disagree. (If it is implemented, then I'll re-use that code in
- e2compr.)
- Another cost of 2 and 3 over 1 is making mkcramfs use a different
- block size, but that just means adding and parsing a -b option.
- Inode Size
- ----------
- Given that cramfs will probably be used for CDs etc. as well as just
- silicon ROMs, it might make sense to expand the inode a little from
- its current 12 bytes. Inodes other than the root inode are followed
- by filename, so the expansion doesn't even have to be a multiple of 4
- bytes.
|