1use crate::fs::{FileSystemError, FileSystemErrorKind};
7use alloc::{boxed::Box, format, string::String, vec};
8use core::mem;
9
10pub const EXT2_SUPER_MAGIC: u16 = 0xEF53;
12
13pub const EXT2_ROOT_INO: u32 = 2;
15
16pub const EXT2_S_IFMT: u16 = 0xF000; pub const EXT2_S_IFREG: u16 = 0x8000; pub const EXT2_S_IFDIR: u16 = 0x4000; pub const EXT2_S_IFLNK: u16 = 0xA000; pub const EXT2_S_IFCHR: u16 = 0x2000; pub const EXT2_S_IFBLK: u16 = 0x6000; pub const EXT2_S_IFIFO: u16 = 0x1000; pub const EXT2_S_IFSOCK: u16 = 0xC000; #[derive(Debug, Clone, Copy)]
31#[repr(C, packed)]
32pub struct Ext2Superblock {
33 pub inodes_count: u32,
35 pub blocks_count: u32,
37 pub r_blocks_count: u32,
39 pub free_blocks_count: u32,
41 pub free_inodes_count: u32,
43 pub first_data_block: u32,
45 pub log_block_size: u32,
47 pub log_frag_size: u32,
49 pub blocks_per_group: u32,
51 pub frags_per_group: u32,
53 pub inodes_per_group: u32,
55 pub mtime: u32,
57 pub wtime: u32,
59 pub mnt_count: u16,
61 pub max_mnt_count: u16,
63 pub magic: u16,
65 pub state: u16,
67 pub errors: u16,
69 pub minor_rev_level: u16,
71 pub lastcheck: u32,
73 pub checkinterval: u32,
75 pub creator_os: u32,
77 pub rev_level: u32,
79 pub def_resuid: u16,
81 pub def_resgid: u16,
83 pub first_ino: u32,
85 pub inode_size: u16,
87 pub block_group_nr: u16,
89 pub feature_compat: u32,
91 pub feature_incompat: u32,
93 pub feature_ro_compat: u32,
95 pub uuid: [u8; 16],
97 pub volume_name: [u8; 16],
99 pub last_mounted: [u8; 64],
101 pub algorithm_usage_bitmap: u32,
103 pub prealloc_blocks: u8,
105 pub prealloc_dir_blocks: u8,
107 pub padding: [u8; 1024 - 204],
109}
110
111impl Ext2Superblock {
112 pub fn from_bytes(data: &[u8]) -> Result<Self, FileSystemError> {
114 if data.len() < 1024 {
116 return Err(FileSystemError::new(
117 FileSystemErrorKind::InvalidData,
118 format!(
119 "Insufficient data for ext2 superblock: got {} bytes, need at least 1024 bytes",
120 data.len()
121 ),
122 ));
123 }
124
125 let superblock = unsafe {
127 let mut aligned_data = [0u8; 1024];
129 aligned_data[..1024].copy_from_slice(&data[..1024]);
130 *(aligned_data.as_ptr() as *const Self)
131 };
132
133 if u16::from_le(superblock.magic) != EXT2_SUPER_MAGIC {
135 return Err(FileSystemError::new(
136 FileSystemErrorKind::InvalidData,
137 "Invalid ext2 magic number",
138 ));
139 }
140
141 Ok(superblock)
142 }
143
144 pub fn from_bytes_boxed(data: &[u8]) -> Result<Box<Self>, FileSystemError> {
146 if data.len() < 1024 {
148 return Err(FileSystemError::new(
149 FileSystemErrorKind::InvalidData,
150 format!(
151 "Insufficient data for ext2 superblock: got {} bytes, need at least 1024 bytes",
152 data.len()
153 ),
154 ));
155 }
156
157 let mut superblock = Box::new(unsafe { core::mem::zeroed::<Self>() });
159
160 unsafe {
162 core::ptr::copy_nonoverlapping(
163 data.as_ptr(),
164 superblock.as_mut() as *mut Self as *mut u8,
165 core::mem::size_of::<Self>().min(1024),
166 );
167 }
168
169 if u16::from_le(superblock.magic) != EXT2_SUPER_MAGIC {
171 return Err(FileSystemError::new(
172 FileSystemErrorKind::InvalidData,
173 "Invalid ext2 magic number",
174 ));
175 }
176
177 Ok(superblock)
178 }
179
180 pub fn get_block_size(&self) -> u32 {
182 1024 << u32::from_le(self.log_block_size)
183 }
184
185 pub fn get_blocks_count(&self) -> u32 {
187 u32::from_le(self.blocks_count)
188 }
189
190 pub fn get_inodes_count(&self) -> u32 {
192 u32::from_le(self.inodes_count)
193 }
194
195 pub fn get_blocks_per_group(&self) -> u32 {
197 u32::from_le(self.blocks_per_group)
198 }
199
200 pub fn get_inodes_per_group(&self) -> u32 {
202 u32::from_le(self.inodes_per_group)
203 }
204
205 pub fn get_inode_size(&self) -> u16 {
207 u16::from_le(self.inode_size)
208 }
209
210 pub fn get_first_data_block(&self) -> u32 {
212 u32::from_le(self.first_data_block)
213 }
214}
215
216#[derive(Debug, Clone, Copy)]
221#[repr(C, packed)]
222pub struct Ext2BlockGroupDescriptor {
223 pub block_bitmap: u32,
225 pub inode_bitmap: u32,
227 pub inode_table: u32,
229 pub free_blocks_count: u16,
231 pub free_inodes_count: u16,
233 pub used_dirs_count: u16,
235 pub pad: u16,
237 pub reserved: [u32; 3],
239}
240
241impl Ext2BlockGroupDescriptor {
242 pub fn from_bytes(data: &[u8]) -> Result<Self, FileSystemError> {
244 if data.len() < mem::size_of::<Self>() {
245 return Err(FileSystemError::new(
246 FileSystemErrorKind::InvalidData,
247 "Insufficient data for ext2 block group descriptor",
248 ));
249 }
250
251 let descriptor = unsafe { *(data.as_ptr() as *const Self) };
253
254 Ok(descriptor)
255 }
256
257 pub fn get_block_bitmap(&self) -> u32 {
259 u32::from_le(self.block_bitmap)
260 }
261
262 pub fn get_inode_bitmap(&self) -> u32 {
264 u32::from_le(self.inode_bitmap)
265 }
266
267 pub fn get_inode_table(&self) -> u32 {
269 u32::from_le(self.inode_table)
270 }
271
272 pub fn get_free_blocks_count(&self) -> u16 {
274 u16::from_le(self.free_blocks_count)
275 }
276
277 pub fn set_free_blocks_count(&mut self, count: u16) {
279 self.free_blocks_count = count.to_le();
280 }
281
282 pub fn get_free_inodes_count(&self) -> u16 {
284 u16::from_le(self.free_inodes_count)
285 }
286
287 pub fn set_free_inodes_count(&mut self, count: u16) {
289 self.free_inodes_count = count.to_le();
290 }
291
292 pub fn get_used_dirs_count(&self) -> u16 {
294 u16::from_le(self.used_dirs_count)
295 }
296
297 pub fn set_used_dirs_count(&mut self, count: u16) {
299 self.used_dirs_count = count.to_le();
300 }
301
302 pub fn write_to_bytes(&self, data: &mut [u8]) {
304 if data.len() >= mem::size_of::<Self>() {
305 unsafe {
306 let ptr = data.as_mut_ptr() as *mut Self;
307 *ptr = *self;
308 }
309 }
310 }
311}
312
313#[derive(Debug, Clone, Copy)]
318#[repr(C, packed)]
319pub struct Ext2Inode {
320 pub mode: u16,
322 pub uid: u16,
324 pub size: u32,
326 pub atime: u32,
328 pub ctime: u32,
330 pub mtime: u32,
332 pub dtime: u32,
334 pub gid: u16,
336 pub links_count: u16,
338 pub blocks: u32,
340 pub flags: u32,
342 pub osd1: u32,
344 pub block: [u32; 15],
346 pub generation: u32,
348 pub file_acl: u32,
350 pub dir_acl: u32,
352 pub faddr: u32,
354 pub osd2: [u8; 12],
356}
357
358impl Ext2Inode {
359 pub fn empty() -> Self {
360 Self {
361 mode: 0,
362 uid: 0,
363 size: 0,
364 atime: 0,
365 ctime: 0,
366 mtime: 0,
367 dtime: 0,
368 gid: 0,
369 links_count: 0,
370 blocks: 0,
371 flags: 0,
372 osd1: 0,
373 block: [0; 15],
374 generation: 0,
375 file_acl: 0,
376 dir_acl: 0,
377 faddr: 0,
378 osd2: [0; 12],
379 }
380 }
381 pub fn from_bytes(data: &[u8]) -> Result<Self, FileSystemError> {
383 if data.len() < mem::size_of::<Self>() {
384 return Err(FileSystemError::new(
385 FileSystemErrorKind::InvalidData,
386 "Insufficient data for ext2 inode",
387 ));
388 }
389
390 let inode = unsafe { *(data.as_ptr() as *const Self) };
392
393 Ok(inode)
394 }
395
396 pub fn get_mode(&self) -> u16 {
398 u16::from_le(self.mode)
399 }
400
401 pub fn get_size(&self) -> u32 {
403 u32::from_le(self.size)
404 }
405
406 pub fn get_mtime(&self) -> u32 {
408 u32::from_le(self.mtime)
409 }
410
411 pub fn get_atime(&self) -> u32 {
413 u32::from_le(self.atime)
414 }
415
416 pub fn get_ctime(&self) -> u32 {
418 u32::from_le(self.ctime)
419 }
420
421 pub fn get_links_count(&self) -> u16 {
423 u16::from_le(self.links_count)
424 }
425
426 pub fn get_blocks(&self) -> u32 {
428 u32::from_le(self.blocks)
429 }
430
431 pub fn get_block(&self, index: usize) -> Option<u32> {
433 if index < 15 {
434 Some(u32::from_le(self.block[index]))
435 } else {
436 None
437 }
438 }
439
440 pub fn is_dir(&self) -> bool {
442 (self.get_mode() & EXT2_S_IFMT) == EXT2_S_IFDIR
443 }
444
445 pub fn is_file(&self) -> bool {
447 (self.get_mode() & EXT2_S_IFMT) == EXT2_S_IFREG
448 }
449
450 pub fn is_char_device(&self) -> bool {
452 (self.get_mode() & EXT2_S_IFMT) == EXT2_S_IFCHR
453 }
454
455 pub fn is_block_device(&self) -> bool {
457 (self.get_mode() & EXT2_S_IFMT) == EXT2_S_IFBLK
458 }
459
460 pub fn is_symlink(&self) -> bool {
462 (self.get_mode() & EXT2_S_IFMT) == EXT2_S_IFLNK
463 }
464
465 pub fn is_fifo(&self) -> bool {
467 (self.get_mode() & EXT2_S_IFMT) == EXT2_S_IFIFO
468 }
469
470 pub fn is_socket(&self) -> bool {
472 (self.get_mode() & EXT2_S_IFMT) == EXT2_S_IFSOCK
473 }
474
475 pub fn get_device_info(&self) -> Option<(u32, u32)> {
478 if self.is_char_device() || self.is_block_device() {
479 let device_id = u32::from_le(self.block[0]);
481 let major = (device_id >> 8) & 0xFF;
482 let minor = device_id & 0xFF;
483 Some((major, minor))
484 } else {
485 None
486 }
487 }
488
489 pub fn read_symlink_target(
503 &self,
504 filesystem: &super::Ext2FileSystem,
505 ) -> Result<String, FileSystemError> {
506 if !self.is_symlink() {
508 return Err(FileSystemError::new(
509 FileSystemErrorKind::NotSupported,
510 "Not a symbolic link",
511 ));
512 }
513
514 let size = self.get_size() as usize;
515
516 if size <= 60 {
517 let inode_bytes = unsafe {
519 core::slice::from_raw_parts(
520 self as *const Self as *const u8,
521 core::mem::size_of::<Self>(),
522 )
523 };
524 let block_start_offset = 40;
526 let block_bytes = &inode_bytes[block_start_offset..block_start_offset + 60];
527 let target_bytes = &block_bytes[..size];
528
529 String::from_utf8(target_bytes.to_vec()).map_err(|_| {
530 FileSystemError::new(
531 FileSystemErrorKind::InvalidData,
532 "Invalid UTF-8 in symlink target",
533 )
534 })
535 } else {
536 let first_block = u32::from_le(self.block[0]);
538 if first_block == 0 {
539 return Err(FileSystemError::new(
540 FileSystemErrorKind::InvalidData,
541 "Symlink has no data block",
542 ));
543 }
544
545 let block_sector = filesystem.block_to_sector(first_block as u64);
547 let request = Box::new(crate::device::block::request::BlockIORequest {
548 request_type: crate::device::block::request::BlockIORequestType::Read,
549 sector: block_sector as usize,
550 sector_count: (filesystem.block_size / 512) as usize,
551 head: 0,
552 cylinder: 0,
553 buffer: vec![0u8; filesystem.block_size as usize],
554 });
555
556 filesystem.block_device.enqueue_request(request);
557 let results = filesystem.block_device.process_requests();
558
559 let block_data = if let Some(result) = results.first() {
560 match &result.result {
561 Ok(_) => result.request.buffer.clone(),
562 Err(_) => {
563 return Err(FileSystemError::new(
564 FileSystemErrorKind::IoError,
565 "Failed to read symlink data block",
566 ));
567 }
568 }
569 } else {
570 return Err(FileSystemError::new(
571 FileSystemErrorKind::IoError,
572 "No result from symlink data block read",
573 ));
574 };
575
576 let target_bytes = &block_data[..size];
577 String::from_utf8(target_bytes.to_vec()).map_err(|_| {
578 FileSystemError::new(
579 FileSystemErrorKind::InvalidData,
580 "Invalid UTF-8 in symlink target",
581 )
582 })
583 }
584 }
585}
586
587#[derive(Debug, Clone, Copy)]
591#[repr(C, packed)]
592pub struct Ext2DirectoryEntryRaw {
593 pub inode: u32,
595 pub rec_len: u16,
597 pub name_len: u8,
599 pub file_type: u8,
601 }
603
604impl Ext2DirectoryEntryRaw {
605 pub fn from_bytes(data: &[u8]) -> Result<Self, FileSystemError> {
607 if data.len() < mem::size_of::<Self>() {
608 return Err(FileSystemError::new(
609 FileSystemErrorKind::InvalidData,
610 "Insufficient data for ext2 directory entry header",
611 ));
612 }
613
614 let entry = unsafe { *(data.as_ptr() as *const Self) };
616
617 Ok(entry)
618 }
619
620 pub fn get_inode(&self) -> u32 {
622 u32::from_le(self.inode)
623 }
624
625 pub fn get_rec_len(&self) -> u16 {
627 u16::from_le(self.rec_len)
628 }
629
630 pub fn get_name_len(&self) -> u8 {
632 self.name_len
633 }
634
635 pub fn get_file_type(&self) -> u8 {
637 self.file_type
638 }
639}
640
641#[derive(Debug, Clone)]
643pub struct Ext2DirectoryEntry {
644 pub entry: Ext2DirectoryEntryRaw,
645 pub name: String,
646}
647
648impl Ext2DirectoryEntry {
649 pub fn from_bytes(data: &[u8]) -> Result<Self, FileSystemError> {
651 if data.len() < 8 {
652 return Err(FileSystemError::new(
653 FileSystemErrorKind::InvalidData,
654 "Insufficient data for ext2 directory entry",
655 ));
656 }
657
658 let entry = Ext2DirectoryEntryRaw::from_bytes(data)?;
659
660 if data.len() < 8 + entry.name_len as usize {
661 return Err(FileSystemError::new(
662 FileSystemErrorKind::InvalidData,
663 "Insufficient data for directory entry name",
664 ));
665 }
666
667 let name_bytes = &data[8..8 + entry.name_len as usize];
668 let name = String::from_utf8(name_bytes.to_vec()).map_err(|_| {
669 FileSystemError::new(
670 FileSystemErrorKind::InvalidData,
671 "Invalid UTF-8 in directory entry name",
672 )
673 })?;
674
675 Ok(Self { entry, name })
676 }
677
678 pub fn name_str(&self) -> Result<String, FileSystemError> {
679 Ok(self.name.clone())
680 }
681}
682
683