1use std::fmt;
12
13use zerocopy::{
14 little_endian::{U16, U32, U64},
15 FromBytes, Immutable, IntoBytes, KnownLayout,
16};
17
18pub const BLOCK_BITS: u8 = 12;
20pub const BLOCK_SIZE: u16 = 1 << BLOCK_BITS;
22
23#[derive(Debug)]
25pub enum FormatError {
26 InvalidDataLayout,
28}
29
30#[derive(Clone, Copy, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
34pub struct FormatField(U16);
35
36impl Default for FormatField {
37 fn default() -> Self {
38 FormatField(0xffff.into())
39 }
40}
41
42impl fmt::Debug for FormatField {
43 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
44 write!(
45 f,
46 "{} = {:?} | {:?}",
47 self.0.get(),
48 InodeLayout::from(*self),
49 DataLayout::try_from(*self)
50 )
51 }
52}
53
54const INODE_LAYOUT_MASK: u16 = 0b00000001;
55const INODE_LAYOUT_COMPACT: u16 = 0;
56const INODE_LAYOUT_EXTENDED: u16 = 1;
57
58#[derive(Debug)]
60#[repr(u16)]
61pub enum InodeLayout {
62 Compact = INODE_LAYOUT_COMPACT,
64 Extended = INODE_LAYOUT_EXTENDED,
66}
67
68impl From<FormatField> for InodeLayout {
69 fn from(value: FormatField) -> Self {
70 match value.0.get() & INODE_LAYOUT_MASK {
71 INODE_LAYOUT_COMPACT => InodeLayout::Compact,
72 INODE_LAYOUT_EXTENDED => InodeLayout::Extended,
73 _ => unreachable!(),
74 }
75 }
76}
77
78const INODE_DATALAYOUT_MASK: u16 = 0b00001110;
79const INODE_DATALAYOUT_FLAT_PLAIN: u16 = 0;
80const INODE_DATALAYOUT_FLAT_INLINE: u16 = 4;
81const INODE_DATALAYOUT_CHUNK_BASED: u16 = 8;
82
83#[derive(Debug)]
85#[repr(u16)]
86pub enum DataLayout {
87 FlatPlain = 0,
89 FlatInline = 4,
91 ChunkBased = 8,
93}
94
95impl TryFrom<FormatField> for DataLayout {
96 type Error = FormatError;
97
98 fn try_from(value: FormatField) -> Result<Self, FormatError> {
99 match value.0.get() & INODE_DATALAYOUT_MASK {
100 INODE_DATALAYOUT_FLAT_PLAIN => Ok(DataLayout::FlatPlain),
101 INODE_DATALAYOUT_FLAT_INLINE => Ok(DataLayout::FlatInline),
102 INODE_DATALAYOUT_CHUNK_BASED => Ok(DataLayout::ChunkBased),
103 _ => Err(FormatError::InvalidDataLayout),
105 }
106 }
107}
108
109impl std::ops::BitOr<DataLayout> for InodeLayout {
110 type Output = FormatField;
111
112 fn bitor(self, datalayout: DataLayout) -> FormatField {
114 FormatField((self as u16 | datalayout as u16).into())
115 }
116}
117
118pub const S_IFMT: u16 = 0o170000;
120pub const S_IFREG: u16 = 0o100000;
122pub const S_IFCHR: u16 = 0o020000;
124pub const S_IFDIR: u16 = 0o040000;
126pub const S_IFBLK: u16 = 0o060000;
128pub const S_IFIFO: u16 = 0o010000;
130pub const S_IFLNK: u16 = 0o120000;
132pub const S_IFSOCK: u16 = 0o140000;
134
135pub const FILE_TYPE_UNKNOWN: u8 = 0;
138pub const FILE_TYPE_REGULAR_FILE: u8 = 1;
140pub const FILE_TYPE_DIRECTORY: u8 = 2;
142pub const FILE_TYPE_CHARACTER_DEVICE: u8 = 3;
144pub const FILE_TYPE_BLOCK_DEVICE: u8 = 4;
146pub const FILE_TYPE_FIFO: u8 = 5;
148pub const FILE_TYPE_SOCKET: u8 = 6;
150pub const FILE_TYPE_SYMLINK: u8 = 7;
152
153#[derive(Clone, Copy, Debug)]
155#[repr(u8)]
156pub enum FileType {
157 Unknown = FILE_TYPE_UNKNOWN,
159 RegularFile = FILE_TYPE_REGULAR_FILE,
161 Directory = FILE_TYPE_DIRECTORY,
163 CharacterDevice = FILE_TYPE_CHARACTER_DEVICE,
165 BlockDevice = FILE_TYPE_BLOCK_DEVICE,
167 Fifo = FILE_TYPE_FIFO,
169 Socket = FILE_TYPE_SOCKET,
171 Symlink = FILE_TYPE_SYMLINK,
173}
174
175impl From<FileTypeField> for FileType {
176 fn from(value: FileTypeField) -> Self {
177 match value.0 {
178 FILE_TYPE_REGULAR_FILE => Self::RegularFile,
179 FILE_TYPE_DIRECTORY => Self::Directory,
180 FILE_TYPE_CHARACTER_DEVICE => Self::CharacterDevice,
181 FILE_TYPE_BLOCK_DEVICE => Self::BlockDevice,
182 FILE_TYPE_FIFO => Self::Fifo,
183 FILE_TYPE_SOCKET => Self::Socket,
184 FILE_TYPE_SYMLINK => Self::Symlink,
185 _ => Self::Unknown,
187 }
188 }
189}
190
191impl From<FileType> for FileTypeField {
192 fn from(value: FileType) -> Self {
193 FileTypeField(value as u8)
194 }
195}
196
197impl std::ops::BitOr<u16> for FileType {
198 type Output = U16;
199
200 fn bitor(self, permissions: u16) -> U16 {
202 (match self {
203 Self::RegularFile => S_IFREG,
204 Self::CharacterDevice => S_IFCHR,
205 Self::Directory => S_IFDIR,
206 Self::BlockDevice => S_IFBLK,
207 Self::Fifo => S_IFIFO,
208 Self::Symlink => S_IFLNK,
209 Self::Socket => S_IFSOCK,
210 Self::Unknown => unreachable!(),
211 } | permissions)
212 .into()
213 }
214}
215
216#[derive(Copy, Clone, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
218pub struct FileTypeField(u8);
219
220impl fmt::Debug for FileTypeField {
221 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
222 fmt::Debug::fmt(&FileType::from(*self), f)
223 }
224}
225
226impl Default for FileTypeField {
227 fn default() -> Self {
228 FileTypeField(0xff)
229 }
230}
231
232#[derive(Clone, Copy, Default, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
235pub struct ModeField(pub U16);
236
237impl ModeField {
238 pub fn is_dir(self) -> bool {
240 self.0.get() & S_IFMT == S_IFDIR
241 }
242}
243
244impl fmt::Debug for ModeField {
245 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
246 let mode = self.0.get();
247 let fmt = match mode & S_IFMT {
248 S_IFREG => "regular file",
249 S_IFCHR => "chardev",
250 S_IFDIR => "directory",
251 S_IFBLK => "blockdev",
252 S_IFIFO => "fifo",
253 S_IFLNK => "symlink",
254 S_IFSOCK => "socket",
255 _ => "INVALID",
256 };
257
258 write!(f, "0{mode:06o} ({fmt})")
259 }
260}
261
262impl std::ops::BitOr<u32> for FileType {
263 type Output = ModeField;
264
265 fn bitor(self, permissions: u32) -> ModeField {
266 ModeField(self | (permissions as u16))
267 }
268}
269
270pub const VERSION: U32 = U32::new(1);
274pub const COMPOSEFS_VERSION: U32 = U32::new(2);
276pub const COMPOSEFS_MAGIC: U32 = U32::new(0xd078629a);
278
279pub const COMPOSEFS_FLAGS_HAS_ACL: U32 = U32::new(1 << 0);
281
282#[derive(Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
284#[repr(C)]
285pub struct ComposefsHeader {
286 pub magic: U32,
288 pub version: U32,
290 pub flags: U32,
292 pub composefs_version: U32,
294 pub unused: [U32; 4],
296}
297
298pub const MAGIC_V1: U32 = U32::new(0xE0F5E1E2);
302pub const FEATURE_COMPAT_MTIME: U32 = U32::new(2);
304pub const FEATURE_COMPAT_XATTR_FILTER: U32 = U32::new(4);
306
307#[derive(Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
309#[repr(C)]
310pub struct Superblock {
311 pub magic: U32,
314 pub checksum: U32,
316 pub feature_compat: U32,
318 pub blkszbits: u8,
320 pub extslots: u8,
322 pub root_nid: U16,
324
325 pub inos: U64,
327 pub build_time: U64,
329
330 pub build_time_nsec: U32,
332 pub blocks: U32,
334 pub meta_blkaddr: U32,
336 pub xattr_blkaddr: U32,
338
339 pub uuid: [u8; 16],
341
342 pub volume_name: [u8; 16],
344
345 pub feature_incompat: U32,
347 pub available_compr_algs: U16,
349 pub extra_devices: U16,
351 pub devt_slotoff: U16,
353 pub dirblkbits: u8,
355 pub xattr_prefix_count: u8,
357 pub xattr_prefix_start: U32,
359
360 pub packed_nid: U64,
362 pub xattr_filter_reserved: u8,
364 pub reserved2: [u8; 23],
366}
367
368#[derive(Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
372#[repr(C)]
373pub struct CompactInodeHeader {
374 pub format: FormatField,
376 pub xattr_icount: U16,
378 pub mode: ModeField,
380 pub nlink: U16,
382
383 pub size: U32,
385 pub reserved: U32,
387
388 pub u: U32,
390 pub ino: U32, pub uid: U16,
395 pub gid: U16,
397 pub reserved2: [u8; 4],
399}
400
401#[derive(Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
403#[repr(C)]
404pub struct ExtendedInodeHeader {
405 pub format: FormatField,
407 pub xattr_icount: U16,
409 pub mode: ModeField,
411 pub reserved: U16,
413 pub size: U64,
415
416 pub u: U32,
418 pub ino: U32, pub uid: U32,
422 pub gid: U32,
424
425 pub mtime: U64,
427
428 pub mtime_nsec: U32,
430 pub nlink: U32,
432
433 pub reserved2: [u8; 16],
435}
436
437#[derive(Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
439#[repr(C)]
440pub struct InodeXAttrHeader {
441 pub name_filter: U32,
443 pub shared_count: u8,
445 pub reserved: [u8; 7],
447}
448
449pub const XATTR_FILTER_SEED: u32 = 0x25BBE08F;
452
453#[derive(Debug, FromBytes, Immutable, IntoBytes, KnownLayout)]
455#[repr(C)]
456pub struct XAttrHeader {
457 pub name_len: u8,
459 pub name_index: u8,
461 pub value_size: U16,
463}
464
465pub const XATTR_PREFIXES: [&[u8]; 7] = [
467 b"",
468 b"user.",
469 b"system.posix_acl_access",
470 b"system.posix_acl_default",
471 b"trusted.",
472 b"lustre.",
473 b"security.",
474];
475
476#[derive(Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
480#[repr(C)]
481pub struct DirectoryEntryHeader {
482 pub inode_offset: U64,
484 pub name_offset: U16,
486 pub file_type: FileTypeField,
488 pub reserved: u8,
490}