From 60f05af55594bfa8200659c3dec6ff22fbd2275f Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Wed, 8 Jan 2020 22:34:28 +1100 Subject: [PATCH 1/2] Move the block-io-to-unaligned-io wrapper to its own crate. --- Cargo.lock | 5 +++ Cargo.toml | 3 +- ahcid/Cargo.toml | 1 + ahcid/src/scheme.rs | 79 ++++++++++--------------------------- block-io-wrapper/.gitignore | 1 + block-io-wrapper/Cargo.toml | 9 +++++ block-io-wrapper/src/lib.rs | 47 ++++++++++++++++++++++ 7 files changed, 86 insertions(+), 59 deletions(-) create mode 100644 block-io-wrapper/.gitignore create mode 100644 block-io-wrapper/Cargo.toml create mode 100644 block-io-wrapper/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index c325744907..5d201fb9dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5,6 +5,7 @@ name = "ahcid" version = "0.1.0" dependencies = [ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "block-io-wrapper 0.1.0", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "partitionlib 0.1.0 (git+https://gitlab.redox-os.org/redox-os/partitionlib.git)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", @@ -65,6 +66,10 @@ name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "block-io-wrapper" +version = "0.1.0" + [[package]] name = "build_const" version = "0.2.1" diff --git a/Cargo.toml b/Cargo.toml index b871350843..8ec096c08a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ members = [ "ahcid", "alxd", "bgad", + "block-io-wrapper", "e1000d", "ihdad", "ixgbed", @@ -13,5 +14,5 @@ members = [ "rtl8168d", "vboxd", "vesad", - "xhcid" + "xhcid", ] diff --git a/ahcid/Cargo.toml b/ahcid/Cargo.toml index bfd9f21315..458d8cac56 100644 --- a/ahcid/Cargo.toml +++ b/ahcid/Cargo.toml @@ -8,3 +8,4 @@ bitflags = "1.2" byteorder = "1.2" partitionlib = { git = "https://gitlab.redox-os.org/redox-os/partitionlib.git" } redox_syscall = "0.1" +block-io-wrapper = { path = "../block-io-wrapper" } diff --git a/ahcid/src/scheme.rs b/ahcid/src/scheme.rs index 1e13a744f3..3e87040b61 100644 --- a/ahcid/src/scheme.rs +++ b/ahcid/src/scheme.rs @@ -50,77 +50,40 @@ impl DiskWrapper { Ok(self.offset) } } - // Perhaps this impl should be used in the rest of the scheme. + // TODO: Perhaps this impl should be used in the rest of the scheme. impl<'a, 'b> Read for Device<'a, 'b> { - fn read(&mut self, mut buf: &mut [u8]) -> io::Result { - // TODO: Yield sometimes, perhaps after a few blocks or something. - use std::ops::{Add, Div, Rem}; - - fn div_round_up(a: T, b: T) -> T - where - T: Add + Div + Rem + PartialEq + From + Copy - { - if a % b != T::from(0u8) { - a / b + T::from(1u8) - } else { - a / b - } - } - - let orig_buf_len = buf.len(); - + fn read(&mut self, buf: &mut [u8]) -> io::Result { let blksize = self.disk.block_length().map_err(|err| io::Error::from_raw_os_error(err.errno))?; + let size_in_blocks = self.disk.size() / u64::from(blksize); - let start_block = self.offset / u64::from(blksize); - let end_block = div_round_up(self.offset + buf.len() as u64, u64::from(blksize)); // The first block not in the range + let disk = &mut self.disk; - let offset_from_start_block: u64 = self.offset % u64::from(blksize); - let offset_to_end_block: u64 = u64::from(blksize) - (self.offset + buf.len() as u64) % u64::from(blksize); - - let first_whole_block = start_block + if offset_from_start_block > 0 { 1 } else { 0 }; - let last_whole_block = end_block - if offset_to_end_block > 0 { 1 } else { 0 } - 1; - - let whole_blocks_to_read = last_whole_block - first_whole_block + 1; - - for block in start_block..end_block { - // TODO: Async/await? I mean, shouldn't AHCI be async? - - loop { - let block = self.offset / u64::from(blksize); - - match self.disk.read(block, self.block_bytes) { - Ok(Some(bytes)) => { - assert_eq!(bytes, self.block_bytes.len()); - assert_eq!(bytes, blksize as usize); - break; - } - Ok(None) => continue, - Err(err) => return Err(io::Error::from_raw_os_error(err.errno)), - } + let read_block = |block: u64, block_bytes: &mut [u8]| { + if block >= size_in_blocks { + return Err(io::Error::from_raw_os_error(syscall::EOVERFLOW)); } + loop { + match disk.read(block, block_bytes) { + Ok(Some(bytes)) => { + assert_eq!(bytes, block_bytes.len()); + assert_eq!(bytes, blksize as usize); + return Ok(()); + } + Ok(None) => continue, + Err(err) => return Err(io::Error::from_raw_os_error(err.errno)), + } + } + }; + let bytes_read = block_io_wrapper::read(self.offset, blksize, buf, self.block_bytes, read_block)?; - let (bytes_to_read, src_buf): (u64, &[u8]) = if block == start_block { - (u64::from(blksize) - offset_from_start_block, &self.block_bytes[offset_from_start_block as usize..]) - } else if block == end_block { - (u64::from(blksize) - offset_to_end_block, &self.block_bytes[..offset_to_end_block as usize]) - } else { - (blksize.into(), &self.block_bytes[..]) - }; - let bytes_to_read = std::cmp::min(bytes_to_read as usize, buf.len()); - buf[..bytes_to_read].copy_from_slice(&src_buf[..bytes_to_read]); - buf = &mut buf[..bytes_to_read]; - } - - let bytes_read = std::cmp::min(orig_buf_len, whole_blocks_to_read as usize * blksize as usize + offset_from_start_block as usize + offset_to_end_block as usize); self.offset += bytes_read as u64; - Ok(bytes_read) } } let mut block_bytes = [0u8; 4096]; - partitionlib::get_partitions(&mut Device { disk, offset: 0, block_bytes: &mut block_bytes[..bs.into()] }, bs).ok().flatten() + partitionlib::get_partitions(&mut Device { disk, offset: 0, block_bytes: &mut block_bytes[..bs.into()] }, bs).map_err(|x| dbg!(x)).ok().flatten() } fn new(mut disk: Box) -> Self { Self { diff --git a/block-io-wrapper/.gitignore b/block-io-wrapper/.gitignore new file mode 100644 index 0000000000..ea8c4bf7f3 --- /dev/null +++ b/block-io-wrapper/.gitignore @@ -0,0 +1 @@ +/target diff --git a/block-io-wrapper/Cargo.toml b/block-io-wrapper/Cargo.toml new file mode 100644 index 0000000000..e40054ccf1 --- /dev/null +++ b/block-io-wrapper/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "block-io-wrapper" +version = "0.1.0" +authors = ["4lDO2 <4lDO2@protonmail.com>"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/block-io-wrapper/src/lib.rs b/block-io-wrapper/src/lib.rs new file mode 100644 index 0000000000..6556067d42 --- /dev/null +++ b/block-io-wrapper/src/lib.rs @@ -0,0 +1,47 @@ +pub fn read(offset: u64, blksize: u32, mut buf: &mut [u8], block_bytes: &mut [u8], mut read: impl FnMut(u64, &mut [u8]) -> Result<(), E>) -> Result { + // TODO: Yield sometimes, perhaps after a few blocks or something. + use std::ops::{Add, Div, Rem}; + + fn div_round_up(a: T, b: T) -> T + where + T: Add + Div + Rem + PartialEq + From + Copy + { + if a % b != T::from(0u8) { + a / b + T::from(1u8) + } else { + a / b + } + } + + let orig_buf_len = buf.len(); + + let start_block = offset / u64::from(blksize); + let end_block = div_round_up(offset + buf.len() as u64, u64::from(blksize)); // The first block not in the range + + let offset_from_start_block: u64 = offset % u64::from(blksize); + let offset_to_end_block: u64 = u64::from(blksize) - (offset + buf.len() as u64) % u64::from(blksize); + + let first_whole_block = start_block + if offset_from_start_block > 0 { 1 } else { 0 }; + let last_whole_block = end_block - if offset_to_end_block > 0 { 1 } else { 0 } - 1; + + let whole_blocks_to_read = last_whole_block - first_whole_block + 1; + + for block in start_block..=end_block { + // TODO: Async/await? I mean, shouldn't AHCI be async? + + read(block, block_bytes)?; + + let (bytes_to_read, src_buf): (u64, &[u8]) = if block == start_block { + (u64::from(blksize) - offset_from_start_block, &block_bytes[offset_from_start_block as usize..]) + } else if block == end_block { + (u64::from(blksize) - offset_to_end_block, &block_bytes[..offset_to_end_block as usize]) + } else { + (blksize.into(), &block_bytes[..]) + }; + let bytes_to_read = std::cmp::min(bytes_to_read as usize, buf.len()); + buf[..bytes_to_read].copy_from_slice(&src_buf[..bytes_to_read]); + buf = &mut buf[bytes_to_read..]; + } + + Ok(std::cmp::min(orig_buf_len, whole_blocks_to_read as usize * blksize as usize + offset_from_start_block as usize + offset_to_end_block as usize)) +} From 5807909ed17a16494d6fa86e3d10b166191755a5 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Thu, 9 Jan 2020 10:38:31 +1100 Subject: [PATCH 2/2] Add partitioning support to nvmed. --- Cargo.lock | 2 + ahcid/src/scheme.rs | 4 +- nvmed/.gitignore | 1 + nvmed/Cargo.toml | 2 + nvmed/src/scheme.rs | 226 ++++++++++++++++++++++++++++++++++++++++---- 5 files changed, 217 insertions(+), 18 deletions(-) create mode 100644 nvmed/.gitignore diff --git a/Cargo.lock b/Cargo.lock index 5d201fb9dd..23f2855227 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -524,6 +524,8 @@ name = "nvmed" version = "0.1.0" dependencies = [ "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "block-io-wrapper 0.1.0", + "partitionlib 0.1.0 (git+https://gitlab.redox-os.org/redox-os/partitionlib.git)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "spin 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/ahcid/src/scheme.rs b/ahcid/src/scheme.rs index 3e87040b61..f2e523c37a 100644 --- a/ahcid/src/scheme.rs +++ b/ahcid/src/scheme.rs @@ -69,7 +69,7 @@ impl DiskWrapper { assert_eq!(bytes, blksize as usize); return Ok(()); } - Ok(None) => continue, + Ok(None) => { std::thread::yield_now(); continue } Err(err) => return Err(io::Error::from_raw_os_error(err.errno)), } } @@ -83,7 +83,7 @@ impl DiskWrapper { let mut block_bytes = [0u8; 4096]; - partitionlib::get_partitions(&mut Device { disk, offset: 0, block_bytes: &mut block_bytes[..bs.into()] }, bs).map_err(|x| dbg!(x)).ok().flatten() + partitionlib::get_partitions(&mut Device { disk, offset: 0, block_bytes: &mut block_bytes[..bs.into()] }, bs).ok().flatten() } fn new(mut disk: Box) -> Self { Self { diff --git a/nvmed/.gitignore b/nvmed/.gitignore new file mode 100644 index 0000000000..ea8c4bf7f3 --- /dev/null +++ b/nvmed/.gitignore @@ -0,0 +1 @@ +/target diff --git a/nvmed/Cargo.toml b/nvmed/Cargo.toml index 7bd235dc99..b0bbfee4b2 100644 --- a/nvmed/Cargo.toml +++ b/nvmed/Cargo.toml @@ -7,3 +7,5 @@ edition = "2018" bitflags = "0.7" spin = "0.4" redox_syscall = "0.1" +partitionlib = { git = "https://gitlab.redox-os.org/redox-os/partitionlib.git" } +block-io-wrapper = { path = "../block-io-wrapper" } diff --git a/nvmed/src/scheme.rs b/nvmed/src/scheme.rs index 15edcbdb45..6a2da55dde 100644 --- a/nvmed/src/scheme.rs +++ b/nvmed/src/scheme.rs @@ -1,34 +1,118 @@ use std::collections::BTreeMap; use std::{cmp, str}; +use std::convert::{TryFrom, TryInto}; use std::fmt::Write; -use std::io::Read; +use std::io::prelude::*; +use std::io; use syscall::{ - Error, EACCES, EBADF, EINVAL, EISDIR, ENOENT, Result, + Error, EACCES, EBADF, EINVAL, EISDIR, ENOENT, EOVERFLOW, Result, Io, SchemeBlockMut, Stat, MODE_DIR, MODE_FILE, O_DIRECTORY, O_STAT, SEEK_CUR, SEEK_END, SEEK_SET}; use crate::nvme::{Nvme, NvmeNamespace}; +use partitionlib::{LogicalBlockSize, PartitionTable}; + #[derive(Clone)] enum Handle { - List(Vec, usize), - Disk(u32, usize) + List(Vec, usize), // entries, offset + Disk(u32, usize), // disk num, offset + Partition(u32, u32, usize), // disk num, part num, offset +} + +pub struct DiskWrapper { + inner: NvmeNamespace, + pt: Option, +} + +impl AsRef for DiskWrapper { + fn as_ref(&self) -> &NvmeNamespace { + &self.inner + } +} + +impl DiskWrapper { + fn pt(disk: &mut NvmeNamespace, nvme: &mut Nvme) -> Option { + let bs = match disk.block_size { + 512 => LogicalBlockSize::Lb512, + 4096 => LogicalBlockSize::Lb4096, + _ => return None, + }; + struct Device<'a, 'b> { disk: &'a mut NvmeNamespace, nvme: &'a mut Nvme, offset: u64, block_bytes: &'b mut [u8] } + + impl<'a, 'b> Seek for Device<'a, 'b> { + fn seek(&mut self, from: io::SeekFrom) -> io::Result { + let size_u = self.disk.blocks * self.disk.block_size; + let size = i64::try_from(size_u).or(Err(io::Error::new(io::ErrorKind::Other, "Disk larger than 2^63 - 1 bytes")))?; + + self.offset = match from { + io::SeekFrom::Start(new_pos) => cmp::min(size_u, new_pos), + io::SeekFrom::Current(new_pos) => cmp::max(0, cmp::min(size, self.offset as i64 + new_pos)) as u64, + io::SeekFrom::End(new_pos) => cmp::max(0, cmp::min(size + new_pos, size)) as u64, + }; + + Ok(self.offset) + } + } + + impl<'a, 'b> Read for Device<'a, 'b> { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let blksize = self.disk.block_size; + let size_in_blocks = self.disk.blocks; + + let disk = &mut self.disk; + let nvme = &mut self.nvme; + + let read_block = |block: u64, block_bytes: &mut [u8]| { + if block >= size_in_blocks { + return Err(io::Error::from_raw_os_error(syscall::EOVERFLOW)); + } + loop { + match unsafe { + nvme.namespace_read(disk.id, block, block_bytes).map_err(|err| io::Error::from_raw_os_error(err.errno))? + } { + Some(bytes) => { + assert_eq!(bytes, block_bytes.len()); + assert_eq!(bytes, blksize as usize); + return Ok(()); + } + None => { std::thread::yield_now(); continue } + // TODO: Does this driver have (internal) error handling at all? + } + } + }; + let bytes_read = block_io_wrapper::read(self.offset, blksize.try_into().expect("Unreasonable block size above 2^32 bytes"), buf, self.block_bytes, read_block)?; + self.offset += bytes_read as u64; + Ok(bytes_read) + } + } + + let mut block_bytes = [0u8; 4096]; + + partitionlib::get_partitions(&mut Device { disk, nvme, offset: 0, block_bytes: &mut block_bytes[..bs as usize] }, bs).ok().flatten() + } + fn new(mut inner: NvmeNamespace, nvme: &mut Nvme) -> Self { + Self { + pt: Self::pt(&mut inner, nvme), + inner, + } + } } pub struct DiskScheme { scheme_name: String, nvme: Nvme, - disks: BTreeMap, + disks: BTreeMap, handles: BTreeMap, next_id: usize } impl DiskScheme { - pub fn new(scheme_name: String, nvme: Nvme, disks: BTreeMap) -> DiskScheme { + pub fn new(scheme_name: String, mut nvme: Nvme, disks: BTreeMap) -> DiskScheme { DiskScheme { scheme_name, + disks: disks.into_iter().map(|(k, v)| (k, DiskWrapper::new(v, &mut nvme))).collect(), nvme, - disks, handles: BTreeMap::new(), next_id: 0 } @@ -61,8 +145,15 @@ impl SchemeBlockMut for DiskScheme { if flags & O_DIRECTORY == O_DIRECTORY || flags & O_STAT == O_STAT { let mut list = String::new(); - for (nsid, _disk) in self.disks.iter() { + for (nsid, disk) in self.disks.iter() { write!(list, "{}\n", nsid).unwrap(); + + if disk.pt.is_none() { + continue; + } + for part_num in 0..disk.pt.as_ref().unwrap().partitions.len() { + write!(list, "{}p{}\n", nsid, part_num).unwrap(); + } } let id = self.next_id; @@ -72,6 +163,29 @@ impl SchemeBlockMut for DiskScheme { } else { Err(Error::new(EISDIR)) } + } else if let Some(p_pos) = path_str.chars().position(|c| c == 'p') { + let nsid_str = &path_str[..p_pos]; + + if p_pos + 1 >= path_str.len() { + return Err(Error::new(ENOENT)); + } + let part_num_str = &path_str[p_pos + 1..]; + + let nsid = nsid_str.parse::().or(Err(Error::new(ENOENT)))?; + let part_num = part_num_str.parse::().or(Err(Error::new(ENOENT)))?; + + if let Some(disk) = self.disks.get(&nsid) { + if disk.pt.as_ref().ok_or(Error::new(ENOENT))?.partitions.get(part_num as usize).is_some() { + let id = self.next_id; + self.next_id += 1; + self.handles.insert(id, Handle::Partition(nsid, part_num, 0)); + Ok(Some(id)) + } else { + Err(Error::new(ENOENT)) + } + } else { + Err(Error::new(ENOENT)) + } } else { let nsid = path_str.parse::().or(Err(Error::new(ENOENT)))?; @@ -115,7 +229,18 @@ impl SchemeBlockMut for DiskScheme { Handle::Disk(number, _) => { let disk = self.disks.get_mut(&number).ok_or(Error::new(EBADF))?; stat.st_mode = MODE_FILE; - stat.st_size = disk.blocks * disk.block_size; + stat.st_blocks = disk.as_ref().blocks; + stat.st_blksize = disk.as_ref().block_size.try_into().expect("Unreasonable block size of over 2^32 bytes"); + stat.st_size = disk.as_ref().blocks * disk.as_ref().block_size; + Ok(Some(0)) + } + Handle::Partition(disk_num, part_num, _) => { + let disk = self.disks.get_mut(&disk_num).ok_or(Error::new(EBADF))?; + let part = disk.pt.as_ref().ok_or(Error::new(EBADF))?.partitions.get(part_num as usize).ok_or(Error::new(EBADF))?; + stat.st_mode = MODE_FILE; + stat.st_size = part.size * disk.as_ref().block_size; + stat.st_blocks = part.size; + stat.st_blksize = disk.as_ref().block_size.try_into().expect("Unreasonable block size of over 2^32 bytes"); Ok(Some(0)) } } @@ -151,6 +276,16 @@ impl SchemeBlockMut for DiskScheme { j += 1; } } + Handle::Partition(disk_num, part_num, _) => { + let number_str = format!("{}p{}", disk_num, part_num); + let number_bytes = number_str.as_bytes(); + j = 0; + while i < buf.len() && j < number_bytes.len() { + buf[i] = number_bytes[j]; + i += 1; + j += 1; + } + } } Ok(Some(i)) @@ -162,12 +297,12 @@ impl SchemeBlockMut for DiskScheme { let count = (&handle[*size..]).read(buf).unwrap(); *size += count; Ok(Some(count)) - }, + } Handle::Disk(number, ref mut size) => { let disk = self.disks.get_mut(&number).ok_or(Error::new(EBADF))?; - let block_size = disk.block_size; + let block_size = disk.as_ref().block_size; if let Some(count) = unsafe { - self.nvme.namespace_read(disk.id, (*size as u64)/block_size, buf)? + self.nvme.namespace_read(disk.as_ref().id, (*size as u64)/block_size, buf)? } { *size += count; Ok(Some(count)) @@ -175,6 +310,28 @@ impl SchemeBlockMut for DiskScheme { Ok(None) } } + Handle::Partition(disk_num, part_num, ref mut offset) => { + let disk = self.disks.get_mut(&disk_num).ok_or(Error::new(EBADF))?; + let part = disk.pt.as_ref().ok_or(Error::new(EBADF))?.partitions.get(part_num as usize).ok_or(Error::new(EBADF))?; + + let block_size = disk.as_ref().block_size; + let rel_block = (*offset as u64) / block_size; + + if rel_block + *offset as u64 / block_size >= part.size { + return Err(Error::new(EOVERFLOW)); + } + + let abs_block = part.start_lba + rel_block; + + if let Some(count) = unsafe { + self.nvme.namespace_read(disk.as_ref().id, abs_block, buf)? + } { + *offset += count; + Ok(Some(count)) + } else { + Ok(None) + } + } } } @@ -185,9 +342,9 @@ impl SchemeBlockMut for DiskScheme { }, Handle::Disk(number, ref mut size) => { let disk = self.disks.get_mut(&number).ok_or(Error::new(EBADF))?; - let block_size = disk.block_size; + let block_size = disk.as_ref().block_size; if let Some(count) = unsafe { - self.nvme.namespace_write(disk.id, (*size as u64)/block_size, buf)? + self.nvme.namespace_write(disk.as_ref().id, (*size as u64)/block_size, buf)? } { *size += count; Ok(Some(count)) @@ -195,6 +352,28 @@ impl SchemeBlockMut for DiskScheme { Ok(None) } } + Handle::Partition(disk_num, part_num, ref mut offset) => { + let disk = self.disks.get_mut(&disk_num).ok_or(Error::new(EBADF))?; + let part = disk.pt.as_ref().ok_or(Error::new(EBADF))?.partitions.get(part_num as usize).ok_or(Error::new(EBADF))?; + + let block_size = disk.as_ref().block_size; + let rel_block = (*offset as u64) / block_size; + + if rel_block + *offset as u64 / block_size >= part.size { + return Err(Error::new(EOVERFLOW)); + } + + let abs_block = part.start_lba + rel_block; + + if let Some(count) = unsafe { + self.nvme.namespace_write(disk.as_ref().id, abs_block, buf)? + } { + *offset += count; + Ok(Some(count)) + } else { + Ok(None) + } + } } } @@ -210,10 +389,25 @@ impl SchemeBlockMut for DiskScheme { }; Ok(Some(*size)) - }, + } Handle::Disk(number, ref mut size) => { let disk = self.disks.get_mut(&number).ok_or(Error::new(EBADF))?; - let len = (disk.blocks * disk.block_size) as usize; + let len = (disk.as_ref().blocks * disk.as_ref().block_size) as usize; + *size = match whence { + SEEK_SET => cmp::min(len, pos), + SEEK_CUR => cmp::max(0, cmp::min(len as isize, *size as isize + pos as isize)) as usize, + SEEK_END => cmp::max(0, cmp::min(len as isize, len as isize + pos as isize)) as usize, + _ => return Err(Error::new(EINVAL)) + }; + + Ok(Some(*size)) + } + Handle::Partition(disk_num, part_num, ref mut size) => { + let disk = self.disks.get_mut(&disk_num).ok_or(Error::new(EBADF))?; + let part = disk.pt.as_ref().ok_or(Error::new(EBADF))?.partitions.get(part_num as usize).ok_or(Error::new(EBADF))?; + + let len = (part.size * disk.as_ref().block_size) as usize; + *size = match whence { SEEK_SET => cmp::min(len, pos), SEEK_CUR => cmp::max(0, cmp::min(len as isize, *size as isize + pos as isize)) as usize,