Grow the L1 table to accommodate at least `new_l1_size` entries. This allocates a new L1 table at file end (guaranteeing contiguity), copies existing entries, updates refcounts, and atomically switches to the new table.
(&mut self, new_l1_size: u32)
| 947 | /// copies existing entries, updates refcounts, and atomically switches |
| 948 | /// to the new table. |
| 949 | fn grow_l1_table(&mut self, new_l1_size: u32) -> BlockResult<()> { |
| 950 | let old_l1_size = self.header.l1_size; |
| 951 | let old_l1_offset = self.header.l1_table_offset; |
| 952 | let cluster_size = self.raw_file.cluster_size(); |
| 953 | |
| 954 | let new_l1_bytes = new_l1_size as u64 * size_of::<u64>() as u64; |
| 955 | let new_l1_clusters = div_round_up_u64(new_l1_bytes, cluster_size); |
| 956 | |
| 957 | // Allocate contiguous clusters at file end for new L1 table |
| 958 | let file_size = self |
| 959 | .raw_file |
| 960 | .file_mut() |
| 961 | .seek(SeekFrom::End(0)) |
| 962 | .map_err(|e| BlockError::new(BlockErrorKind::Io, Error::ResizeIo(e)))?; |
| 963 | let new_l1_offset = self.raw_file.cluster_address(file_size + cluster_size - 1); |
| 964 | |
| 965 | // Extend file to fit all L1 clusters |
| 966 | let new_file_end = new_l1_offset + new_l1_clusters * cluster_size; |
| 967 | self.raw_file |
| 968 | .file_mut() |
| 969 | .set_len(new_file_end) |
| 970 | .map_err(|e| BlockError::new(BlockErrorKind::Io, Error::SettingFileSize(e)))?; |
| 971 | |
| 972 | // Set refcounts for the contiguous range |
| 973 | for i in 0..new_l1_clusters { |
| 974 | self.set_cluster_refcount(new_l1_offset + i * cluster_size, 1) |
| 975 | .map_err(|e| BlockError::new(BlockErrorKind::Io, Error::ResizeIo(e)))?; |
| 976 | } |
| 977 | |
| 978 | let mut new_l1_data = vec![0u64; new_l1_size as usize]; |
| 979 | let old_entries = self.l1_table.get_values(); |
| 980 | new_l1_data[..old_entries.len()].copy_from_slice(old_entries); |
| 981 | |
| 982 | for (i, l2_addr) in new_l1_data.iter_mut().enumerate() { |
| 983 | if *l2_addr != 0 && i < old_entries.len() { |
| 984 | let refcount = self |
| 985 | .refcounts |
| 986 | .get_cluster_refcount(&mut self.raw_file, *l2_addr) |
| 987 | .map_err(|e| BlockError::new(BlockErrorKind::Io, Error::GettingRefcount(e)))?; |
| 988 | *l2_addr = l1_entry_make(*l2_addr, refcount == 1); |
| 989 | } |
| 990 | } |
| 991 | |
| 992 | // Write the new L1 table to the file. |
| 993 | self.raw_file |
| 994 | .write_pointer_table_direct(new_l1_offset, new_l1_data.iter()) |
| 995 | .map_err(|e| BlockError::new(BlockErrorKind::Io, Error::ResizeIo(e)))?; |
| 996 | |
| 997 | self.raw_file |
| 998 | .file_mut() |
| 999 | .sync_all() |
| 1000 | .map_err(|e| BlockError::new(BlockErrorKind::Io, Error::SyncingHeader(e)))?; |
| 1001 | |
| 1002 | self.header.l1_size = new_l1_size; |
| 1003 | self.header.l1_table_offset = new_l1_offset; |
| 1004 | |
| 1005 | self.raw_file |
| 1006 | .file_mut() |
no test coverage detected