use crate::error::Result;
use crate::io::BlockDevice;
use crate::ondisk::*;
pub struct FormatOptions {
pub volume_name: String,
pub enable_deldir: bool,
}
impl Default for FormatOptions {
fn default() -> Self {
Self {
volume_name: "Untitled".into(),
enable_deldir: false,
}
}
}
#[derive(Debug)]
pub struct FormatResult {
pub volume_name: String,
pub total_blocks: u64,
pub data_blocks: u64,
pub blocks_free: u64,
pub num_reserved: u32,
pub reserved_blksize: u32,
}
pub fn format_with_size(
dev: &dyn BlockDevice,
total_blocks: u64,
opts: &FormatOptions,
) -> Result<FormatResult> {
let bs = dev.block_size() as usize;
let mut resblocksize: u32 = 1024;
let mut supermode = false;
if total_blocks > MAXSMALLDISK {
supermode = true;
let max_1k = (MAXBITMAPINDEX as u64 + 1) * 253 * 253 * 32;
let max_2k = (MAXBITMAPINDEX as u64 + 1) * 509 * 509 * 32;
if total_blocks > max_1k {
resblocksize = 2048;
}
if total_blocks > max_2k {
resblocksize = 4096;
}
}
if resblocksize < bs as u32 {
resblocksize = bs as u32;
}
let rescluster = resblocksize / bs as u32;
let numreserved = calc_num_reserved(total_blocks, resblocksize);
let mut resbm_1k = 1u32;
let mut i = 125u32;
while i < numreserved / 32 {
resbm_1k += 1;
i += 256;
}
let resbm_resblocks = (1024 * resbm_1k).div_ceil(resblocksize);
let rblkcluster = rescluster * resbm_resblocks;
let firstreserved: u32 = 2;
let lastreserved = rescluster * numreserved + firstreserved - 1;
let mut alloc = ReservedAllocator::new(numreserved, resbm_resblocks);
let rext_idx = alloc.alloc()?;
let rext_blk = firstreserved + rext_idx * rescluster;
let mut options: u32 = MODE_HARDDISK
| MODE_SPLITTED_ANODES
| MODE_DIR_EXTENSION
| MODE_SIZEFIELD
| MODE_DATESTAMP
| MODE_EXTROVING
| MODE_LONGFN
| MODE_EXTENSION;
if supermode {
options |= MODE_SUPERINDEX;
}
let (cday, cmin, ctick) = current_amiga_datestamp();
let index_per_block = (resblocksize / 4) - 3;
let data_blocks = total_blocks as u32 - (lastreserved - firstreserved + 1) - firstreserved;
let bits_per_bmb = index_per_block * 32;
let no_bmb = data_blocks.div_ceil(bits_per_bmb);
let no_bmi = no_bmb.div_ceil(index_per_block);
let mut boot = vec![0u8; bs];
boot[0..4].copy_from_slice(&ID_PFS_DISK.to_be_bytes());
dev.write_block(0, &boot)?;
dev.write_block(1, &vec![0u8; bs])?;
let mut bm_blocknrs = Vec::new();
for _ in 0..no_bmb {
let idx = alloc.alloc()?;
bm_blocknrs.push(firstreserved + idx * rescluster);
}
let mut bmi_blocknrs = Vec::new();
for _ in 0..no_bmi {
let idx = alloc.alloc()?;
bmi_blocknrs.push(firstreserved + idx * rescluster);
}
let anidx_blk = firstreserved + alloc.alloc()? * rescluster;
let anode_blk = firstreserved + alloc.alloc()? * rescluster;
let rootdir_blk = firstreserved + alloc.alloc()? * rescluster;
let rb_size = rblkcluster as usize * bs;
let mut rb_data = vec![0u8; rb_size];
put_u32(&mut rb_data, 0x00, ID_PFS_DISK);
put_u32(&mut rb_data, 0x04, options);
put_u32(&mut rb_data, 0x08, 1); put_u16(&mut rb_data, 0x0C, cday);
put_u16(&mut rb_data, 0x0E, cmin);
put_u16(&mut rb_data, 0x10, ctick);
put_u16(&mut rb_data, 0x12, 0xF0);
let name = opts.volume_name.as_bytes();
let namelen = name.len().min(30);
rb_data[0x14] = namelen as u8;
rb_data[0x15..0x15 + namelen].copy_from_slice(&name[..namelen]);
put_u32(&mut rb_data, 0x34, lastreserved);
put_u32(&mut rb_data, 0x38, firstreserved);
put_u32(&mut rb_data, 0x3C, alloc.free_count());
put_u16(&mut rb_data, 0x40, resblocksize as u16);
put_u16(&mut rb_data, 0x42, rblkcluster as u16);
put_u32(&mut rb_data, 0x44, data_blocks);
put_u32(&mut rb_data, 0x48, data_blocks / 20); put_u32(&mut rb_data, 0x54, total_blocks as u32); put_u32(&mut rb_data, 0x58, rext_blk);
if supermode {
for (i, &blknr) in bmi_blocknrs.iter().enumerate() {
put_u32(&mut rb_data, 0x60 + i * 4, blknr);
}
} else {
for (i, &blknr) in bmi_blocknrs.iter().enumerate() {
if i <= MAXSMALLBITMAPINDEX {
put_u32(&mut rb_data, 0x60 + i * 4, blknr);
}
}
let idx_base = 0x60 + (MAXSMALLBITMAPINDEX + 1) * 4;
put_u32(&mut rb_data, idx_base, anidx_blk);
}
let rbm_off = bs;
put_u16(&mut rb_data, rbm_off, BMBLKID);
put_u32(&mut rb_data, rbm_off + 8, 0); let bm_longs = alloc.build_bitmap();
for (i, &val) in bm_longs.iter().enumerate() {
let off = rbm_off + 12 + i * 4;
if off + 4 <= rb_data.len() {
put_u32(&mut rb_data, off, val);
}
}
for i in 0..rblkcluster as usize {
dev.write_block(
(firstreserved + i as u32) as u64,
&rb_data[i * bs..(i + 1) * bs],
)?;
}
let mut rext = vec![0u8; resblocksize as usize];
put_u16(&mut rext, 0x00, EXTENSIONID);
put_u32(&mut rext, 0x08, 1); put_u32(&mut rext, 0x0C, 0x0013_0002); put_u16(&mut rext, 0x10, cday);
put_u16(&mut rext, 0x12, cmin);
put_u16(&mut rext, 0x14, ctick);
put_u16(&mut rext, 0x38, 32); if supermode {
put_u32(&mut rext, 0x40, anidx_blk); }
write_reserved_blocks(dev, rext_blk as u64, &rext, rescluster, bs)?;
for (seq, &bmi_blknr) in bmi_blocknrs.iter().enumerate() {
let mut bmi = vec![0u8; resblocksize as usize];
put_u16(&mut bmi, 0, BMIBLKID);
put_u32(&mut bmi, 4, 1); put_u32(&mut bmi, 8, seq as u32); for j in 0..index_per_block as usize {
let bm_idx = seq * index_per_block as usize + j;
if bm_idx < bm_blocknrs.len() {
put_u32(&mut bmi, 12 + j * 4, bm_blocknrs[bm_idx]);
}
}
write_reserved_blocks(dev, bmi_blknr as u64, &bmi, rescluster, bs)?;
}
for (seq, &bm_blknr) in bm_blocknrs.iter().enumerate() {
let mut bm = vec![0u8; resblocksize as usize];
put_u16(&mut bm, 0, BMBLKID);
put_u32(&mut bm, 4, 1); put_u32(&mut bm, 8, seq as u32); for j in 0..index_per_block as usize {
let block_idx = seq as u32 * bits_per_bmb + j as u32 * 32;
if block_idx < data_blocks {
let remaining = (data_blocks - block_idx).min(32);
let val = if remaining >= 32 {
0xFFFF_FFFF
} else {
(0xFFFF_FFFFu32) << (32 - remaining)
};
put_u32(&mut bm, 12 + j * 4, val);
}
}
write_reserved_blocks(dev, bm_blknr as u64, &bm, rescluster, bs)?;
}
let mut anidx = vec![0u8; resblocksize as usize];
put_u16(&mut anidx, 0, IBLKID);
put_u32(&mut anidx, 4, 1);
put_u32(&mut anidx, 8, 0); put_u32(&mut anidx, 12, anode_blk); write_reserved_blocks(dev, anidx_blk as u64, &anidx, rescluster, bs)?;
let mut an = vec![0u8; resblocksize as usize];
put_u16(&mut an, 0, ABLKID);
put_u32(&mut an, 4, 1);
put_u32(&mut an, 8, 0); let an_off = ANODE_BLOCK_HEADER_SIZE + ANODE_ROOTDIR as usize * ANODE_SIZE;
put_u32(&mut an, an_off, 1); put_u32(&mut an, an_off + 4, rootdir_blk); put_u32(&mut an, an_off + 8, 0); write_reserved_blocks(dev, anode_blk as u64, &an, rescluster, bs)?;
let mut dir = vec![0u8; resblocksize as usize];
put_u16(&mut dir, 0x00, DBLKID);
put_u32(&mut dir, 0x04, 1); put_u32(&mut dir, 0x0C, ANODE_ROOTDIR);
put_u32(&mut dir, 0x10, ANODE_ROOTDIR); write_reserved_blocks(dev, rootdir_blk as u64, &dir, rescluster, bs)?;
dev.flush()?;
Ok(FormatResult {
volume_name: opts.volume_name.clone(),
total_blocks,
data_blocks: data_blocks as u64,
blocks_free: data_blocks as u64,
num_reserved: numreserved,
reserved_blksize: resblocksize,
})
}
fn calc_num_reserved(total_blocks: u64, resblocksize: u32) -> u32 {
let mut taken: u32 = 32;
let mut i: u64 = 2048;
while i > 0 && i / 2 < total_blocks {
let m: u32 = if i >= 512 * 2048 { 10 } else { 14 };
taken += taken * m / 16;
i = i.checked_shl(1).unwrap_or(0);
}
taken /= resblocksize / 1024;
taken = taken.saturating_sub(1).min(MAXNUMRESERVED as u32);
taken = (taken + 31) & !0x1F;
taken.max(32)
}
const MAXSMALLDISK: u64 = (MAXSMALLBITMAPINDEX as u64 + 1) * 253 * 253 * 32;
const MAXNUMRESERVED: usize = 4096 + 255 * 1024 * 8;
pub use crate::util::current_amiga_datestamp;
struct ReservedAllocator {
bitmap: Vec<bool>, numreserved: u32,
roving: u32,
}
impl ReservedAllocator {
fn new(numreserved: u32, rblkcluster_resblocks: u32) -> Self {
let mut bitmap = vec![true; numreserved as usize];
for i in 0..rblkcluster_resblocks as usize {
if i < bitmap.len() {
bitmap[i] = false;
}
}
Self {
bitmap,
numreserved,
roving: rblkcluster_resblocks,
}
}
fn alloc(&mut self) -> crate::error::Result<u32> {
for i in 0..self.numreserved {
let idx = (self.roving + i) % self.numreserved;
if self.bitmap[idx as usize] {
self.bitmap[idx as usize] = false;
self.roving = (idx + 1) % self.numreserved;
return Ok(idx);
}
}
Err(crate::error::Error::DiskFull(
"out of reserved blocks".into(),
))
}
fn free_count(&self) -> u32 {
self.bitmap.iter().filter(|&&b| b).count() as u32
}
fn build_bitmap(&self) -> Vec<u32> {
let mut longs = Vec::new();
for i in (0..self.numreserved).step_by(32) {
let mut val = 0u32;
for bit in 0..32 {
if (i + bit) < self.numreserved && self.bitmap[(i + bit) as usize] {
val |= 0x8000_0000 >> bit;
}
}
longs.push(val);
}
longs
}
}