From 2e5a154895d4bf33246c031132ee23e63e49bb1d Mon Sep 17 00:00:00 2001 From: husky Date: Thu, 17 Aug 2023 17:59:56 -0700 Subject: [PATCH] potentially correct infinite indirect read --- src/lib.rs | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/structs.rs | 2 ++ 2 files changed, 76 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 958dc49..0c7b23e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -295,6 +295,80 @@ pub fn next_journal_position(sb: &Superblock, bd: &mut dyn BlockDevice) -> Optio } } +/// Reads an indirect datablock from a list of 3 indirect datablocks. +pub fn read_indirect_datablock(sb: &Superblock, bd: &mut dyn BlockDevice, dbas: [Index; 12], address: Index) -> Vec { + if address < 12 { + return read_datablock(dbas[address as usize], sb, bd); + } else { + let mut n = address - 12; + let mut blocks_left = n / (sb.block_size as u64 - 8); + let mut indexes_left = n % (sb.block_size as u64 - 8); + let mut current_block = dbas[9]; // first indirect block + let mut visited = vec![]; + loop { + if visited.contains(¤t_block) { + return vec![]; // we've looped around + } + visited.push(current_block); + let mut head = 0; + let buf = read_datablock(current_block, sb, bd); + let count = u64::from_be_bytes(buf[0..8].try_into().unwrap()); + head += 8; + // if blocks_left == 0, we're in the right block (either that or we're in the right block to recurse downwards into) + if blocks_left == 0 { + for _ in 0..count.min(indexes_left) { + let isdata_depth = &buf[head..head + 8]; + head += 8; + let ptr_data = &buf[head..head + 8]; + let ptr = u64::from_be_bytes(ptr_data.try_into().unwrap()); + let is_data = isdata_depth[1] != 0; + // if not data, we need to recurse + if !is_data { + current_block = ptr; + break; // skip the rest of the loop + } else { + // if indexes_left == 0, we found the correct index + if indexes_left == 0 { + return read_datablock(ptr, sb, bd); + } else { + indexes_left -= 1; + } + } + } + } else { + for _ in 0..count { + let isdata_depth = &buf[head..head + 8]; + head += 8; + let ptr_data = &buf[head..head + 8]; + let ptr = u64::from_be_bytes(ptr_data.try_into().unwrap()); + let is_data = isdata_depth[1] != 0; + let mut depth = isdata_depth.to_vec(); + depth[0] = 0; + let depth = u64::from_be_bytes(depth.try_into().unwrap()); + // if blocks_left is less than the depth, we are at the correct block + if !is_data { + if blocks_left < depth { + // if not data, we need to recurse + blocks_left = 0; + current_block = ptr; + break; // skip the rest of the loop + } else { + blocks_left -= depth; + } + } else { + // if indexes_left == 0, we found the correct index + if indexes_left == 0 { + return read_datablock(ptr, sb, bd); + } else { + indexes_left -= 1; + } + } + } + } + } + } +} + /// Creates a journal entry for a single block write operation. /// Should be safe to call at anytime, and shouldn't corrupt anything if the system crashes. /// Returns None if the journal is full, or if the block device cannot be written to. diff --git a/src/structs.rs b/src/structs.rs index f3596f2..11a02bb 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -221,6 +221,8 @@ pub struct Inode { /// // read block, if not data then check depth, /// // if depth implies that this indirect block could contain the nth data block, then recurse /// // otherwise, subtract the depth from n, and continue + /// ==== + /// ideally, this should not take up too much storage space, while allowing for a large number of blocks to be addressed with minimal performance impact pub direct_block_addresses: [Index; 12], /// CRC32 checksum of this inode pub checksum: u32,