1use crate::bgd::{self, BlockGroupDescriptor};
4use crate::block_io::BlockDevice;
5use crate::checksum::Checksummer;
6use crate::error::{Error, Result};
7use crate::features;
8use crate::inode::Inode;
9use crate::superblock::Superblock;
10use std::borrow::Cow;
11use std::collections::{BTreeMap, HashMap};
12use std::sync::{Arc, Mutex};
13
14pub(crate) struct BlockBuffer {
23 pub dirty: BTreeMap<u64, Vec<u8>>,
24 pub uninit_cleared: BTreeMap<usize, u16>,
33}
34
35impl BlockBuffer {
36 pub fn new(_block_size: u32) -> Self {
45 Self {
46 dirty: BTreeMap::new(),
47 uninit_cleared: BTreeMap::new(),
48 }
49 }
50
51 pub fn get_mut(&mut self, fs: &Filesystem, block: u64) -> Result<&mut Vec<u8>> {
55 if let std::collections::btree_map::Entry::Vacant(e) = self.dirty.entry(block) {
56 let buf = fs.read_block(block)?;
57 e.insert(buf);
58 }
59 Ok(self.dirty.get_mut(&block).unwrap())
60 }
61
62 pub fn put(&mut self, block: u64, bytes: Vec<u8>) {
66 self.dirty.insert(block, bytes);
67 }
68}
69
70fn patch_counter_u32(buf: &mut [u8], lo_off: usize, hi_off: Option<usize>, delta: i32) {
76 let cur_lo = u16::from_le_bytes(buf[lo_off..lo_off + 2].try_into().unwrap()) as u32;
77 let cur_hi = hi_off
78 .map(|h| u16::from_le_bytes(buf[h..h + 2].try_into().unwrap()) as u32)
79 .unwrap_or(0);
80 let cur = (cur_hi << 16) | cur_lo;
81 let new = (cur as i64 + delta as i64).clamp(0, u32::MAX as i64) as u32;
82 buf[lo_off..lo_off + 2].copy_from_slice(&((new & 0xFFFF) as u16).to_le_bytes());
83 if let Some(h) = hi_off {
84 buf[h..h + 2].copy_from_slice(&(((new >> 16) & 0xFFFF) as u16).to_le_bytes());
85 }
86}
87
88#[inline]
94fn pack_nsec_lo(nsec: u32) -> u32 {
95 (nsec & 0x3FFF_FFFF) << 2
96}
97
98pub const TIME_OMIT: i64 = i64::MIN;
106
107fn split_parent_and_base(path: &str) -> Result<(String, String)> {
110 let trimmed = path.trim_end_matches('/');
111 if trimmed.is_empty() {
112 return Err(Error::InvalidArgument("empty path"));
113 }
114 let last_slash = trimmed
115 .rfind('/')
116 .ok_or(Error::InvalidArgument("relative path"))?;
117 let base = &trimmed[last_slash + 1..];
118 let parent = if last_slash == 0 {
119 "/"
120 } else {
121 &trimmed[..last_slash]
122 };
123 if base.is_empty() {
124 return Err(Error::NotADirectory);
126 }
127 Ok((parent.to_string(), base.to_string()))
128}
129
130pub(crate) struct FsBlockReader<'a> {
139 pub(crate) fs: &'a Filesystem,
140}
141
142impl<'a> crate::extent_mut::DeepReader for FsBlockReader<'a> {
143 fn read_block(&self, block: u64, out: &mut [u8]) -> Result<()> {
144 let bytes = self.fs.read_block(block)?;
145 if bytes.len() != out.len() {
146 return Err(Error::Corrupt(
147 "FsBlockReader: block length mismatch (callers must pass a buffer sized to fs block_size)",
148 ));
149 }
150 out.copy_from_slice(&bytes);
151 Ok(())
152 }
153}
154
155fn now_unix_seconds() -> u32 {
159 use std::time::{SystemTime, UNIX_EPOCH};
160 SystemTime::now()
161 .duration_since(UNIX_EPOCH)
162 .map(|d| d.as_secs() as u32)
163 .unwrap_or(0)
164}
165
166use std::sync::atomic::{AtomicU32, Ordering};
173static INODE_GEN_COUNTER: AtomicU32 = AtomicU32::new(1);
176
177fn write_inode_timestamps(raw: &mut [u8], now: u32) {
180 use crate::inode::{INODE_SIZE_WITH_CRTIME, OFF_ATIME, OFF_CRTIME, OFF_CTIME, OFF_MTIME};
181 raw[OFF_ATIME..OFF_ATIME + 4].copy_from_slice(&now.to_le_bytes());
182 raw[OFF_CTIME..OFF_CTIME + 4].copy_from_slice(&now.to_le_bytes());
183 raw[OFF_MTIME..OFF_MTIME + 4].copy_from_slice(&now.to_le_bytes());
184 if raw.len() >= INODE_SIZE_WITH_CRTIME {
187 raw[OFF_CRTIME..OFF_CRTIME + 4].copy_from_slice(&now.to_le_bytes());
188 }
189}
190
191fn alloc_inode_generation() -> u32 {
195 std::process::id().wrapping_add(INODE_GEN_COUNTER.fetch_add(1, Ordering::Relaxed))
196}
197
198fn write_inode_generation(raw: &mut [u8], generation: u32) {
200 use crate::inode::OFF_GENERATION;
201 raw[OFF_GENERATION..OFF_GENERATION + 4].copy_from_slice(&generation.to_le_bytes());
202}
203
204fn write_inode_extra_isize(raw: &mut [u8]) {
207 use crate::inode::{EXTRA_ISIZE_DEFAULT, INODE_SIZE_WITH_EXTRA, OFF_EXTRA_ISIZE};
208 if raw.len() >= INODE_SIZE_WITH_EXTRA {
209 raw[OFF_EXTRA_ISIZE..OFF_EXTRA_ISIZE + 2]
210 .copy_from_slice(&EXTRA_ISIZE_DEFAULT.to_le_bytes());
211 }
212}
213
214pub struct Filesystem {
215 pub dev: Arc<dyn BlockDevice>,
216 pub sb: Superblock,
217 pub groups: Vec<BlockGroupDescriptor>,
218 uninit_cleared: Mutex<HashMap<usize, u16>>,
232 pub csum: Checksummer,
233 pub flavor: features::FsFlavor,
238 pub journal: Option<std::sync::Mutex<crate::journal_writer::JournalWriter>>,
243}
244
245struct NewInodePlan {
250 new_ino: u32,
252 parent_ino: u32,
254 parent_inode: crate::inode::Inode,
256 buf: BlockBuffer,
258 base_name: String,
260}
261
262#[derive(Clone, Copy, PartialEq, Eq)]
265pub(crate) enum BgdUninitFlag {
266 Inode,
267 Block,
268}
269
270impl Filesystem {
271 pub fn mount(dev: Arc<dyn BlockDevice>) -> Result<Self> {
278 Self::mount_inner(dev, false)
279 }
280
281 pub fn mount_lazy(dev: Arc<dyn BlockDevice>) -> Result<Self> {
294 Self::mount_inner(dev, true)
295 }
296
297 fn mount_inner(dev: Arc<dyn BlockDevice>, defer_replay: bool) -> Result<Self> {
298 let sb = Superblock::read(dev.as_ref())?;
299 features::check_mountable(sb.feature_incompat, sb.feature_ro_compat)?;
300 let flavor = features::FsFlavor::detect(sb.feature_compat, sb.feature_incompat);
301 let csum = Checksummer::from_superblock(&sb);
302 if csum.enabled && !csum.verify_superblock(&sb.raw) {
303 return Err(Error::BadChecksum { what: "superblock" });
304 }
305 let groups = bgd::read_all(dev.as_ref(), &sb, &csum)?;
306 let dev: Arc<dyn BlockDevice> = Arc::new(crate::block_cache::CachedDevice::new(
317 dev,
318 sb.block_size(),
319 256,
320 ));
321 let mut fs = Self {
322 dev,
323 sb,
324 groups,
325 uninit_cleared: Mutex::new(HashMap::new()),
326 csum,
327 flavor,
328 journal: None,
329 };
330
331 if fs.dev.is_writable()
359 && fs.sb.feature_incompat & crate::features::Incompat::MMP.bits() != 0
360 {
361 return Err(crate::error::Error::UnsupportedIncompat(
362 crate::features::Incompat::MMP.bits(),
363 ));
364 }
365
366 if !defer_replay && fs.dev.is_writable() {
367 crate::journal_apply::replay_if_dirty(&fs)?;
373 }
374
375 if fs.dev.is_writable() {
381 if let Some(jw) = crate::journal_writer::JournalWriter::open(&fs)? {
382 fs.journal = Some(std::sync::Mutex::new(jw));
383 }
384 }
385
386 if fs.dev.is_writable() && !defer_replay {
392 let _ = fs.recover_orphans();
393 }
394
395 Ok(fs)
396 }
397
398 pub fn replay_journal_if_dirty(&self) -> Result<usize> {
403 let n = crate::journal_apply::replay_if_dirty(self)?;
404 if n > 0 {
413 self.dev.unpin_all();
414 }
415 Ok(n)
416 }
417
418 pub fn orphan_list(&self) -> Result<Vec<u32>> {
430 let mut out = Vec::new();
431 let mut cur = self.sb.last_orphan;
432 let cap = self.sb.inodes_count;
433 let mut steps = 0u32;
434 while cur != 0 {
435 if steps > cap {
436 return Err(Error::Corrupt(
437 "orphan_list: chain longer than inodes_count (cycle?)",
438 ));
439 }
440 out.push(cur);
441 let raw = self.read_inode_raw(cur)?;
445 if raw.len() < 0x18 {
446 return Err(Error::Corrupt("orphan_list: inode too short"));
447 }
448 cur = u32::from_le_bytes(raw[0x14..0x18].try_into().unwrap());
449 steps += 1;
450 }
451 Ok(out)
452 }
453
454 pub fn recover_orphans(&self) -> Result<usize> {
468 if !self.dev.is_writable() {
469 return Ok(0);
470 }
471 let chain = self.orphan_list()?;
472 if chain.is_empty() {
473 return Ok(0);
474 }
475
476 let bs = self.sb.block_size();
477 let sectors_per_block = bs as u64 / 512;
478 let mut buf = BlockBuffer::new(bs);
479 let mut total_freed_blocks: u64 = 0;
480 let mut reclaimed = 0usize;
481
482 for &orphan_ino in &chain {
483 let mut raw = self.read_inode_raw(orphan_ino)?;
486 let parsed = match Inode::parse(&raw) {
487 Ok(i) => i,
488 Err(_) => continue, };
490 if parsed.has_extents() && parsed.size > 0 {
493 let (_sc, muts) = match crate::file_mut::plan_truncate_shrink(
494 parsed.size,
495 0,
496 &parsed.block,
497 bs,
498 ) {
499 Ok(p) => p,
500 Err(_) => continue,
501 };
502 for m in &muts {
503 if let crate::extent_mut::ExtentMutation::FreePhysicalRun { start, len } = m {
504 total_freed_blocks +=
505 self.buffer_free_block_run_and_bgd(&mut buf, *start, *len as u64)?;
506 }
507 }
508 }
509 self.buffer_free_inode_slot(&mut buf, orphan_ino)?;
511
512 let inode_size = self.sb.inode_size as usize;
514 let old_gen = parsed.generation;
515 for b in &mut raw[..inode_size] {
516 *b = 0;
517 }
518 let dtime = now_unix_seconds();
519 raw[0x14..0x18].copy_from_slice(&dtime.to_le_bytes());
520 raw[0x64..0x68].copy_from_slice(&old_gen.to_le_bytes());
521 self.finalize_inode_raw(orphan_ino, old_gen, &mut raw)?;
522 self.buffer_write_inode(&mut buf, orphan_ino, &raw)?;
523
524 reclaimed += 1;
525 }
526
527 self.buffer_patch_sb_counters(&mut buf, total_freed_blocks as i64, reclaimed as i32)?;
530 self.buffer_patch_sb_last_orphan(&mut buf, 0)?;
531
532 let _ = sectors_per_block;
536
537 self.commit_block_buffer(buf)?;
538 Ok(reclaimed)
539 }
540
541 pub fn read_block(&self, block_num: u64) -> Result<Vec<u8>> {
546 let block_size = self.sb.block_size() as usize;
547 let byte_offset = block_num
548 .checked_mul(block_size as u64)
549 .ok_or(Error::Corrupt("block byte offset overflow"))?;
550 let mut buf = vec![0u8; block_size];
551 self.dev.read_at(byte_offset, &mut buf)?;
552 Ok(buf)
553 }
554
555 pub fn read_inode_raw(&self, ino: u32) -> Result<Vec<u8>> {
557 let (block, offset) = bgd::locate_inode(&self.sb, &self.groups, ino)?;
558 let block_data = self.read_block(block)?;
559 let inode_size = self.sb.inode_size as usize;
560 let off = offset as usize;
561 let end = off
562 .checked_add(inode_size)
563 .ok_or(Error::Corrupt("inode slice end overflows usize"))?;
564 if end > block_data.len() {
565 return Err(Error::Corrupt("inode slice exceeds block data"));
566 }
567 Ok(block_data[off..end].to_vec())
568 }
569
570 pub fn read_inode_verified(&self, ino: u32) -> Result<(Inode, Vec<u8>)> {
576 let raw = self.read_inode_raw(ino)?;
577 let inode = Inode::parse(&raw)?;
578 if self.csum.enabled && !self.csum.verify_inode(ino, inode.generation, &raw) {
579 return Err(Error::BadChecksum { what: "inode" });
580 }
581 if inode.is_dir() {
597 let filesystem_bytes = self
598 .sb
599 .blocks_count
600 .saturating_mul(self.sb.block_size() as u64);
601 if inode.size > filesystem_bytes {
602 return Err(Error::Corrupt(
603 "directory inode declares more bytes than the filesystem holds",
604 ));
605 }
606 }
607 Ok((inode, raw))
608 }
609
610 pub fn map_inode_logical(&self, inode: &Inode, logical_block: u64) -> Result<Option<u64>> {
627 let bs = self.sb.block_size();
628 if (inode.flags & crate::inode::InodeFlags::EXTENTS.bits()) != 0 {
629 crate::extent::map_logical(&inode.block, self.dev.as_ref(), bs, logical_block)
630 } else {
631 let mut cache = crate::indirect::IndirectCache::new();
632 crate::indirect::lookup(
633 &inode.block,
634 self.dev.as_ref(),
635 bs,
636 logical_block,
637 &mut cache,
638 )
639 }
640 }
641
642 pub fn write_inode_raw(&self, ino: u32, raw: &[u8]) -> Result<()> {
652 if raw.len() != self.sb.inode_size as usize {
653 return Err(Error::Corrupt("write_inode_raw: length != inode_size"));
654 }
655 let (block, offset) = bgd::locate_inode(&self.sb, &self.groups, ino)?;
656 let block_size = self.sb.block_size() as u64;
657 let byte_offset = block * block_size + offset as u64;
658 self.dev.write_at(byte_offset, raw)?;
659 Ok(())
660 }
661
662 pub fn patch_inode_size_and_blocks(
667 raw: &mut [u8],
668 new_size: u64,
669 new_block_count: u64,
670 ) -> Result<()> {
671 if raw.len() < 128 {
672 return Err(Error::Corrupt("patch_inode: buffer too small"));
673 }
674 let size_lo = (new_size & 0xFFFF_FFFF) as u32;
676 let size_hi = (new_size >> 32) as u32;
677 raw[0x04..0x08].copy_from_slice(&size_lo.to_le_bytes());
678 raw[0x6C..0x70].copy_from_slice(&size_hi.to_le_bytes());
679 let blocks_lo = (new_block_count & 0xFFFF_FFFF) as u32;
681 let blocks_hi = ((new_block_count >> 32) & 0xFFFF) as u16;
682 raw[0x1C..0x20].copy_from_slice(&blocks_lo.to_le_bytes());
683 raw[0x74..0x76].copy_from_slice(&blocks_hi.to_le_bytes());
684 Ok(())
685 }
686
687 pub fn patch_inode_block_area(raw: &mut [u8], new_root: &[u8]) -> Result<()> {
690 if raw.len() < 128 {
691 return Err(Error::Corrupt("patch_inode_block_area: buffer too small"));
692 }
693 if new_root.len() != 60 {
694 return Err(Error::Corrupt(
695 "patch_inode_block_area: new_root != 60 bytes",
696 ));
697 }
698 raw[0x28..0x64].copy_from_slice(new_root);
699 Ok(())
700 }
701
702 pub fn apply_truncate_shrink(&self, ino: u32, new_size: u64) -> Result<()> {
715 if !self.dev.is_writable() {
716 return Err(Error::ReadOnly);
717 }
718 let (inode, mut raw) = self.read_inode_verified(ino)?;
719 if new_size > inode.size {
720 return Err(Error::InvalidArgument(
721 "truncate: new_size > old_size (grow not supported)",
722 ));
723 }
724
725 let (_size_change, muts) = crate::file_mut::plan_truncate_shrink(
726 inode.size,
727 new_size,
728 &inode.block,
729 self.sb.block_size(),
730 )?;
731
732 let bs = self.sb.block_size() as u64;
733 let mut freed_sectors: u64 = 0;
734 let mut freed_blocks: u64 = 0;
735
736 let mut buf = BlockBuffer::new(self.sb.block_size());
739
740 for m in &muts {
741 match m {
742 crate::extent_mut::ExtentMutation::WriteRoot { bytes } => {
743 Self::patch_inode_block_area(&mut raw, bytes)?;
744 }
745 crate::extent_mut::ExtentMutation::FreePhysicalRun { start, len } => {
746 freed_blocks +=
747 self.buffer_free_block_run_and_bgd(&mut buf, *start, *len as u64)?;
748 freed_sectors += (*len as u64) * (bs / 512);
749 }
750 _ => {
751 return Err(Error::Corrupt(
752 "apply_truncate_shrink: unexpected mutation type",
753 ));
754 }
755 }
756 }
757
758 let new_blocks = inode.blocks.saturating_sub(freed_sectors);
760 Self::patch_inode_size_and_blocks(&mut raw, new_size, new_blocks)?;
761 self.finalize_inode_raw(ino, inode.generation, &mut raw)?;
762 self.buffer_write_inode(&mut buf, ino, &raw)?;
763
764 if freed_blocks > 0 {
765 self.buffer_patch_sb_counters(&mut buf, freed_blocks as i64, 0)?;
766 }
767
768 self.commit_block_buffer(buf)
769 }
770
771 pub fn apply_truncate_grow(&self, ino: u32, new_size: u64) -> Result<()> {
780 if !self.dev.is_writable() {
781 return Err(Error::ReadOnly);
782 }
783 let (inode, mut raw) = self.read_inode_verified(ino)?;
784 if new_size < inode.size {
785 return Err(Error::InvalidArgument(
786 "apply_truncate_grow: new_size < old_size (use apply_truncate_shrink)",
787 ));
788 }
789 Self::patch_inode_size_and_blocks(&mut raw, new_size, inode.blocks)?;
790
791 let now = now_unix_seconds();
792 raw[0x0C..0x10].copy_from_slice(&now.to_le_bytes()); raw[0x10..0x14].copy_from_slice(&now.to_le_bytes()); self.finalize_inode_raw(ino, inode.generation, &mut raw)?;
796 self.commit_inode_write(ino, &raw)
797 }
798
799 pub fn apply_fallocate_keep_size(&self, ino: u32, offset: u64, len: u64) -> Result<()> {
816 if !self.dev.is_writable() {
817 return Err(Error::ReadOnly);
818 }
819 if len == 0 {
820 return Ok(());
821 }
822 let bs = self.sb.block_size() as u64;
823 let bs_u32 = self.sb.block_size();
824 let first_block = offset / bs;
825 let last_block_excl = offset
826 .checked_add(len)
827 .ok_or(Error::InvalidArgument("fallocate: offset+len overflow"))?
828 .div_ceil(bs);
829 let need_blocks_u64 = last_block_excl - first_block;
830 if need_blocks_u64 > u32::MAX as u64 {
831 return Err(Error::InvalidArgument(
832 "fallocate: range exceeds u32 block count",
833 ));
834 }
835 let need_blocks = need_blocks_u64 as u32;
836
837 let (inode, mut raw) = self.read_inode_verified(ino)?;
838 if !inode.is_file() {
839 return Err(Error::InvalidArgument(
840 "fallocate: target is not a regular file",
841 ));
842 }
843 if !inode.has_extents() {
844 return Err(Error::InvalidArgument(
845 "fallocate: legacy (non-extents) inodes not supported",
846 ));
847 }
848
849 for log in first_block..last_block_excl {
853 if crate::extent::map_logical(&inode.block, self.dev.as_ref(), bs_u32, log)?.is_some() {
854 return Err(Error::InvalidArgument(
855 "fallocate: range partially mapped (v1 limitation)",
856 ));
857 }
858 }
859
860 let inode_group = (ino - 1) / self.sb.inodes_per_group;
862 let mut bitmap_reader = |block: u64| self.read_block(block);
863 let plan = crate::alloc::plan_block_allocation(
864 &self.sb,
865 &self.allocation_groups(),
866 need_blocks,
867 inode_group,
868 &mut bitmap_reader,
869 )?;
870
871 if need_blocks > 0x7FFF {
875 return Err(Error::InvalidArgument(
876 "fallocate: single-extent length > 32K blocks (split needed)",
877 ));
878 }
879 let new_extent = crate::extent::Extent {
880 logical_block: first_block as u32,
881 length: need_blocks as u16,
882 physical_block: plan.first_block,
883 uninitialized: true,
884 };
885 let muts = crate::extent_mut::plan_insert_extent(&inode.block, new_extent)?;
886
887 let mut buf = BlockBuffer::new(self.sb.block_size());
889 self.buffer_mark_block_run_used(&mut buf, plan.first_block, need_blocks as u64)?;
890 self.buffer_patch_bgd_counters(
891 &mut buf,
892 plan.bgd.group_idx as usize,
893 plan.bgd.free_blocks_delta,
894 plan.bgd.free_inodes_delta,
895 plan.bgd.used_dirs_delta,
896 )?;
897 self.buffer_patch_sb_counters(
898 &mut buf,
899 plan.sb.free_blocks_delta,
900 plan.sb.free_inodes_delta,
901 )?;
902
903 for m in &muts {
905 if let crate::extent_mut::ExtentMutation::WriteRoot { bytes } = m {
906 Self::patch_inode_block_area(&mut raw, bytes)?;
907 }
908 }
909
910 let sectors_per_block = bs / 512;
912 let new_i_blocks = inode
913 .blocks
914 .saturating_add(need_blocks as u64 * sectors_per_block);
915 Self::patch_inode_size_and_blocks(&mut raw, inode.size, new_i_blocks)?;
916
917 let now = now_unix_seconds();
919 raw[0x0C..0x10].copy_from_slice(&now.to_le_bytes());
920 raw[0x10..0x14].copy_from_slice(&now.to_le_bytes());
921
922 self.finalize_inode_raw(ino, inode.generation, &mut raw)?;
923 self.buffer_write_inode(&mut buf, ino, &raw)?;
924
925 self.commit_block_buffer(buf)
926 }
927
928 pub fn apply_fallocate_punch_hole(&self, ino: u32, offset: u64, len: u64) -> Result<()> {
941 if !self.dev.is_writable() {
942 return Err(Error::ReadOnly);
943 }
944 if len == 0 {
945 return Ok(());
946 }
947 let bs = self.sb.block_size() as u64;
948 let bs_u32 = self.sb.block_size();
949 let punch_first = offset / bs;
950 let punch_last_excl = offset
951 .checked_add(len)
952 .ok_or(Error::InvalidArgument("punch_hole: offset+len overflow"))?
953 .div_ceil(bs);
954
955 let (inode, mut raw) = self.read_inode_verified(ino)?;
956 if !inode.is_file() {
957 return Err(Error::InvalidArgument("punch_hole: not a regular file"));
958 }
959 if !inode.has_extents() {
960 return Err(Error::InvalidArgument(
961 "punch_hole: legacy (non-extents) inodes not supported",
962 ));
963 }
964
965 let extents = crate::extent::collect_all(&inode.block, self.dev.as_ref(), bs_u32)?;
966 let mut new_entries: Vec<crate::extent::Extent> = Vec::new();
967 let mut freed_blocks: u64 = 0;
968 let mut buf = BlockBuffer::new(bs_u32);
969
970 for e in &extents {
971 let el = e.logical_block as u64;
972 let er = el + e.length as u64;
973
974 if er <= punch_first || el >= punch_last_excl {
975 new_entries.push(*e);
977 continue;
978 }
979 if el >= punch_first && er <= punch_last_excl {
980 freed_blocks += self.buffer_free_block_run_and_bgd(
982 &mut buf,
983 e.physical_block,
984 e.length as u64,
985 )?;
986 continue;
987 }
988 let free_lo = el.max(punch_first);
991 let free_hi = er.min(punch_last_excl);
992 let free_offset_in_e = free_lo - el;
993 let free_len = (free_hi - free_lo) as u32;
994 let free_phys = e.physical_block + free_offset_in_e;
995 freed_blocks +=
996 self.buffer_free_block_run_and_bgd(&mut buf, free_phys, free_len as u64)?;
997
998 if el < punch_first {
999 new_entries.push(crate::extent::Extent {
1000 logical_block: el as u32,
1001 length: (punch_first - el) as u16,
1002 physical_block: e.physical_block,
1003 uninitialized: e.uninitialized,
1004 });
1005 }
1006 if er > punch_last_excl {
1007 new_entries.push(crate::extent::Extent {
1008 logical_block: punch_last_excl as u32,
1009 length: (er - punch_last_excl) as u16,
1010 physical_block: e.physical_block + (punch_last_excl - el),
1011 uninitialized: e.uninitialized,
1012 });
1013 }
1014 }
1015
1016 if new_entries.len() > 4 {
1017 return Err(Error::Corrupt(
1018 "punch_hole: surviving entries exceed inline-root capacity (4); needs depth>=1",
1019 ));
1020 }
1021
1022 let gen = u32::from_le_bytes(inode.block[8..12].try_into().unwrap());
1024 let mut root = vec![0u8; 60];
1025 root[0..2].copy_from_slice(&crate::extent::EXT4_EXT_MAGIC.to_le_bytes());
1026 root[2..4].copy_from_slice(&(new_entries.len() as u16).to_le_bytes());
1027 root[4..6].copy_from_slice(&4u16.to_le_bytes());
1028 root[8..12].copy_from_slice(&gen.to_le_bytes());
1030 for (i, e) in new_entries.iter().enumerate() {
1031 let off = 12 + i * 12;
1032 root[off..off + 4].copy_from_slice(&e.logical_block.to_le_bytes());
1033 let ee_len = if e.uninitialized {
1034 e.length + crate::extent::EXT_INIT_MAX_LEN
1035 } else {
1036 e.length
1037 };
1038 root[off + 4..off + 6].copy_from_slice(&ee_len.to_le_bytes());
1039 let (phys_hi, phys_lo) = crate::extent_mut::split_phys_block(e.physical_block);
1040 root[off + 6..off + 8].copy_from_slice(&phys_hi.to_le_bytes());
1041 root[off + 8..off + 12].copy_from_slice(&phys_lo.to_le_bytes());
1042 }
1043 Self::patch_inode_block_area(&mut raw, &root)?;
1044
1045 let sectors_per_block = bs / 512;
1048 let new_i_blocks = inode
1049 .blocks
1050 .saturating_sub(freed_blocks * sectors_per_block);
1051 Self::patch_inode_size_and_blocks(&mut raw, inode.size, new_i_blocks)?;
1052 let now = now_unix_seconds();
1053 raw[0x0C..0x10].copy_from_slice(&now.to_le_bytes());
1054 raw[0x10..0x14].copy_from_slice(&now.to_le_bytes());
1055 self.finalize_inode_raw(ino, inode.generation, &mut raw)?;
1056 self.buffer_write_inode(&mut buf, ino, &raw)?;
1057
1058 if freed_blocks > 0 {
1059 self.buffer_patch_sb_counters(&mut buf, freed_blocks as i64, 0)?;
1060 }
1061
1062 self.commit_block_buffer(buf)
1063 }
1064
1065 pub fn apply_fallocate_zero_range(&self, ino: u32, offset: u64, len: u64) -> Result<()> {
1074 if len == 0 {
1075 return Ok(());
1076 }
1077 self.apply_fallocate_punch_hole(ino, offset, len)?;
1078 self.apply_fallocate_keep_size(ino, offset, len)
1079 }
1080
1081 pub fn apply_chmod(&self, path: &str, mode: u16) -> Result<()> {
1089 if !self.dev.is_writable() {
1090 return Err(Error::ReadOnly);
1091 }
1092 let mut reader = |ino: u32| self.read_inode_verified(ino).map(|(i, _)| i);
1093 let ino = crate::path::lookup(self.dev.as_ref(), &self.sb, &mut reader, path)?;
1094 let (inode, mut raw) = self.read_inode_verified(ino)?;
1095
1096 let file_type_bits = inode.mode & crate::inode::S_IFMT;
1099 let new_mode = file_type_bits | (mode & 0x0FFF);
1100 raw[0x00..0x02].copy_from_slice(&new_mode.to_le_bytes());
1101
1102 let now = now_unix_seconds();
1104 raw[0x0C..0x10].copy_from_slice(&now.to_le_bytes());
1105
1106 self.finalize_inode_raw(ino, inode.generation, &mut raw)?;
1107 self.commit_inode_write(ino, &raw)
1108 }
1109
1110 fn commit_inode_write(&self, ino: u32, new_inode_raw: &[u8]) -> Result<()> {
1118 let mut buf = BlockBuffer::new(self.sb.block_size());
1119 self.buffer_write_inode(&mut buf, ino, new_inode_raw)?;
1120 self.commit_block_buffer(buf)
1121 }
1122
1123 pub(crate) fn buffer_write_inode(
1136 &self,
1137 buf: &mut BlockBuffer,
1138 ino: u32,
1139 inode_raw: &[u8],
1140 ) -> Result<()> {
1141 let (block, offset) = bgd::locate_inode(&self.sb, &self.groups, ino)?;
1142 let it_buf = buf.get_mut(self, block)?;
1143 let off = offset as usize;
1144 it_buf[off..off + inode_raw.len()].copy_from_slice(inode_raw);
1145 Ok(())
1146 }
1147
1148 pub(crate) fn buffer_free_block_run_and_bgd(
1153 &self,
1154 buf: &mut BlockBuffer,
1155 start: u64,
1156 len: u64,
1157 ) -> Result<u64> {
1158 let bpg = self.sb.blocks_per_group as u64;
1159 let first_data = self.sb.first_data_block as u64;
1160 let gi = ((start - first_data) / bpg) as usize;
1161 if gi >= self.groups.len() {
1162 return Err(Error::InvalidBlock(start));
1163 }
1164 let group_start = first_data + gi as u64 * bpg;
1165 let bit_start = (start - group_start) as u32;
1166 let bitmap_block = self.groups[gi].block_bitmap;
1167 {
1168 let bm = buf.get_mut(self, bitmap_block)?;
1169 for i in 0..len {
1170 let bit = bit_start as u64 + i;
1171 let byte = (bit / 8) as usize;
1172 let mask = 1u8 << (bit % 8);
1173 if byte < bm.len() {
1174 bm[byte] &= !mask;
1175 }
1176 }
1177 }
1178 self.buffer_refresh_bitmap_csum(buf, gi, false)?;
1179 self.buffer_patch_bgd_counters(buf, gi, len as i32, 0, 0)?;
1180 Ok(len)
1181 }
1182
1183 fn clear_bgd_uninit_flag_if_set(
1191 &self,
1192 buf: &mut BlockBuffer,
1193 gi: usize,
1194 which: BgdUninitFlag,
1195 ) -> Result<bool> {
1196 const INODE_UNINIT: u16 = 0x0001;
1197 const BLOCK_UNINIT: u16 = 0x0002;
1198 let flag = match which {
1199 BgdUninitFlag::Inode => INODE_UNINIT,
1200 BgdUninitFlag::Block => BLOCK_UNINIT,
1201 };
1202
1203 let bs = self.sb.block_size() as u64;
1204 let desc_size = self.sb.desc_size as u64;
1205 let bgt_first_block = self.sb.first_data_block as u64 + 1;
1206 let byte_in_bgt = gi as u64 * desc_size;
1207 let bgt_block = bgt_first_block + byte_in_bgt / bs;
1208 let off = (byte_in_bgt % bs) as usize;
1209
1210 let block = buf.get_mut(self, bgt_block)?;
1211 let flags_off = off + 0x12;
1212 let flags = u16::from_le_bytes(block[flags_off..flags_off + 2].try_into().unwrap());
1213 if flags & flag == 0 {
1214 return Ok(false);
1215 }
1216 let new_flags = flags & !flag;
1217 block[flags_off..flags_off + 2].copy_from_slice(&new_flags.to_le_bytes());
1218 buf.uninit_cleared
1229 .entry(gi)
1230 .and_modify(|f| *f &= !flag)
1231 .or_insert(new_flags);
1232 Ok(true)
1233 }
1234
1235 fn allocation_groups(&self) -> Cow<'_, [BlockGroupDescriptor]> {
1240 let cleared = self.uninit_cleared.lock().unwrap();
1241 if cleared.is_empty() {
1242 return Cow::Borrowed(&self.groups);
1243 }
1244 let mut groups = self.groups.clone();
1245 for (&gi, &flags) in cleared.iter() {
1246 groups[gi].flags = flags;
1247 }
1248 Cow::Owned(groups)
1249 }
1250
1251 fn group_owned_metadata_blocks(
1260 &self,
1261 gi: usize,
1262 group_start: u64,
1263 bpg: u64,
1264 ) -> Vec<(u64, u64)> {
1265 let bs = self.sb.block_size() as u64;
1266 let mut runs = Vec::new();
1267
1268 if self.sb.group_has_super(gi as u64) {
1284 let gdt_blocks = (self.groups.len() as u64 * self.sb.desc_size as u64).div_ceil(bs);
1285 let reserved = u64::from(self.sb.reserved_gdt_blocks);
1286 runs.push((0, 1 + gdt_blocks + reserved));
1287 }
1288
1289 let itable_blocks =
1292 (self.sb.inodes_per_group as u64 * self.sb.inode_size as u64).div_ceil(bs);
1293 let g = &self.groups[gi];
1294 for (block, count) in [
1295 (g.block_bitmap, 1),
1296 (g.inode_bitmap, 1),
1297 (g.inode_table, itable_blocks),
1298 ] {
1299 if block >= group_start && block < group_start + bpg {
1300 runs.push((block - group_start, count));
1301 }
1302 }
1303 runs
1304 }
1305
1306 pub(crate) fn buffer_mark_block_run_used(
1307 &self,
1308 buf: &mut BlockBuffer,
1309 start: u64,
1310 len: u64,
1311 ) -> Result<()> {
1312 let bpg = self.sb.blocks_per_group as u64;
1313 let first_data = self.sb.first_data_block as u64;
1314 let gi = ((start - first_data) / bpg) as usize;
1315 if gi >= self.groups.len() {
1316 return Err(Error::InvalidBlock(start));
1317 }
1318 let group_start = first_data + gi as u64 * bpg;
1319 let bit_start = (start - group_start) as u32;
1320
1321 let was_uninit = self.clear_bgd_uninit_flag_if_set(buf, gi, BgdUninitFlag::Block)?;
1349 let reserved_runs = if was_uninit {
1350 self.group_owned_metadata_blocks(gi, group_start, bpg)
1351 } else {
1352 Vec::new()
1353 };
1354 let bitmap_block = self.groups[gi].block_bitmap;
1355 let bm = buf.get_mut(self, bitmap_block)?;
1356 if was_uninit {
1357 bm.iter_mut().for_each(|byte| *byte = 0);
1358 for (first_bit, count) in reserved_runs {
1359 for bit in first_bit..(first_bit + count).min(bpg) {
1360 let byte = (bit / 8) as usize;
1361 let mask = 1u8 << (bit % 8);
1362 if byte < bm.len() {
1363 bm[byte] |= mask;
1364 }
1365 }
1366 }
1367 }
1368 for i in 0..len {
1369 let bit = bit_start as u64 + i;
1370 let byte = (bit / 8) as usize;
1371 let mask = 1u8 << (bit % 8);
1372 if byte < bm.len() {
1373 bm[byte] |= mask;
1374 }
1375 }
1376 self.buffer_refresh_bitmap_csum(buf, gi, false)?;
1377 Ok(())
1378 }
1379
1380 pub(crate) fn buffer_refresh_bitmap_csum(
1386 &self,
1387 buf: &mut BlockBuffer,
1388 gi: usize,
1389 inode_bitmap: bool,
1390 ) -> Result<()> {
1391 if !self.csum.enabled {
1392 return Ok(());
1393 }
1394 let (bitmap_block, coverage, lo_off, hi_off) = if inode_bitmap {
1395 (
1396 self.groups[gi].inode_bitmap,
1397 (self.sb.inodes_per_group as usize).div_ceil(8),
1398 0x1A,
1399 0x3A,
1400 )
1401 } else {
1402 (
1403 self.groups[gi].block_bitmap,
1404 (self.sb.blocks_per_group as usize).div_ceil(8),
1405 0x18,
1406 0x38,
1407 )
1408 };
1409 let csum = {
1410 let bm = buf.get_mut(self, bitmap_block)?;
1411 let end = coverage.min(bm.len());
1412 crate::checksum::linux_crc32c(self.csum.seed, &bm[..end])
1413 };
1414
1415 let bs = self.sb.block_size() as u64;
1416 let desc_size = self.sb.desc_size as u64;
1417 let bgt_first_block = self.sb.first_data_block as u64 + 1;
1418 let byte_in_bgt = gi as u64 * desc_size;
1419 let bgt_block = bgt_first_block + byte_in_bgt / bs;
1420 let off = (byte_in_bgt % bs) as usize;
1421 let has_hi = desc_size >= 0x40;
1422 let block = buf.get_mut(self, bgt_block)?;
1423 block[off + lo_off..off + lo_off + 2]
1424 .copy_from_slice(&((csum & 0xFFFF) as u16).to_le_bytes());
1425 if has_hi {
1426 block[off + hi_off..off + hi_off + 2]
1427 .copy_from_slice(&(((csum >> 16) & 0xFFFF) as u16).to_le_bytes());
1428 }
1429 let stored_at = off + 0x1E;
1431 let end_desc = off + desc_size as usize;
1432 block[stored_at..stored_at + 2].copy_from_slice(&[0, 0]);
1433 let mut c = crate::checksum::linux_crc32c(self.csum.seed, &(gi as u32).to_le_bytes());
1434 c = crate::checksum::linux_crc32c(c, &block[off..end_desc]);
1435 block[stored_at..stored_at + 2].copy_from_slice(&(c as u16).to_le_bytes());
1436 Ok(())
1437 }
1438
1439 pub(crate) fn buffer_free_inode_slot(&self, buf: &mut BlockBuffer, ino: u32) -> Result<()> {
1445 let ipg = self.sb.inodes_per_group;
1446 let gi = ((ino - 1) / ipg) as usize;
1447 if gi >= self.groups.len() {
1448 return Err(Error::InvalidInode(ino));
1449 }
1450 let bit = ((ino - 1) % ipg) as u64;
1451 let bitmap_block = self.groups[gi].inode_bitmap;
1452 {
1453 let bm = buf.get_mut(self, bitmap_block)?;
1454 let byte = (bit / 8) as usize;
1455 let mask = 1u8 << (bit % 8);
1456 if byte < bm.len() {
1457 bm[byte] &= !mask;
1458 }
1459 }
1460 self.buffer_refresh_bitmap_csum(buf, gi, true)?;
1461 self.buffer_patch_bgd_counters(buf, gi, 0, 1, 0)
1462 }
1463
1464 pub(crate) fn buffer_mark_inode_used(&self, buf: &mut BlockBuffer, ino: u32) -> Result<()> {
1469 let ipg = self.sb.inodes_per_group;
1470 let gi = ((ino - 1) / ipg) as usize;
1471 if gi >= self.groups.len() {
1472 return Err(Error::InvalidInode(ino));
1473 }
1474 let bit = ((ino - 1) % ipg) as u64;
1475 let bitmap_block = self.groups[gi].inode_bitmap;
1476
1477 let was_uninit = self.clear_bgd_uninit_flag_if_set(buf, gi, BgdUninitFlag::Inode)?;
1494 let bm = buf.get_mut(self, bitmap_block)?;
1495 if was_uninit {
1496 bm.iter_mut().for_each(|byte| *byte = 0);
1497 let bits_per_block = (bm.len() as u64) * 8;
1505 for pad_bit in (ipg as u64)..bits_per_block {
1506 let byte = (pad_bit / 8) as usize;
1507 let mask = 1u8 << (pad_bit % 8);
1508 bm[byte] |= mask;
1509 }
1510 }
1511 let byte = (bit / 8) as usize;
1512 let mask = 1u8 << (bit % 8);
1513 if byte < bm.len() {
1514 bm[byte] |= mask;
1515 }
1516 self.buffer_refresh_bitmap_csum(buf, gi, true)?;
1517
1518 let floor = ipg.saturating_sub(bit as u32 + 1);
1527 let bs = self.sb.block_size() as u64;
1528 let desc_size = self.sb.desc_size as u64;
1529 let bgt_first_block = self.sb.first_data_block as u64 + 1;
1530 let byte_in_bgt = gi as u64 * desc_size;
1531 let bgt_block = bgt_first_block + byte_in_bgt / bs;
1532 let off = (byte_in_bgt % bs) as usize;
1533 let has_hi = desc_size >= 0x40;
1534 let block = buf.get_mut(self, bgt_block)?;
1535 let cur_lo = u16::from_le_bytes(block[off + 0x1C..off + 0x1E].try_into().unwrap()) as u32;
1536 let cur_hi = if has_hi {
1537 u16::from_le_bytes(block[off + 0x32..off + 0x34].try_into().unwrap()) as u32
1538 } else {
1539 0
1540 };
1541 let cur = (cur_hi << 16) | cur_lo;
1542 if floor < cur {
1543 block[off + 0x1C..off + 0x1E].copy_from_slice(&((floor & 0xFFFF) as u16).to_le_bytes());
1544 if has_hi {
1545 block[off + 0x32..off + 0x34]
1546 .copy_from_slice(&(((floor >> 16) & 0xFFFF) as u16).to_le_bytes());
1547 }
1548 if self.csum.enabled {
1549 let stored_at = off + 0x1E;
1550 let end_desc = off + desc_size as usize;
1551 block[stored_at..stored_at + 2].copy_from_slice(&[0, 0]);
1552 let seed = self.csum.seed;
1553 let mut c = crate::checksum::linux_crc32c(seed, &(gi as u32).to_le_bytes());
1554 c = crate::checksum::linux_crc32c(c, &block[off..end_desc]);
1555 block[stored_at..stored_at + 2].copy_from_slice(&(c as u16).to_le_bytes());
1556 }
1557 }
1558 Ok(())
1559 }
1560
1561 pub(crate) fn buffer_patch_bgd_counters(
1565 &self,
1566 buf: &mut BlockBuffer,
1567 gi: usize,
1568 free_blocks_delta: i32,
1569 free_inodes_delta: i32,
1570 used_dirs_delta: i32,
1571 ) -> Result<()> {
1572 let bs = self.sb.block_size() as u64;
1573 let desc_size = self.sb.desc_size as u64;
1574 let bgt_first_block = self.sb.first_data_block as u64 + 1;
1575 let byte_in_bgt = gi as u64 * desc_size;
1576 let bgt_block = bgt_first_block + byte_in_bgt / bs;
1577 let off_in_block = (byte_in_bgt % bs) as usize;
1578
1579 let block = buf.get_mut(self, bgt_block)?;
1580 patch_counter_u32(
1581 block,
1582 off_in_block + 0x0C,
1583 if desc_size >= 0x40 {
1584 Some(off_in_block + 0x2A)
1585 } else {
1586 None
1587 },
1588 free_blocks_delta,
1589 );
1590 patch_counter_u32(
1591 block,
1592 off_in_block + 0x0E,
1593 if desc_size >= 0x40 {
1594 Some(off_in_block + 0x2C)
1595 } else {
1596 None
1597 },
1598 free_inodes_delta,
1599 );
1600 patch_counter_u32(
1601 block,
1602 off_in_block + 0x10,
1603 if desc_size >= 0x40 {
1604 Some(off_in_block + 0x2E)
1605 } else {
1606 None
1607 },
1608 used_dirs_delta,
1609 );
1610
1611 if self.csum.enabled {
1612 let stored_at = off_in_block + 0x1E;
1613 let end_desc = off_in_block + desc_size as usize;
1614 block[stored_at..stored_at + 2].copy_from_slice(&[0, 0]);
1615 let seed = self.csum.seed;
1616 let mut c = crate::checksum::linux_crc32c(seed, &(gi as u32).to_le_bytes());
1617 c = crate::checksum::linux_crc32c(c, &block[off_in_block..end_desc]);
1618 let new_csum = c as u16;
1619 block[stored_at..stored_at + 2].copy_from_slice(&new_csum.to_le_bytes());
1620 }
1621 Ok(())
1622 }
1623
1624 pub(crate) fn buffer_patch_sb_counters(
1630 &self,
1631 buf: &mut BlockBuffer,
1632 free_blocks_delta: i64,
1633 free_inodes_delta: i32,
1634 ) -> Result<()> {
1635 let bs = self.sb.block_size() as u64;
1636 let sb_offset = crate::superblock::SUPERBLOCK_OFFSET; let sb_block = sb_offset / bs;
1638 let off_in_block = (sb_offset % bs) as usize;
1639
1640 let block = buf.get_mut(self, sb_block)?;
1641 let sb = &mut block[off_in_block..off_in_block + 1024];
1642
1643 let fi = u32::from_le_bytes(sb[0x10..0x14].try_into().unwrap()) as i64;
1645 let fi_new = (fi + free_inodes_delta as i64).max(0) as u32;
1646 sb[0x10..0x14].copy_from_slice(&fi_new.to_le_bytes());
1647
1648 let lo = u32::from_le_bytes(sb[0x0C..0x10].try_into().unwrap()) as u64;
1650 let hi = u32::from_le_bytes(sb[0x158..0x15C].try_into().unwrap()) as u64;
1651 let cur = ((hi << 32) | lo) as i64;
1652 let new = (cur + free_blocks_delta).max(0) as u64;
1653 sb[0x0C..0x10].copy_from_slice(&(new as u32).to_le_bytes());
1654 sb[0x158..0x15C].copy_from_slice(&((new >> 32) as u32).to_le_bytes());
1655
1656 if self.csum.enabled {
1657 let csum = crate::checksum::linux_crc32c(!0, &sb[..0x3FC]);
1658 sb[0x3FC..0x400].copy_from_slice(&csum.to_le_bytes());
1659 }
1660 Ok(())
1661 }
1662
1663 pub(crate) fn buffer_patch_sb_last_orphan(
1667 &self,
1668 buf: &mut BlockBuffer,
1669 value: u32,
1670 ) -> Result<()> {
1671 let bs = self.sb.block_size() as u64;
1672 let sb_offset = crate::superblock::SUPERBLOCK_OFFSET;
1673 let sb_block = sb_offset / bs;
1674 let off_in_block = (sb_offset % bs) as usize;
1675 let block = buf.get_mut(self, sb_block)?;
1676 let sb = &mut block[off_in_block..off_in_block + 1024];
1677 sb[0xE8..0xEC].copy_from_slice(&value.to_le_bytes());
1678 if self.csum.enabled {
1679 let csum = crate::checksum::linux_crc32c(!0, &sb[..0x3FC]);
1680 sb[0x3FC..0x400].copy_from_slice(&csum.to_le_bytes());
1681 }
1682 Ok(())
1683 }
1684
1685 pub(crate) fn buffer_remove_dir_entry(
1690 &self,
1691 buf: &mut BlockBuffer,
1692 parent_ino: u32,
1693 parent_inode: &Inode,
1694 name: &[u8],
1695 ) -> Result<()> {
1696 let bs = self.sb.block_size();
1697 let has_ft = self.sb.feature_incompat & features::Incompat::FILETYPE.bits() != 0;
1698 let n_blocks = parent_inode.size.div_ceil(bs as u64);
1699 for logical in 0..n_blocks {
1700 let Some(phys) = self.map_inode_logical(parent_inode, logical)? else {
1701 continue;
1702 };
1703 let block = buf.get_mut(self, phys)?;
1704 let reserved_tail = if self.csum.enabled && crate::dir::has_csum_tail(block) {
1705 12
1706 } else {
1707 0
1708 };
1709 if crate::dir::remove_entry_from_block(block, name, has_ft, reserved_tail)? {
1710 if self.csum.enabled && reserved_tail == 12 {
1711 self.csum
1712 .patch_dir_entry_tail(parent_ino, parent_inode.generation, block);
1713 }
1714 return Ok(());
1715 }
1716 }
1717 Err(Error::NotFound)
1718 }
1719
1720 pub(crate) fn buffer_update_dotdot(
1724 &self,
1725 buf: &mut BlockBuffer,
1726 dir_ino: u32,
1727 dir_inode: &Inode,
1728 new_parent_ino: u32,
1729 ) -> Result<()> {
1730 let phys = self
1731 .map_inode_logical(dir_inode, 0)?
1732 .ok_or(Error::Corrupt("buffer_update_dotdot: dir block 0 missing"))?;
1733 let block = buf.get_mut(self, phys)?;
1734 if block.len() < 24 {
1735 return Err(Error::Corrupt("buffer_update_dotdot: dir block too small"));
1736 }
1737 block[12..16].copy_from_slice(&new_parent_ino.to_le_bytes());
1738 if self.csum.enabled && crate::dir::has_csum_tail(block) {
1739 self.csum
1740 .patch_dir_entry_tail(dir_ino, dir_inode.generation, block);
1741 }
1742 Ok(())
1743 }
1744
1745 pub(crate) fn buffer_add_dir_entry_inplace(
1755 &self,
1756 buf: &mut BlockBuffer,
1757 parent_ino: u32,
1758 parent_inode: &Inode,
1759 name: &[u8],
1760 target_ino: u32,
1761 file_type: crate::dir::DirEntryType,
1762 ) -> Result<()> {
1763 let bs = self.sb.block_size();
1764 let has_ft = self.sb.feature_incompat & features::Incompat::FILETYPE.bits() != 0;
1765 let n_blocks = parent_inode.size.div_ceil(bs as u64);
1766 for logical in 0..n_blocks {
1767 let Some(phys) = self.map_inode_logical(parent_inode, logical)? else {
1768 continue;
1769 };
1770 let block = buf.get_mut(self, phys)?;
1771 let reserved_tail = if self.csum.enabled && crate::dir::has_csum_tail(block) {
1772 12
1773 } else {
1774 0
1775 };
1776 match crate::dir::add_entry_to_block(
1777 block,
1778 target_ino,
1779 name,
1780 file_type,
1781 has_ft,
1782 reserved_tail,
1783 ) {
1784 Ok(()) => {
1785 if self.csum.enabled && reserved_tail == 12 {
1786 self.csum
1787 .patch_dir_entry_tail(parent_ino, parent_inode.generation, block);
1788 }
1789 return Ok(());
1790 }
1791 Err(Error::OutOfBounds) => continue,
1792 Err(e) => return Err(e),
1793 }
1794 }
1795 Err(Error::OutOfBounds)
1798 }
1799
1800 pub(crate) fn commit_block_buffer(&self, buf: BlockBuffer) -> Result<()> {
1813 if buf.dirty.is_empty() {
1814 return Ok(());
1815 }
1816 let cleared = buf.uninit_cleared.clone();
1817 let publish = |fs: &Self| {
1818 let mut map = fs.uninit_cleared.lock().unwrap();
1819 for (gi, flags) in cleared {
1820 map.entry(gi).and_modify(|f| *f &= flags).or_insert(flags);
1821 }
1822 };
1823 if let Some(jw_mu) = &self.journal {
1824 let mut jw = jw_mu.lock().map_err(|_| {
1825 Error::Corrupt("journal writer mutex poisoned (prior write panicked)")
1826 })?;
1827 let mut tx = jw.begin();
1828 for (block, bytes) in &buf.dirty {
1829 tx.add_write(*block, bytes.clone())?;
1830 }
1831 jw.commit(self.dev.as_ref(), &tx)?;
1832 for (block, bytes) in buf.dirty {
1836 self.dev.populate_cache(block, bytes);
1837 }
1838 publish(self);
1839 Ok(())
1840 } else {
1841 let bs = self.sb.block_size() as u64;
1842 for (block, bytes) in buf.dirty {
1843 self.dev.write_at(block * bs, &bytes)?;
1844 }
1845 self.dev.flush()?;
1846 publish(self);
1847 Ok(())
1848 }
1849 }
1850
1851 pub fn apply_chown(&self, path: &str, uid: u32, gid: u32) -> Result<()> {
1859 if !self.dev.is_writable() {
1860 return Err(Error::ReadOnly);
1861 }
1862 let mut reader = |ino: u32| self.read_inode_verified(ino).map(|(i, _)| i);
1863 let ino = crate::path::lookup(self.dev.as_ref(), &self.sb, &mut reader, path)?;
1864 let (inode, mut raw) = self.read_inode_verified(ino)?;
1865
1866 if uid != u32::MAX {
1867 let lo = (uid & 0xFFFF) as u16;
1868 let hi = ((uid >> 16) & 0xFFFF) as u16;
1869 raw[0x02..0x04].copy_from_slice(&lo.to_le_bytes());
1870 raw[0x78..0x7A].copy_from_slice(&hi.to_le_bytes());
1871 }
1872 if gid != u32::MAX {
1873 let lo = (gid & 0xFFFF) as u16;
1874 let hi = ((gid >> 16) & 0xFFFF) as u16;
1875 raw[0x18..0x1A].copy_from_slice(&lo.to_le_bytes());
1876 raw[0x7A..0x7C].copy_from_slice(&hi.to_le_bytes());
1877 }
1878
1879 let now = now_unix_seconds();
1880 raw[0x0C..0x10].copy_from_slice(&now.to_le_bytes());
1881
1882 self.finalize_inode_raw(ino, inode.generation, &mut raw)?;
1883 self.commit_inode_write(ino, &raw)
1884 }
1885
1886 pub fn apply_set_flags(&self, path: &str, flags: u32) -> Result<()> {
1894 use crate::inode::{InodeFlags, OFF_CTIME, OFF_FLAGS};
1895 if !self.dev.is_writable() {
1896 return Err(Error::ReadOnly);
1897 }
1898 let mut reader = |ino: u32| self.read_inode_verified(ino).map(|(i, _)| i);
1899 let ino = crate::path::lookup(self.dev.as_ref(), &self.sb, &mut reader, path)?;
1900 let (inode, mut raw) = self.read_inode_verified(ino)?;
1901
1902 let managed = InodeFlags::EXTENTS.bits()
1903 | InodeFlags::INLINE_DATA.bits()
1904 | InodeFlags::EA_INODE.bits();
1905 if (flags ^ inode.flags) & managed != 0 {
1906 return Err(Error::InvalidArgument(
1907 "set_flags: cannot modify internally-managed inode flags (EXTENTS, INLINE_DATA, EA_INODE)",
1908 ));
1909 }
1910
1911 raw[OFF_FLAGS..OFF_FLAGS + 4].copy_from_slice(&flags.to_le_bytes());
1912
1913 let now = now_unix_seconds();
1914 raw[OFF_CTIME..OFF_CTIME + 4].copy_from_slice(&now.to_le_bytes());
1915
1916 self.finalize_inode_raw(ino, inode.generation, &mut raw)?;
1917 self.commit_inode_write(ino, &raw)
1918 }
1919
1920 pub fn apply_removexattr(&self, path: &str, name: &str) -> Result<()> {
1937 if !self.dev.is_writable() {
1938 return Err(Error::ReadOnly);
1939 }
1940 let mut reader = |ino: u32| self.read_inode_verified(ino).map(|(i, _)| i);
1941 let ino = crate::path::lookup(self.dev.as_ref(), &self.sb, &mut reader, path)?;
1942 let (inode, mut raw) = self.read_inode_verified(ino)?;
1943
1944 let inode_size = self.sb.inode_size as usize;
1946 let i_extra_isize = if raw.len() >= 0x82 {
1947 u16::from_le_bytes(raw[0x80..0x82].try_into().unwrap()) as usize
1948 } else {
1949 0
1950 };
1951 let region_start = 128 + i_extra_isize;
1952 let region_end = inode_size.min(raw.len());
1953 if region_start + 4 <= region_end {
1954 let region = &mut raw[region_start..region_end];
1955 match crate::xattr::plan_remove_in_inode_region(region, name)? {
1956 crate::xattr::RemoveOutcome::Removed => {
1957 self.finalize_inode_raw(ino, inode.generation, &mut raw)?;
1958 return self.commit_inode_write(ino, &raw);
1959 }
1960 crate::xattr::RemoveOutcome::NotFound => { }
1961 }
1962 }
1963
1964 if inode.file_acl != 0 {
1967 let bs = self.sb.block_size();
1968 let bs_u64 = bs as u64;
1969 let block_nr = inode.file_acl;
1970 let mut block = vec![0u8; bs as usize];
1971 self.dev.read_at(block_nr * bs_u64, &mut block)?;
1972 match crate::xattr::plan_remove_from_external_block(&mut block, name, 1)? {
1973 crate::xattr::BlockRemoveOutcome::Removed => {
1974 if self.csum.enabled {
1975 self.csum.patch_xattr_block(block_nr, &mut block);
1976 }
1977 self.dev.write_at(block_nr * bs_u64, &block)?;
1978 self.bump_inode_ctime(ino, inode.generation, &mut raw)?;
1979 self.dev.flush()?;
1980 return Ok(());
1981 }
1982 crate::xattr::BlockRemoveOutcome::RemovedNowEmpty => {
1983 let mut buf = BlockBuffer::new(bs);
1990 self.buffer_free_block_run_and_bgd(&mut buf, block_nr, 1)?;
1991 self.buffer_patch_sb_counters(&mut buf, 1, 0)?;
1992 raw[0x68..0x6C].copy_from_slice(&0u32.to_le_bytes());
1993 if raw.len() >= 0x76 {
1994 raw[0x74..0x76].copy_from_slice(&0u16.to_le_bytes());
1995 }
1996 let sectors_per_block = bs_u64 / 512;
1997 let new_blocks = inode.blocks.saturating_sub(sectors_per_block);
1998 Self::patch_inode_size_and_blocks(&mut raw, inode.size, new_blocks)?;
1999 raw[0x0C..0x10].copy_from_slice(&now_unix_seconds().to_le_bytes());
2000 self.finalize_inode_raw(ino, inode.generation, &mut raw)?;
2001 self.buffer_write_inode(&mut buf, ino, &raw)?;
2002 return self.commit_block_buffer(buf);
2003 }
2004 crate::xattr::BlockRemoveOutcome::NotFound => { }
2005 }
2006 }
2007 Err(Error::NotFound)
2008 }
2009
2010 pub fn apply_setxattr(&self, path: &str, name: &str, value: &[u8]) -> Result<()> {
2023 if !self.dev.is_writable() {
2024 return Err(Error::ReadOnly);
2025 }
2026 let mut reader = |ino: u32| self.read_inode_verified(ino).map(|(i, _)| i);
2027 let ino = crate::path::lookup(self.dev.as_ref(), &self.sb, &mut reader, path)?;
2028 let (inode, mut raw) = self.read_inode_verified(ino)?;
2029
2030 let inode_size = self.sb.inode_size as usize;
2031 let i_extra_isize = if raw.len() >= 0x82 {
2032 u16::from_le_bytes(raw[0x80..0x82].try_into().unwrap()) as usize
2033 } else {
2034 0
2035 };
2036 let region_start = 128 + i_extra_isize;
2037 let region_end = inode_size.min(raw.len());
2038 let inline_capable = region_start + 8 <= region_end;
2039
2040 let inline_result = if inline_capable {
2042 let region = &mut raw[region_start..region_end];
2043 crate::xattr::plan_set_in_inode_region(region, name, value)
2044 } else {
2045 Err(Error::NoSpaceLeftOnDevice)
2046 };
2047
2048 match inline_result {
2049 Ok(_) => {
2050 self.finalize_inode_raw(ino, inode.generation, &mut raw)?;
2052 self.commit_inode_write(ino, &raw)
2053 }
2054 Err(Error::NoSpaceLeftOnDevice) => {
2055 self.apply_setxattr_external_block(ino, &inode, &mut raw, name, value)
2056 }
2057 Err(e) => Err(e),
2058 }
2059 }
2060
2061 fn finalize_inode_raw(&self, ino: u32, generation: u32, raw: &mut [u8]) -> Result<()> {
2064 if self.csum.enabled {
2065 if let Some((lo, hi)) = self.csum.compute_inode_checksum(ino, generation, raw) {
2066 raw[0x7C..0x7E].copy_from_slice(&lo.to_le_bytes());
2067 if raw.len() >= 0x84 {
2068 raw[0x82..0x84].copy_from_slice(&hi.to_le_bytes());
2069 }
2070 }
2071 }
2072 Ok(())
2073 }
2074
2075 fn apply_setxattr_external_block(
2079 &self,
2080 ino: u32,
2081 inode: &crate::inode::Inode,
2082 raw: &mut [u8],
2083 name: &str,
2084 value: &[u8],
2085 ) -> Result<()> {
2086 let bs = self.sb.block_size();
2087 let bs_u64 = bs as u64;
2088
2089 let mut buf = BlockBuffer::new(bs);
2092
2093 if inode.file_acl != 0 {
2095 let block_nr = inode.file_acl;
2096 let mut block = vec![0u8; bs as usize];
2097 self.dev.read_at(block_nr * bs_u64, &mut block)?;
2098 crate::xattr::plan_set_in_external_block(&mut block, name, value, 1)?;
2099 if self.csum.enabled {
2100 self.csum.patch_xattr_block(block_nr, &mut block);
2101 }
2102 buf.put(block_nr, block);
2103 let now = now_unix_seconds();
2105 raw[0x0C..0x10].copy_from_slice(&now.to_le_bytes());
2106 self.finalize_inode_raw(ino, inode.generation, raw)?;
2107 self.buffer_write_inode(&mut buf, ino, raw)?;
2108 return self.commit_block_buffer(buf);
2109 }
2110
2111 let mut bitmap_reader = |block: u64| self.read_block(block);
2114 let inode_group = (ino - 1) / self.sb.inodes_per_group;
2115 let plan = crate::alloc::plan_block_allocation(
2116 &self.sb,
2117 &self.allocation_groups(),
2118 1,
2119 inode_group,
2120 &mut bitmap_reader,
2121 )?;
2122 let block_nr = plan.first_block;
2123
2124 let mut block = vec![0u8; bs as usize];
2125 crate::xattr::plan_set_in_external_block(&mut block, name, value, 1)?;
2126 if self.csum.enabled {
2127 self.csum.patch_xattr_block(block_nr, &mut block);
2128 }
2129 buf.put(block_nr, block);
2130
2131 self.buffer_mark_block_run_used(&mut buf, block_nr, 1)?;
2133 self.buffer_patch_bgd_counters(
2134 &mut buf,
2135 plan.bgd.group_idx as usize,
2136 plan.bgd.free_blocks_delta,
2137 plan.bgd.free_inodes_delta,
2138 plan.bgd.used_dirs_delta,
2139 )?;
2140 self.buffer_patch_sb_counters(
2141 &mut buf,
2142 plan.sb.free_blocks_delta,
2143 plan.sb.free_inodes_delta,
2144 )?;
2145
2146 let (acl_hi, acl_lo) = crate::extent_mut::split_phys_block(block_nr);
2149 raw[0x68..0x6C].copy_from_slice(&acl_lo.to_le_bytes());
2150 if raw.len() >= 0x76 {
2151 raw[0x74..0x76].copy_from_slice(&acl_hi.to_le_bytes());
2152 }
2153 let sectors_per_block = bs_u64 / 512;
2156 let new_blocks = inode.blocks.saturating_add(sectors_per_block);
2157 Self::patch_inode_size_and_blocks(raw, inode.size, new_blocks)?;
2158 let now = now_unix_seconds();
2159 raw[0x0C..0x10].copy_from_slice(&now.to_le_bytes());
2160 self.finalize_inode_raw(ino, inode.generation, raw)?;
2161 self.buffer_write_inode(&mut buf, ino, raw)?;
2162
2163 self.commit_block_buffer(buf)
2164 }
2165
2166 fn bump_inode_ctime(&self, ino: u32, generation: u32, raw: &mut [u8]) -> Result<()> {
2170 let now = now_unix_seconds();
2171 raw[0x0C..0x10].copy_from_slice(&now.to_le_bytes());
2172 self.finalize_inode_raw(ino, generation, raw)?;
2173 self.commit_inode_write(ino, raw)
2174 }
2175
2176 pub fn apply_utimens(
2197 &self,
2198 path: &str,
2199 atime_sec: i64,
2200 atime_nsec: u32,
2201 mtime_sec: i64,
2202 mtime_nsec: u32,
2203 ) -> Result<()> {
2204 if !self.dev.is_writable() {
2205 return Err(Error::ReadOnly);
2206 }
2207 for secs in [atime_sec, mtime_sec] {
2208 if secs != TIME_OMIT
2209 && !(crate::inode::MIN_ENCODABLE_TIME..=crate::inode::MAX_ENCODABLE_TIME)
2210 .contains(&secs)
2211 {
2212 return Err(Error::InvalidArgument(
2213 "timestamp outside the range ext4 can store (1901..2446)",
2214 ));
2215 }
2216 }
2217 let mut reader = |ino: u32| self.read_inode_verified(ino).map(|(i, _)| i);
2218 let ino = crate::path::lookup(self.dev.as_ref(), &self.sb, &mut reader, path)?;
2219 let (inode, mut raw) = self.read_inode_verified(ino)?;
2220
2221 let (atime_base, atime_epoch) = crate::inode::encode_extra_time(atime_sec);
2222 let (mtime_base, mtime_epoch) = crate::inode::encode_extra_time(mtime_sec);
2223
2224 let i_extra_isize = if raw.len() >= 0x82 {
2231 u16::from_le_bytes(raw[0x80..0x82].try_into().unwrap())
2232 } else {
2233 0
2234 };
2235 let has_mtime_extra = i_extra_isize >= 12 && raw.len() >= 0x8C;
2236 let has_atime_extra = i_extra_isize >= 16 && raw.len() >= 0x90;
2237
2238 if (mtime_sec != TIME_OMIT && mtime_epoch != 0 && !has_mtime_extra)
2241 || (atime_sec != TIME_OMIT && atime_epoch != 0 && !has_atime_extra)
2242 {
2243 return Err(Error::InvalidArgument(
2244 "timestamp past 2038 needs an *_extra field this inode is too small to hold",
2245 ));
2246 }
2247
2248 if atime_sec != TIME_OMIT {
2249 raw[0x08..0x0C].copy_from_slice(&atime_base.to_le_bytes());
2250 }
2251 if mtime_sec != TIME_OMIT {
2252 raw[0x10..0x14].copy_from_slice(&mtime_base.to_le_bytes());
2253 }
2254 let now = now_unix_seconds();
2256 raw[0x0C..0x10].copy_from_slice(&now.to_le_bytes());
2257
2258 if i_extra_isize >= 8 && raw.len() >= 0x88 {
2259 raw[0x84..0x88].copy_from_slice(&0u32.to_le_bytes());
2263 }
2264 if mtime_sec != TIME_OMIT && has_mtime_extra {
2265 let packed = pack_nsec_lo(mtime_nsec) | mtime_epoch;
2266 raw[0x88..0x8C].copy_from_slice(&packed.to_le_bytes());
2267 }
2268 if atime_sec != TIME_OMIT && has_atime_extra {
2269 let packed = pack_nsec_lo(atime_nsec) | atime_epoch;
2270 raw[0x8C..0x90].copy_from_slice(&packed.to_le_bytes());
2271 }
2272
2273 self.finalize_inode_raw(ino, inode.generation, &mut raw)?;
2274 self.commit_inode_write(ino, &raw)
2275 }
2276
2277 pub fn apply_unlink(&self, path: &str) -> Result<()> {
2294 if !self.dev.is_writable() {
2295 return Err(Error::ReadOnly);
2296 }
2297 let trailing_slash = path.len() > 1 && path.ends_with('/');
2302 let (parent_ino, base_name) = split_parent_and_base(path)?;
2303
2304 let mut reader = |ino: u32| self.read_inode_verified(ino).map(|(i, _)| i);
2306 let parent_ino_num =
2307 crate::path::lookup(self.dev.as_ref(), &self.sb, &mut reader, &parent_ino)?;
2308 let (parent_inode, _parent_raw) = self.read_inode_verified(parent_ino_num)?;
2309 if !parent_inode.is_dir() {
2310 return Err(Error::NotADirectory);
2311 }
2312
2313 let target_ino = self.find_entry_in_dir(&parent_inode, base_name.as_bytes())?;
2314 let (target_inode, mut target_raw) = self.read_inode_verified(target_ino)?;
2315 if target_inode.is_dir() {
2316 return Err(Error::IsADirectory);
2319 }
2320 if trailing_slash {
2321 return Err(Error::NotADirectory);
2324 }
2325
2326 let mut buf = BlockBuffer::new(self.sb.block_size());
2328
2329 let has_ft = self.sb.feature_incompat & features::Incompat::FILETYPE.bits() != 0;
2332 let bs = self.sb.block_size();
2333 let parent_blocks = parent_inode.size.div_ceil(bs as u64);
2334 let mut removed = false;
2335 for logical in 0..parent_blocks {
2336 let Some(phys) = self.map_inode_logical(&parent_inode, logical)? else {
2337 continue;
2338 };
2339 let block = buf.get_mut(self, phys)?;
2340 let reserved_tail = if self.csum.enabled && crate::dir::has_csum_tail(block) {
2343 12
2344 } else {
2345 0
2346 };
2347 if crate::dir::remove_entry_from_block(
2348 block,
2349 base_name.as_bytes(),
2350 has_ft,
2351 reserved_tail,
2352 )? {
2353 if self.csum.enabled && reserved_tail == 12 {
2355 self.csum
2356 .patch_dir_entry_tail(parent_ino_num, parent_inode.generation, block);
2357 }
2358 removed = true;
2359 break;
2360 }
2361 }
2362 if !removed {
2363 return Err(Error::NotFound);
2364 }
2365
2366 let new_links = target_inode.links_count.saturating_sub(1);
2368 target_raw[0x1A..0x1C].copy_from_slice(&new_links.to_le_bytes());
2369
2370 if new_links > 0 {
2371 self.finalize_inode_raw(target_ino, target_inode.generation, &mut target_raw)?;
2372 self.buffer_write_inode(&mut buf, target_ino, &target_raw)?;
2373 return self.commit_block_buffer(buf);
2374 }
2375
2376 let mut freed_sectors: u64 = 0;
2379 let sectors_per_block = bs as u64 / 512;
2380 if target_inode.has_extents() && target_inode.size > 0 {
2381 let (_sc, muts) = crate::file_mut::plan_truncate_shrink(
2382 target_inode.size,
2383 0,
2384 &target_inode.block,
2385 bs,
2386 )?;
2387 for m in &muts {
2388 if let crate::extent_mut::ExtentMutation::FreePhysicalRun { start, len } = m {
2389 self.buffer_free_block_run_and_bgd(&mut buf, *start, *len as u64)?;
2390 freed_sectors += *len as u64 * sectors_per_block;
2391 }
2392 }
2393 }
2394
2395 self.buffer_free_inode_slot(&mut buf, target_ino)?;
2399
2400 let freed_blocks = freed_sectors.checked_div(sectors_per_block).unwrap_or(0);
2401 self.buffer_patch_sb_counters(&mut buf, freed_blocks as i64, 1)?;
2402
2403 let inode_size = self.sb.inode_size as usize;
2406 let old_gen = target_inode.generation;
2407 for b in &mut target_raw[..inode_size] {
2408 *b = 0;
2409 }
2410 let dtime = now_unix_seconds();
2411 target_raw[0x14..0x18].copy_from_slice(&dtime.to_le_bytes()); target_raw[0x64..0x68].copy_from_slice(&old_gen.to_le_bytes()); self.finalize_inode_raw(target_ino, old_gen, &mut target_raw)?;
2414 self.buffer_write_inode(&mut buf, target_ino, &target_raw)?;
2415
2416 self.commit_block_buffer(buf)
2417 }
2418
2419 fn plan_new_inode_in_dir(&self, path: &str) -> Result<NewInodePlan> {
2424 let (parent_path, base_name) = split_parent_and_base(path)?;
2425 if base_name.len() > 255 {
2426 return Err(Error::NameTooLong);
2427 }
2428
2429 let mut reader = |ino: u32| self.read_inode_verified(ino).map(|(i, _)| i);
2430 let parent_ino =
2431 crate::path::lookup(self.dev.as_ref(), &self.sb, &mut reader, &parent_path)?;
2432 let (parent_inode, _) = self.read_inode_verified(parent_ino)?;
2433 if !parent_inode.is_dir() {
2434 return Err(Error::NotADirectory);
2435 }
2436 if self
2437 .find_entry_in_dir(&parent_inode, base_name.as_bytes())
2438 .is_ok()
2439 {
2440 return Err(Error::AlreadyExists);
2441 }
2442
2443 let parent_group = (parent_ino - 1) / self.sb.inodes_per_group;
2444 let bs = self.sb.block_size();
2445 let mut bitmap_reader = |block: u64| self.read_block(block);
2446 let plan = crate::alloc::plan_inode_allocation(
2447 &self.sb,
2448 &self.allocation_groups(),
2449 false,
2450 parent_group,
2451 &mut bitmap_reader,
2452 )?;
2453 let new_ino = plan.inode;
2454
2455 let mut buf = BlockBuffer::new(bs);
2456 self.buffer_mark_inode_used(&mut buf, new_ino)?;
2457 self.buffer_patch_bgd_counters(
2458 &mut buf,
2459 plan.bgd.group_idx as usize,
2460 plan.bgd.free_blocks_delta,
2461 plan.bgd.free_inodes_delta,
2462 plan.bgd.used_dirs_delta,
2463 )?;
2464 self.buffer_patch_sb_counters(
2465 &mut buf,
2466 plan.sb.free_blocks_delta,
2467 plan.sb.free_inodes_delta,
2468 )?;
2469
2470 Ok(NewInodePlan {
2471 new_ino,
2472 parent_ino,
2473 parent_inode,
2474 buf,
2475 base_name,
2476 })
2477 }
2478
2479 pub fn apply_create(&self, path: &str, mode: u16) -> Result<u32> {
2494 if !self.dev.is_writable() {
2495 return Err(Error::ReadOnly);
2496 }
2497 let NewInodePlan {
2498 new_ino,
2499 parent_ino,
2500 parent_inode,
2501 mut buf,
2502 base_name,
2503 } = self.plan_new_inode_in_dir(path)?;
2504
2505 let raw = self.build_regular_file_inode(new_ino, mode)?;
2506 self.buffer_write_inode(&mut buf, new_ino, &raw)?;
2507
2508 match self.buffer_add_dir_entry_inplace(
2513 &mut buf,
2514 parent_ino,
2515 &parent_inode,
2516 base_name.as_bytes(),
2517 new_ino,
2518 crate::dir::DirEntryType::RegFile,
2519 ) {
2520 Ok(()) => {
2521 self.commit_block_buffer(buf)?;
2522 Ok(new_ino)
2523 }
2524 Err(Error::OutOfBounds) => {
2525 self.commit_block_buffer(buf)?;
2531 self.extend_dir_and_add_entry(
2532 parent_ino,
2533 base_name.as_bytes(),
2534 new_ino,
2535 crate::dir::DirEntryType::RegFile,
2536 )?;
2537 Ok(new_ino)
2538 }
2539 Err(e) => Err(e),
2540 }
2541 }
2542
2543 pub fn apply_mknod(&self, path: &str, mode: u16, major: u32, minor: u32) -> Result<u32> {
2548 if !self.dev.is_writable() {
2549 return Err(Error::ReadOnly);
2550 }
2551 let file_type = mode & crate::inode::S_IFMT;
2552 let dir_entry_type = match file_type {
2553 crate::inode::S_IFCHR => crate::dir::DirEntryType::CharDev,
2554 crate::inode::S_IFBLK => crate::dir::DirEntryType::BlockDev,
2555 crate::inode::S_IFIFO => crate::dir::DirEntryType::Fifo,
2556 crate::inode::S_IFSOCK => crate::dir::DirEntryType::Socket,
2557 _ => {
2558 return Err(Error::InvalidArgument(
2559 "mknod: unsupported type; use create/mkdir for reg/dir",
2560 ))
2561 }
2562 };
2563 let NewInodePlan {
2564 new_ino,
2565 parent_ino,
2566 parent_inode,
2567 mut buf,
2568 base_name,
2569 } = self.plan_new_inode_in_dir(path)?;
2570
2571 let raw = self.build_special_file_inode(new_ino, mode, major, minor)?;
2572 self.buffer_write_inode(&mut buf, new_ino, &raw)?;
2573
2574 match self.buffer_add_dir_entry_inplace(
2575 &mut buf,
2576 parent_ino,
2577 &parent_inode,
2578 base_name.as_bytes(),
2579 new_ino,
2580 dir_entry_type,
2581 ) {
2582 Ok(()) => {
2583 self.commit_block_buffer(buf)?;
2584 Ok(new_ino)
2585 }
2586 Err(Error::OutOfBounds) => {
2587 self.commit_block_buffer(buf)?;
2588 self.extend_dir_and_add_entry(
2589 parent_ino,
2590 base_name.as_bytes(),
2591 new_ino,
2592 dir_entry_type,
2593 )?;
2594 Ok(new_ino)
2595 }
2596 Err(e) => Err(e),
2597 }
2598 }
2599
2600 fn stamp_inode_checksum(&self, raw: &mut [u8], ino: u32, generation: u32) {
2603 use crate::inode::{INODE_SIZE_WITH_EXTRA, OFF_CHECKSUM_HI, OFF_CHECKSUM_LO};
2604 if self.csum.enabled {
2605 if let Some((lo, hi)) = self.csum.compute_inode_checksum(ino, generation, raw) {
2606 raw[OFF_CHECKSUM_LO..OFF_CHECKSUM_LO + 2].copy_from_slice(&lo.to_le_bytes());
2607 if raw.len() >= INODE_SIZE_WITH_EXTRA {
2608 raw[OFF_CHECKSUM_HI..OFF_CHECKSUM_HI + 2].copy_from_slice(&hi.to_le_bytes());
2609 }
2610 }
2611 }
2612 }
2613
2614 fn build_special_file_inode(
2615 &self,
2616 ino: u32,
2617 mode: u16,
2618 major: u32,
2619 minor: u32,
2620 ) -> Result<Vec<u8>> {
2621 use crate::inode::{OFF_BLOCK, OFF_LINKS_COUNT, OFF_MODE};
2622 let inode_size = self.sb.inode_size as usize;
2623 let mut raw = vec![0u8; inode_size];
2624
2625 raw[OFF_MODE..OFF_MODE + 2].copy_from_slice(&mode.to_le_bytes());
2626 raw[OFF_LINKS_COUNT..OFF_LINKS_COUNT + 2].copy_from_slice(&1u16.to_le_bytes());
2627
2628 let file_type = mode & crate::inode::S_IFMT;
2631 if file_type == crate::inode::S_IFBLK || file_type == crate::inode::S_IFCHR {
2632 let old_dev = (major << 8) | (minor & 0xff);
2633 raw[OFF_BLOCK..OFF_BLOCK + 4].copy_from_slice(&old_dev.to_le_bytes());
2634 let new_dev = (minor & 0xff) | (major << 8) | ((minor & !0xff) << 12);
2635 raw[OFF_BLOCK + 4..OFF_BLOCK + 8].copy_from_slice(&new_dev.to_le_bytes());
2636 }
2637
2638 let now = now_unix_seconds();
2639 write_inode_timestamps(&mut raw, now);
2640 let generation = alloc_inode_generation();
2641 write_inode_generation(&mut raw, generation);
2642 write_inode_extra_isize(&mut raw);
2643 self.stamp_inode_checksum(&mut raw, ino, generation);
2644 Ok(raw)
2645 }
2646
2647 pub fn apply_symlink(&self, target: &str, linkpath: &str) -> Result<u32> {
2662 if !self.dev.is_writable() {
2663 return Err(Error::ReadOnly);
2664 }
2665 if target.is_empty() {
2666 return Err(Error::InvalidArgument("symlink target is empty"));
2667 }
2668 let max_target = 4096usize.min(self.sb.block_size() as usize);
2672 if target.len() > max_target {
2673 return Err(Error::NameTooLong);
2674 }
2675
2676 let NewInodePlan {
2677 new_ino,
2678 parent_ino,
2679 parent_inode,
2680 mut buf,
2681 base_name,
2682 } = self.plan_new_inode_in_dir(linkpath)?;
2683
2684 let parent_group = (parent_ino - 1) / self.sb.inodes_per_group;
2685 let bs = self.sb.block_size();
2686
2687 let raw = if target.len() < 60 {
2693 self.build_fast_symlink_inode(new_ino, target.as_bytes())?
2694 } else {
2695 let mut bitmap_reader = |block: u64| self.read_block(block);
2696 let bplan = crate::alloc::plan_block_allocation(
2697 &self.sb,
2698 &self.allocation_groups(),
2699 1,
2700 parent_group,
2701 &mut bitmap_reader,
2702 )?;
2703 let data_phys = bplan.first_block;
2704
2705 self.buffer_mark_block_run_used(&mut buf, data_phys, 1)?;
2706 self.buffer_patch_bgd_counters(
2707 &mut buf,
2708 bplan.bgd.group_idx as usize,
2709 bplan.bgd.free_blocks_delta,
2710 bplan.bgd.free_inodes_delta,
2711 bplan.bgd.used_dirs_delta,
2712 )?;
2713 self.buffer_patch_sb_counters(
2714 &mut buf,
2715 bplan.sb.free_blocks_delta,
2716 bplan.sb.free_inodes_delta,
2717 )?;
2718
2719 let mut block = vec![0u8; bs as usize];
2720 block[..target.len()].copy_from_slice(target.as_bytes());
2721 buf.put(data_phys, block);
2722
2723 self.build_slow_symlink_inode(new_ino, target.as_bytes(), data_phys)?
2724 };
2725 self.buffer_write_inode(&mut buf, new_ino, &raw)?;
2726
2727 match self.buffer_add_dir_entry_inplace(
2728 &mut buf,
2729 parent_ino,
2730 &parent_inode,
2731 base_name.as_bytes(),
2732 new_ino,
2733 crate::dir::DirEntryType::Symlink,
2734 ) {
2735 Ok(()) => {
2736 self.commit_block_buffer(buf)?;
2737 Ok(new_ino)
2738 }
2739 Err(Error::OutOfBounds) => {
2740 self.commit_block_buffer(buf)?;
2741 self.extend_dir_and_add_entry(
2742 parent_ino,
2743 base_name.as_bytes(),
2744 new_ino,
2745 crate::dir::DirEntryType::Symlink,
2746 )?;
2747 Ok(new_ino)
2748 }
2749 Err(e) => Err(e),
2750 }
2751 }
2752
2753 fn build_fast_symlink_inode(&self, ino: u32, target: &[u8]) -> Result<Vec<u8>> {
2758 use crate::inode::{OFF_BLOCK, OFF_FLAGS, OFF_LINKS_COUNT, OFF_MODE, OFF_SIZE_LO};
2759 debug_assert!(target.len() < 60);
2760 let mut raw = vec![0u8; self.sb.inode_size as usize];
2761
2762 let mode_bits = crate::inode::S_IFLNK | 0o0777;
2765 raw[OFF_MODE..OFF_MODE + 2].copy_from_slice(&mode_bits.to_le_bytes());
2766 raw[OFF_SIZE_LO..OFF_SIZE_LO + 4].copy_from_slice(&(target.len() as u32).to_le_bytes());
2767 raw[OFF_LINKS_COUNT..OFF_LINKS_COUNT + 2].copy_from_slice(&1u16.to_le_bytes());
2768 raw[OFF_FLAGS..OFF_FLAGS + 4].copy_from_slice(&0u32.to_le_bytes());
2770 let inline_target_off = OFF_BLOCK;
2771 raw[inline_target_off..inline_target_off + target.len()].copy_from_slice(target);
2772
2773 let now = now_unix_seconds();
2774 write_inode_timestamps(&mut raw, now);
2775 let generation = alloc_inode_generation();
2776 write_inode_generation(&mut raw, generation);
2777 write_inode_extra_isize(&mut raw);
2778 self.stamp_inode_checksum(&mut raw, ino, generation);
2779 Ok(raw)
2780 }
2781
2782 fn build_slow_symlink_inode(&self, ino: u32, target: &[u8], data_phys: u64) -> Result<Vec<u8>> {
2790 use crate::inode::{
2791 OFF_BLOCK, OFF_BLOCKS_LO, OFF_FLAGS, OFF_LINKS_COUNT, OFF_MODE, OFF_SIZE_LO,
2792 };
2793 debug_assert!(target.len() >= 60 && target.len() <= 4096);
2794 let mut raw = vec![0u8; self.sb.inode_size as usize];
2795
2796 let mode_bits = crate::inode::S_IFLNK | 0o0777;
2797 raw[OFF_MODE..OFF_MODE + 2].copy_from_slice(&mode_bits.to_le_bytes());
2798 raw[OFF_SIZE_LO..OFF_SIZE_LO + 4].copy_from_slice(&(target.len() as u32).to_le_bytes());
2799 raw[OFF_LINKS_COUNT..OFF_LINKS_COUNT + 2].copy_from_slice(&1u16.to_le_bytes());
2800 let bs = self.sb.block_size() as u64;
2801 let sectors = bs / 512;
2802 raw[OFF_BLOCKS_LO..OFF_BLOCKS_LO + 4].copy_from_slice(&(sectors as u32).to_le_bytes());
2803 raw[OFF_FLAGS..OFF_FLAGS + 4]
2804 .copy_from_slice(&crate::inode::InodeFlags::EXTENTS.bits().to_le_bytes());
2805
2806 let extent_header_off = OFF_BLOCK;
2808 raw[extent_header_off..extent_header_off + 2]
2809 .copy_from_slice(&crate::extent::EXT4_EXT_MAGIC.to_le_bytes());
2810 raw[extent_header_off + 2..extent_header_off + 4].copy_from_slice(&1u16.to_le_bytes());
2811 raw[extent_header_off + 4..extent_header_off + 6].copy_from_slice(&4u16.to_le_bytes());
2812 raw[extent_header_off + 6..extent_header_off + 8].copy_from_slice(&0u16.to_le_bytes());
2813
2814 let extent_entry_off = extent_header_off + 12;
2816 raw[extent_entry_off..extent_entry_off + 4].copy_from_slice(&0u32.to_le_bytes());
2817 raw[extent_entry_off + 4..extent_entry_off + 6].copy_from_slice(&1u16.to_le_bytes());
2818 let (extent_phys_hi, extent_phys_lo) = crate::extent_mut::split_phys_block(data_phys);
2819 raw[extent_entry_off + 6..extent_entry_off + 8]
2820 .copy_from_slice(&extent_phys_hi.to_le_bytes());
2821 raw[extent_entry_off + 8..extent_entry_off + 12]
2822 .copy_from_slice(&extent_phys_lo.to_le_bytes());
2823
2824 let now = now_unix_seconds();
2825 write_inode_timestamps(&mut raw, now);
2826 let generation = alloc_inode_generation();
2827 write_inode_generation(&mut raw, generation);
2828 write_inode_extra_isize(&mut raw);
2829 self.stamp_inode_checksum(&mut raw, ino, generation);
2830 Ok(raw)
2831 }
2832
2833 fn build_regular_file_inode(&self, ino: u32, mode: u16) -> Result<Vec<u8>> {
2838 use crate::inode::{OFF_BLOCK, OFF_FLAGS, OFF_LINKS_COUNT, OFF_MODE};
2839 let mut raw = vec![0u8; self.sb.inode_size as usize];
2840
2841 let mode_bits = crate::inode::S_IFREG | (mode & 0x0FFF);
2842 raw[OFF_MODE..OFF_MODE + 2].copy_from_slice(&mode_bits.to_le_bytes());
2843 raw[OFF_LINKS_COUNT..OFF_LINKS_COUNT + 2].copy_from_slice(&1u16.to_le_bytes());
2844
2845 if self.flavor.uses_extents() {
2851 raw[OFF_FLAGS..OFF_FLAGS + 4]
2852 .copy_from_slice(&crate::inode::InodeFlags::EXTENTS.bits().to_le_bytes());
2853
2854 let extent_header_off = OFF_BLOCK;
2855 raw[extent_header_off..extent_header_off + 2]
2856 .copy_from_slice(&crate::extent::EXT4_EXT_MAGIC.to_le_bytes());
2857 raw[extent_header_off + 2..extent_header_off + 4].copy_from_slice(&0u16.to_le_bytes());
2858 raw[extent_header_off + 4..extent_header_off + 6].copy_from_slice(&4u16.to_le_bytes());
2859 raw[extent_header_off + 6..extent_header_off + 8].copy_from_slice(&0u16.to_le_bytes());
2860 }
2861
2862 let now = now_unix_seconds();
2863 write_inode_timestamps(&mut raw, now);
2864 let generation = alloc_inode_generation();
2865 write_inode_generation(&mut raw, generation);
2866 write_inode_extra_isize(&mut raw);
2867 self.stamp_inode_checksum(&mut raw, ino, generation);
2868 Ok(raw)
2869 }
2870
2871 pub fn apply_replace_file_content(&self, path: &str, data: &[u8]) -> Result<u64> {
2887 if !self.dev.is_writable() {
2888 return Err(Error::ReadOnly);
2889 }
2890 let mut reader = |ino: u32| self.read_inode_verified(ino).map(|(i, _)| i);
2891 let ino = crate::path::lookup(self.dev.as_ref(), &self.sb, &mut reader, path)?;
2892 let (inode, mut raw) = self.read_inode_verified(ino)?;
2893 if !inode.is_file() {
2894 return Err(Error::InvalidArgument(
2895 "write_file target is not a regular file",
2896 ));
2897 }
2898 if !inode.has_extents() {
2899 return self.apply_replace_file_content_indirect(ino, inode, raw, data);
2906 }
2907
2908 let bs = self.sb.block_size();
2909 let sectors_per_block = bs as u64 / 512;
2910 let group_idx_of_inode = ((ino - 1) / self.sb.inodes_per_group) as usize;
2911
2912 let mut buf = BlockBuffer::new(bs);
2916
2917 let mut freed_fs_blocks: u64 = 0;
2920 if inode.size > 0 {
2921 let (_sc, muts) =
2922 crate::file_mut::plan_truncate_shrink(inode.size, 0, &inode.block, bs)?;
2923 for m in &muts {
2924 if let crate::extent_mut::ExtentMutation::FreePhysicalRun { start, len } = m {
2925 freed_fs_blocks +=
2926 self.buffer_free_block_run_and_bgd(&mut buf, *start, *len as u64)?;
2927 }
2928 }
2929 }
2930
2931 let mut root = vec![0u8; 60];
2933 root[0..2].copy_from_slice(&crate::extent::EXT4_EXT_MAGIC.to_le_bytes());
2934 root[4..6].copy_from_slice(&4u16.to_le_bytes()); Self::patch_inode_block_area(&mut raw, &root)?;
2936
2937 if data.is_empty() {
2940 self.finalize_inode_raw_after_write(ino, &mut raw, &inode, 0, 0)?;
2941 if freed_fs_blocks > 0 {
2942 self.buffer_patch_sb_counters(&mut buf, freed_fs_blocks as i64, 0)?;
2943 }
2944 self.buffer_write_inode(&mut buf, ino, &raw)?;
2945 self.commit_block_buffer(buf)?;
2946 return Ok(0);
2947 }
2948
2949 let needed_blocks: u32 = data.len().div_ceil(bs as usize) as u32;
2951 let mut bitmap_reader = |block: u64| self.read_block(block);
2952 let plan = crate::alloc::plan_block_allocation(
2953 &self.sb,
2954 &self.allocation_groups(),
2955 needed_blocks,
2956 group_idx_of_inode as u32,
2957 &mut bitmap_reader,
2958 )?;
2959
2960 self.buffer_mark_block_run_used(&mut buf, plan.first_block, needed_blocks as u64)?;
2963 self.buffer_patch_bgd_counters(
2964 &mut buf,
2965 plan.bgd.group_idx as usize,
2966 plan.bgd.free_blocks_delta,
2967 plan.bgd.free_inodes_delta,
2968 plan.bgd.used_dirs_delta,
2969 )?;
2970 let net_block_delta = freed_fs_blocks as i64 - needed_blocks as i64;
2971 self.buffer_patch_sb_counters(&mut buf, net_block_delta, 0)?;
2972
2973 for i in 0..needed_blocks as u64 {
2975 let off_in_data = (i as usize) * bs as usize;
2976 let chunk_end = ((i as usize + 1) * bs as usize).min(data.len());
2977 let mut block = vec![0u8; bs as usize];
2978 block[..chunk_end - off_in_data].copy_from_slice(&data[off_in_data..chunk_end]);
2979 buf.put(plan.first_block + i, block);
2980 }
2981
2982 let new_extent = crate::extent::Extent {
2985 logical_block: 0,
2986 length: needed_blocks as u16,
2987 physical_block: plan.first_block,
2988 uninitialized: false,
2989 };
2990 let muts = crate::extent_mut::plan_insert_extent(&root, new_extent)?;
2991 for m in &muts {
2992 if let crate::extent_mut::ExtentMutation::WriteRoot { bytes } = m {
2993 Self::patch_inode_block_area(&mut raw, bytes)?;
2994 }
2995 }
2996 let new_size = data.len() as u64;
2997 let new_sectors = needed_blocks as u64 * sectors_per_block;
2998 self.finalize_inode_raw_after_write(ino, &mut raw, &inode, new_size, new_sectors)?;
2999 self.buffer_write_inode(&mut buf, ino, &raw)?;
3000
3001 self.commit_block_buffer(buf)?;
3002 Ok(new_size)
3003 }
3004
3005 fn apply_replace_file_content_indirect(
3017 &self,
3018 ino: u32,
3019 inode: Inode,
3020 mut raw: Vec<u8>,
3021 data: &[u8],
3022 ) -> Result<u64> {
3023 let bs = self.sb.block_size();
3024 let sectors_per_block = bs as u64 / 512;
3025 let group_idx_of_inode = ((ino - 1) / self.sb.inodes_per_group) as usize;
3026
3027 let mut freed_fs_blocks: u64 = 0;
3031 if inode.size > 0 {
3032 let block_count = inode.size.div_ceil(bs as u64) as u32;
3033 let freed = crate::indirect_mut::collect_for_free(
3034 &inode.block,
3035 bs,
3036 block_count,
3037 self.dev.as_ref(),
3038 )?;
3039 for run in &freed.data_runs {
3040 freed_fs_blocks += self.free_block_run_and_bgd(run.start, run.len as u64)?;
3041 }
3042 for &iblk in &freed.indirect_blocks {
3043 freed_fs_blocks += self.free_block_run_and_bgd(iblk, 1)?;
3044 }
3045 }
3046 let zero_iblock = [0u8; 60];
3048 Self::patch_inode_block_area(&mut raw, &zero_iblock)?;
3049
3050 if data.is_empty() {
3051 self.finalize_inode_after_write(ino, &mut raw, &inode, 0, 0)?;
3052 if freed_fs_blocks > 0 {
3053 self.patch_sb_counters(freed_fs_blocks as i64, 0)?;
3054 }
3055 self.dev.flush()?;
3056 return Ok(0);
3057 }
3058
3059 let needed_data_blocks: u32 = data.len().div_ceil(bs as usize) as u32;
3065 let n_indirect: u32 = crate::indirect_mut::count_indirect_blocks(needed_data_blocks, bs)
3066 .try_into()
3067 .map_err(|_| Error::Corrupt("indirect_mut: indirect block count overflow"))?;
3068 let total_run = needed_data_blocks
3069 .checked_add(n_indirect)
3070 .ok_or(Error::Corrupt("indirect_mut: total run count overflow"))?;
3071
3072 let mut bitmap_reader = |block: u64| self.read_block(block);
3073 let plan = crate::alloc::plan_block_allocation(
3074 &self.sb,
3075 &self.allocation_groups(),
3076 total_run,
3077 group_idx_of_inode as u32,
3078 &mut bitmap_reader,
3079 )?;
3080 let first_indirect = plan.first_block;
3081 let first_data = plan.first_block + n_indirect as u64;
3082
3083 let mut next_indirect = first_indirect;
3087 let i_plan =
3088 crate::indirect_mut::plan_contiguous(needed_data_blocks, first_data, bs, || {
3089 let v = next_indirect;
3090 next_indirect += 1;
3091 Ok(v)
3092 })?;
3093
3094 self.set_block_run_used(plan.first_block, total_run as u64)?;
3097 self.patch_bgd_counters(
3098 plan.bgd.group_idx as usize,
3099 plan.bgd.free_blocks_delta,
3100 plan.bgd.free_inodes_delta,
3101 plan.bgd.used_dirs_delta,
3102 )?;
3103 let net_block_delta = freed_fs_blocks as i64 - total_run as i64;
3104 self.patch_sb_counters(net_block_delta, 0)?;
3105
3106 for i in 0..needed_data_blocks as u64 {
3108 let off_in_data = (i as usize) * bs as usize;
3109 let chunk_end = ((i as usize + 1) * bs as usize).min(data.len());
3110 let mut block = vec![0u8; bs as usize];
3111 block[..chunk_end - off_in_data].copy_from_slice(&data[off_in_data..chunk_end]);
3112 self.dev.write_at((first_data + i) * bs as u64, &block)?;
3113 }
3114
3115 for (blk, buf) in &i_plan.block_writes {
3117 self.dev.write_at(blk * bs as u64, buf)?;
3118 }
3119
3120 Self::patch_inode_block_area(&mut raw, &i_plan.i_block)?;
3122
3123 let new_size = data.len() as u64;
3127 let new_sectors = (needed_data_blocks as u64 + n_indirect as u64) * sectors_per_block;
3128 self.finalize_inode_after_write(ino, &mut raw, &inode, new_size, new_sectors)?;
3129 self.dev.flush()?;
3130 Ok(new_size)
3131 }
3132
3133 pub fn apply_pwrite(&self, path: &str, offset: u64, data: &[u8]) -> Result<u64> {
3171 if !self.dev.is_writable() {
3172 return Err(Error::ReadOnly);
3173 }
3174 let mut reader = |ino: u32| self.read_inode_verified(ino).map(|(i, _)| i);
3175 let ino = crate::path::lookup(self.dev.as_ref(), &self.sb, &mut reader, path)?;
3176 let (inode, mut raw) = self.read_inode_verified(ino)?;
3177 if !inode.is_file() {
3178 return Err(Error::InvalidArgument(
3179 "pwrite target is not a regular file",
3180 ));
3181 }
3182 if !inode.has_extents() {
3183 return Err(Error::InvalidArgument(
3184 "pwrite: legacy (non-extents) inodes not supported in v1",
3185 ));
3186 }
3187
3188 if data.is_empty() {
3189 return Ok(inode.size);
3192 }
3193
3194 let bs = self.sb.block_size() as u64;
3195 let bs_usize = bs as usize;
3196 let sectors_per_block = bs / 512;
3197 let len = data.len() as u64;
3198 let end = offset
3199 .checked_add(len)
3200 .ok_or(Error::InvalidArgument("pwrite: offset+len overflow"))?;
3201 let first_lb = offset / bs;
3202 let last_lb_excl = end.div_ceil(bs);
3203
3204 let tags_per_desc = (bs_usize.saturating_sub(12)) / 16;
3211 let max_data_blocks = tags_per_desc.saturating_sub(8).max(1) as u64;
3218 let max_chunk = max_data_blocks * bs;
3219 if len > max_chunk {
3220 let mut chunk_off = 0u64;
3221 while chunk_off < len {
3222 let take = max_chunk.min(len - chunk_off);
3223 let s = chunk_off as usize;
3224 let e = (chunk_off + take) as usize;
3225 self.apply_pwrite(path, offset + chunk_off, &data[s..e])?;
3226 chunk_off += take;
3227 }
3228 let (after, _) = self.read_inode_verified(ino)?;
3229 return Ok(after.size);
3230 }
3231
3232 let mut root_bytes: Vec<u8> = inode.block.to_vec();
3236
3237 let mut buf = BlockBuffer::new(self.sb.block_size());
3238 let group_idx_of_inode = ((ino - 1) / self.sb.inodes_per_group) as u32;
3239
3240 let mut newly_alloc: std::collections::BTreeSet<u64> = std::collections::BTreeSet::new();
3245 let mut alloc_total_blocks: u64 = 0;
3246
3247 let mut lb = first_lb;
3253 while lb < last_lb_excl {
3254 let mapped = crate::extent::map_logical(
3255 &root_bytes,
3256 self.dev.as_ref(),
3257 self.sb.block_size(),
3258 lb,
3259 )?;
3260 if mapped.is_some() {
3261 lb += 1;
3262 continue;
3263 }
3264 let mut run_end = lb + 1;
3266 while run_end < last_lb_excl {
3267 let p = crate::extent::map_logical(
3268 &root_bytes,
3269 self.dev.as_ref(),
3270 self.sb.block_size(),
3271 run_end,
3272 )?;
3273 if p.is_some() {
3274 break;
3275 }
3276 run_end += 1;
3277 }
3278 let run_len_u64 = run_end - lb;
3279 if run_len_u64 > u32::MAX as u64 {
3280 return Err(Error::InvalidArgument(
3281 "pwrite: unmapped run exceeds u32 block count",
3282 ));
3283 }
3284
3285 let mut remaining_in_run = run_len_u64 as u32;
3293 let mut sub_lb = lb;
3294 while remaining_in_run > 0 {
3295 let mut want = remaining_in_run;
3296 let plan = loop {
3297 let plan_result = {
3298 let mut bitmap_reader = |b: u64| -> Result<Vec<u8>> {
3299 if let Some(bytes) = buf.dirty.get(&b) {
3300 return Ok(bytes.clone());
3301 }
3302 self.read_block(b)
3303 };
3304 crate::alloc::plan_block_allocation(
3305 &self.sb,
3306 &self.allocation_groups(),
3307 want,
3308 group_idx_of_inode,
3309 &mut bitmap_reader,
3310 )
3311 };
3312 match plan_result {
3313 Ok(p) => break p,
3314 Err(Error::Corrupt(msg)) if msg.contains("contiguous free run") => {
3315 if want == 1 {
3316 return Err(Error::NoSpaceLeftOnDevice);
3319 }
3320 want /= 2;
3325 }
3326 Err(e) => return Err(e),
3327 }
3328 };
3329
3330 let got = want;
3331 let got_u64 = got as u64;
3332
3333 self.buffer_mark_block_run_used(&mut buf, plan.first_block, got_u64)?;
3334 self.buffer_patch_bgd_counters(
3335 &mut buf,
3336 plan.bgd.group_idx as usize,
3337 plan.bgd.free_blocks_delta,
3338 plan.bgd.free_inodes_delta,
3339 plan.bgd.used_dirs_delta,
3340 )?;
3341 alloc_total_blocks += got_u64;
3342
3343 let new_extent = crate::extent::Extent {
3344 logical_block: sub_lb as u32,
3345 length: got as u16,
3346 physical_block: plan.first_block,
3347 uninitialized: false,
3348 };
3349
3350 match crate::extent_mut::plan_insert_extent(&root_bytes, new_extent) {
3354 Ok(muts) => {
3355 for m in &muts {
3356 if let crate::extent_mut::ExtentMutation::WriteRoot { bytes } = m {
3357 root_bytes = bytes.clone();
3358 }
3359 }
3360 }
3361 Err(Error::CorruptExtentTree(msg))
3362 if msg.contains("LEAF_FULL_NEEDS_PROMOTION")
3363 || msg.contains("multi-level tree mutation") =>
3364 {
3365 let reader = FsBlockReader { fs: self };
3378 let mut meta_blocks_alloc: u64 = 0;
3379 let inode_generation = inode.generation;
3380 let deep_plan = {
3381 let mut alloc_closure = || -> Result<u64> {
3382 let p = {
3383 let mut bitmap_reader = |b: u64| -> Result<Vec<u8>> {
3384 if let Some(bytes) = buf.dirty.get(&b) {
3385 return Ok(bytes.clone());
3386 }
3387 self.read_block(b)
3388 };
3389 crate::alloc::plan_block_allocation(
3390 &self.sb,
3391 &self.allocation_groups(),
3392 1,
3393 group_idx_of_inode,
3394 &mut bitmap_reader,
3395 )?
3396 };
3397 self.buffer_mark_block_run_used(&mut buf, p.first_block, 1)?;
3398 self.buffer_patch_bgd_counters(
3399 &mut buf,
3400 p.bgd.group_idx as usize,
3401 p.bgd.free_blocks_delta,
3402 0,
3403 0,
3404 )?;
3405 meta_blocks_alloc += 1;
3406 Ok(p.first_block)
3407 };
3408 crate::extent_mut::plan_insert_extent_deep(
3409 &root_bytes,
3410 new_extent,
3411 self.sb.block_size(),
3412 &reader,
3413 &mut alloc_closure,
3414 )?
3415 };
3416 root_bytes = deep_plan.new_root;
3417 let bs_u64 = self.sb.block_size() as u64;
3418 for (block, bytes) in deep_plan.block_writes {
3419 let mut bytes = bytes;
3420 if self.csum.enabled {
3421 self.csum
3422 .patch_extent_tail(ino, inode_generation, &mut bytes);
3423 }
3424 self.dev.write_at(block * bs_u64, &bytes)?;
3435 buf.put(block, bytes);
3436 }
3437 alloc_total_blocks += meta_blocks_alloc;
3438 }
3439 Err(e) => return Err(e),
3440 }
3441
3442 for x in sub_lb..(sub_lb + got_u64) {
3446 newly_alloc.insert(x);
3447 }
3448
3449 sub_lb += got_u64;
3450 remaining_in_run -= got;
3451 }
3452
3453 lb = run_end;
3454 }
3455
3456 let mut data_off: usize = 0;
3458 for cur_lb in first_lb..last_lb_excl {
3459 let block_byte_start = cur_lb * bs;
3460 let block_byte_end = block_byte_start + bs;
3461 let chunk_start = offset.max(block_byte_start);
3462 let chunk_end = end.min(block_byte_end);
3463 let in_block_off = (chunk_start - block_byte_start) as usize;
3464 let chunk_len = (chunk_end - chunk_start) as usize;
3465
3466 let phys = crate::extent::map_logical(
3467 &root_bytes,
3468 self.dev.as_ref(),
3469 self.sb.block_size(),
3470 cur_lb,
3471 )?
3472 .ok_or(Error::Corrupt(
3473 "pwrite Phase 2: logical block unmapped after Phase 1 (allocator/extent insert mismatch)",
3474 ))?;
3475
3476 if newly_alloc.contains(&cur_lb) {
3477 let mut block = vec![0u8; bs_usize];
3480 block[in_block_off..in_block_off + chunk_len]
3481 .copy_from_slice(&data[data_off..data_off + chunk_len]);
3482 buf.put(phys, block);
3483 } else {
3484 let block = buf.get_mut(self, phys)?;
3487 if block.len() != bs_usize {
3488 return Err(Error::Corrupt(
3489 "pwrite Phase 2: existing block has wrong size",
3490 ));
3491 }
3492 block[in_block_off..in_block_off + chunk_len]
3493 .copy_from_slice(&data[data_off..data_off + chunk_len]);
3494 }
3495
3496 data_off += chunk_len;
3497 }
3498 debug_assert_eq!(data_off, data.len());
3499
3500 Self::patch_inode_block_area(&mut raw, &root_bytes)?;
3503 let new_size = inode.size.max(end);
3504 let new_sectors = inode
3505 .blocks
3506 .checked_add(alloc_total_blocks * sectors_per_block)
3507 .ok_or(Error::Corrupt("pwrite: i_blocks overflow"))?;
3508 self.finalize_inode_raw_after_write(ino, &mut raw, &inode, new_size, new_sectors)?;
3509 self.buffer_write_inode(&mut buf, ino, &raw)?;
3510
3511 if alloc_total_blocks > 0 {
3513 self.buffer_patch_sb_counters(&mut buf, -(alloc_total_blocks as i64), 0)?;
3514 }
3515
3516 self.commit_block_buffer(buf)?;
3518 Ok(new_size)
3519 }
3520
3521 fn finalize_inode_after_write(
3525 &self,
3526 ino: u32,
3527 raw: &mut [u8],
3528 orig: &Inode,
3529 new_size: u64,
3530 new_sectors: u64,
3531 ) -> Result<()> {
3532 self.finalize_inode_raw_after_write(ino, raw, orig, new_size, new_sectors)?;
3533 self.write_inode_raw(ino, raw)
3534 }
3535
3536 fn finalize_inode_raw_after_write(
3541 &self,
3542 ino: u32,
3543 raw: &mut [u8],
3544 orig: &Inode,
3545 new_size: u64,
3546 new_sectors: u64,
3547 ) -> Result<()> {
3548 Self::patch_inode_size_and_blocks(raw, new_size, new_sectors)?;
3549 let now = now_unix_seconds();
3550 raw[0x0C..0x10].copy_from_slice(&now.to_le_bytes()); raw[0x10..0x14].copy_from_slice(&now.to_le_bytes()); if self.csum.enabled {
3553 if let Some((lo, hi)) = self.csum.compute_inode_checksum(ino, orig.generation, raw) {
3554 raw[0x7C..0x7E].copy_from_slice(&lo.to_le_bytes());
3555 if raw.len() >= 0x84 {
3556 raw[0x82..0x84].copy_from_slice(&hi.to_le_bytes());
3557 }
3558 }
3559 }
3560 Ok(())
3561 }
3562
3563 fn set_block_run_used(&self, start: u64, len: u64) -> Result<()> {
3564 let bpg = self.sb.blocks_per_group as u64;
3565 let first_data = self.sb.first_data_block as u64;
3566 let gi = ((start - first_data) / bpg) as usize;
3567 if gi >= self.groups.len() {
3568 return Err(Error::InvalidBlock(start));
3569 }
3570 let group_start = first_data + gi as u64 * bpg;
3571 let bit_start = (start - group_start) as u32;
3572 let bitmap_block = self.groups[gi].block_bitmap;
3573 let bs = self.sb.block_size() as u64;
3574 let mut buf = vec![0u8; bs as usize];
3575 self.dev.read_at(bitmap_block * bs, &mut buf)?;
3576 for i in 0..len {
3577 let bit = bit_start as u64 + i;
3578 let byte = (bit / 8) as usize;
3579 let mask = 1u8 << (bit % 8);
3580 if byte < buf.len() {
3581 buf[byte] |= mask;
3582 }
3583 }
3584 self.dev.write_at(bitmap_block * bs, &buf)?;
3585 Ok(())
3586 }
3587
3588 fn find_entry_in_dir(&self, dir_inode: &Inode, name: &[u8]) -> Result<u32> {
3591 let has_ft = self.sb.feature_incompat & features::Incompat::FILETYPE.bits() != 0;
3592 let bs = self.sb.block_size();
3593 let n_blocks = dir_inode.size.div_ceil(bs as u64);
3594 for logical in 0..n_blocks {
3595 let Some(phys) = self.map_inode_logical(dir_inode, logical)? else {
3596 continue;
3597 };
3598 let block = self.read_block(phys)?;
3599 for entry in crate::dir::DirBlockIter::new(&block, has_ft) {
3600 let e = entry?;
3601 if e.name == name {
3602 return Ok(e.inode);
3603 }
3604 }
3605 }
3606 Err(Error::NotFound)
3607 }
3608
3609 pub(crate) fn patch_bgd_counters(
3615 &self,
3616 gi: usize,
3617 free_blocks_delta: i32,
3618 free_inodes_delta: i32,
3619 used_dirs_delta: i32,
3620 ) -> Result<()> {
3621 let bs = self.sb.block_size() as u64;
3622 let desc_size = self.sb.desc_size as u64;
3623 let bgt_first_block = self.sb.first_data_block as u64 + 1;
3624 let byte_in_bgt = gi as u64 * desc_size;
3625 let bgt_block = bgt_first_block + byte_in_bgt / bs;
3626 let off_in_block = (byte_in_bgt % bs) as usize;
3627
3628 let mut block = self.read_block(bgt_block)?;
3629
3630 patch_counter_u32(
3632 &mut block,
3633 off_in_block + 0x0C,
3634 if desc_size >= 0x40 {
3635 Some(off_in_block + 0x2A)
3636 } else {
3637 None
3638 },
3639 free_blocks_delta,
3640 );
3641 patch_counter_u32(
3643 &mut block,
3644 off_in_block + 0x0E,
3645 if desc_size >= 0x40 {
3646 Some(off_in_block + 0x2C)
3647 } else {
3648 None
3649 },
3650 free_inodes_delta,
3651 );
3652 patch_counter_u32(
3655 &mut block,
3656 off_in_block + 0x10,
3657 if desc_size >= 0x40 {
3658 Some(off_in_block + 0x2E)
3659 } else {
3660 None
3661 },
3662 used_dirs_delta,
3663 );
3664
3665 if self.csum.enabled {
3666 let stored_at = off_in_block + 0x1E;
3667 let end_desc = off_in_block + desc_size as usize;
3668 block[stored_at..stored_at + 2].copy_from_slice(&[0, 0]);
3669 let seed = self.csum.seed;
3670 let mut c = crate::checksum::linux_crc32c(seed, &(gi as u32).to_le_bytes());
3671 c = crate::checksum::linux_crc32c(c, &block[off_in_block..end_desc]);
3672 let new_csum = c as u16;
3673 block[stored_at..stored_at + 2].copy_from_slice(&new_csum.to_le_bytes());
3674 }
3675 self.dev.write_at(bgt_block * bs, &block)?;
3676 Ok(())
3677 }
3678
3679 pub(crate) fn patch_sb_counters(
3682 &self,
3683 free_blocks_delta: i64,
3684 free_inodes_delta: i32,
3685 ) -> Result<()> {
3686 let mut buf = BlockBuffer::new(self.sb.block_size());
3695 self.buffer_patch_sb_counters(&mut buf, free_blocks_delta, free_inodes_delta)?;
3696 self.commit_block_buffer(buf)?;
3697 Ok(())
3698 }
3699
3700 fn free_block_run(&self, start: u64, len: u64) -> Result<()> {
3705 let bpg = self.sb.blocks_per_group as u64;
3706 let first_data = self.sb.first_data_block as u64;
3707 let gi = ((start - first_data) / bpg) as usize;
3709 if gi >= self.groups.len() {
3710 return Err(Error::InvalidBlock(start));
3711 }
3712 let group_start = first_data + gi as u64 * bpg;
3713 let bit_start = (start - group_start) as u32;
3714 let bg = &self.groups[gi];
3715 let bitmap_block = bg.block_bitmap;
3716
3717 let bs = self.sb.block_size() as u64;
3718 let mut buf = vec![0u8; bs as usize];
3719 self.dev.read_at(bitmap_block * bs, &mut buf)?;
3720 for i in 0..len {
3721 let bit = bit_start as u64 + i;
3722 let byte = (bit / 8) as usize;
3723 let mask = 1u8 << (bit % 8);
3724 if byte < buf.len() {
3725 buf[byte] &= !mask;
3726 }
3727 }
3728 self.dev.write_at(bitmap_block * bs, &buf)?;
3729 Ok(())
3730 }
3731
3732 fn free_block_run_and_bgd(&self, start: u64, len: u64) -> Result<u64> {
3741 self.free_block_run(start, len)?;
3742 let bpg = self.sb.blocks_per_group as u64;
3743 let first_data = self.sb.first_data_block as u64;
3744 let gi = ((start - first_data) / bpg) as usize;
3745 if gi < self.groups.len() {
3746 self.patch_bgd_counters(gi, len as i32, 0, 0)?;
3747 }
3748 Ok(len)
3749 }
3750
3751 fn build_directory_inode(&self, ino: u32, mode: u16, data_phys_block: u64) -> Result<Vec<u8>> {
3761 use crate::inode::{
3762 OFF_BLOCK, OFF_BLOCKS_HI, OFF_BLOCKS_LO, OFF_FLAGS, OFF_LINKS_COUNT, OFF_MODE,
3763 OFF_SIZE_HI, OFF_SIZE_LO,
3764 };
3765 let mut raw = vec![0u8; self.sb.inode_size as usize];
3766
3767 let mode_bits = crate::inode::S_IFDIR | (mode & 0x0FFF);
3768 raw[OFF_MODE..OFF_MODE + 2].copy_from_slice(&mode_bits.to_le_bytes());
3769 raw[OFF_LINKS_COUNT..OFF_LINKS_COUNT + 2].copy_from_slice(&2u16.to_le_bytes());
3771 raw[OFF_FLAGS..OFF_FLAGS + 4]
3772 .copy_from_slice(&crate::inode::InodeFlags::EXTENTS.bits().to_le_bytes());
3773
3774 let extent_header_off = OFF_BLOCK;
3776 raw[extent_header_off..extent_header_off + 2]
3777 .copy_from_slice(&crate::extent::EXT4_EXT_MAGIC.to_le_bytes());
3778 raw[extent_header_off + 2..extent_header_off + 4].copy_from_slice(&1u16.to_le_bytes());
3779 raw[extent_header_off + 4..extent_header_off + 6].copy_from_slice(&4u16.to_le_bytes());
3780 let extent_entry_off = extent_header_off + 12;
3784 raw[extent_entry_off..extent_entry_off + 4].copy_from_slice(&0u32.to_le_bytes());
3785 raw[extent_entry_off + 4..extent_entry_off + 6].copy_from_slice(&1u16.to_le_bytes());
3786 let (extent_phys_hi, extent_phys_lo) = crate::extent_mut::split_phys_block(data_phys_block);
3787 raw[extent_entry_off + 6..extent_entry_off + 8]
3788 .copy_from_slice(&extent_phys_hi.to_le_bytes());
3789 raw[extent_entry_off + 8..extent_entry_off + 12]
3790 .copy_from_slice(&extent_phys_lo.to_le_bytes());
3791
3792 let bs = self.sb.block_size() as u64;
3794 raw[OFF_SIZE_LO..OFF_SIZE_LO + 4]
3795 .copy_from_slice(&((bs & 0xFFFF_FFFF) as u32).to_le_bytes());
3796 raw[OFF_SIZE_HI..OFF_SIZE_HI + 4].copy_from_slice(&((bs >> 32) as u32).to_le_bytes());
3797
3798 let sectors = bs / 512;
3799 raw[OFF_BLOCKS_LO..OFF_BLOCKS_LO + 4].copy_from_slice(&(sectors as u32).to_le_bytes());
3800 raw[OFF_BLOCKS_HI..OFF_BLOCKS_HI + 2]
3801 .copy_from_slice(&(((sectors >> 32) & 0xFFFF) as u16).to_le_bytes());
3802
3803 let now = now_unix_seconds();
3804 write_inode_timestamps(&mut raw, now);
3805 let generation = alloc_inode_generation();
3806 write_inode_generation(&mut raw, generation);
3807 write_inode_extra_isize(&mut raw);
3808 self.stamp_inode_checksum(&mut raw, ino, generation);
3809 Ok(raw)
3810 }
3811
3812 fn seed_directory_block(
3817 &self,
3818 new_ino: u32,
3819 parent_ino: u32,
3820 new_generation: u32,
3821 ) -> Result<Vec<u8>> {
3822 let bs = self.sb.block_size() as usize;
3823 let mut block = vec![0u8; bs];
3824 let has_ft = self.sb.feature_incompat & features::Incompat::FILETYPE.bits() != 0;
3825 let reserved_tail = if self.csum.enabled { 12 } else { 0 };
3826 let usable = bs - reserved_tail;
3827
3828 block[0..4].copy_from_slice(&new_ino.to_le_bytes());
3830 block[4..6].copy_from_slice(&12u16.to_le_bytes());
3831 block[6] = 1; block[7] = if has_ft {
3833 crate::dir::DirEntryType::Directory as u8
3834 } else {
3835 0
3836 };
3837 block[8] = b'.';
3838
3839 let off = 12;
3841 block[off..off + 4].copy_from_slice(&parent_ino.to_le_bytes());
3842 let rec_len = (usable - off) as u16;
3843 block[off + 4..off + 6].copy_from_slice(&rec_len.to_le_bytes());
3844 block[off + 6] = 2;
3845 block[off + 7] = if has_ft {
3846 crate::dir::DirEntryType::Directory as u8
3847 } else {
3848 0
3849 };
3850 block[off + 8] = b'.';
3851 block[off + 9] = b'.';
3852
3853 if reserved_tail == 12 {
3856 self.csum
3857 .patch_dir_entry_tail(new_ino, new_generation, &mut block);
3858 }
3859
3860 Ok(block)
3861 }
3862
3863 fn patch_inode_nlink(&self, ino: u32, raw: &mut [u8], inode: &Inode, delta: i32) -> Result<()> {
3865 let new_count = (inode.links_count as i32 + delta).max(0) as u16;
3866 raw[0x1A..0x1C].copy_from_slice(&new_count.to_le_bytes());
3867 if self.csum.enabled {
3868 if let Some((lo, hi)) = self.csum.compute_inode_checksum(ino, inode.generation, raw) {
3869 raw[0x7C..0x7E].copy_from_slice(&lo.to_le_bytes());
3870 if raw.len() >= 0x84 {
3871 raw[0x82..0x84].copy_from_slice(&hi.to_le_bytes());
3872 }
3873 }
3874 }
3875 Ok(())
3876 }
3877
3878 pub fn apply_mkdir(&self, path: &str, mode: u16) -> Result<u32> {
3887 if !self.dev.is_writable() {
3888 return Err(Error::ReadOnly);
3889 }
3890 let (parent_path, base_name) = split_parent_and_base(path)?;
3891 if base_name.len() > 255 {
3892 return Err(Error::NameTooLong);
3893 }
3894
3895 let mut reader = |ino: u32| self.read_inode_verified(ino).map(|(i, _)| i);
3896 let parent_ino =
3897 crate::path::lookup(self.dev.as_ref(), &self.sb, &mut reader, &parent_path)?;
3898 let (parent_inode, mut parent_raw) = self.read_inode_verified(parent_ino)?;
3899 if !parent_inode.is_dir() {
3900 return Err(Error::NotADirectory);
3901 }
3902 if self
3903 .find_entry_in_dir(&parent_inode, base_name.as_bytes())
3904 .is_ok()
3905 {
3906 return Err(Error::AlreadyExists);
3907 }
3908
3909 let bs = self.sb.block_size();
3910 let parent_group = (parent_ino - 1) / self.sb.inodes_per_group;
3911 let mut bitmap_reader = |block: u64| self.read_block(block);
3912
3913 let iplan = crate::alloc::plan_inode_allocation(
3915 &self.sb,
3916 &self.allocation_groups(),
3917 true,
3918 parent_group,
3919 &mut bitmap_reader,
3920 )?;
3921 let new_ino = iplan.inode;
3922
3923 let bplan = crate::alloc::plan_block_allocation(
3925 &self.sb,
3926 &self.allocation_groups(),
3927 1,
3928 iplan.bgd.group_idx,
3929 &mut bitmap_reader,
3930 )?;
3931 let data_block = bplan.first_block;
3932
3933 let mut buf = BlockBuffer::new(bs);
3937 self.buffer_mark_inode_used(&mut buf, new_ino)?;
3938 self.buffer_patch_bgd_counters(
3939 &mut buf,
3940 iplan.bgd.group_idx as usize,
3941 iplan.bgd.free_blocks_delta,
3942 iplan.bgd.free_inodes_delta,
3943 iplan.bgd.used_dirs_delta,
3944 )?;
3945 self.buffer_patch_sb_counters(
3946 &mut buf,
3947 iplan.sb.free_blocks_delta,
3948 iplan.sb.free_inodes_delta,
3949 )?;
3950
3951 self.buffer_mark_block_run_used(&mut buf, data_block, 1)?;
3952 self.buffer_patch_bgd_counters(
3953 &mut buf,
3954 bplan.bgd.group_idx as usize,
3955 bplan.bgd.free_blocks_delta,
3956 bplan.bgd.free_inodes_delta,
3957 bplan.bgd.used_dirs_delta,
3958 )?;
3959 self.buffer_patch_sb_counters(
3960 &mut buf,
3961 bplan.sb.free_blocks_delta,
3962 bplan.sb.free_inodes_delta,
3963 )?;
3964
3965 let raw = self.build_directory_inode(new_ino, mode, data_block)?;
3966 let gen = u32::from_le_bytes(raw[0x64..0x68].try_into().unwrap());
3967 self.buffer_write_inode(&mut buf, new_ino, &raw)?;
3968
3969 let seed = self.seed_directory_block(new_ino, parent_ino, gen)?;
3971 buf.put(data_block, seed);
3972
3973 let parent_extends = match self.buffer_add_dir_entry_inplace(
3975 &mut buf,
3976 parent_ino,
3977 &parent_inode,
3978 base_name.as_bytes(),
3979 new_ino,
3980 crate::dir::DirEntryType::Directory,
3981 ) {
3982 Ok(()) => false,
3983 Err(Error::OutOfBounds) => true,
3984 Err(e) => return Err(e),
3985 };
3986
3987 if !parent_extends {
3988 self.patch_inode_nlink(parent_ino, &mut parent_raw, &parent_inode, 1)?;
3990 self.buffer_write_inode(&mut buf, parent_ino, &parent_raw)?;
3991 self.commit_block_buffer(buf)?;
3992 } else {
3993 self.commit_block_buffer(buf)?;
3997 self.extend_dir_and_add_entry(
3998 parent_ino,
3999 base_name.as_bytes(),
4000 new_ino,
4001 crate::dir::DirEntryType::Directory,
4002 )?;
4003 let (refreshed_parent, mut refreshed_raw) = self.read_inode_verified(parent_ino)?;
4005 self.patch_inode_nlink(parent_ino, &mut refreshed_raw, &refreshed_parent, 1)?;
4006 self.commit_inode_write(parent_ino, &refreshed_raw)?;
4007 }
4008
4009 Ok(new_ino)
4010 }
4011
4012 pub fn apply_link(&self, src: &str, dst: &str) -> Result<()> {
4023 if !self.dev.is_writable() {
4024 return Err(Error::ReadOnly);
4025 }
4026 let (dst_parent_path, dst_name) = split_parent_and_base(dst)?;
4027 if dst_name.len() > 255 {
4028 return Err(Error::NameTooLong);
4029 }
4030
4031 let mut reader = |ino: u32| self.read_inode_verified(ino).map(|(i, _)| i);
4032 let src_ino = crate::path::lookup(self.dev.as_ref(), &self.sb, &mut reader, src)?;
4033 let (src_inode, mut src_raw) = self.read_inode_verified(src_ino)?;
4034 if src_inode.is_dir() {
4035 return Err(Error::IsADirectory);
4038 }
4039
4040 let dst_parent_ino =
4041 crate::path::lookup(self.dev.as_ref(), &self.sb, &mut reader, &dst_parent_path)?;
4042 let (dst_parent_inode, _) = self.read_inode_verified(dst_parent_ino)?;
4043 if !dst_parent_inode.is_dir() {
4044 return Err(Error::NotADirectory);
4045 }
4046 if self
4047 .find_entry_in_dir(&dst_parent_inode, dst_name.as_bytes())
4048 .is_ok()
4049 {
4050 return Err(Error::AlreadyExists);
4051 }
4052
4053 let dir_type = match src_inode.file_type() {
4054 crate::inode::S_IFREG => crate::dir::DirEntryType::RegFile,
4055 crate::inode::S_IFLNK => crate::dir::DirEntryType::Symlink,
4056 crate::inode::S_IFCHR => crate::dir::DirEntryType::CharDev,
4057 crate::inode::S_IFBLK => crate::dir::DirEntryType::BlockDev,
4058 crate::inode::S_IFIFO => crate::dir::DirEntryType::Fifo,
4059 crate::inode::S_IFSOCK => crate::dir::DirEntryType::Socket,
4060 _ => crate::dir::DirEntryType::Unknown,
4061 };
4062
4063 let mut buf = BlockBuffer::new(self.sb.block_size());
4067 self.patch_inode_nlink(src_ino, &mut src_raw, &src_inode, 1)?;
4068 self.buffer_write_inode(&mut buf, src_ino, &src_raw)?;
4069
4070 match self.buffer_add_dir_entry_inplace(
4071 &mut buf,
4072 dst_parent_ino,
4073 &dst_parent_inode,
4074 dst_name.as_bytes(),
4075 src_ino,
4076 dir_type,
4077 ) {
4078 Ok(()) => self.commit_block_buffer(buf),
4079 Err(Error::OutOfBounds) => {
4080 self.commit_block_buffer(buf)?;
4084 self.extend_dir_and_add_entry(
4085 dst_parent_ino,
4086 dst_name.as_bytes(),
4087 src_ino,
4088 dir_type,
4089 )
4090 }
4091 Err(e) => Err(e),
4092 }
4093 }
4094
4095 pub fn apply_rename(&self, src: &str, dst: &str, replace_if_exists: bool) -> Result<()> {
4145 if !self.dev.is_writable() {
4146 return Err(Error::ReadOnly);
4147 }
4148 if src == dst {
4149 return Ok(());
4150 }
4151
4152 let (src_parent_path, src_name) = split_parent_and_base(src)?;
4153 let (dst_parent_path, dst_name) = split_parent_and_base(dst)?;
4154 if dst_name.len() > 255 {
4155 return Err(Error::NameTooLong);
4156 }
4157
4158 let mut reader = |ino: u32| self.read_inode_verified(ino).map(|(i, _)| i);
4159 let src_parent_ino =
4160 crate::path::lookup(self.dev.as_ref(), &self.sb, &mut reader, &src_parent_path)?;
4161 let dst_parent_ino =
4162 crate::path::lookup(self.dev.as_ref(), &self.sb, &mut reader, &dst_parent_path)?;
4163 let (src_parent_inode, _) = self.read_inode_verified(src_parent_ino)?;
4164 let (dst_parent_inode, _) = self.read_inode_verified(dst_parent_ino)?;
4165 if !src_parent_inode.is_dir() || !dst_parent_inode.is_dir() {
4166 return Err(Error::NotADirectory);
4167 }
4168
4169 let src_ino = self.find_entry_in_dir(&src_parent_inode, src_name.as_bytes())?;
4170 let existing_dst_ino = self
4171 .find_entry_in_dir(&dst_parent_inode, dst_name.as_bytes())
4172 .ok();
4173 if existing_dst_ino.is_some() && !replace_if_exists {
4174 return Err(Error::AlreadyExists);
4175 }
4176
4177 let (src_inode, _) = self.read_inode_verified(src_ino)?;
4178 let src_is_dir = src_inode.is_dir();
4179
4180 if src_is_dir {
4183 let src_slash = format!("{}/", src.trim_end_matches('/'));
4184 if dst == src || dst.starts_with(&src_slash) {
4185 return Err(Error::InvalidArgument(
4186 "rename: cannot move directory into its own subtree",
4187 ));
4188 }
4189 }
4190
4191 let dir_type = match src_inode.file_type() {
4193 crate::inode::S_IFREG => crate::dir::DirEntryType::RegFile,
4194 crate::inode::S_IFDIR => crate::dir::DirEntryType::Directory,
4195 crate::inode::S_IFLNK => crate::dir::DirEntryType::Symlink,
4196 _ => crate::dir::DirEntryType::Unknown,
4197 };
4198
4199 if let Some(dst_old_ino) = existing_dst_ino {
4203 if dst_old_ino == src_ino {
4208 return Ok(());
4209 }
4210
4211 let (dst_old_inode, mut dst_old_raw) = self.read_inode_verified(dst_old_ino)?;
4212 let dst_is_dir = dst_old_inode.is_dir();
4213
4214 if !src_is_dir && dst_is_dir {
4217 return Err(Error::IsADirectory);
4218 }
4219 if src_is_dir && !dst_is_dir {
4220 return Err(Error::NotADirectory);
4221 }
4222
4223 if dst_is_dir {
4226 let bs = self.sb.block_size();
4227 let has_ft = self.sb.feature_incompat & features::Incompat::FILETYPE.bits() != 0;
4228 let blocks = dst_old_inode.size.div_ceil(bs as u64);
4229 for logical in 0..blocks {
4230 let Some(phys) = crate::extent::map_logical(
4231 &dst_old_inode.block,
4232 self.dev.as_ref(),
4233 bs,
4234 logical,
4235 )?
4236 else {
4237 continue;
4238 };
4239 let block = self.read_block(phys)?;
4240 for entry in crate::dir::DirBlockIter::new(&block, has_ft) {
4241 let e = entry?;
4242 if e.name != b"." && e.name != b".." {
4243 return Err(Error::DirectoryNotEmpty);
4244 }
4245 }
4246 }
4247 }
4248
4249 let mut buf = BlockBuffer::new(self.sb.block_size());
4256
4257 let mut parent_nlink: BTreeMap<u32, i32> = BTreeMap::new();
4276
4277 self.buffer_remove_dir_entry(
4280 &mut buf,
4281 dst_parent_ino,
4282 &dst_parent_inode,
4283 dst_name.as_bytes(),
4284 )?;
4285
4286 let dst_extends = match self.buffer_add_dir_entry_inplace(
4290 &mut buf,
4291 dst_parent_ino,
4292 &dst_parent_inode,
4293 dst_name.as_bytes(),
4294 src_ino,
4295 dir_type,
4296 ) {
4297 Ok(()) => false,
4298 Err(Error::OutOfBounds) => true,
4299 Err(e) => return Err(e),
4300 };
4301 if dst_extends {
4302 self.commit_block_buffer(buf)?;
4305 self.extend_dir_and_add_entry(
4306 dst_parent_ino,
4307 dst_name.as_bytes(),
4308 src_ino,
4309 dir_type,
4310 )?;
4311 buf = BlockBuffer::new(self.sb.block_size());
4312 }
4313
4314 self.buffer_remove_dir_entry(
4316 &mut buf,
4317 src_parent_ino,
4318 &src_parent_inode,
4319 src_name.as_bytes(),
4320 )?;
4321
4322 if src_is_dir && src_parent_ino != dst_parent_ino {
4327 self.buffer_update_dotdot(&mut buf, src_ino, &src_inode, dst_parent_ino)?;
4328 *parent_nlink.entry(src_parent_ino).or_default() -= 1;
4329 *parent_nlink.entry(dst_parent_ino).or_default() += 1;
4330 }
4331
4332 let new_links = dst_old_inode.links_count.saturating_sub(1);
4337 if new_links > 0 && !dst_is_dir {
4338 dst_old_raw[0x1A..0x1C].copy_from_slice(&new_links.to_le_bytes());
4340 self.finalize_inode_raw(dst_old_ino, dst_old_inode.generation, &mut dst_old_raw)?;
4341 self.buffer_write_inode(&mut buf, dst_old_ino, &dst_old_raw)?;
4342 } else {
4343 let bs = self.sb.block_size();
4344 let sectors_per_block = bs as u64 / 512;
4345 let mut freed_sectors: u64 = 0;
4346 if dst_old_inode.has_extents() && dst_old_inode.size > 0 {
4347 if dst_is_dir {
4348 let extents = crate::extent::collect_all(
4352 &dst_old_inode.block,
4353 self.dev.as_ref(),
4354 bs,
4355 )?;
4356 for e in &extents {
4357 self.buffer_free_block_run_and_bgd(
4358 &mut buf,
4359 e.physical_block,
4360 e.length as u64,
4361 )?;
4362 freed_sectors += e.length as u64 * sectors_per_block;
4363 }
4364 } else {
4365 let (_sc, muts) = crate::file_mut::plan_truncate_shrink(
4366 dst_old_inode.size,
4367 0,
4368 &dst_old_inode.block,
4369 bs,
4370 )?;
4371 for m in &muts {
4372 if let crate::extent_mut::ExtentMutation::FreePhysicalRun {
4373 start,
4374 len,
4375 } = m
4376 {
4377 self.buffer_free_block_run_and_bgd(&mut buf, *start, *len as u64)?;
4378 freed_sectors += *len as u64 * sectors_per_block;
4379 }
4380 }
4381 }
4382 }
4383
4384 self.buffer_free_inode_slot(&mut buf, dst_old_ino)?;
4385 if dst_is_dir {
4386 let dst_old_gi = ((dst_old_ino - 1) / self.sb.inodes_per_group) as usize;
4388 self.buffer_patch_bgd_counters(&mut buf, dst_old_gi, 0, 0, -1)?;
4389 }
4390 let freed_blocks = freed_sectors.checked_div(sectors_per_block).unwrap_or(0);
4391 self.buffer_patch_sb_counters(&mut buf, freed_blocks as i64, 1)?;
4392
4393 let inode_size = self.sb.inode_size as usize;
4395 let old_gen = dst_old_inode.generation;
4396 for b in &mut dst_old_raw[..inode_size] {
4397 *b = 0;
4398 }
4399 let dtime = now_unix_seconds();
4400 dst_old_raw[0x14..0x18].copy_from_slice(&dtime.to_le_bytes());
4401 dst_old_raw[0x64..0x68].copy_from_slice(&old_gen.to_le_bytes());
4402 self.finalize_inode_raw(dst_old_ino, old_gen, &mut dst_old_raw)?;
4403 self.buffer_write_inode(&mut buf, dst_old_ino, &dst_old_raw)?;
4404
4405 if dst_is_dir {
4410 *parent_nlink.entry(dst_parent_ino).or_default() -= 1;
4411 }
4412 }
4413
4414 self.apply_parent_nlink_deltas(&mut buf, &parent_nlink)?;
4415 return self.commit_block_buffer(buf);
4416 }
4417
4418 let mut buf = BlockBuffer::new(self.sb.block_size());
4428 let mut parent_nlink: BTreeMap<u32, i32> = BTreeMap::new();
4429
4430 let dst_extends = match self.buffer_add_dir_entry_inplace(
4431 &mut buf,
4432 dst_parent_ino,
4433 &dst_parent_inode,
4434 dst_name.as_bytes(),
4435 src_ino,
4436 dir_type,
4437 ) {
4438 Ok(()) => false,
4439 Err(Error::OutOfBounds) => true,
4440 Err(e) => return Err(e),
4441 };
4442
4443 if dst_extends {
4444 self.commit_block_buffer(buf)?;
4448 self.extend_dir_and_add_entry(dst_parent_ino, dst_name.as_bytes(), src_ino, dir_type)?;
4449 buf = BlockBuffer::new(self.sb.block_size());
4452 }
4453
4454 self.buffer_remove_dir_entry(
4455 &mut buf,
4456 src_parent_ino,
4457 &src_parent_inode,
4458 src_name.as_bytes(),
4459 )?;
4460
4461 if src_is_dir && src_parent_ino != dst_parent_ino {
4462 self.buffer_update_dotdot(&mut buf, src_ino, &src_inode, dst_parent_ino)?;
4463 *parent_nlink.entry(src_parent_ino).or_default() -= 1;
4464 *parent_nlink.entry(dst_parent_ino).or_default() += 1;
4465 }
4466
4467 self.apply_parent_nlink_deltas(&mut buf, &parent_nlink)?;
4470 self.commit_block_buffer(buf)
4471 }
4472
4473 fn apply_parent_nlink_deltas(
4488 &self,
4489 buf: &mut BlockBuffer,
4490 deltas: &BTreeMap<u32, i32>,
4491 ) -> Result<()> {
4492 for (&ino, &delta) in deltas {
4493 if delta == 0 {
4494 continue;
4495 }
4496 let (inode, mut raw) = self.read_inode_verified(ino)?;
4497 self.patch_inode_nlink(ino, &mut raw, &inode, delta)?;
4498 self.buffer_write_inode(buf, ino, &raw)?;
4499 }
4500 Ok(())
4501 }
4502
4503 fn commit_dir_block_alloc(
4516 &self,
4517 phys: u64,
4518 plan: &crate::alloc::BlockAllocationPlan,
4519 ) -> Result<()> {
4520 let mut buf = BlockBuffer::new(self.sb.block_size());
4521 self.buffer_mark_block_run_used(&mut buf, phys, 1)?;
4522 self.buffer_patch_bgd_counters(
4523 &mut buf,
4524 plan.bgd.group_idx as usize,
4525 plan.bgd.free_blocks_delta,
4526 plan.bgd.free_inodes_delta,
4527 plan.bgd.used_dirs_delta,
4528 )?;
4529 self.buffer_patch_sb_counters(
4530 &mut buf,
4531 plan.sb.free_blocks_delta,
4532 plan.sb.free_inodes_delta,
4533 )?;
4534 self.commit_block_buffer(buf)
4535 }
4536
4537 fn extend_dir_and_add_entry(
4538 &self,
4539 parent_ino: u32,
4540 name: &[u8],
4541 target_ino: u32,
4542 file_type: crate::dir::DirEntryType,
4543 ) -> Result<()> {
4544 let bs = self.sb.block_size();
4545 let bs_u64 = bs as u64;
4546 let has_ft = self.sb.feature_incompat & features::Incompat::FILETYPE.bits() != 0;
4547
4548 let (parent_inode, mut parent_raw) = self.read_inode_verified(parent_ino)?;
4550 if !parent_inode.is_dir() {
4551 return Err(Error::NotADirectory);
4552 }
4553 let new_logical_block = parent_inode.size.div_ceil(bs_u64);
4554
4555 let parent_group = (parent_ino - 1) / self.sb.inodes_per_group;
4557 let mut bitmap_reader = |block: u64| self.read_block(block);
4558 let plan = crate::alloc::plan_block_allocation(
4559 &self.sb,
4560 &self.allocation_groups(),
4561 1,
4562 parent_group,
4563 &mut bitmap_reader,
4564 )?;
4565 let new_phys = plan.first_block;
4566
4567 let new_extent = crate::extent::Extent {
4572 logical_block: new_logical_block as u32,
4573 length: 1,
4574 physical_block: new_phys,
4575 uninitialized: false,
4576 };
4577 let root_header = crate::extent::ExtentHeader::parse(&parent_inode.block)?;
4582 if root_header.depth == 1 {
4583 return self.extend_dir_and_add_entry_depth1(
4584 parent_ino,
4585 &parent_inode,
4586 &mut parent_raw,
4587 name,
4588 target_ino,
4589 file_type,
4590 has_ft,
4591 new_phys,
4592 new_extent,
4593 plan,
4594 );
4595 }
4596 if root_header.depth > 1 {
4597 return self.extend_dir_and_add_entry_deep(
4598 parent_ino,
4599 &parent_inode,
4600 &mut parent_raw,
4601 name,
4602 target_ino,
4603 file_type,
4604 has_ft,
4605 new_phys,
4606 new_extent,
4607 plan,
4608 );
4609 }
4610
4611 let (new_root, leaf_meta_alloc) =
4612 match crate::extent_mut::plan_insert_extent(&parent_inode.block, new_extent) {
4613 Ok(muts) => {
4614 let root = muts
4615 .into_iter()
4616 .find_map(|m| match m {
4617 crate::extent_mut::ExtentMutation::WriteRoot { bytes } => Some(bytes),
4618 _ => None,
4619 })
4620 .ok_or(Error::Corrupt(
4621 "extend_dir_and_add_entry: plan produced no WriteRoot",
4622 ))?;
4623 (root, None)
4624 }
4625 Err(Error::CorruptExtentTree(msg)) if msg.contains("LEAF_FULL_NEEDS_PROMOTION") => {
4626 self.commit_dir_block_alloc(new_phys, &plan)?;
4629
4630 let mut reader2 = |block: u64| -> Result<Vec<u8>> {
4632 let mut buf = vec![0u8; bs as usize];
4633 self.dev.read_at(block * bs_u64, &mut buf)?;
4634 Ok(buf)
4635 };
4636 let meta_plan = crate::alloc::plan_block_allocation(
4637 &self.sb,
4638 &self.allocation_groups(),
4639 1,
4640 parent_group,
4641 &mut reader2,
4642 )?;
4643 let leaf_meta_phys = meta_plan.first_block;
4644
4645 let promo = crate::extent_mut::plan_promote_leaf(
4646 &parent_inode.block,
4647 new_extent,
4648 bs as usize,
4649 leaf_meta_phys,
4650 self.csum.enabled,
4651 )?;
4652 let mut leaf = promo.leaf_bytes;
4653 if self.csum.enabled {
4654 self.csum
4655 .patch_extent_tail(parent_ino, parent_inode.generation, &mut leaf);
4656 }
4657 self.dev.write_at(leaf_meta_phys * bs_u64, &leaf)?;
4658 (promo.new_root_bytes, Some(meta_plan))
4659 }
4660 Err(e) => return Err(e),
4661 };
4662 Self::patch_inode_block_area(&mut parent_raw, &new_root)?;
4663
4664 let blocks_consumed: u64 = 1 + if leaf_meta_alloc.is_some() { 1 } else { 0 };
4667 let new_size = parent_inode.size + bs_u64;
4668 let new_blocks = parent_inode.blocks + (bs_u64 / 512) * blocks_consumed;
4669 Self::patch_inode_size_and_blocks(&mut parent_raw, new_size, new_blocks)?;
4670
4671 if self.csum.enabled {
4673 if let Some((lo, hi)) =
4674 self.csum
4675 .compute_inode_checksum(parent_ino, parent_inode.generation, &parent_raw)
4676 {
4677 parent_raw[0x7C..0x7E].copy_from_slice(&lo.to_le_bytes());
4678 if parent_raw.len() >= 0x84 {
4679 parent_raw[0x82..0x84].copy_from_slice(&hi.to_le_bytes());
4680 }
4681 }
4682 }
4683 self.write_inode_raw(parent_ino, &parent_raw)?;
4684
4685 let reserved_tail = if self.csum.enabled { 12 } else { 0 };
4688 let usable = (bs as usize) - reserved_tail;
4689 let mut block = vec![0u8; bs as usize];
4690 block[0..4].copy_from_slice(&0u32.to_le_bytes());
4691 block[4..6].copy_from_slice(&(usable as u16).to_le_bytes());
4692
4693 crate::dir::add_entry_to_block(
4694 &mut block,
4695 target_ino,
4696 name,
4697 file_type,
4698 has_ft,
4699 reserved_tail,
4700 )?;
4701
4702 if self.csum.enabled && reserved_tail == 12 {
4703 self.csum
4704 .patch_dir_entry_tail(parent_ino, parent_inode.generation, &mut block);
4705 }
4706 self.dev.write_at(new_phys * bs_u64, &block)?;
4707
4708 if let Some(meta_plan) = leaf_meta_alloc {
4713 self.commit_dir_block_alloc(meta_plan.first_block, &meta_plan)?;
4714 } else {
4715 self.commit_dir_block_alloc(new_phys, &plan)?;
4716 }
4717
4718 Ok(())
4719 }
4720
4721 #[allow(clippy::too_many_arguments)]
4727 fn extend_dir_and_add_entry_deep(
4728 &self,
4729 parent_ino: u32,
4730 parent_inode: &Inode,
4731 parent_raw: &mut [u8],
4732 name: &[u8],
4733 target_ino: u32,
4734 file_type: crate::dir::DirEntryType,
4735 has_ft: bool,
4736 new_phys: u64,
4737 new_extent: crate::extent::Extent,
4738 data_plan: crate::alloc::BlockAllocationPlan,
4739 ) -> Result<()> {
4740 let bs = self.sb.block_size();
4741 let bs_u64 = bs as u64;
4742 let parent_group = (parent_ino - 1) / self.sb.inodes_per_group;
4743
4744 let data_block = data_plan.first_block;
4755 let mut pending_meta: Vec<crate::alloc::BlockAllocationPlan> = Vec::new();
4756
4757 let reader = FsBlockReader { fs: self };
4758 let mut meta_block_count: u64 = 0;
4759 let mut alloc_fn = || -> Result<u64> {
4760 let mut bm_reader = |block: u64| -> Result<Vec<u8>> {
4761 let mut buf = vec![0u8; bs as usize];
4762 self.dev.read_at(block * bs_u64, &mut buf)?;
4763 Ok(buf)
4764 };
4765 let meta_plan = crate::alloc::plan_block_allocation(
4766 &self.sb,
4767 &self.allocation_groups(),
4768 1,
4769 parent_group,
4770 &mut bm_reader,
4771 )?;
4772 if meta_plan.first_block == data_block {
4773 return Err(Error::NoSpaceLeftOnDevice);
4777 }
4778 meta_block_count += 1;
4779 pending_meta.push(meta_plan);
4780 Ok(pending_meta.last().unwrap().first_block)
4781 };
4782
4783 let deep_plan = crate::extent_mut::plan_insert_extent_deep(
4784 &parent_inode.block,
4785 new_extent,
4786 bs,
4787 &reader,
4788 &mut alloc_fn,
4789 )?;
4790
4791 for (block, mut bytes) in deep_plan.block_writes {
4793 if self.csum.enabled {
4794 self.csum
4795 .patch_extent_tail(parent_ino, parent_inode.generation, &mut bytes);
4796 }
4797 self.dev.write_at(block * bs_u64, &bytes)?;
4798 }
4799
4800 Self::patch_inode_block_area(parent_raw, &deep_plan.new_root)?;
4802 let new_size = parent_inode.size + bs_u64;
4803 let new_blocks = parent_inode.blocks + (bs_u64 / 512) * (1 + meta_block_count);
4804 Self::patch_inode_size_and_blocks(parent_raw, new_size, new_blocks)?;
4805 if self.csum.enabled {
4806 if let Some((lo, hi)) =
4807 self.csum
4808 .compute_inode_checksum(parent_ino, parent_inode.generation, parent_raw)
4809 {
4810 parent_raw[0x7C..0x7E].copy_from_slice(&lo.to_le_bytes());
4811 if parent_raw.len() >= 0x84 {
4812 parent_raw[0x82..0x84].copy_from_slice(&hi.to_le_bytes());
4813 }
4814 }
4815 }
4816 self.write_inode_raw(parent_ino, parent_raw)?;
4817
4818 let reserved_tail = if self.csum.enabled { 12 } else { 0 };
4820 let usable = (bs as usize) - reserved_tail;
4821 let mut block = vec![0u8; bs as usize];
4822 block[0..4].copy_from_slice(&0u32.to_le_bytes());
4823 block[4..6].copy_from_slice(&(usable as u16).to_le_bytes());
4824 crate::dir::add_entry_to_block(
4825 &mut block,
4826 target_ino,
4827 name,
4828 file_type,
4829 has_ft,
4830 reserved_tail,
4831 )?;
4832 if self.csum.enabled && reserved_tail == 12 {
4833 self.csum
4834 .patch_dir_entry_tail(parent_ino, parent_inode.generation, &mut block);
4835 }
4836 self.dev.write_at(new_phys * bs_u64, &block)?;
4837
4838 self.commit_dir_block_alloc(data_plan.first_block, &data_plan)?;
4843 for plan in pending_meta {
4844 self.commit_dir_block_alloc(plan.first_block, &plan)?;
4845 }
4846
4847 Ok(())
4848 }
4849
4850 #[allow(clippy::too_many_arguments)]
4858 fn extend_dir_and_add_entry_depth1(
4859 &self,
4860 parent_ino: u32,
4861 parent_inode: &Inode,
4862 parent_raw: &mut [u8],
4863 name: &[u8],
4864 target_ino: u32,
4865 file_type: crate::dir::DirEntryType,
4866 has_ft: bool,
4867 new_phys: u64,
4868 new_extent: crate::extent::Extent,
4869 plan: crate::alloc::BlockAllocationPlan,
4870 ) -> Result<()> {
4871 let bs = self.sb.block_size();
4872 let bs_u64 = bs as u64;
4873
4874 let idx = crate::extent::ExtentIdx::parse(
4876 &parent_inode.block
4877 [crate::extent::EXT4_EXT_NODE_SIZE..2 * crate::extent::EXT4_EXT_NODE_SIZE],
4878 )?;
4879 let leaf_phys = idx.leaf_block;
4880
4881 let mut leaf = vec![0u8; bs as usize];
4886 self.dev.read_at(leaf_phys * bs_u64, &mut leaf)?;
4887 if self.csum.enabled
4890 && !self
4891 .csum
4892 .verify_extent_tail(parent_ino, parent_inode.generation, &leaf)
4893 {
4894 return Err(Error::BadChecksum {
4895 what: "extent block",
4896 });
4897 }
4898
4899 let muts = match crate::extent_mut::plan_insert_extent(&leaf, new_extent) {
4900 Ok(muts) => muts,
4901 Err(Error::CorruptExtentTree(msg)) if msg.contains("LEAF_FULL_NEEDS_PROMOTION") => {
4902 return self.extend_dir_and_add_entry_deep(
4907 parent_ino,
4908 parent_inode,
4909 parent_raw,
4910 name,
4911 target_ino,
4912 file_type,
4913 has_ft,
4914 new_phys,
4915 new_extent,
4916 plan,
4917 );
4918 }
4919 Err(e) => return Err(e),
4920 };
4921 let new_leaf = muts
4922 .into_iter()
4923 .find_map(|m| match m {
4924 crate::extent_mut::ExtentMutation::WriteRoot { bytes } => Some(bytes),
4925 _ => None,
4926 })
4927 .ok_or(Error::Corrupt(
4928 "extend_dir_and_add_entry_depth1: plan produced no WriteRoot",
4929 ))?;
4930 let mut new_leaf = new_leaf;
4931 if self.csum.enabled {
4932 self.csum
4933 .patch_extent_tail(parent_ino, parent_inode.generation, &mut new_leaf);
4934 }
4935 self.dev.write_at(leaf_phys * bs_u64, &new_leaf)?;
4936
4937 let new_size = parent_inode.size + bs_u64;
4939 let new_blocks = parent_inode.blocks + (bs_u64 / 512);
4940 Self::patch_inode_size_and_blocks(parent_raw, new_size, new_blocks)?;
4941 if self.csum.enabled {
4942 if let Some((lo, hi)) =
4943 self.csum
4944 .compute_inode_checksum(parent_ino, parent_inode.generation, parent_raw)
4945 {
4946 parent_raw[0x7C..0x7E].copy_from_slice(&lo.to_le_bytes());
4947 if parent_raw.len() >= 0x84 {
4948 parent_raw[0x82..0x84].copy_from_slice(&hi.to_le_bytes());
4949 }
4950 }
4951 }
4952 self.write_inode_raw(parent_ino, parent_raw)?;
4953
4954 let reserved_tail = if self.csum.enabled { 12 } else { 0 };
4956 let usable = (bs as usize) - reserved_tail;
4957 let mut block = vec![0u8; bs as usize];
4958 block[0..4].copy_from_slice(&0u32.to_le_bytes());
4959 block[4..6].copy_from_slice(&(usable as u16).to_le_bytes());
4960
4961 crate::dir::add_entry_to_block(
4962 &mut block,
4963 target_ino,
4964 name,
4965 file_type,
4966 has_ft,
4967 reserved_tail,
4968 )?;
4969
4970 if self.csum.enabled && reserved_tail == 12 {
4971 self.csum
4972 .patch_dir_entry_tail(parent_ino, parent_inode.generation, &mut block);
4973 }
4974 self.dev.write_at(new_phys * bs_u64, &block)?;
4975
4976 self.commit_dir_block_alloc(new_phys, &plan)?;
4978
4979 Ok(())
4980 }
4981
4982 pub fn apply_rmdir(&self, path: &str) -> Result<()> {
4986 if !self.dev.is_writable() {
4987 return Err(Error::ReadOnly);
4988 }
4989 let (parent_path, base_name) = split_parent_and_base(path)?;
4990 let mut reader = |ino: u32| self.read_inode_verified(ino).map(|(i, _)| i);
4991 let parent_ino =
4992 crate::path::lookup(self.dev.as_ref(), &self.sb, &mut reader, &parent_path)?;
4993 let (parent_inode, mut parent_raw) = self.read_inode_verified(parent_ino)?;
4994 if !parent_inode.is_dir() {
4995 return Err(Error::NotADirectory);
4996 }
4997 let target_ino = self.find_entry_in_dir(&parent_inode, base_name.as_bytes())?;
4998 let (target_inode, _) = self.read_inode_verified(target_ino)?;
4999 if !target_inode.is_dir() {
5000 return Err(Error::NotADirectory);
5001 }
5002
5003 let bs = self.sb.block_size();
5005 let has_ft = self.sb.feature_incompat & features::Incompat::FILETYPE.bits() != 0;
5006 let blocks = target_inode.size.div_ceil(bs as u64);
5007 for logical in 0..blocks {
5008 let Some(phys) =
5009 crate::extent::map_logical(&target_inode.block, self.dev.as_ref(), bs, logical)?
5010 else {
5011 continue;
5012 };
5013 let block = self.read_block(phys)?;
5014 for entry in crate::dir::DirBlockIter::new(&block, has_ft) {
5015 let e = entry?;
5016 if e.name != b"." && e.name != b".." {
5017 return Err(Error::DirectoryNotEmpty);
5018 }
5019 }
5020 }
5021
5022 let mut buf = BlockBuffer::new(bs);
5025
5026 let extents = crate::extent::collect_all(&target_inode.block, self.dev.as_ref(), bs)?;
5029 let mut freed_blocks: u64 = 0;
5030 for e in &extents {
5031 freed_blocks +=
5032 self.buffer_free_block_run_and_bgd(&mut buf, e.physical_block, e.length as u64)?;
5033 }
5034
5035 self.buffer_free_inode_slot(&mut buf, target_ino)?;
5039 let target_gi = ((target_ino - 1) / self.sb.inodes_per_group) as usize;
5040 self.buffer_patch_bgd_counters(&mut buf, target_gi, 0, 0, -1)?;
5041 self.buffer_patch_sb_counters(&mut buf, freed_blocks as i64, 1)?;
5043
5044 let inode_size = self.sb.inode_size as usize;
5050 let mut target_raw = vec![0u8; inode_size];
5051 let dtime = now_unix_seconds();
5052 target_raw[0x14..0x18].copy_from_slice(&dtime.to_le_bytes());
5053 target_raw[0x64..0x68].copy_from_slice(&target_inode.generation.to_le_bytes());
5054 self.finalize_inode_raw(target_ino, target_inode.generation, &mut target_raw)?;
5055 self.buffer_write_inode(&mut buf, target_ino, &target_raw)?;
5056
5057 let parent_blocks = parent_inode.size.div_ceil(bs as u64);
5059 let mut removed = false;
5060 for logical in 0..parent_blocks {
5061 let Some(phys) = self.map_inode_logical(&parent_inode, logical)? else {
5062 continue;
5063 };
5064 let block = buf.get_mut(self, phys)?;
5065 let reserved_tail = if self.csum.enabled && crate::dir::has_csum_tail(block) {
5066 12
5067 } else {
5068 0
5069 };
5070 if crate::dir::remove_entry_from_block(
5071 block,
5072 base_name.as_bytes(),
5073 has_ft,
5074 reserved_tail,
5075 )? {
5076 if self.csum.enabled && reserved_tail == 12 {
5077 self.csum
5078 .patch_dir_entry_tail(parent_ino, parent_inode.generation, block);
5079 }
5080 removed = true;
5081 break;
5082 }
5083 }
5084 if !removed {
5085 return Err(Error::Corrupt(
5086 "apply_rmdir: entry disappeared mid-operation",
5087 ));
5088 }
5089
5090 self.patch_inode_nlink(parent_ino, &mut parent_raw, &parent_inode, -1)?;
5092 self.buffer_write_inode(&mut buf, parent_ino, &parent_raw)?;
5093
5094 self.commit_block_buffer(buf)
5095 }
5096}
5097
5098#[cfg(test)]
5099mod tests {
5100 use super::*;
5101 use crate::inode::{
5102 EXTRA_ISIZE_DEFAULT, INODE_SIZE_WITH_CRTIME, INODE_SIZE_WITH_EXTRA, OFF_ATIME, OFF_CRTIME,
5103 OFF_CTIME, OFF_EXTRA_ISIZE, OFF_GENERATION, OFF_MTIME,
5104 };
5105
5106 fn read_le32(buf: &[u8], off: usize) -> u32 {
5107 u32::from_le_bytes(buf[off..off + 4].try_into().unwrap())
5108 }
5109 fn read_le16(buf: &[u8], off: usize) -> u16 {
5110 u16::from_le_bytes(buf[off..off + 2].try_into().unwrap())
5111 }
5112
5113 #[test]
5116 fn write_inode_timestamps_sets_atime_ctime_mtime() {
5117 let mut raw = vec![0u8; 256];
5118 write_inode_timestamps(&mut raw, 0xDEAD_BEEF);
5119 assert_eq!(read_le32(&raw, OFF_ATIME), 0xDEAD_BEEF);
5120 assert_eq!(read_le32(&raw, OFF_CTIME), 0xDEAD_BEEF);
5121 assert_eq!(read_le32(&raw, OFF_MTIME), 0xDEAD_BEEF);
5122 }
5123
5124 #[test]
5125 fn write_inode_timestamps_sets_crtime_when_large_enough() {
5126 let mut raw = vec![0u8; INODE_SIZE_WITH_CRTIME + 4];
5127 write_inode_timestamps(&mut raw, 0x1234_5678);
5128 assert_eq!(read_le32(&raw, OFF_CRTIME), 0x1234_5678);
5129 }
5130
5131 #[test]
5132 fn write_inode_timestamps_skips_crtime_when_too_small() {
5133 let mut raw = vec![0xAAu8; INODE_SIZE_WITH_CRTIME - 1];
5134 write_inode_timestamps(&mut raw, 0x1234_5678);
5135 assert_eq!(read_le32(&raw, OFF_ATIME), 0x1234_5678);
5138 }
5139
5140 #[test]
5141 fn write_inode_timestamps_zero_now() {
5142 let mut raw = vec![0xFFu8; 256];
5143 write_inode_timestamps(&mut raw, 0);
5144 assert_eq!(read_le32(&raw, OFF_ATIME), 0);
5145 assert_eq!(read_le32(&raw, OFF_CTIME), 0);
5146 assert_eq!(read_le32(&raw, OFF_MTIME), 0);
5147 assert_eq!(read_le32(&raw, OFF_CRTIME), 0);
5148 }
5149
5150 #[test]
5153 fn write_inode_generation_writes_at_correct_offset() {
5154 let mut raw = vec![0u8; 256];
5155 write_inode_generation(&mut raw, 0xCAFE_BABE);
5156 assert_eq!(read_le32(&raw, OFF_GENERATION), 0xCAFE_BABE);
5157 }
5158
5159 #[test]
5160 fn write_inode_generation_overwrites_existing() {
5161 let mut raw = vec![0xFFu8; 256];
5162 write_inode_generation(&mut raw, 0);
5163 assert_eq!(read_le32(&raw, OFF_GENERATION), 0);
5164 }
5165
5166 #[test]
5169 fn write_inode_extra_isize_sets_default_when_large_enough() {
5170 let mut raw = vec![0u8; INODE_SIZE_WITH_EXTRA + 4];
5171 write_inode_extra_isize(&mut raw);
5172 assert_eq!(read_le16(&raw, OFF_EXTRA_ISIZE), EXTRA_ISIZE_DEFAULT);
5173 }
5174
5175 #[test]
5176 fn write_inode_extra_isize_skips_when_too_small() {
5177 let mut raw = vec![0u8; INODE_SIZE_WITH_EXTRA - 1];
5178 write_inode_extra_isize(&mut raw); }
5181
5182 #[test]
5185 fn alloc_inode_generation_produces_unique_values() {
5186 let g1 = alloc_inode_generation();
5187 let g2 = alloc_inode_generation();
5188 assert_ne!(g1, g2, "successive calls must produce distinct values");
5189 }
5190}