use anyhow::{anyhow, Result}; use byteorder::{LittleEndian, WriteBytesExt}; use nom::{number::complete::*, IResult}; use std::io::Cursor; use crate::checksum; use crate::io_engine::*; use crate::pdata::space_map_common::*; use crate::pdata::unpack::*; use crate::write_batcher::*; //------------------------------------------ const MAX_METADATA_BITMAPS: usize = 255; //------------------------------------------ pub struct MetadataIndex { pub blocknr: u64, pub indexes: Vec, } impl Unpack for MetadataIndex { fn disk_size() -> u32 { BLOCK_SIZE as u32 } fn unpack(i: &[u8]) -> IResult<&[u8], MetadataIndex> { // FIXME: check the checksum let (i, _csum) = le_u32(i)?; let (i, _padding) = le_u32(i)?; let (i, blocknr) = le_u64(i)?; let (i, indexes) = nom::multi::count(IndexEntry::unpack, MAX_METADATA_BITMAPS)(i)?; // Filter out unused entries let indexes: Vec = indexes .iter() .take_while(|e| e.blocknr != 0) .cloned() .collect(); Ok((i, MetadataIndex { blocknr, indexes })) } } impl Pack for MetadataIndex { fn pack(&self, w: &mut W) -> Result<()> { w.write_u32::(0)?; // csum w.write_u32::(0)?; // padding w.write_u64::(self.blocknr)?; assert!(self.indexes.len() <= MAX_METADATA_BITMAPS); for ie in &self.indexes { ie.pack(w)?; } Ok(()) } } //------------------------------------------ fn block_to_bitmap(b: u64) -> usize { (b / ENTRIES_PER_BITMAP as u64) as usize } fn adjust_counts( w: &mut WriteBatcher, ie: &IndexEntry, begin: u64, end: u64, ) -> Result { use BitmapEntry::*; let mut first_free = ie.none_free_before; let nr_free = ie.nr_free - (end - begin) as u32; // Read the bitmap let bitmap_block = w.engine.read(ie.blocknr)?; let (_, mut bitmap) = Bitmap::unpack(bitmap_block.get_data())?; // Update all the entries for a in begin..end { if first_free == a as u32 { first_free = a as u32 + 1; } bitmap.entries[a as usize] = Small(1); } // Write the bitmap let mut cur = Cursor::new(bitmap_block.get_data()); bitmap.pack(&mut cur)?; w.write(bitmap_block, checksum::BT::BITMAP)?; // Return the adjusted index entry Ok(IndexEntry { blocknr: ie.blocknr, nr_free, none_free_before: first_free, }) } pub fn write_metadata_sm(w: &mut WriteBatcher) -> Result { let r1 = w.get_reserved_range(); let (mut indexes, ref_count_root) = write_metadata_common(w)?; let bitmap_root = w.alloc_zeroed()?; // Now we need to patch up the counts for the metadata that was used for storing // the space map itself. These ref counts all went from 0 to 1. let r2 = w.get_reserved_range(); if r2.end < r1.end { return Err(anyhow!("unsupported allocation pattern")); } let bi_begin = block_to_bitmap(r1.end); let bi_end = block_to_bitmap(r2.end) + 1; for (bm, ie) in indexes.iter_mut().enumerate().take(bi_end).skip(bi_begin) { let begin = if bm == bi_begin { r1.end % ENTRIES_PER_BITMAP as u64 } else { 0 }; let end = if bm == bi_end - 1 { r2.end % ENTRIES_PER_BITMAP as u64 } else { ENTRIES_PER_BITMAP as u64 }; *ie = adjust_counts(w, ie, begin, end)? } // Write out the metadata index let metadata_index = MetadataIndex { blocknr: bitmap_root.loc, indexes, }; let mut cur = Cursor::new(bitmap_root.get_data()); metadata_index.pack(&mut cur)?; let loc = bitmap_root.loc; w.write(bitmap_root, checksum::BT::INDEX)?; w.flush()?; let sm = w.sm.lock().unwrap(); Ok(SMRoot { nr_blocks: sm.get_nr_blocks()?, nr_allocated: sm.get_nr_allocated()?, bitmap_root: loc, ref_count_root, }) } //------------------------------------------