1use std::mem::MaybeUninit;
8use std::os::unix::fs::MetadataExt;
9
10use anyhow::bail;
11use anyhow::Result;
12use enumn::N;
13use zerocopy::FromBytes;
14use zerocopy::Immutable;
15use zerocopy::IntoBytes;
16use zerocopy::KnownLayout;
17
18use crate::arena::Arena;
19use crate::arena::BlockId;
20use crate::blockgroup::GroupMetaData;
21use crate::xattr::InlineXattrs;
22
23#[derive(Debug, PartialEq, Eq, Clone, Copy, N)]
25pub enum InodeType {
26 Fifo = 0x1,
27 Char = 0x2,
28 Directory = 0x4,
29 Block = 0x6,
30 Regular = 0x8,
31 Symlink = 0xa,
32 Socket = 0xc,
33}
34
35impl InodeType {
36 pub fn into_dir_entry_file_type(self) -> u8 {
39 match self {
40 InodeType::Regular => 1,
41 InodeType::Directory => 2,
42 InodeType::Char => 3,
43 InodeType::Block => 4,
44 InodeType::Fifo => 5,
45 InodeType::Socket => 6,
46 InodeType::Symlink => 7,
47 }
48 }
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
54pub(crate) struct InodeNum(pub u32);
55
56impl InodeNum {
57 pub fn new(inode: u32) -> Result<Self> {
58 if inode == 0 {
59 bail!("inode number is 1-indexed");
60 }
61 Ok(Self(inode))
62 }
63
64 pub fn to_table_index(self) -> usize {
66 self.0 as usize - 1
68 }
69}
70
71impl From<InodeNum> for u32 {
72 fn from(inode: InodeNum) -> Self {
73 inode.0
74 }
75}
76
77impl From<InodeNum> for usize {
78 fn from(inode: InodeNum) -> Self {
79 inode.0 as usize
80 }
81}
82
83const INODE_BLOCK_LEN: usize = 60;
85#[repr(C)]
91#[derive(Debug, Copy, Clone, FromBytes, Immutable, IntoBytes, KnownLayout)]
92pub(crate) struct InodeBlock(pub [u8; INODE_BLOCK_LEN]);
93
94impl Default for InodeBlock {
95 fn default() -> Self {
96 Self([0; INODE_BLOCK_LEN])
97 }
98}
99
100impl InodeBlock {
101 pub const NUM_DIRECT_BLOCKS: usize = 12;
104 const INDIRECT_BLOCK_TABLE_ID: usize = Self::NUM_DIRECT_BLOCKS;
105 const DOUBLE_INDIRECT_BLOCK_TABLE_ID: usize = 13;
106
107 pub fn set_block_id(&mut self, index: usize, block_id: &BlockId) -> Result<()> {
109 let offset = index * std::mem::size_of::<BlockId>();
110 let bytes = block_id.as_bytes();
111 if self.0.len() < offset + bytes.len() {
112 bail!("index out of bounds when setting block_id to InodeBlock: index={index}, block_id: {:?}", block_id);
113 }
114 self.0[offset..offset + bytes.len()].copy_from_slice(bytes);
115 Ok(())
116 }
117
118 pub fn set_direct_blocks_from(
120 &mut self,
121 start_idx: usize,
122 block_ids: &[BlockId],
123 ) -> Result<()> {
124 let bytes = block_ids.as_bytes();
125 if bytes.len() + start_idx * 4 > self.0.len() {
126 bail!(
127 "length of direct blocks is {} bytes, but it must not exceed {}",
128 bytes.len(),
129 self.0.len()
130 );
131 }
132 self.0[start_idx * 4..(start_idx * 4 + bytes.len())].copy_from_slice(bytes);
133 Ok(())
134 }
135
136 pub fn set_direct_blocks(&mut self, block_ids: &[BlockId]) -> Result<()> {
138 self.set_direct_blocks_from(0, block_ids)
139 }
140
141 pub fn set_indirect_block_table(&mut self, block_id: &BlockId) -> Result<()> {
143 self.set_block_id(Self::INDIRECT_BLOCK_TABLE_ID, block_id)
144 }
145
146 pub fn set_double_indirect_block_table(&mut self, block_id: &BlockId) -> Result<()> {
148 self.set_block_id(Self::DOUBLE_INDIRECT_BLOCK_TABLE_ID, block_id)
149 }
150
151 pub const fn max_inline_symlink_len() -> usize {
154 INODE_BLOCK_LEN
155 }
156
157 pub fn set_inline_symlink(&mut self, symlink: &str) -> Result<()> {
159 let bytes = symlink.as_bytes();
160 if bytes.len() >= Self::max_inline_symlink_len() {
161 bail!(
162 "symlink '{symlink}' exceeds or equals tomax length: {} >= {}",
163 bytes.len(),
164 Self::max_inline_symlink_len()
165 );
166 }
167 self.0[..bytes.len()].copy_from_slice(bytes);
168 Ok(())
169 }
170}
171
172#[repr(C)]
176#[derive(Debug, Copy, Clone, FromBytes, Immutable, IntoBytes, KnownLayout)]
177pub(crate) struct Inode {
178 mode: u16,
179 uid: u16,
180 pub size: u32,
181 atime: u32,
182 ctime: u32,
183 mtime: u32,
184 _dtime: u32,
185 gid: u16,
186 pub links_count: u16,
187 pub blocks: InodeBlocksCount,
188 _flags: u32,
189 _osd1: u32,
190 pub block: InodeBlock,
191 _generation: u32,
192 _file_acl: u32,
193 _dir_acl: u32,
194 _faddr: u32,
195 _fragment_num: u8,
196 _fragment_size: u8,
197 _reserved1: u16,
198 uid_high: u16,
199 gid_high: u16,
200 _reserved2: u32, pub extra_size: u16,
207 _paddings: u16, }
209
210impl Default for Inode {
211 fn default() -> Self {
212 let mut r: Self = unsafe { MaybeUninit::zeroed().assume_init() };
214 r.extra_size = 4;
216 r
217 }
218}
219
220#[repr(C)]
225#[derive(Default, Debug, Copy, Clone, FromBytes, Immutable, IntoBytes, KnownLayout)]
226pub struct InodeBlocksCount(u32);
227
228impl InodeBlocksCount {
229 const INODE_BLOCKS_SIZE: u32 = 512;
230
231 pub fn from_bytes_len(len: u32) -> Self {
232 Self(len / Self::INODE_BLOCKS_SIZE)
233 }
234
235 pub fn add(&mut self, v: u32) {
236 self.0 += v / Self::INODE_BLOCKS_SIZE;
237 }
238}
239
240impl Inode {
241 pub const INODE_RECORD_SIZE: usize = 256;
250
251 pub const XATTR_AREA_SIZE: usize = Inode::INODE_RECORD_SIZE - std::mem::size_of::<Inode>();
253
254 pub fn new<'a>(
255 arena: &'a Arena<'a>,
256 group: &mut GroupMetaData,
257 inode_num: InodeNum,
258 typ: InodeType,
259 size: u32,
260 xattr: Option<InlineXattrs>,
261 ) -> Result<&'a mut Self> {
262 const EXT2_S_IRUSR: u16 = 0x0100; const EXT2_S_IXUSR: u16 = 0x0040; const EXT2_S_IRGRP: u16 = 0x0020; const EXT2_S_IXGRP: u16 = 0x0008; const EXT2_S_IROTH: u16 = 0x0004; const EXT2_S_IXOTH: u16 = 0x0001; let inode_offset = inode_num.to_table_index() * Inode::INODE_RECORD_SIZE;
270 let inode =
271 arena.allocate::<Inode>(BlockId::from(group.group_desc.inode_table), inode_offset)?;
272
273 let mode = ((typ as u16) << 12)
275 | EXT2_S_IRUSR
276 | EXT2_S_IXUSR
277 | EXT2_S_IRGRP
278 | EXT2_S_IXGRP
279 | EXT2_S_IROTH
280 | EXT2_S_IXOTH;
281
282 let now = std::time::SystemTime::now()
283 .duration_since(std::time::UNIX_EPOCH)?
284 .as_secs() as u32;
285
286 let uid = unsafe { libc::geteuid() };
288 let uid_high = (uid >> 16) as u16;
289 let uid_low = uid as u16;
290 let gid = unsafe { libc::getegid() };
292 let gid_high = (gid >> 16) as u16;
293 let gid_low = gid as u16;
294
295 *inode = Self {
296 mode,
297 size,
298 atime: now,
299 ctime: now,
300 mtime: now,
301 uid: uid_low,
302 gid: gid_low,
303 uid_high,
304 gid_high,
305 ..Default::default()
306 };
307 if let Some(xattr) = xattr {
308 Self::add_xattr(arena, group, inode, inode_offset, xattr)?;
309 }
310
311 Ok(inode)
312 }
313
314 pub fn from_metadata<'a>(
315 arena: &'a Arena<'a>,
316 group: &mut GroupMetaData,
317 inode_num: InodeNum,
318 m: &std::fs::Metadata,
319 size: u32,
320 links_count: u16,
321 blocks: InodeBlocksCount,
322 block: InodeBlock,
323 xattr: Option<InlineXattrs>,
324 ) -> Result<&'a mut Self> {
325 let inodes_per_group = group.inode_bitmap.len();
326 let inode_offset =
328 ((usize::from(inode_num) - 1) % inodes_per_group) * Inode::INODE_RECORD_SIZE;
329 let inode =
330 arena.allocate::<Inode>(BlockId::from(group.group_desc.inode_table), inode_offset)?;
331
332 let mode = m.mode() as u16;
333
334 let uid = m.uid();
335 let uid_high = (uid >> 16) as u16;
336 let uid_low: u16 = uid as u16;
337 let gid = m.gid();
338 let gid_high = (gid >> 16) as u16;
339 let gid_low: u16 = gid as u16;
340
341 let atime = m.atime() as u32;
342 let ctime = m.ctime() as u32;
343 let mtime = m.mtime() as u32;
344
345 *inode = Inode {
346 mode,
347 uid: uid_low,
348 gid: gid_low,
349 size,
350 atime,
351 ctime,
352 mtime,
353 links_count,
354 blocks,
355 block,
356 uid_high,
357 gid_high,
358 ..Default::default()
359 };
360
361 if let Some(xattr) = xattr {
362 Self::add_xattr(arena, group, inode, inode_offset, xattr)?;
363 }
364
365 Ok(inode)
366 }
367
368 fn add_xattr<'a>(
369 arena: &'a Arena<'a>,
370 group: &mut GroupMetaData,
371 inode: &mut Inode,
372 inode_offset: usize,
373 xattr: InlineXattrs,
374 ) -> Result<()> {
375 let xattr_region = arena.allocate::<[u8; Inode::XATTR_AREA_SIZE]>(
376 BlockId::from(group.group_desc.inode_table),
377 inode_offset + std::mem::size_of::<Inode>(),
378 )?;
379
380 if !xattr.entry_table.is_empty() {
381 inode.extra_size = 4;
385 let InlineXattrs {
386 entry_table,
387 values,
388 } = xattr;
389
390 if entry_table.len() + values.len() > Inode::XATTR_AREA_SIZE {
391 bail!("xattr size is too large for inline store: entry_table.len={}, values.len={}, inline region size={}",
392 entry_table.len(), values.len(), Inode::XATTR_AREA_SIZE);
393 }
394 xattr_region[..entry_table.len()].copy_from_slice(&entry_table);
396 xattr_region[Inode::XATTR_AREA_SIZE - values.len()..].copy_from_slice(&values);
397 }
398 Ok(())
399 }
400
401 pub fn update_metadata(&mut self, m: &std::fs::Metadata) {
402 self.mode = m.mode() as u16;
403
404 let uid: u32 = m.uid();
405 self.uid_high = (uid >> 16) as u16;
406 self.uid = uid as u16;
407 let gid = m.gid();
408 self.gid_high = (gid >> 16) as u16;
409 self.gid = gid as u16;
410
411 self.atime = m.atime() as u32;
412 self.ctime = m.ctime() as u32;
413 self.mtime = m.mtime() as u32;
414 }
415
416 pub fn typ(&self) -> Option<InodeType> {
417 InodeType::n((self.mode >> 12) as u8)
418 }
419}
420
421#[cfg(test)]
422mod tests {
423 use super::*;
424
425 #[test]
426 fn test_inode_size() {
427 assert_eq!(std::mem::offset_of!(Inode, extra_size), 128);
428 assert_eq!(
430 std::mem::offset_of!(Inode, _paddings) + std::mem::size_of::<u16>(),
431 std::mem::size_of::<Inode>()
432 );
433
434 assert!(128 < std::mem::size_of::<Inode>());
435 assert!(std::mem::size_of::<Inode>() <= Inode::INODE_RECORD_SIZE);
436 }
437}