Skip to main content

erofs/
format.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Raw on-disk format structs for erofs. See
6//! https://erofs.docs.kernel.org/en/latest/ondisk/core_ondisk.html for more details.
7
8use static_assertions::assert_eq_size;
9use zerocopy::byteorder::little_endian::{U16 as LEU16, U32 as LEU32, U64 as LEU64};
10use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};
11
12/// Magic number for erofs filesystems.
13pub const EROFS_MAGIC: u32 = 0xE0F5E1E2;
14pub const SUPERBLOCK_OFFSET: u64 = 1024;
15pub const INODE_SLOT_SIZE: u64 = 32;
16pub const DIRENT_SIZE: usize = std::mem::size_of::<Dirent>();
17
18/// The on-disk format of an erofs superblock.
19#[derive(Debug, KnownLayout, FromBytes, IntoBytes, Immutable, Unaligned)]
20#[repr(C)]
21pub struct SuperBlock {
22    /// Magic number. Should be equal to EROFS_MAGIC.
23    pub magic: LEU32,
24    /// CRC-32 checksum of the block containing the superblock. This field is set to zero in the
25    /// checksummed version.
26    pub checksum: LEU32,
27    /// Feature flags. If any flags in here are not recognized the filesystem can still mount
28    /// without a loss of correctness.
29    pub feature_compat: LEU32,
30    /// Block size stored as a power of two.
31    pub block_size_bits: u8,
32    /// Number of 16-byte superblock extension slots.
33    pub sb_ext_slots: u8,
34    /// Node ID of the root directory.
35    pub root_nid: LEU16,
36    /// Total number of inodes - primarily for statfs. Can potentially be set to zero, don't rely
37    /// on it for validation.
38    pub inode_count: LEU64,
39    /// UNIX timestamp of when the filesystem was created, used as mtime for compact inodes.
40    pub epoch: LEU64,
41    /// Fixed nanosecond timestamp used as mtime for compact inodes.
42    pub fixed_nsec: LEU32,
43    /// Total number of blocks - primarily for statfs. Can potentially be set to zero, don't rely
44    /// on it for validation.
45    pub blocks: LEU32,
46    /// Start block address of inode metadata zone. This is essentially an offset for node id
47    /// calculations, not a guarantee that the inode data actually starts at this offset.
48    pub meta_block_addr: LEU32,
49    /// Start block address of the xattr zone. Similar to meta_block_addr.
50    pub xattr_block_addr: LEU32,
51    /// 128-bit UUID for this volume.
52    pub uuid: [u8; 16],
53    /// The volume name, zero-padded.
54    pub volume_name: [u8; 16],
55    /// Feature flags. If any flags here are not recognized, the filesystem can _not_ be mounted.
56    pub feature_incompat: LEU32,
57    /// Info about compression algorithms. Set to zero if image is not compressed.
58    pub available_compr_algs: LEU16,
59    /// External device support, ignored in core format.
60    pub extra_devices: LEU32,
61    /// Set to zero in the core format. This can be used to make directory blocks larger than
62    /// regular blocks (it modifies the block_size_bits field).
63    pub dirblkbits: u8,
64    /// There are some other fields we will care about when we implement xattr and compression
65    /// support, but for now with the core format we don't care about the rest of the superblock.
66    // TODO(https://fxbug.dev/479841115): Implement xattr support.
67    // TODO(https://fxbug.dev/479841115): Implement compression support.
68    pub reserved: [u8; 37],
69}
70assert_eq_size!(SuperBlock, [u8; 128]);
71
72/// Compact inode on-disk format. Fits within a single inode slot.
73#[derive(Debug, Clone, Copy, KnownLayout, FromBytes, IntoBytes, Immutable, Unaligned)]
74#[repr(C)]
75pub struct InodeCompact {
76    /// Format information about this particular inode. Indicates if it is compact or extended, and
77    /// the data layout (i.e. what i_u means for this node).
78    pub format: LEU16,
79    /// Size of the inline xattr region.
80    pub xattr_icount: LEU16,
81    /// Standard unix file type and permission bits.
82    pub mode: LEU16,
83    /// Number of hard links.
84    pub link_count: LEU16,
85    /// File size in bytes.
86    pub size: LEU32,
87    /// Reserved section.
88    pub reserved_1: [u8; 4],
89    /// Inode data union - the exact meaning of this field is dependent on the inode format field.
90    pub i_u: [u8; 4],
91    /// Inode number for stat compatibility.
92    pub ino: LEU32,
93    /// Owner UID.
94    pub uid: LEU16,
95    /// Owner GID.
96    pub gid: LEU16,
97    /// Reserved section.
98    pub reserved_2: [u8; 4],
99}
100assert_eq_size!(InodeCompact, [u8; 32]);
101
102/// Extended inode on-disk format. Uses two inode slots. Allows for more metadata and also larger
103/// files.
104#[derive(Debug, Clone, Copy, KnownLayout, FromBytes, IntoBytes, Immutable, Unaligned)]
105#[repr(C)]
106pub struct InodeExtended {
107    /// Format information about this particular inode. Indicates if it is compact or extended, and
108    /// the data layout (i.e. what i_u means for this node).
109    pub format: LEU16,
110    /// Size of the inline xattr region.
111    pub xattr_icount: LEU16,
112    /// Standard unix file type and permission bits.
113    pub mode: LEU16,
114    /// Reserved section.
115    pub reserved_1: [u8; 2],
116    /// File size in bytes.
117    pub size: LEU64,
118    /// Inode data union - the exact meaning of this field is dependent on the inode format field.
119    pub i_u: [u8; 4],
120    /// Inode number for stat compatibility.
121    pub ino: LEU32,
122    /// Owner UID.
123    pub uid: LEU32,
124    /// Owner GID.
125    pub gid: LEU32,
126    /// Last modification time in seconds since unix epoch.
127    pub mtime: LEU64,
128    /// Nanosecond part of the last modification time.
129    pub mtime_ns: LEU32,
130    /// Number of hard links.
131    pub link_count: LEU32,
132    /// Reserved section.
133    pub reserved_2: [u8; 16],
134}
135assert_eq_size!(InodeExtended, [u8; 64]);
136
137/// Directory entry on-disk format. Contained within the directory data blocks.
138#[derive(Debug, Clone, Copy, KnownLayout, FromBytes, IntoBytes, Immutable, Unaligned)]
139#[repr(C)]
140pub struct Dirent {
141    /// Node number of the target inode for this entry.
142    pub nid: LEU64,
143    /// Byte offset of the filename, relative to the start of this block. The name offset of the
144    /// first dirent in a directory block indicates how many entries there are in that block.
145    pub nameoff: LEU16,
146    /// File type code.
147    pub file_type: u8,
148    /// Reserved section.
149    pub reserved: u8,
150}
151assert_eq_size!(Dirent, [u8; 12]);