1use alloc::borrow::ToOwned;
4use alloc::ffi::CString;
5use alloc::string::{String, ToString};
6use alloc::vec;
7use alloc::vec::Vec;
8use core::fmt::Debug;
9use core::ptr::{addr_of, addr_of_mut, slice_from_raw_parts};
10
11use bitflags::Flags;
12use deku::no_std_io::{Read, Seek, SeekFrom, Write};
13use itertools::Itertools;
14use spin::Mutex;
15
16use super::Ext2Fs;
17use super::directory::{self, Entry, FileType};
18use super::error::Ext2Error;
19use super::inode::{Inode, TypePermissions};
20use crate::arch::{u32_to_usize, u64_to_usize, usize_to_u64};
21use crate::dev::Device;
22use crate::dev::address::Address;
23use crate::error::Error;
24use crate::fs::PATH_MAX;
25use crate::fs::error::FsError;
26use crate::fs::ext2::block::Block;
27use crate::fs::ext2::inode::DIRECT_BLOCK_POINTER_COUNT;
28use crate::fs::file::{self, DirectoryEntry, DirectoryRead, Stat, Type, TypeWithFile};
29use crate::fs::permissions::Permissions;
30use crate::fs::structures::indirection::IndirectedBlocks;
31use crate::fs::types::{Blkcnt, Blksize, Gid, Ino, Mode, Nlink, Off, Time, Timespec, Uid};
32use crate::path::{CUR_DIR, PARENT_DIR, UnixStr};
33
34pub const SYMBOLIC_LINK_INODE_STORE_LIMIT: usize = 60;
37
38pub struct File<Dev: Device> {
40 filesystem: Ext2Fs<Dev>,
42
43 inode_number: u32,
45
46 inode: Inode,
48
49 io_offset: u64,
51}
52
53impl<Dev: Device> Debug for File<Dev> {
54 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
55 formatter
56 .debug_struct("File")
57 .field("device_id", &self.filesystem.lock().device_id)
58 .field("inode_number", &self.inode_number)
59 .field("inode", &self.inode)
60 .field("io_offset", &self.io_offset)
61 .finish()
62 }
63}
64
65impl<Dev: Device> Clone for File<Dev> {
66 fn clone(&self) -> Self {
67 Self {
68 filesystem: self.filesystem.clone(),
69 inode_number: self.inode_number,
70 inode: self.inode,
71 io_offset: self.io_offset,
72 }
73 }
74}
75
76impl<Dev: Device> File<Dev> {
77 pub fn new(filesystem: &Ext2Fs<Dev>, inode_number: u32) -> Result<Self, Error<Ext2Error>> {
83 let fs = filesystem.lock();
84 let inode = Inode::parse(&fs, inode_number)?;
85 Ok(Self {
86 filesystem: filesystem.clone(),
87 inode_number,
88 inode,
89 io_offset: 0,
90 })
91 }
92
93 fn update_inner_inode(&mut self) -> Result<(), Error<Ext2Error>> {
95 let fs = self.filesystem.lock();
96 self.inode = Inode::parse(&fs, self.inode_number)?;
97 Ok(())
98 }
99
100 unsafe fn set_inode(&mut self, inode: &Inode) -> Result<(), Error<Ext2Error>> {
110 let fs = self.filesystem.lock();
111 unsafe { Inode::write_on_device(&fs, self.inode_number, *inode) }?;
112 drop(fs);
113
114 self.update_inner_inode()?;
115
116 Ok(())
117 }
118
119 pub fn truncate(&mut self, size: u64) -> Result<(), Error<Ext2Error>> {
125 if self.inode.data_size() <= size {
126 return Ok(());
127 }
128
129 let mut fs = self.filesystem.lock();
130
131 let mut new_inode = self.inode;
132 new_inode.size = unsafe { u32::try_from(u64::from(u32::MAX) & size).unwrap_unchecked() };
134
135 let time = fs.get_time();
136 new_inode.atime = time;
137 new_inode.mtime = time;
138
139 let kept_data_blocks_number = if size == 0 {
140 0
141 } else {
142 unsafe {
144 1 + u32::try_from(size.saturating_sub(1) / u64::from(fs.superblock().block_size())).unwrap_unchecked()
145 }
146 };
147 let indirection_blocks = self.inode.indirected_blocks(&fs)?;
148
149 let mut new_indirection_blocks = indirection_blocks.clone();
150 new_indirection_blocks.truncate_back_data_blocks(kept_data_blocks_number);
151
152 new_inode.blocks = (new_indirection_blocks.data_block_count()
153 + new_indirection_blocks.indirection_block_count())
154 * fs.superblock().block_size()
155 / 512;
156
157 let mut direct_block_pointers = new_inode.direct_block_pointers;
158 for i in 0..u32_to_usize(DIRECT_BLOCK_POINTER_COUNT) {
159 let block = unsafe { direct_block_pointers.get_mut(i).unwrap_unchecked() };
161 *block = new_indirection_blocks.direct_blocks.get(i).copied().unwrap_or_default();
162 }
163 new_inode.direct_block_pointers = direct_block_pointers;
164 new_inode.singly_indirect_block_pointer = new_indirection_blocks.singly_indirected_blocks.0;
165 new_inode.doubly_indirect_block_pointer = new_indirection_blocks.doubly_indirected_blocks.0;
166 new_inode.triply_indirect_block_pointer = new_indirection_blocks.triply_indirected_blocks.0;
167
168 let symmetrical_difference = indirection_blocks.truncate_front_data_blocks(kept_data_blocks_number);
169
170 let mut deallocated_blocks = symmetrical_difference.changed_data_blocks();
171 deallocated_blocks.append(
172 &mut symmetrical_difference
173 .changed_indirected_blocks()
174 .into_iter()
175 .map(|(_, (indirection_block, _))| indirection_block)
176 .collect_vec(),
177 );
178
179 unsafe {
181 Inode::write_on_device(&fs, self.inode_number, new_inode)?;
182 };
183
184 fs.deallocate_blocks(&deallocated_blocks)?;
185
186 drop(fs);
187
188 self.io_offset = 0;
189
190 self.update_inner_inode()
191 }
192
193 pub fn read_all(&mut self) -> Result<Vec<u8>, Error<Ext2Error>> {
201 let mut buffer = vec![0_u8; u64_to_usize(self.inode.data_size()).map_err(Error::from_infallible)?];
202 let previous_offset = self.seek(SeekFrom::Start(0))?;
203 self.read_exact(&mut buffer)?;
204 self.seek(SeekFrom::Start(previous_offset))?;
205 Ok(buffer)
206 }
207}
208
209impl<Dev: Device> file::Base for File<Dev> {
210 type FsError = Ext2Error;
211}
212
213impl<Dev: Device> file::FileRead for File<Dev> {
214 fn stat(&self) -> file::Stat {
215 let filesystem = self.filesystem.lock();
216
217 Stat {
218 dev: crate::fs::types::Dev(filesystem.device_id),
219 ino: Ino(u64::from(self.inode_number)),
220 mode: Mode(self.inode.mode),
221 nlink: Nlink(u32::from(self.inode.links_count)),
222 uid: Uid(self.inode.uid.into()),
223 gid: Gid(self.inode.gid.into()),
224 rdev: crate::fs::types::Dev::default(),
225 size: Off(self.inode.data_size().try_into().unwrap_or_default()),
226 atim: Timespec {
227 tv_sec: Time(self.inode.atime.into()),
228 tv_nsec: u32::default(),
229 },
230 mtim: Timespec {
231 tv_sec: Time(self.inode.mtime.into()),
232 tv_nsec: u32::default(),
233 },
234 ctim: Timespec {
235 tv_sec: Time(self.inode.ctime.into()),
236 tv_nsec: u32::default(),
237 },
238 blksize: Blksize(unsafe { u32_to_usize(filesystem.superblock.block_size()).try_into().unwrap_unchecked() }),
240 blkcnt: Blkcnt(self.inode.blocks.into()),
241 }
242 }
243
244 fn get_type(&self) -> file::Type {
245 self.inode.file_type().unwrap_or_else(|_| {
246 panic!(
247 "The inner inode with number {} is in an incoherent state: its file type is not valid",
248 self.inode_number
249 )
250 })
251 }
252}
253
254impl<Dev: Device> file::File for File<Dev> {
255 fn set_mode(&mut self, mode: Mode) -> Result<(), Error<Self::FsError>> {
256 let mut new_inode = self.inode;
257 new_inode.mode = *mode | self.inode.type_permissions().file_type().bits();
258 unsafe { self.set_inode(&new_inode) }
260 }
261
262 fn set_uid(&mut self, uid: Uid) -> Result<(), Error<Self::FsError>> {
263 let mut new_inode = self.inode;
264 new_inode.uid = TryInto::<u16>::try_into(uid.0)
265 .map_err(|_| Error::Fs(FsError::Implementation(Ext2Error::UidTooLarge(uid.0))))?;
266 unsafe { self.set_inode(&new_inode) }
268 }
269
270 fn set_gid(&mut self, gid: Gid) -> Result<(), Error<Self::FsError>> {
271 let mut new_inode = self.inode;
272 new_inode.gid = TryInto::<u16>::try_into(gid.0)
273 .map_err(|_| Error::Fs(FsError::Implementation(Ext2Error::GidTooLarge(gid.0))))?;
274 unsafe { self.set_inode(&new_inode) }
276 }
277
278 fn set_atim(&mut self, atim: Timespec) -> Result<(), Error<Self::FsError>> {
279 let mut new_inode = self.inode;
280 new_inode.atime = unsafe { u32::try_from(*atim.tv_sec % i64::from(u32::MAX)).unwrap_unchecked() };
282 unsafe { self.set_inode(&new_inode) }
284 }
285
286 fn set_mtim(&mut self, mtim: Timespec) -> Result<(), Error<Self::FsError>> {
287 let mut new_inode = self.inode;
288 new_inode.mtime = unsafe { u32::try_from(*mtim.tv_sec % i64::from(u32::MAX)).unwrap_unchecked() };
290 unsafe { self.set_inode(&new_inode) }
292 }
293
294 fn set_ctim(&mut self, ctim: Timespec) -> Result<(), Error<Self::FsError>> {
295 let mut new_inode = self.inode;
296 new_inode.ctime = unsafe { u32::try_from(*ctim.tv_sec % i64::from(u32::MAX)).unwrap_unchecked() };
298 unsafe { self.set_inode(&new_inode) }
300 }
301}
302
303macro_rules! impl_file {
304 ($id:ident) => {
305 impl<Dev: Device> crate::fs::file::Base for $id<Dev> {
306 type FsError = Ext2Error;
307 }
308
309 impl<Dev: Device> crate::fs::file::FileRead for $id<Dev> {
310 fn stat(&self) -> Stat {
311 self.file.stat()
312 }
313
314 fn get_type(&self) -> file::Type {
315 self.file.get_type()
316 }
317 }
318
319 impl<Dev: Device> crate::fs::file::File for $id<Dev> {
320 fn set_mode(&mut self, mode: Mode) -> Result<(), Error<Self::FsError>> {
321 self.file.set_mode(mode)
322 }
323
324 fn set_uid(&mut self, uid: Uid) -> Result<(), Error<Self::FsError>> {
325 self.file.set_uid(uid)
326 }
327
328 fn set_gid(&mut self, gid: Gid) -> Result<(), Error<Self::FsError>> {
329 self.file.set_gid(gid)
330 }
331
332 fn set_atim(&mut self, atim: Timespec) -> Result<(), Error<Self::FsError>> {
333 self.file.set_atim(atim)
334 }
335
336 fn set_mtim(&mut self, mtim: Timespec) -> Result<(), Error<Self::FsError>> {
337 self.file.set_mtim(mtim)
338 }
339
340 fn set_ctim(&mut self, ctim: Timespec) -> Result<(), Error<Self::FsError>> {
341 self.file.set_ctim(ctim)
342 }
343 }
344 };
345}
346
347impl<Dev: Device> Read for File<Dev> {
348 fn read(&mut self, buf: &mut [u8]) -> deku::no_std_io::Result<usize> {
349 let filesystem = self.filesystem.lock();
350 let bytes = self
351 .inode
352 .read_data(&filesystem, buf, self.io_offset)
353 .inspect(|&bytes| {
354 self.io_offset += usize_to_u64(bytes);
355 })
356 .map_err(|err| deku::no_std_io::Error::new(deku::no_std_io::ErrorKind::InvalidData, err.to_string()))?;
357
358 let mut device = filesystem.device.lock();
359 if let Some(now) = device.now() {
360 drop(device);
361
362 let mut new_inode = self.inode;
363 new_inode.atime = unsafe { (now.tv_sec.0 & i64::from(u32::MAX)).try_into().unwrap_unchecked() };
365 unsafe {
367 Inode::write_on_device(&filesystem, self.inode_number, new_inode).map_err(|err| {
368 deku::no_std_io::Error::new(deku::no_std_io::ErrorKind::InvalidData, err.to_string())
369 })?;
370 };
371 }
372
373 Ok(bytes)
374 }
375}
376
377impl<Dev: Device> Write for File<Dev> {
378 #[allow(clippy::too_many_lines)]
379 fn write(&mut self, buf: &[u8]) -> deku::no_std_io::Result<usize> {
380 let mut fs = self.filesystem.lock();
381 let superblock = fs.superblock().clone();
382 let block_size = u64::from(fs.superblock().block_size());
383
384 let buf_len = usize_to_u64(buf.len());
385 if buf_len > fs.superblock().max_file_size() {
386 return Err(deku::no_std_io::Error::new(
387 deku::no_std_io::ErrorKind::InvalidInput,
388 "Tried to read a file from a buffer with a length greater than the max file length",
389 ));
390 }
391
392 let bytes_to_write = buf_len;
394 let data_blocks_needed =
395 1 + unsafe { u32::try_from((bytes_to_write + self.io_offset - 1) / block_size).unwrap_unchecked() };
397
398 if !fs.options().large_files && u64::from(data_blocks_needed) * block_size >= u64::from(u32::MAX) {
399 return Err(deku::no_std_io::Error::new(
400 deku::no_std_io::ErrorKind::InvalidInput,
401 "Tried to write a large file while the filesystem does not have the required feature set",
402 ));
403 }
404
405 let mut indirected_blocks = self.inode.indirected_blocks(&fs)?;
406 indirected_blocks.truncate_back_data_blocks(unsafe {
408 1 + u32::try_from((self.inode.data_size().max(1) - 1) / block_size).unwrap_unchecked()
410 });
411
412 let current_data_block_count = indirected_blocks.data_block_count();
413 let data_blocks_to_request = data_blocks_needed.saturating_sub(current_data_block_count);
414
415 let current_indirection_block_count = indirected_blocks.indirection_block_count();
416 let indirection_blocks_to_request =
417 IndirectedBlocks::<DIRECT_BLOCK_POINTER_COUNT>::necessary_indirection_block_count(
418 data_blocks_needed,
419 fs.superblock().base().block_size() / 4,
420 ) - current_indirection_block_count;
421
422 let start_block_group = indirected_blocks
423 .last_data_block_allocated()
424 .map(|(block, _)| superblock.block_group(block))
425 .unwrap_or_default();
426
427 let free_blocks =
428 fs.free_blocks_offset(data_blocks_to_request + indirection_blocks_to_request, start_block_group)?;
429
430 fs.allocate_blocks(&free_blocks)?;
431
432 drop(fs);
433
434 let (new_indirected_blocks, changed_blocks) = indirected_blocks.append_blocks_with_difference(
435 &free_blocks,
436 Some(unsafe { u32::try_from(self.io_offset / u64::from(superblock.block_size())).unwrap_unchecked() }),
438 );
439
440 for (starting_index, (indirection_block, blocks)) in changed_blocks.changed_indirected_blocks() {
441 let mut block = Block::new(self.filesystem.clone(), indirection_block);
442 if starting_index != 0 {
443 block.seek(SeekFrom::Start(usize_to_u64(starting_index)))?;
444 }
445
446 block.write_all(unsafe { &*slice_from_raw_parts(blocks.as_ptr().cast::<u8>(), blocks.len() * 4) })?;
448 }
449
450 let mut written_bytes = 0_usize;
451
452 let changed_data_blocks = changed_blocks.changed_data_blocks();
453 let changed_data_blocks_iterator = &mut changed_data_blocks.iter();
454
455 if let Some(block_number) = changed_data_blocks_iterator.next() {
456 let mut block = Block::new(self.filesystem.clone(), *block_number);
457 block.seek(SeekFrom::Start(self.io_offset % u64::from(superblock.block_size())))?;
458 written_bytes += block.write(buf)?;
459 }
460
461 for block_number in changed_data_blocks_iterator {
462 let mut block = Block::new(self.filesystem.clone(), *block_number);
463 let Some(buffer_end) = buf.get(
464 written_bytes
465 ..written_bytes
466 + u32_to_usize(superblock.base().blocks_per_group)
467 .min(u64_to_usize(buf_len).map_err(Error::<Ext2Error>::from_infallible)? - written_bytes),
468 ) else {
469 break;
470 };
471
472 let new_written_bytes = block.write(buffer_end)?;
473 if new_written_bytes == 0 {
474 break;
475 }
476 written_bytes += new_written_bytes;
477 }
478
479 let mut updated_inode = self.inode;
480
481 let total_block_used =
482 new_indirected_blocks.data_block_count() + new_indirected_blocks.indirection_block_count();
483 let (
484 mut direct_block_pointers,
485 singly_indirected_block_pointer,
486 doubly_indirected_block_pointer,
487 triply_indirected_block_pointer,
488 ) = new_indirected_blocks.blocks();
489
490 direct_block_pointers
491 .append(&mut vec![0_u32; 12].into_iter().take(12 - direct_block_pointers.len()).collect_vec());
492
493 let mut updated_direct_block_pointers = updated_inode.direct_block_pointers;
494 updated_direct_block_pointers.clone_from_slice(&direct_block_pointers);
495 updated_inode.direct_block_pointers = updated_direct_block_pointers;
496
497 updated_inode.singly_indirect_block_pointer = singly_indirected_block_pointer.0;
498 updated_inode.doubly_indirect_block_pointer = doubly_indirected_block_pointer.0;
499 updated_inode.triply_indirect_block_pointer = triply_indirected_block_pointer.0;
500
501 let new_size = self.inode.data_size().max(self.io_offset + buf_len);
502
503 updated_inode.size = unsafe { u32::try_from(new_size & u64::from(u32::MAX)).unwrap_unchecked() };
505 updated_inode.blocks = (total_block_used * self.filesystem.lock().superblock().block_size()) / 512;
506
507 updated_inode.dir_acl = unsafe { u32::try_from((new_size >> 32) & u64::from(u32::MAX)).unwrap_unchecked() };
509
510 let fs = self.filesystem.lock();
511
512 let time = fs.get_time();
513 updated_inode.atime = time;
514 updated_inode.mtime = time;
515
516 drop(fs);
517
518 unsafe { self.set_inode(&updated_inode) }?;
520
521 self.seek(SeekFrom::Current(i64::try_from(buf_len).expect("Could not fit the buffer length on an i64")))?;
522
523 Ok(written_bytes)
524 }
525
526 fn flush(&mut self) -> deku::no_std_io::Result<()> {
527 Ok(())
528 }
529}
530
531impl<Dev: Device> Seek for File<Dev> {
532 fn seek(&mut self, pos: SeekFrom) -> deku::no_std_io::Result<u64> {
533 let file_length = unsafe { i64::try_from(self.inode.data_size()).unwrap_unchecked() };
535
536 let previous_offset = self.io_offset;
537 match pos {
538 SeekFrom::Start(offset) => self.io_offset = offset,
539 SeekFrom::End(back_offset) => {
540 self.io_offset = TryInto::<u64>::try_into(file_length + back_offset).map_err(|_err| {
541 deku::no_std_io::Error::new(
542 deku::no_std_io::ErrorKind::InvalidInput,
543 "Invalid seek to a negative or overflowing position",
544 )
545 })?;
546 },
547 SeekFrom::Current(add_offset) => {
548 self.io_offset = (unsafe { TryInto::<i64>::try_into(previous_offset).unwrap_unchecked() } + add_offset)
550 .try_into()
551 .map_err(|_err| {
552 deku::no_std_io::Error::new(
553 deku::no_std_io::ErrorKind::InvalidInput,
554 "Invalid seek to a negative or overflowing position",
555 )
556 })?;
557 },
558 }
559
560 if self.io_offset > self.inode.data_size() {
561 Err(deku::no_std_io::Error::new(
562 deku::no_std_io::ErrorKind::InvalidInput,
563 "Invalid seek to a negative or overflowing position",
564 ))
565 } else {
566 Ok(previous_offset)
567 }
568 }
569}
570
571#[derive(Debug)]
573pub struct Regular<Dev: Device> {
574 file: File<Dev>,
576}
577
578impl<Dev: Device> Regular<Dev> {
579 pub fn new(filesystem: &Ext2Fs<Dev>, inode_number: u32) -> Result<Self, Error<Ext2Error>> {
585 Ok(Self {
586 file: File::new(&filesystem.clone(), inode_number)?,
587 })
588 }
589
590 pub fn read_all(&mut self) -> Result<Vec<u8>, Error<Ext2Error>> {
598 self.file.read_all()
599 }
600}
601
602impl<Dev: Device> Clone for Regular<Dev> {
603 fn clone(&self) -> Self {
604 Self {
605 file: self.file.clone(),
606 }
607 }
608}
609
610impl_file!(Regular);
611
612impl<Dev: Device> Read for Regular<Dev> {
613 fn read(&mut self, buf: &mut [u8]) -> deku::no_std_io::Result<usize> {
614 self.file.read(buf)
615 }
616}
617
618impl<Dev: Device> Write for Regular<Dev> {
619 fn write(&mut self, buf: &[u8]) -> deku::no_std_io::Result<usize> {
620 self.file.write(buf)
621 }
622
623 fn flush(&mut self) -> deku::no_std_io::Result<()> {
624 self.file.flush()
625 }
626}
627
628impl<Dev: Device> Seek for Regular<Dev> {
629 fn seek(&mut self, pos: SeekFrom) -> deku::no_std_io::Result<u64> {
630 self.file.seek(pos)
631 }
632}
633
634impl<Dev: Device> file::RegularRead for Regular<Dev> {}
635
636impl<Dev: Device> file::Regular for Regular<Dev> {
637 fn truncate(&mut self, size: u64) -> Result<(), Error<Self::FsError>> {
638 self.file.truncate(size)
639 }
640}
641
642#[derive(Debug)]
647pub struct Directory<Dev: Device> {
648 file: File<Dev>,
650
651 entries: Mutex<Vec<Vec<Entry>>>,
655}
656
657impl<Dev: Device> Directory<Dev> {
658 pub fn new(filesystem: &Ext2Fs<Dev>, inode_number: u32) -> Result<Self, Error<Ext2Error>> {
664 let file = File::new(filesystem, inode_number)?;
665 let entries = Mutex::new(Self::parse(&file)?);
666
667 Ok(Self { file, entries })
668 }
669
670 fn parse(file: &File<Dev>) -> Result<Vec<Vec<Entry>>, Error<Ext2Error>> {
676 let fs = file.filesystem.lock();
677
678 let block_size = u64::from(fs.superblock().block_size());
679 let data_size = file.inode.data_size();
680 let data_blocks = 1 + (data_size - 1) / block_size;
681
682 let mut indirected_blocks = file.inode.indirected_blocks(&fs)?;
683 indirected_blocks.truncate_back_data_blocks(unsafe { u32::try_from(data_blocks).unwrap_unchecked() });
685
686 let mut entries = Vec::new();
687
688 for block in indirected_blocks.flatten_data_blocks() {
689 let mut entries_in_block = Vec::new();
690 let mut accumulated_size = 0_u64;
691 while accumulated_size < block_size {
692 let starting_addr = Address::from(
693 u64_to_usize(u64::from(block) * block_size + accumulated_size).map_err(Error::from_infallible)?,
694 );
695
696 let entry = Entry::parse(&fs, starting_addr)?;
697 accumulated_size += u64::from(entry.rec_len);
698 entries_in_block.push(entry);
699 }
700 entries.push(entries_in_block);
701 }
702
703 Ok(entries)
704 }
705
706 fn update_inner_entries(&self) -> Result<(), Error<Ext2Error>> {
712 *self.entries.lock() = Self::parse(&self.file)?;
713 Ok(())
714 }
715
716 unsafe fn write_block_entry(&mut self, block_index: usize) -> Result<(), Error<Ext2Error>> {
726 let block_size = u64::from(self.file.filesystem.lock().superblock().block_size());
727 self.file.seek(SeekFrom::Start(usize_to_u64(block_index) * block_size))?;
728
729 let mut buffer = Vec::new();
730 let entries = self.entries.lock();
731 for entry in unsafe { entries.get_unchecked(block_index) } {
732 buffer.append(&mut entry.as_bytes().clone());
733 buffer.append(&mut vec![0_u8; u32_to_usize(entry.free_space().into())]);
734 }
735 self.file.write_all(&buffer)?;
736
737 Ok(())
738 }
739
740 unsafe fn write_all_entries(&mut self) -> Result<(), Error<Ext2Error>> {
750 self.file.truncate(0)?;
751 let nb_entries = self.entries.lock().len();
752 for block_index in 0..nb_entries {
753 unsafe { self.write_block_entry(block_index) }?;
754 }
755 Ok(())
756 }
757
758 fn defragment(&self) {
760 let block_size = u16::try_from(self.file.filesystem.lock().superblock().block_size())
761 .expect("Ill-formed superblock: block size should be castable in a u16");
762
763 let mut new_entries = Vec::new();
764
765 let mut entries_in_block = Vec::<Entry>::new();
766 let mut accumulated_size = 0_u16;
767 for mut entry in self.entries.lock().clone().into_iter().flatten() {
768 if accumulated_size + entry.minimal_size() > block_size {
769 if let Some(ent) = entries_in_block.last_mut() {
770 ent.rec_len = block_size - accumulated_size;
771 }
772 new_entries.push(entries_in_block);
773 accumulated_size = 0;
774 entries_in_block = Vec::new();
775 }
776 entry.rec_len = entry.minimal_size();
777 accumulated_size += entry.minimal_size();
778 entries_in_block.push(entry);
779 }
780
781 if let Some(ent) = entries_in_block.last_mut() {
782 ent.rec_len = block_size - accumulated_size;
783 new_entries.push(entries_in_block);
784 }
785
786 *self.entries.lock() = new_entries;
787 }
788
789 fn find_space(&self, necessary_space: u16) -> Option<(usize, usize)> {
791 for (block_index, entries_in_block) in self.entries.lock().iter().enumerate() {
792 for (entry_index, entry) in entries_in_block.iter().enumerate() {
793 if entry.free_space() >= necessary_space {
794 return Some((block_index, entry_index));
795 }
796 }
797 }
798
799 None
800 }
801}
802
803impl<Dev: Device> Clone for Directory<Dev> {
804 fn clone(&self) -> Self {
805 Self {
806 file: self.file.clone(),
807 entries: Mutex::new(self.entries.lock().clone()),
808 }
809 }
810}
811
812impl_file!(Directory);
813
814impl<Dev: Device> file::DirectoryRead for Directory<Dev> {
815 type BlockDevice = BlockDevice<Dev>;
816 type CharacterDevice = CharacterDevice<Dev>;
817 type Fifo = Fifo<Dev>;
818 type Regular = Regular<Dev>;
819 type Socket = Socket<Dev>;
820 type SymbolicLink = SymbolicLink<Dev>;
821
822 fn entries(&self) -> Result<Vec<file::DirectoryEntry<'_, Self>>, Error<Ext2Error>> {
823 let mut entries = Vec::new();
824
825 self.update_inner_entries()?;
826
827 for entry in self.entries.lock().iter().flatten() {
828 entries.push(DirectoryEntry {
829 filename: entry
830 .name
831 .clone()
832 .try_into()
833 .unwrap_or_else(|_| panic!("The entry with name {:?} is not a valid UTF-8 sequence", entry.name)),
834 file: self.file.filesystem.file(entry.inode)?,
835 });
836 }
837
838 Ok(entries)
839 }
840}
841
842impl<Dev: Device> file::Directory for Directory<Dev> {
843 fn add_entry(
844 &mut self,
845 name: UnixStr<'_>,
846 file_type: Type,
847 permissions: Permissions,
848 user_id: Uid,
849 group_id: Gid,
850 ) -> Result<TypeWithFile<Self>, Error<Self::FsError>> {
851 if let Ok(file) = self.entry(name.clone())
852 && file.is_some()
853 {
854 return Err(Error::Fs(FsError::EntryAlreadyExist(name.to_string())));
855 }
856
857 let mut fs = self.file.filesystem.lock();
858 let block_size = fs.superblock().block_size();
859
860 let inode_number = fs.free_inode()?;
861 fs.allocate_inode(
862 inode_number,
863 TypePermissions::from(permissions) | TypePermissions::from(file_type),
864 user_id
865 .0
866 .try_into()
867 .map_err(|_| Error::Fs(FsError::Implementation(Ext2Error::UidTooLarge(user_id.0))))?,
868 group_id
869 .0
870 .try_into()
871 .map_err(|_| Error::Fs(FsError::Implementation(Ext2Error::GidTooLarge(group_id.0))))?,
872 Flags::empty(),
873 0,
874 [0; 12],
875 )?;
876
877 let file_type_feature = fs.options.file_type;
878
879 drop(fs);
880
881 if file_type == Type::Directory {
882 let mut dir = File::new(&self.file.filesystem, inode_number)?;
883 let self_and_parent = [
884 &Entry {
885 inode: inode_number,
886 rec_len: 9,
887 name_len: 1,
888 file_type: if file_type_feature { u8::from(FileType::Dir) } else { 0 },
889 name: unsafe { CString::from_vec_unchecked(vec![b'.']) },
891 },
892 &Entry {
893 inode: self.file.inode_number,
894 rec_len: u16::try_from(block_size - 9)
895 .expect("Ill-formed superblock: block size should be castable in a u16"),
896 name_len: 2,
897 file_type: if file_type_feature { u8::from(FileType::Dir) } else { 0 },
898 name: unsafe { CString::from_vec_unchecked(vec![b'.', b'.']) },
900 },
901 ];
902
903 let self_and_parent_bytes = self_and_parent.map(Entry::as_bytes).concat();
904 dir.seek(SeekFrom::Start(0))?;
905 dir.write_all(&self_and_parent_bytes)?;
906 dir.flush()?;
907 }
908
909 let mut new_entry = Entry {
910 inode: inode_number,
911 rec_len: 0,
912 name_len: u8::try_from(name.to_string().len())
913 .map_err(|_err| Error::Fs(FsError::Implementation(Ext2Error::NameTooLong(name.to_string()))))?,
914 file_type: directory::FileType::from(file_type).into(),
915 name: name.into(),
916 };
917 new_entry.rec_len = new_entry.minimal_size();
918 if let Some((block_index, entry_index)) = self.find_space(new_entry.minimal_size()) {
919 let mut self_entries = self.entries.lock();
920 let entries_in_block = unsafe { self_entries.get_unchecked_mut(block_index) };
922 let previous_entry = unsafe { entries_in_block.get_unchecked_mut(entry_index) };
924
925 new_entry.rec_len = previous_entry.rec_len - previous_entry.minimal_size();
926 previous_entry.rec_len = previous_entry.minimal_size();
927
928 entries_in_block.insert(entry_index + 1, new_entry);
929 drop(self_entries);
930
931 unsafe { self.write_block_entry(block_index) }?;
933 } else {
934 self.entries.lock().push(vec![new_entry]);
935 self.defragment();
936 unsafe { self.write_all_entries() }?;
938 }
939
940 let fs = self.file.filesystem.lock();
941 let mut new_inode = fs.inode(inode_number)?;
942
943 let time = fs.get_time();
944 new_inode.atime = time;
945 new_inode.mtime = time;
946 new_inode.ctime = time;
947
948 unsafe { Inode::write_on_device(&fs, inode_number, new_inode)? };
949 drop(fs);
950
951 self.file.filesystem.file(inode_number)
952 }
953
954 fn remove_entry(&mut self, entry_name: crate::path::UnixStr) -> Result<(), Error<Self::FsError>> {
955 if entry_name == *CUR_DIR || entry_name == *PARENT_DIR {
956 return Err(Error::Fs(FsError::RemoveRefused));
957 }
958
959 let block_size = u64::from(self.file.filesystem.lock().superblock().block_size());
960 let entry_name_cstring = Into::<CString>::into(entry_name.clone());
961 let entries_clone = self.entries.lock().clone();
962 for (block_index, entries_in_block) in entries_clone.into_iter().enumerate() {
963 for (index, entry) in entries_in_block.clone().into_iter().enumerate() {
964 if entry.name == entry_name_cstring {
965 let mut self_entries = self.entries.lock();
966 let block = unsafe { self_entries.get_unchecked_mut(block_index) };
968 block.remove(index);
969
970 if index > 0
972 && let Some(previous_entry) = block.get_mut(index - 1)
973 {
974 previous_entry.rec_len += entry.rec_len;
975 }
976 else if let Some(next_entry) = block.get_mut(index) {
978 next_entry.rec_len += entry.rec_len;
979 }
980 else {
982 self.entries.lock().remove(block_index);
983 let nb_entries = self.entries.lock().len();
984 self.file.truncate(block_size * usize_to_u64(nb_entries))?;
985 }
986
987 let block_len = block.len();
988 drop(self_entries);
989 if index > 0 || block_len > 0 {
990 unsafe { self.write_block_entry(block_index) }?;
992 } else {
993 let nb_entries = self.entries.lock().len();
994 for i in block_index..nb_entries {
995 unsafe { self.write_block_entry(i) }?;
997 }
998 }
999
1000 if let TypeWithFile::Directory(mut dir) = self.file.filesystem.file(entry.inode)? {
1001 let mut new_inode = self.file.inode;
1002 new_inode.links_count -= 1;
1003 let sub_entries = dir.entries.lock().clone().into_iter().flatten().collect_vec();
1004 for sub_entry in sub_entries {
1005 let sub_entry_name: UnixStr<'_> = sub_entry.name.clone().try_into().unwrap_or_else(|_| {
1006 panic!("The entry with name {:?} is not a valid UTF-8 sequence", sub_entry.name)
1007 });
1008 if sub_entry_name != *CUR_DIR && sub_entry_name != *PARENT_DIR {
1009 dir.remove_entry(sub_entry_name.clone())?;
1010 }
1011 }
1012
1013 unsafe {
1016 self.file.set_inode(&new_inode)?;
1017 };
1018 }
1019
1020 let mut fs = self.file.filesystem.lock();
1021 return fs.deallocate_inode(entry.inode);
1022 }
1023 }
1024 }
1025
1026 Err(Error::Fs(FsError::NotFound(entry_name.to_string())))
1027 }
1028}
1029
1030#[derive(Debug)]
1032pub struct SymbolicLink<Dev: Device> {
1033 file: File<Dev>,
1035
1036 pointed_file: String,
1038}
1039
1040impl<Dev: Device> SymbolicLink<Dev> {
1041 pub fn new(filesystem: &Ext2Fs<Dev>, inode_number: u32) -> Result<Self, Error<Ext2Error>> {
1052 let file = File::new(&filesystem.clone(), inode_number)?;
1053
1054 let data_size = usize::try_from(file.inode.data_size()).unwrap_or(PATH_MAX);
1055
1056 let mut buffer = vec![0_u8; data_size];
1057
1058 if data_size < SYMBOLIC_LINK_INODE_STORE_LIMIT {
1059 buffer.clone_from_slice(unsafe {
1061 core::slice::from_raw_parts(addr_of!(file.inode.direct_block_pointers).cast(), data_size)
1062 });
1063 } else {
1064 let _: usize = file.inode.read_data(&filesystem.lock(), &mut buffer, 0)?;
1065 }
1066 let pointed_file = buffer
1067 .split(|char| *char == b'\0')
1068 .next()
1069 .ok_or(Ext2Error::BadString)
1070 .map_err(FsError::Implementation)?
1071 .to_vec();
1072 Ok(Self {
1073 file,
1074 pointed_file: String::from_utf8(pointed_file)
1075 .map_err(|_err| Ext2Error::BadString)
1076 .map_err(FsError::Implementation)?,
1077 })
1078 }
1079}
1080
1081impl<Dev: Device> Clone for SymbolicLink<Dev> {
1082 fn clone(&self) -> Self {
1083 Self {
1084 file: self.file.clone(),
1085 pointed_file: self.pointed_file.clone(),
1086 }
1087 }
1088}
1089
1090impl_file!(SymbolicLink);
1091
1092impl<Dev: Device> file::SymbolicLinkRead for SymbolicLink<Dev> {
1093 fn get_pointed_file(&self) -> Result<&str, Error<Self::FsError>> {
1094 Ok(&self.pointed_file)
1095 }
1096}
1097
1098impl<Dev: Device> file::SymbolicLink for SymbolicLink<Dev> {
1099 fn set_pointed_file(&mut self, pointed_file: &str) -> Result<(), Error<Self::FsError>> {
1100 let bytes = pointed_file.as_bytes();
1101
1102 if bytes.len() > PATH_MAX {
1103 return Err(Error::Fs(FsError::NameTooLong(pointed_file.to_owned())));
1104 } else if bytes.len() > SYMBOLIC_LINK_INODE_STORE_LIMIT {
1105 if self.pointed_file.len() <= SYMBOLIC_LINK_INODE_STORE_LIMIT {
1106 let mut new_inode = self.file.inode;
1107
1108 let data_ptr = addr_of_mut!(new_inode.direct_block_pointers).cast::<u8>();
1109 let data_slice = unsafe { core::slice::from_raw_parts_mut(data_ptr, SYMBOLIC_LINK_INODE_STORE_LIMIT) };
1111 data_slice.clone_from_slice(&[b'\0'; SYMBOLIC_LINK_INODE_STORE_LIMIT]);
1112
1113 let fs = self.file.filesystem.lock();
1114 unsafe {
1116 Inode::write_on_device(&fs, self.file.inode_number, new_inode)?;
1117 };
1118
1119 drop(fs);
1120
1121 self.file.update_inner_inode()?;
1122 }
1123
1124 self.file.seek(SeekFrom::Start(0))?;
1125 self.file.write_all(bytes)?;
1126 self.file.truncate(usize_to_u64(bytes.len()))?;
1127 } else {
1128 self.file.truncate(0)?;
1129
1130 let mut new_inode = self.file.inode;
1131 new_inode.size = unsafe { u32::try_from(bytes.len()).unwrap_unchecked() };
1133
1134 let data_ptr = addr_of_mut!(new_inode.direct_block_pointers).cast::<u8>();
1135 let data_slice = unsafe { core::slice::from_raw_parts_mut(data_ptr, bytes.len()) };
1137 data_slice.clone_from_slice(bytes);
1138
1139 let fs = self.file.filesystem.lock();
1140 unsafe {
1142 Inode::write_on_device(&fs, self.file.inode_number, new_inode)?;
1143 };
1144 drop(fs);
1145
1146 self.file.update_inner_inode()?;
1147 }
1148
1149 pointed_file.clone_into(&mut self.pointed_file);
1150 Ok(())
1151 }
1152}
1153
1154macro_rules! generic_file {
1155 ($id:ident) => {
1156 #[doc = concat!("Basic implementation of a [`", stringify!($id), "`](crate::fs::file::", stringify!($id), ") for ext2.")]
1157 #[derive(Debug)]
1158 pub struct $id<Dev: Device> {
1159 file: File<Dev>,
1161 }
1162
1163 impl<Dev: Device> Clone for $id<Dev> {
1164 fn clone(&self) -> Self {
1165 Self {
1166 file: self.file.clone(),
1167 }
1168 }
1169 }
1170
1171 impl_file!($id);
1172
1173 impl<Dev: Device> $id<Dev> {
1174 #[doc = concat!("Returns a new ext2's [`", stringify!($id), "`] from an [`Ext2Fs`] instance and the inode number of the file.")]
1175 pub fn new(filesystem: &Ext2Fs<Dev>, inode_number: u32) -> Result<Self, Error<Ext2Error>> {
1180 Ok(Self { file: File::new(filesystem, inode_number)? })
1181 }
1182 }
1183
1184 impl<Dev: Device> crate::fs::file::$id for $id<Dev> {}
1185 impl<Dev: Device> crate::fs::file::${concat($id, Read)} for $id<Dev> {}
1186 };
1187}
1188
1189generic_file!(Fifo);
1190generic_file!(CharacterDevice);
1191generic_file!(BlockDevice);
1192generic_file!(Socket);
1193
1194#[cfg(test)]
1195mod test {
1196 use alloc::string::{String, ToString};
1197 use alloc::vec;
1198 use alloc::vec::Vec;
1199 use core::time::Duration;
1200 use std::fs::File;
1201
1202 use deku::no_std_io::{Read, Seek, SeekFrom, Write};
1203 use itertools::Itertools;
1204
1205 use crate::arch::usize_to_u64;
1206 use crate::dev::address::Address;
1207 use crate::fs::FilesystemRead;
1208 use crate::fs::ext2::directory::Entry;
1209 use crate::fs::ext2::file::Directory;
1210 use crate::fs::ext2::inode::{Inode, ROOT_DIRECTORY_INODE, TypePermissions};
1211 use crate::fs::ext2::{Ext2, Ext2Fs};
1212 use crate::fs::file::{DirectoryRead, FileRead, Regular, SymbolicLink, SymbolicLinkRead, Type, TypeWithFile};
1213 use crate::fs::permissions::Permissions;
1214 use crate::fs::types::{Gid, Mode, Uid};
1215 use crate::path::{Path, UnixStr};
1216 use crate::tests::{LOREM, LOREM_LENGTH, new_device_id};
1217
1218 fn parse_root(file: File) {
1219 let ext2 = Ext2Fs::new(file, new_device_id()).unwrap();
1220 let root = Directory::new(&ext2, ROOT_DIRECTORY_INODE).unwrap();
1221 assert_eq!(
1222 root.entries
1223 .lock()
1224 .clone()
1225 .into_iter()
1226 .flatten()
1227 .map(|entry| entry.name.to_string_lossy().to_string())
1228 .collect::<Vec<String>>(),
1229 vec![".", "..", "lost+found", "big_file", "symlink"]
1230 );
1231 }
1232
1233 fn parse_root_entries(file: File) {
1234 let fs = Ext2::new(file, new_device_id()).unwrap();
1235 let root_inode = Inode::parse(&fs, ROOT_DIRECTORY_INODE).unwrap();
1236
1237 let dot = Entry::parse(
1238 &fs,
1239 Address::new(u64::from(root_inode.direct_block_pointers[0]) * u64::from(fs.superblock().block_size())),
1240 )
1241 .unwrap();
1242 let two_dots = Entry::parse(
1243 &fs,
1244 Address::new(
1245 u64::from(root_inode.direct_block_pointers[0]) * u64::from(fs.superblock().block_size())
1246 + u64::from(dot.rec_len),
1247 ),
1248 )
1249 .unwrap();
1250 let lost_and_found = Entry::parse(
1251 &fs,
1252 Address::new(
1253 (u64::from(root_inode.direct_block_pointers[0]) * u64::from(fs.superblock().block_size()))
1254 + u64::from(dot.rec_len + two_dots.rec_len),
1255 ),
1256 )
1257 .unwrap();
1258 let big_file = Entry::parse(
1259 &fs,
1260 Address::new(
1261 (u64::from(root_inode.direct_block_pointers[0]) * u64::from(fs.superblock().block_size()))
1262 + u64::from(dot.rec_len + two_dots.rec_len + lost_and_found.rec_len),
1263 ),
1264 )
1265 .unwrap();
1266 let symlink = Entry::parse(
1267 &fs,
1268 Address::new(
1269 (u64::from(root_inode.direct_block_pointers[0]) * u64::from(fs.superblock().block_size()))
1270 + u64::from(dot.rec_len + two_dots.rec_len + lost_and_found.rec_len + big_file.rec_len),
1271 ),
1272 )
1273 .unwrap();
1274
1275 assert_eq!(dot.name.as_c_str().to_string_lossy(), ".");
1276 assert_eq!(two_dots.name.as_c_str().to_string_lossy(), "..");
1277 assert_eq!(lost_and_found.name.as_c_str().to_string_lossy(), "lost+found");
1278 assert_eq!(big_file.name.as_c_str().to_string_lossy(), "big_file");
1279 assert_eq!(symlink.name.as_c_str().to_string_lossy(), "symlink");
1280 }
1281
1282 fn parse_big_file_inode_data(file: File) {
1283 let ext2 = Ext2Fs::new(file, new_device_id()).unwrap();
1284 let root = Directory::new(&ext2, ROOT_DIRECTORY_INODE).unwrap();
1285
1286 let fs = ext2.lock();
1287 let big_file_inode_number = root
1288 .entries
1289 .lock()
1290 .iter()
1291 .flatten()
1292 .find(|entry| entry.name.to_string_lossy() == "big_file")
1293 .unwrap()
1294 .inode;
1295 let big_file_inode = fs.inode(big_file_inode_number).unwrap();
1296
1297 let singly_indirect_block_pointer = big_file_inode.singly_indirect_block_pointer;
1298 let doubly_indirect_block_pointer = big_file_inode.doubly_indirect_block_pointer;
1299 assert_ne!(singly_indirect_block_pointer, 0);
1300 assert_ne!(doubly_indirect_block_pointer, 0);
1301
1302 assert_ne!(big_file_inode.data_size(), 0);
1303
1304 for offset in 0_usize..1_024_usize {
1305 let mut buffer = [0_u8; 1_024];
1306 big_file_inode.read_data(&fs, &mut buffer, usize_to_u64(offset * 1_024)).unwrap();
1307
1308 assert_eq!(buffer.iter().all_equal_value(), Ok(&1));
1309 }
1310
1311 let mut buffer = [0_u8; 1_024];
1312 big_file_inode.read_data(&fs, &mut buffer, 0x0010_0000).unwrap();
1313 assert_eq!(buffer.iter().all_equal_value(), Ok(&0));
1314 }
1315
1316 fn read_file(file: File) {
1317 let ext2 = Ext2Fs::new(file, new_device_id()).unwrap();
1318
1319 let TypeWithFile::Directory(root) = ext2.file(ROOT_DIRECTORY_INODE).unwrap() else {
1320 panic!("The root is always a directory.")
1321 };
1322 let TypeWithFile::Regular(mut foo) = root.entry(UnixStr::new("foo.txt").unwrap()).unwrap().unwrap() else {
1323 panic!("`foo.txt` has been created as a regular file")
1324 };
1325
1326 assert_eq!(foo.file.read_all().unwrap(), b"Hello world!\n");
1327 }
1328
1329 fn read_file_with_offset(file: File) {
1330 let ext2 = Ext2Fs::new(file, new_device_id()).unwrap();
1331
1332 let TypeWithFile::Directory(root) = ext2.file(ROOT_DIRECTORY_INODE).unwrap() else {
1333 panic!("The root is always a directory.")
1334 };
1335 let TypeWithFile::Regular(mut foo) = root.entry(UnixStr::new("foo.txt").unwrap()).unwrap().unwrap() else {
1336 panic!("`foo.txt` has been created as a regular file")
1337 };
1338
1339 foo.seek(SeekFrom::Start(6)).unwrap();
1340 let mut buf = [0; 7];
1341 foo.file.read_exact(&mut buf).unwrap();
1342
1343 assert_eq!(&buf, b"world!\n");
1344 }
1345
1346 fn read_lorem(file: File) {
1347 let ext2 = Ext2Fs::new(file, new_device_id()).unwrap();
1348
1349 let TypeWithFile::Directory(root) = ext2.file(ROOT_DIRECTORY_INODE).unwrap() else {
1350 panic!("The root is always a directory.")
1351 };
1352 let TypeWithFile::Regular(mut lorem) = root.entry(UnixStr::new("lorem.txt").unwrap()).unwrap().unwrap() else {
1353 panic!("`lorem.txt` has been created as a regular file")
1354 };
1355
1356 assert_eq!(LOREM.as_bytes(), lorem.read_all().unwrap());
1357
1358 lorem.seek(SeekFrom::Start(504)).unwrap();
1359 let mut buf = [0_u8; 3000];
1360 lorem.read_exact(&mut buf).unwrap();
1361 assert_eq!(buf, LOREM.as_bytes()[504..(504 + 3000)]);
1362
1363 lorem.seek(SeekFrom::End(-830)).unwrap();
1364 let mut buf = [0_u8; 300];
1365 lorem.read_exact(&mut buf).unwrap();
1366 assert_eq!(buf, LOREM.as_bytes()[(LOREM_LENGTH - 830)..(LOREM_LENGTH - 830 + 300)]);
1367 }
1368
1369 fn read_symlink(file: File) {
1370 let ext2 = Ext2Fs::new(file, new_device_id()).unwrap();
1371 let root = Directory::new(&ext2, ROOT_DIRECTORY_INODE).unwrap();
1372
1373 let TypeWithFile::SymbolicLink(symlink) = root.entry(UnixStr::new("symlink").unwrap()).unwrap().unwrap() else {
1374 panic!("`symlink` has been created as a symbolic link")
1375 };
1376
1377 assert_eq!(symlink.pointed_file, "big_file");
1378 }
1379
1380 fn set_inode(file: File) {
1381 let ext2 = Ext2Fs::new(file, new_device_id()).unwrap();
1382 let TypeWithFile::Directory(root) = ext2.file(ROOT_DIRECTORY_INODE).unwrap() else {
1383 panic!("The root is always a directory.")
1384 };
1385 let TypeWithFile::Regular(mut foo) = root.entry(UnixStr::new("foo.txt").unwrap()).unwrap().unwrap() else {
1386 panic!("`foo.txt` has been created as a regular file")
1387 };
1388
1389 let mut new_inode = foo.file.inode;
1390 new_inode.uid = 0x1234;
1391 new_inode.gid = 0x2345;
1392 new_inode.flags = 0xabcd;
1393 unsafe { foo.file.set_inode(&new_inode) }.unwrap();
1394
1395 assert_eq!(foo.file.inode, Inode::parse(&ext2.lock(), foo.file.inode_number).unwrap());
1396 assert_eq!(foo.file.inode, new_inode);
1397 }
1398
1399 fn write_file_dbp_replace_without_allocation(file: File) {
1400 let ext2 = Ext2Fs::new(file, new_device_id()).unwrap();
1401 let TypeWithFile::Directory(root) = ext2.file(ROOT_DIRECTORY_INODE).unwrap() else {
1402 panic!("The root is always a directory.")
1403 };
1404 let TypeWithFile::Regular(mut foo) = root.entry(UnixStr::new("foo.txt").unwrap()).unwrap().unwrap() else {
1405 panic!("`foo.txt` has been created as a regular file")
1406 };
1407
1408 foo.seek(SeekFrom::Start(6)).unwrap();
1409 let replace_text = b"earth";
1410 foo.write_all(replace_text).unwrap();
1411 foo.flush().unwrap();
1412
1413 assert_eq!(foo.file.inode, Inode::parse(&ext2.lock(), foo.file.inode_number).unwrap());
1414
1415 assert_eq!(String::from_utf8(foo.read_all().unwrap()).unwrap(), "Hello earth!\n");
1416 }
1417
1418 fn write_file_dbp_extend_without_allocation(file: File) {
1419 let ext2 = Ext2Fs::new(file, new_device_id()).unwrap();
1420 let TypeWithFile::Directory(root) = ext2.file(ROOT_DIRECTORY_INODE).unwrap() else {
1421 panic!("The root is always a directory.")
1422 };
1423 let TypeWithFile::Regular(mut foo) = root.entry(UnixStr::new("foo.txt").unwrap()).unwrap().unwrap() else {
1424 panic!("`foo.txt` has been created as a regular file")
1425 };
1426
1427 foo.seek(SeekFrom::Start(6)).unwrap();
1428 let replace_text = b"earth!\nI love dogs!\n";
1429 foo.write_all(replace_text).unwrap();
1430 foo.flush().unwrap();
1431
1432 assert_eq!(foo.file.inode, Inode::parse(&ext2.lock(), foo.file.inode_number).unwrap());
1433
1434 assert_eq!(foo.read_all().unwrap(), b"Hello earth!\nI love dogs!\n");
1435 }
1436
1437 fn write_file_dbp_extend_with_allocation(file: File) {
1438 const BYTES_TO_WRITE: usize = 12_000;
1439
1440 let ext2 = Ext2Fs::new(file, new_device_id()).unwrap();
1441 let TypeWithFile::Directory(root) = ext2.file(ROOT_DIRECTORY_INODE).unwrap() else {
1442 panic!("The root is always a directory.")
1443 };
1444 let TypeWithFile::Regular(mut foo) = root.entry(UnixStr::new("foo.txt").unwrap()).unwrap().unwrap() else {
1445 panic!("`foo.txt` has been created as a regular file")
1446 };
1447
1448 let replace_text = &[b'a'; BYTES_TO_WRITE];
1449 foo.write_all(replace_text).unwrap();
1450 foo.flush().unwrap();
1451
1452 assert_eq!(foo.file.inode, Inode::parse(&ext2.lock(), foo.file.inode_number).unwrap());
1453
1454 assert_eq!(foo.read_all().unwrap().len(), BYTES_TO_WRITE);
1455 assert_eq!(foo.read_all().unwrap().into_iter().all_equal_value(), Ok(b'a'));
1456 }
1457
1458 fn write_file_singly_indirect_block_pointer(file: File) {
1459 const BYTES_TO_WRITE: usize = 23_000;
1460
1461 let ext2 = Ext2Fs::new(file, new_device_id()).unwrap();
1462 let TypeWithFile::Directory(root) = ext2.file(ROOT_DIRECTORY_INODE).unwrap() else {
1463 panic!("The root is always a directory.")
1464 };
1465 let TypeWithFile::Regular(mut foo) = root.entry(UnixStr::new("foo.txt").unwrap()).unwrap().unwrap() else {
1466 panic!("`foo.txt` has been created as a regular file")
1467 };
1468
1469 let mut replace_text = vec![];
1470 for i in 0..=u8::MAX {
1471 replace_text.append(&mut vec![i; BYTES_TO_WRITE / 256]);
1472 }
1473 replace_text.append(&mut vec![b'a'; BYTES_TO_WRITE - 256 * (BYTES_TO_WRITE / 256)]);
1474
1475 foo.write_all(&replace_text).unwrap();
1476 foo.flush().unwrap();
1477
1478 assert_eq!(foo.file.inode, Inode::parse(&ext2.lock(), foo.file.inode_number).unwrap());
1479 assert_eq!(foo.read_all().unwrap().len(), BYTES_TO_WRITE);
1480 assert_eq!(foo.read_all().unwrap(), replace_text);
1481 }
1482
1483 fn write_file_doubly_indirect_block_pointer(file: File) {
1484 const BYTES_TO_WRITE: usize = 400_000;
1485
1486 let ext2 = Ext2Fs::new(file, new_device_id()).unwrap();
1487 let TypeWithFile::Directory(root) = ext2.file(ROOT_DIRECTORY_INODE).unwrap() else {
1488 panic!("The root is always a directory.")
1489 };
1490 let TypeWithFile::Regular(mut foo) = root.entry(UnixStr::new("foo.txt").unwrap()).unwrap().unwrap() else {
1491 panic!("`foo.txt` has been created as a regular file")
1492 };
1493
1494 let mut replace_text = vec![];
1495 for i in 0..=u8::MAX {
1496 replace_text.append(&mut vec![i; BYTES_TO_WRITE / 256]);
1497 }
1498 replace_text.append(&mut vec![b'a'; BYTES_TO_WRITE - 256 * (BYTES_TO_WRITE / 256)]);
1499
1500 foo.write_all(&replace_text).unwrap();
1501 foo.flush().unwrap();
1502
1503 assert_eq!(foo.file.inode, Inode::parse(&ext2.lock(), foo.file.inode_number).unwrap());
1504 assert_eq!(foo.read_all().unwrap().len(), BYTES_TO_WRITE);
1505 assert_eq!(foo.read_all().unwrap(), replace_text);
1506 }
1507
1508 fn write_file_triply_indirect_block_pointer(file: File) {
1509 const BYTES_TO_WRITE: usize = 70_000_000;
1510
1511 let ext2 = Ext2Fs::new(file, new_device_id()).unwrap();
1512 let TypeWithFile::Directory(root) = ext2.file(ROOT_DIRECTORY_INODE).unwrap() else {
1513 panic!("The root is always a directory.")
1514 };
1515 let TypeWithFile::Regular(mut foo) = root.entry(UnixStr::new("foo.txt").unwrap()).unwrap().unwrap() else {
1516 panic!("`foo.txt` has been created as a regular file")
1517 };
1518
1519 let mut replace_text = vec![b'a'; BYTES_TO_WRITE / 2];
1520 replace_text.append(&mut vec![b'b'; BYTES_TO_WRITE / 2]);
1521 foo.write_all(&replace_text).unwrap();
1522 foo.flush().unwrap();
1523
1524 assert_eq!(foo.file.inode, Inode::parse(&ext2.lock(), foo.file.inode_number).unwrap());
1525 assert_eq!(foo.read_all().unwrap().len(), BYTES_TO_WRITE);
1526 assert_eq!(foo.read_all().unwrap(), replace_text);
1527 }
1528
1529 fn write_file_twice(file: File) {
1530 const BYTES_TO_WRITE: usize = 23_000;
1531
1532 let ext2 = Ext2Fs::new(file, new_device_id()).unwrap();
1533 let TypeWithFile::Directory(root) = ext2.file(ROOT_DIRECTORY_INODE).unwrap() else {
1534 panic!("The root is always a directory.")
1535 };
1536 let TypeWithFile::Regular(mut foo) = root.entry(UnixStr::new("foo.txt").unwrap()).unwrap().unwrap() else {
1537 panic!("`foo.txt` has been created as a regular file")
1538 };
1539
1540 let mut replace_text = vec![];
1541 for i in 0..=u8::MAX {
1542 replace_text.append(&mut vec![i; BYTES_TO_WRITE / 256]);
1543 }
1544 replace_text.append(&mut vec![b'a'; BYTES_TO_WRITE - 256 * (BYTES_TO_WRITE / 256)]);
1545
1546 foo.write_all(&replace_text[..BYTES_TO_WRITE / 2]).unwrap();
1547 foo.write_all(&replace_text[BYTES_TO_WRITE / 2..]).unwrap();
1548 foo.flush().unwrap();
1549
1550 assert_eq!(foo.file.inode, Inode::parse(&ext2.lock(), foo.file.inode_number).unwrap());
1551 assert_eq!(foo.read_all().unwrap().len(), BYTES_TO_WRITE);
1552 assert_eq!(foo.read_all().unwrap(), replace_text);
1553 }
1554
1555 #[allow(clippy::similar_names)]
1556 fn file_mode(file: File) {
1557 let ext2 = Ext2Fs::new(file, new_device_id()).unwrap();
1558 let TypeWithFile::Directory(root) = ext2.file(ROOT_DIRECTORY_INODE).unwrap() else {
1559 panic!("The root is always a directory.")
1560 };
1561 let TypeWithFile::Regular(mut foo) = root.entry(UnixStr::new("foo.txt").unwrap()).unwrap().unwrap() else {
1562 panic!("`foo.txt` has been created as a regular file")
1563 };
1564
1565 assert_eq!(foo.get_type(), Type::Regular);
1566 assert_eq!(
1567 foo.permissions(),
1568 Permissions::USER_READ | Permissions::USER_WRITE | Permissions::GROUP_READ | Permissions::OTHER_READ
1569 );
1570
1571 crate::fs::file::File::set_mode(&mut foo, Mode::from(Permissions::USER_READ | Permissions::USER_WRITE))
1572 .unwrap();
1573 crate::fs::file::File::set_uid(&mut foo, Uid(1)).unwrap();
1574 crate::fs::file::File::set_gid(&mut foo, Gid(2)).unwrap();
1575
1576 let fs = ext2.lock();
1577 let inode = Inode::parse(&fs, foo.file.inode_number).unwrap();
1578
1579 let mode = inode.mode;
1580 assert_eq!(mode, (TypePermissions::REGULAR_FILE | TypePermissions::USER_R | TypePermissions::USER_W).bits());
1581 let uid = inode.uid;
1582 assert_eq!(uid, 1);
1583 let gid = inode.gid;
1584 assert_eq!(gid, 2);
1585 }
1586
1587 fn file_truncation(file: File) {
1588 const BYTES_TO_WRITE: usize = 400_000;
1589
1590 let ext2 = Ext2Fs::new(file, new_device_id()).unwrap();
1591 let TypeWithFile::Directory(root) = ext2.file(ROOT_DIRECTORY_INODE).unwrap() else {
1592 panic!("The root is always a directory.")
1593 };
1594 let TypeWithFile::Regular(mut foo) = root.entry(UnixStr::new("foo.txt").unwrap()).unwrap().unwrap() else {
1595 panic!("`foo.txt` has been created as a regular file")
1596 };
1597
1598 let initial_free_block_number = ext2.lock().superblock().base().free_blocks_count;
1599 let initial_foo_size = foo.file.inode.data_size();
1600
1601 let replace_text = vec![b'a'; BYTES_TO_WRITE];
1602 foo.write_all(&replace_text).unwrap();
1603 foo.flush().unwrap();
1604
1605 assert_eq!(foo.file.inode.data_size(), usize_to_u64(BYTES_TO_WRITE));
1606
1607 foo.truncate(10000).unwrap();
1608
1609 assert_eq!(foo.file.inode.data_size(), 10000);
1610 assert_eq!(foo.read_all().unwrap().len(), 10000);
1611
1612 foo.truncate(initial_foo_size).unwrap();
1613 let new_free_block_number = ext2.lock().superblock().base().free_blocks_count;
1614
1615 assert_eq!(foo.file.inode.data_size(), initial_foo_size);
1616 assert!(new_free_block_number >= initial_free_block_number); }
1618
1619 fn file_symlinks(file: File) {
1620 let ext2 = Ext2Fs::new(file, new_device_id()).unwrap();
1621 let TypeWithFile::Directory(root) = ext2.file(ROOT_DIRECTORY_INODE).unwrap() else {
1622 panic!("The root is always a directory.");
1623 };
1624 let TypeWithFile::SymbolicLink(mut bar) = root.entry(UnixStr::new("bar.txt").unwrap()).unwrap().unwrap() else {
1625 panic!("`bar.txt` has been created as a symbolic link");
1626 };
1627
1628 assert_eq!(bar.get_type(), Type::SymbolicLink);
1629
1630 assert_eq!(bar.get_pointed_file().unwrap(), "foo.txt");
1631 assert_eq!(bar.file.inode.data_size(), 7);
1632
1633 bar.set_pointed_file("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
1634 .unwrap();
1635 assert_eq!(
1636 bar.get_pointed_file().unwrap(),
1637 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
1638 );
1639 assert_eq!(
1640 bar.file.read_all().unwrap(),
1641 b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
1642 );
1643 assert_eq!(bar.file.inode.data_size(), 70);
1644
1645 bar.set_pointed_file("foo.txt").unwrap();
1646
1647 assert_eq!(bar.get_pointed_file().unwrap(), "foo.txt");
1648 assert!(bar.file.read_all().is_err());
1649 assert_eq!(bar.file.inode.data_size(), 7);
1650 }
1651
1652 fn new_files(file: File) {
1653 let ext2 = Ext2Fs::new(file, new_device_id()).unwrap();
1654 let TypeWithFile::Directory(mut root) = ext2.file(ROOT_DIRECTORY_INODE).unwrap() else {
1655 panic!("The root is always a directory.");
1656 };
1657
1658 assert!(root.entry(UnixStr::new("boo.txt").unwrap()).is_ok_and(|res| res.is_none()));
1659 let TypeWithFile::Regular(mut boo) = crate::fs::file::Directory::add_entry(
1660 &mut root,
1661 UnixStr::new("boo.txt").unwrap(),
1662 Type::Regular,
1663 Permissions::USER_READ | Permissions::USER_WRITE | Permissions::USER_EXECUTION,
1664 Uid(0),
1665 Gid(0),
1666 )
1667 .unwrap() else {
1668 panic!("boo has been created as a regular file.")
1669 };
1670
1671 let TypeWithFile::Directory(root) = ext2.file(ROOT_DIRECTORY_INODE).unwrap() else {
1672 panic!("The root is always a directory.");
1673 };
1674 assert!(root.entry(UnixStr::new("boo.txt").unwrap()).is_ok_and(|res| res.is_some()));
1675
1676 let ctime = boo.file.inode.ctime;
1677 let atime = boo.file.inode.atime;
1678 let mtime = boo.file.inode.mtime;
1679 assert_ne!(ctime, 0);
1680 assert_ne!(atime, 0);
1681 assert_ne!(mtime, 0);
1682
1683 let root_atime = root.file.inode.atime;
1684 assert!(atime - root_atime < 1);
1685
1686 boo.write_all(b"Hello earth!\n").unwrap();
1687 assert_eq!(boo.read_all().unwrap(), b"Hello earth!\n");
1688 }
1689
1690 #[allow(clippy::similar_names)]
1691 fn remove_files(file: File) {
1692 let ext2 = Ext2Fs::new(file, new_device_id()).unwrap();
1693 let TypeWithFile::Directory(mut root) = ext2.file(ROOT_DIRECTORY_INODE).unwrap() else {
1694 panic!("The root is always a directory.");
1695 };
1696
1697 assert!(root.entry(UnixStr::new("bar.txt").unwrap()).is_ok_and(|res| res.is_some()));
1698 crate::fs::file::Directory::remove_entry(&mut root, UnixStr::new("bar.txt").unwrap()).unwrap();
1699 let TypeWithFile::Directory(mut root) = ext2.file(ROOT_DIRECTORY_INODE).unwrap() else {
1700 panic!("The root is always a directory.");
1701 };
1702 assert!(root.entry(UnixStr::new("bar.txt").unwrap()).is_ok_and(|res| res.is_none()));
1703
1704 let TypeWithFile::Regular(ex1) = ext2
1705 .get_file(&Path::new(UnixStr::new("/folder/ex1.txt").unwrap()), root.clone(), false)
1706 .unwrap()
1707 else {
1708 panic!("ex1.txt is a regular file.");
1709 };
1710 let ex1_inode = ex1.file.inode_number;
1711
1712 let TypeWithFile::SymbolicLink(ex2) = ext2
1713 .get_file(&Path::new(UnixStr::new("/folder/ex2.txt").unwrap()), root.clone(), false)
1714 .unwrap()
1715 else {
1716 panic!("ex2.txt is a symbolic link.");
1717 };
1718 let ex2_inode = ex2.file.inode_number;
1719
1720 let fs = ext2.lock();
1721 let superblock = fs.superblock();
1722 let ex1_bitmap = fs.get_inode_bitmap(Inode::block_group(superblock, ex1_inode)).unwrap();
1723 assert!(Inode::is_used(ex1_inode, superblock, &ex1_bitmap));
1724 let ex2_bitmap = fs.get_inode_bitmap(Inode::block_group(superblock, ex2_inode)).unwrap();
1725 assert!(Inode::is_used(ex2_inode, superblock, &ex2_bitmap));
1726 drop(fs);
1727
1728 crate::fs::file::Directory::remove_entry(&mut root, UnixStr::new("folder").unwrap()).unwrap();
1729
1730 let fs = ext2.lock();
1731 let superblock = fs.superblock();
1732 let ex1_bitmap = fs.get_inode_bitmap(Inode::block_group(superblock, ex1_inode)).unwrap();
1733 assert!(Inode::is_free(ex1_inode, superblock, &ex1_bitmap));
1734 let ex2_bitmap = fs.get_inode_bitmap(Inode::block_group(superblock, ex2_inode)).unwrap();
1735 assert!(Inode::is_free(ex2_inode, superblock, &ex2_bitmap));
1736 }
1737
1738 fn atime_and_mtime(file: File) {
1739 let ext2 = Ext2Fs::new(file, new_device_id()).unwrap();
1740 let TypeWithFile::Directory(root) = ext2.file(ROOT_DIRECTORY_INODE).unwrap() else {
1741 panic!("The root is always a directory.")
1742 };
1743 let TypeWithFile::Regular(mut foo) = root.entry(UnixStr::new("foo.txt").unwrap()).unwrap().unwrap() else {
1744 panic!("`foo.txt` has been created as a regular file")
1745 };
1746
1747 let atime = foo.file.inode.atime;
1748 let mtime = foo.file.inode.mtime;
1749
1750 std::thread::sleep(Duration::from_secs(2));
1751
1752 foo.write_all(b"Hello earth!").unwrap();
1753
1754 let fs = ext2.lock();
1755 let new_inode = Inode::parse(&fs, foo.file.inode_number).unwrap();
1756 drop(fs);
1757 assert!(new_inode.atime > atime);
1758 assert!(new_inode.mtime > mtime);
1759
1760 let atime = new_inode.atime;
1761 let mtime = new_inode.mtime;
1762
1763 std::thread::sleep(Duration::from_secs(1));
1764
1765 foo.write_all(b" and other planets too!").unwrap();
1766
1767 let fs = ext2.lock();
1768 let new_inode = Inode::parse(&fs, foo.file.inode_number).unwrap();
1769 drop(fs);
1770 assert!(new_inode.atime < atime + 3);
1771 assert!(new_inode.mtime < mtime + 3);
1772 }
1773
1774 mod generated {
1775 use crate::tests::{PostCheck, generate_fs_test};
1776
1777 generate_fs_test!(parse_root, "./tests/fs/ext2/extended.ext2", PostCheck::Ext);
1778 generate_fs_test!(parse_root_entries, "./tests/fs/ext2/extended.ext2", PostCheck::Ext);
1779 generate_fs_test!(parse_big_file_inode_data, "./tests/fs/ext2/extended.ext2", PostCheck::Ext);
1780 generate_fs_test!(read_file, "./tests/fs/ext2/io_operations.ext2", PostCheck::Ext);
1781 generate_fs_test!(read_file_with_offset, "./tests/fs/ext2/io_operations.ext2", PostCheck::Ext);
1782 generate_fs_test!(read_lorem, "./tests/fs/ext2/io_operations.ext2", PostCheck::Ext);
1783 generate_fs_test!(read_symlink, "./tests/fs/ext2/extended.ext2", PostCheck::Ext);
1784 generate_fs_test!(
1785 write_file_dbp_extend_without_allocation,
1786 "./tests/fs/ext2/io_operations.ext2",
1787 PostCheck::Ext
1788 );
1789 generate_fs_test!(
1790 write_file_dbp_replace_without_allocation,
1791 "./tests/fs/ext2/io_operations.ext2",
1792 PostCheck::Ext
1793 );
1794 generate_fs_test!(write_file_dbp_extend_with_allocation, "./tests/fs/ext2/io_operations.ext2", PostCheck::Ext);
1795 generate_fs_test!(
1796 write_file_singly_indirect_block_pointer,
1797 "./tests/fs/ext2/io_operations.ext2",
1798 PostCheck::Ext
1799 );
1800 generate_fs_test!(
1801 write_file_doubly_indirect_block_pointer,
1802 "./tests/fs/ext2/io_operations.ext2",
1803 PostCheck::Ext
1804 );
1805 generate_fs_test!(
1806 write_file_triply_indirect_block_pointer,
1807 "./tests/fs/ext2/io_operations.ext2",
1808 PostCheck::Ext
1809 );
1810 generate_fs_test!(write_file_twice, "./tests/fs/ext2/io_operations.ext2", PostCheck::Ext);
1811 generate_fs_test!(file_mode, "./tests/fs/ext2/io_operations.ext2", PostCheck::Ext);
1812 generate_fs_test!(file_truncation, "./tests/fs/ext2/io_operations.ext2", PostCheck::Ext);
1813 generate_fs_test!(file_symlinks, "./tests/fs/ext2/io_operations.ext2", PostCheck::Ext);
1814 generate_fs_test!(new_files, "./tests/fs/ext2/io_operations.ext2", PostCheck::Ext);
1815 generate_fs_test!(remove_files, "./tests/fs/ext2/io_operations.ext2", PostCheck::Ext);
1816 generate_fs_test!(atime_and_mtime, "./tests/fs/ext2/io_operations.ext2", PostCheck::Ext);
1817
1818 generate_fs_test!(set_inode, "./tests/fs/ext2/io_operations.ext2", PostCheck::None);
1820 }
1821}