use crate::BlockDevice;
use crate::fscore::structs::{DmuObjectType, DnodePhys};
use crate::io::pipeline::Pipeline;
use crate::storage::dmu::ObjectSet;
use alloc::vec;
use alloc::vec::Vec;
use lazy_static::lazy_static;
use spin::Mutex;
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct ZvolPhys {
pub object_id: u64, pub volsize: u64, pub volblocksize: u64, pub checksum: u8, pub compression: u8, pub copies: u8, pub readonly: u8, pub txg_birth: u64, pub reserved: [u64; 8], }
impl ZvolPhys {
pub fn new(object_id: u64, volsize: u64, volblocksize: u64) -> Self {
Self {
object_id,
volsize,
volblocksize,
checksum: 1, compression: 1, copies: 1, readonly: 0, txg_birth: 1,
reserved: [0; 8],
}
}
}
pub struct Zvol {
pub phys: ZvolPhys,
pub dnode: DnodePhys, cache: Mutex<Vec<u8>>, }
impl Zvol {
pub fn create(object_id: u64, volsize: u64, volblocksize: u64) -> Result<Self, &'static str> {
crate::lcpfs_println!(
"[ ZVOL ] Creating volume: size={} MB, block={}K",
volsize / 1024 / 1024,
volblocksize / 1024
);
let mut dnode = DnodePhys::zero();
dnode.object_type = DmuObjectType::Zvol as u8;
dnode.max_blkid = (volsize / volblocksize) - 1;
dnode.used_bytes = 0;
let phys = ZvolPhys::new(object_id, volsize, volblocksize);
Ok(Self {
phys,
dnode,
cache: Mutex::new(Vec::new()), })
}
pub fn load(dnode: DnodePhys) -> Result<Self, &'static str> {
let metadata = ObjectSet::read_dnode_data(&dnode, 0, core::mem::size_of::<ZvolPhys>())
.map_err(|_| "Failed to read ZVOL metadata")?;
let phys = unsafe { core::ptr::read_unaligned(metadata.as_ptr() as *const ZvolPhys) };
crate::lcpfs_println!(
"[ ZVOL ] Loaded volume: size={} MB, block={}K",
phys.volsize / 1024 / 1024,
phys.volblocksize / 1024
);
Ok(Self {
phys,
dnode,
cache: Mutex::new(Vec::new()), })
}
pub fn get_size(&self) -> u64 {
self.phys.volsize
}
pub fn get_blocksize(&self) -> u64 {
self.phys.volblocksize
}
pub fn set_size(&mut self, new_size: u64) -> Result<(), &'static str> {
if new_size < self.phys.volsize {
return Err("Cannot shrink ZVOL");
}
crate::lcpfs_println!(
"[ ZVOL ] Resizing volume: {} MB -> {} MB",
self.phys.volsize / 1024 / 1024,
new_size / 1024 / 1024
);
self.phys.volsize = new_size;
self.dnode.max_blkid = (new_size / self.phys.volblocksize) - 1;
Ok(())
}
fn read_range(&self, offset: u64, length: usize) -> Result<Vec<u8>, &'static str> {
if offset + length as u64 > self.phys.volsize {
return Err("ZVOL read out of bounds");
}
let cache = self.cache.lock();
if cache.len() < (offset as usize + length) {
return Ok(vec![0u8; length]); }
let start = offset as usize;
let end = start + length;
Ok(cache[start..end].to_vec())
}
fn write_range(&mut self, offset: u64, data: &[u8]) -> Result<(), &'static str> {
if offset + data.len() as u64 > self.phys.volsize {
return Err("ZVOL write out of bounds");
}
let mut cache = self.cache.lock();
let required_size = offset as usize + data.len();
if cache.len() < required_size {
cache.resize(required_size, 0);
}
let start = offset as usize;
let end = start + data.len();
cache[start..end].copy_from_slice(data);
Ok(())
}
pub fn snapshot(&self, snap_name: &str) -> Result<Self, &'static str> {
crate::lcpfs_println!("[ ZVOL ] Creating snapshot: {}", snap_name);
let mut snap_dnode = self.dnode;
snap_dnode.max_blkid += 1000;
let snap = Self {
phys: self.phys,
dnode: snap_dnode,
cache: Mutex::new(self.cache.lock().clone()),
};
Ok(snap)
}
}
impl BlockDevice for Zvol {
fn read_block(&mut self, block_num: usize, buffer: &mut [u8]) -> Result<(), &'static str> {
let offset = block_num as u64 * self.phys.volblocksize;
let blocksize = self.phys.volblocksize as usize;
if offset >= self.phys.volsize {
return Err("ZVOL read_block: out of bounds");
}
let data = self.read_range(offset, blocksize)?;
let copy_len = core::cmp::min(buffer.len(), data.len());
buffer[..copy_len].copy_from_slice(&data[..copy_len]);
Ok(())
}
fn write_block(&mut self, block_num: usize, buffer: &[u8]) -> Result<(), &'static str> {
let offset = block_num as u64 * self.phys.volblocksize;
if offset >= self.phys.volsize {
return Err("ZVOL write_block: out of bounds");
}
self.write_range(offset, buffer)
}
fn size(&self) -> Result<u64, &'static str> {
Ok(self.phys.volsize)
}
fn block_size(&self) -> usize {
self.phys.volblocksize as usize
}
fn block_count(&self) -> usize {
(self.phys.volsize / self.phys.volblocksize) as usize
}
}
lazy_static! {
pub static ref ZVOL_REGISTRY: Mutex<Vec<Zvol>> = Mutex::new(Vec::new());
}
pub struct ZvolManager;
impl ZvolManager {
pub fn create_zvol(name: &str, size_mb: u64, blocksize_kb: u64) -> Result<usize, &'static str> {
let volsize = size_mb * 1024 * 1024;
let volblocksize = blocksize_kb * 1024;
let object_id = {
let registry = ZVOL_REGISTRY.lock();
(registry.len() as u64) + 1000 };
let zvol = Zvol::create(object_id, volsize, volblocksize)?;
let mut registry = ZVOL_REGISTRY.lock();
registry.push(zvol);
let index = registry.len() - 1;
crate::lcpfs_println!(
"[ ZVOL ] Created {} ({} MB) at index {}",
name,
size_mb,
index
);
Ok(index)
}
pub fn destroy_zvol(index: usize) -> Result<(), &'static str> {
let mut registry = ZVOL_REGISTRY.lock();
if index >= registry.len() {
return Err("Invalid ZVOL index");
}
registry.remove(index);
crate::lcpfs_println!("[ ZVOL ] Destroyed ZVOL at index {}", index);
Ok(())
}
pub fn get_zvol(index: usize) -> Result<(), &'static str> {
let registry = ZVOL_REGISTRY.lock();
if index >= registry.len() {
return Err("Invalid ZVOL index");
}
Ok(())
}
pub fn list_zvols() -> Vec<(usize, u64, u64)> {
let registry = ZVOL_REGISTRY.lock();
registry
.iter()
.enumerate()
.map(|(i, z)| (i, z.phys.volsize, z.phys.volblocksize))
.collect()
}
pub fn stats() -> (usize, u64) {
let registry = ZVOL_REGISTRY.lock();
let count = registry.len();
let total_size: u64 = registry.iter().map(|z| z.phys.volsize).sum();
(count, total_size)
}
}
pub fn create_swap_zvol(size_mb: u64) -> Result<usize, &'static str> {
crate::lcpfs_println!("[ ZVOL ] Creating swap volume: {} MB", size_mb);
ZvolManager::create_zvol("swap", size_mb, 128)
}
pub fn create_database_zvol(name: &str, size_mb: u64) -> Result<usize, &'static str> {
crate::lcpfs_println!(
"[ ZVOL ] Creating database volume: {} ({} MB)",
name,
size_mb
);
ZvolManager::create_zvol(name, size_mb, 8)
}