Merge branch 'unify_disk_scheme_handling' into 'master'
Mostly unify disk scheme implementations See merge request redox-os/drivers!249
This commit is contained in:
Generated
+4
-6
@@ -52,7 +52,6 @@ dependencies = [
|
||||
"log",
|
||||
"pcid",
|
||||
"redox-daemon",
|
||||
"redox-scheme 0.3.0",
|
||||
"redox_event",
|
||||
"redox_syscall",
|
||||
]
|
||||
@@ -180,7 +179,6 @@ dependencies = [
|
||||
"fdt 0.2.0-alpha1",
|
||||
"libredox",
|
||||
"redox-daemon",
|
||||
"redox-scheme 0.3.0",
|
||||
"redox_event",
|
||||
"redox_syscall",
|
||||
]
|
||||
@@ -392,7 +390,9 @@ checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
|
||||
name = "driver-block"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libredox",
|
||||
"partitionlib",
|
||||
"redox-scheme 0.3.0",
|
||||
"redox_syscall",
|
||||
]
|
||||
|
||||
@@ -680,7 +680,6 @@ dependencies = [
|
||||
"log",
|
||||
"pcid",
|
||||
"redox-daemon",
|
||||
"redox-scheme 0.3.0",
|
||||
"redox_event",
|
||||
"redox_syscall",
|
||||
]
|
||||
@@ -781,9 +780,9 @@ name = "lived"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"driver-block",
|
||||
"libredox",
|
||||
"redox-daemon",
|
||||
"redox-scheme 0.3.0",
|
||||
"redox_event",
|
||||
"redox_syscall",
|
||||
]
|
||||
@@ -856,7 +855,6 @@ dependencies = [
|
||||
"log",
|
||||
"pcid",
|
||||
"redox-daemon",
|
||||
"redox-scheme 0.3.0",
|
||||
"redox_event",
|
||||
"redox_syscall",
|
||||
"smallvec 1.13.2",
|
||||
@@ -1673,7 +1671,7 @@ dependencies = [
|
||||
"log",
|
||||
"pcid",
|
||||
"redox-daemon",
|
||||
"redox-scheme 0.3.0",
|
||||
"redox_event",
|
||||
"redox_syscall",
|
||||
"spin",
|
||||
"static_assertions",
|
||||
|
||||
@@ -72,13 +72,13 @@ impl<T: NetworkAdapter> NetworkScheme<T> {
|
||||
&mut self.adapter
|
||||
}
|
||||
|
||||
/// Process pending and new packets.
|
||||
/// Process pending and new requests.
|
||||
///
|
||||
/// This needs to be called each time there is a new event on the scheme
|
||||
/// file and each time a new network packet has been received by the
|
||||
/// driver.
|
||||
// FIXME maybe split into one method for events on the scheme fd and one
|
||||
// to call when an irq is received to indicate that blocked packets can
|
||||
// to call when an irq is received to indicate that blocked requests can
|
||||
// be processed.
|
||||
pub fn tick(&mut self) -> io::Result<()> {
|
||||
// Handle any blocked requests
|
||||
|
||||
@@ -14,5 +14,4 @@ common = { path = "../../common" }
|
||||
driver-block = { path = "../driver-block" }
|
||||
pcid = { path = "../../pcid" }
|
||||
libredox = "0.1.3"
|
||||
redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git" }
|
||||
redox_event = "0.4"
|
||||
|
||||
+32
-92
@@ -7,18 +7,14 @@ use std::io::{Read, Write};
|
||||
use std::os::fd::AsRawFd;
|
||||
use std::usize;
|
||||
|
||||
use common::io::Io;
|
||||
use driver_block::DiskScheme;
|
||||
use event::{EventFlags, RawEventQueue};
|
||||
use pcid_interface::PciFunctionHandle;
|
||||
use redox_scheme::{RequestKind, Response, SignalBehavior, Socket};
|
||||
use syscall::error::{Error, ENODEV};
|
||||
|
||||
use log::{error, info};
|
||||
use syscall::{EAGAIN, EWOULDBLOCK};
|
||||
|
||||
use crate::scheme::DiskScheme;
|
||||
|
||||
pub mod ahci;
|
||||
pub mod scheme;
|
||||
|
||||
fn main() {
|
||||
redox_daemon::Daemon::new(daemon).expect("ahcid: failed to daemonize");
|
||||
@@ -48,18 +44,27 @@ fn daemon(daemon: redox_daemon::Daemon) -> ! {
|
||||
|
||||
let address = unsafe { pcid_handle.map_bar(5) }.ptr.as_ptr() as usize;
|
||||
{
|
||||
let (hba_mem, disks) = ahci::disks(address as usize, &name);
|
||||
|
||||
let scheme_name = format!("disk.{}", name);
|
||||
let socket = Socket::nonblock(&scheme_name).expect("ahcid: failed to create disk scheme");
|
||||
let mut scheme = DiskScheme::new(
|
||||
scheme_name,
|
||||
disks
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i, disk)| (i as u32, disk))
|
||||
.collect(),
|
||||
);
|
||||
|
||||
let mut irq_file = irq.irq_handle("ahcid");
|
||||
let irq_fd = irq_file.as_raw_fd() as usize;
|
||||
|
||||
let mut event_queue = RawEventQueue::new().expect("ahcid: failed to create event queue");
|
||||
let event_queue = RawEventQueue::new().expect("ahcid: failed to create event queue");
|
||||
|
||||
libredox::call::setrens(0, 0).expect("ahcid: failed to enter null namespace");
|
||||
|
||||
event_queue
|
||||
.subscribe(socket.inner().raw(), 1, EventFlags::READ)
|
||||
.subscribe(scheme.event_handle().raw(), 1, EventFlags::READ)
|
||||
.expect("ahcid: failed to event scheme socket");
|
||||
event_queue
|
||||
.subscribe(irq_fd, 1, EventFlags::READ)
|
||||
@@ -67,52 +72,10 @@ fn daemon(daemon: redox_daemon::Daemon) -> ! {
|
||||
|
||||
daemon.ready().expect("ahcid: failed to notify parent");
|
||||
|
||||
let (hba_mem, disks) = ahci::disks(address as usize, &name);
|
||||
let mut scheme = DiskScheme::new(scheme_name, hba_mem, disks);
|
||||
|
||||
let mut mounted = true;
|
||||
let mut todo = Vec::new();
|
||||
while mounted {
|
||||
let Some(event) = event_queue
|
||||
.next()
|
||||
.transpose()
|
||||
.expect("ahcid: failed to read event file")
|
||||
else {
|
||||
break;
|
||||
};
|
||||
if event.fd == socket.inner().raw() {
|
||||
loop {
|
||||
let sqe = match socket.next_request(SignalBehavior::Interrupt) {
|
||||
Ok(None) => {
|
||||
mounted = false;
|
||||
break;
|
||||
}
|
||||
Ok(Some(s)) => {
|
||||
if let RequestKind::Call(call) = s.kind() {
|
||||
call
|
||||
} else {
|
||||
// TODO: Support e.g. cancellation
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
if err.errno == EWOULDBLOCK || err.errno == EAGAIN {
|
||||
break;
|
||||
} else {
|
||||
panic!("ahcid: failed to read disk scheme: {}", err);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(response) = sqe.handle_scheme_block(&mut scheme) {
|
||||
// TODO: handle full CQE?
|
||||
socket
|
||||
.write_response(response, SignalBehavior::Restart)
|
||||
.expect("ahcid: failed to write disk scheme");
|
||||
} else {
|
||||
todo.push(sqe);
|
||||
}
|
||||
}
|
||||
for event in event_queue {
|
||||
let event = event.unwrap();
|
||||
if event.fd == scheme.event_handle().raw() {
|
||||
scheme.tick().unwrap();
|
||||
} else if event.fd == irq_fd {
|
||||
let mut irq = [0; 8];
|
||||
if irq_file
|
||||
@@ -120,52 +83,29 @@ fn daemon(daemon: redox_daemon::Daemon) -> ! {
|
||||
.expect("ahcid: failed to read irq file")
|
||||
>= irq.len()
|
||||
{
|
||||
if scheme.irq() {
|
||||
let is = hba_mem.is.read();
|
||||
if is > 0 {
|
||||
let pi = hba_mem.pi.read();
|
||||
let pi_is = pi & is;
|
||||
for i in 0..hba_mem.ports.len() {
|
||||
if pi_is & 1 << i > 0 {
|
||||
let port = &mut hba_mem.ports[i];
|
||||
let is = port.is.read();
|
||||
port.is.write(is);
|
||||
}
|
||||
}
|
||||
hba_mem.is.write(is);
|
||||
|
||||
irq_file
|
||||
.write(&irq)
|
||||
.expect("ahcid: failed to write irq file");
|
||||
|
||||
// Handle todos in order to finish previous packets if possible
|
||||
let mut i = 0;
|
||||
while i < todo.len() {
|
||||
if let Some(resp) = todo[i].handle_scheme_block(&mut scheme) {
|
||||
let _sqe = todo.remove(i);
|
||||
socket
|
||||
.write_response(resp, SignalBehavior::Restart)
|
||||
.expect("ahcid: failed to write disk scheme");
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
scheme.tick().unwrap();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error!("Unknown event {}", event.fd);
|
||||
}
|
||||
|
||||
// Handle todos to start new packets if possible
|
||||
let mut i = 0;
|
||||
while i < todo.len() {
|
||||
if let Some(response) = todo[i].handle_scheme_block(&mut scheme) {
|
||||
let _sqe = todo.remove(i);
|
||||
socket
|
||||
.write_response(response, SignalBehavior::Restart)
|
||||
.expect("ahcid: failed to write disk scheme");
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if !mounted {
|
||||
for sqe in todo.drain(..) {
|
||||
socket
|
||||
.write_response(
|
||||
Response::new(&sqe, Err(Error::new(ENODEV))),
|
||||
SignalBehavior::Restart,
|
||||
)
|
||||
.expect("ahcid: failed to write disk scheme");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,329 +0,0 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt::Write;
|
||||
use std::str;
|
||||
|
||||
use common::io::Io as _;
|
||||
use driver_block::{Disk, DiskWrapper};
|
||||
use redox_scheme::{CallerCtx, OpenResult, SchemeBlock};
|
||||
use syscall::schemev2::NewFdFlags;
|
||||
use syscall::{
|
||||
Error, Result, Stat, EACCES, EBADF, EISDIR, ENOENT, ENOLCK, MODE_DIR, MODE_FILE, O_DIRECTORY,
|
||||
O_STAT,
|
||||
};
|
||||
|
||||
use crate::ahci::hba::HbaMem;
|
||||
|
||||
enum Handle {
|
||||
List(Vec<u8>), // Dir contents buffer
|
||||
Disk(usize), // Disk index
|
||||
Partition(usize, u32), // Disk index, partition index
|
||||
}
|
||||
|
||||
pub struct DiskScheme {
|
||||
scheme_name: String,
|
||||
hba_mem: &'static mut HbaMem,
|
||||
disks: Box<[DiskWrapper<Box<dyn Disk>>]>,
|
||||
handles: BTreeMap<usize, Handle>,
|
||||
next_id: usize,
|
||||
}
|
||||
|
||||
impl DiskScheme {
|
||||
pub fn new(
|
||||
scheme_name: String,
|
||||
hba_mem: &'static mut HbaMem,
|
||||
disks: Vec<Box<dyn Disk>>,
|
||||
) -> DiskScheme {
|
||||
DiskScheme {
|
||||
scheme_name,
|
||||
hba_mem,
|
||||
disks: disks
|
||||
.into_iter()
|
||||
.map(DiskWrapper::new)
|
||||
.collect::<Vec<_>>()
|
||||
.into_boxed_slice(),
|
||||
handles: BTreeMap::new(),
|
||||
next_id: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn irq(&mut self) -> bool {
|
||||
let is = self.hba_mem.is.read();
|
||||
if is > 0 {
|
||||
let pi = self.hba_mem.pi.read();
|
||||
let pi_is = pi & is;
|
||||
for i in 0..self.hba_mem.ports.len() {
|
||||
if pi_is & 1 << i > 0 {
|
||||
let port = &mut self.hba_mem.ports[i];
|
||||
let is = port.is.read();
|
||||
port.is.write(is);
|
||||
}
|
||||
}
|
||||
self.hba_mem.is.write(is);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// Checks if any conflicting handles already exist
|
||||
fn check_locks(&self, disk_i: usize, part_i_opt: Option<u32>) -> Result<()> {
|
||||
for (_, handle) in self.handles.iter() {
|
||||
match handle {
|
||||
Handle::Disk(i) => {
|
||||
if disk_i == *i {
|
||||
return Err(Error::new(ENOLCK));
|
||||
}
|
||||
}
|
||||
Handle::Partition(i, p) => {
|
||||
if disk_i == *i {
|
||||
match part_i_opt {
|
||||
Some(part_i) => {
|
||||
if part_i == *p {
|
||||
return Err(Error::new(ENOLCK));
|
||||
}
|
||||
}
|
||||
None => {
|
||||
return Err(Error::new(ENOLCK));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl SchemeBlock for DiskScheme {
|
||||
fn xopen(&mut self, path: &str, flags: usize, ctx: &CallerCtx) -> Result<Option<OpenResult>> {
|
||||
if ctx.uid != 0 {
|
||||
return Err(Error::new(EACCES));
|
||||
}
|
||||
let path_str = path.trim_matches('/');
|
||||
|
||||
let handle = if path_str.is_empty() {
|
||||
if flags & O_DIRECTORY != O_DIRECTORY && flags & O_STAT != O_STAT {
|
||||
return Err(Error::new(EISDIR));
|
||||
}
|
||||
let mut list = String::new();
|
||||
|
||||
for (disk_index, disk) in self.disks.iter().enumerate() {
|
||||
write!(list, "{}\n", disk_index).unwrap();
|
||||
|
||||
if disk.pt.is_none() {
|
||||
continue;
|
||||
}
|
||||
for part_index in 0..disk.pt.as_ref().unwrap().partitions.len() {
|
||||
write!(list, "{}p{}\n", disk_index, part_index).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
Handle::List(list.into_bytes())
|
||||
} else if let Some(p_pos) = path_str.chars().position(|c| c == 'p') {
|
||||
let disk_id_str = &path_str[..p_pos];
|
||||
if p_pos + 1 >= path_str.len() {
|
||||
return Err(Error::new(ENOENT));
|
||||
}
|
||||
let part_id_str = &path_str[p_pos + 1..];
|
||||
let i = disk_id_str.parse::<usize>().or(Err(Error::new(ENOENT)))?;
|
||||
let p = part_id_str.parse::<u32>().or(Err(Error::new(ENOENT)))?;
|
||||
|
||||
let disk = self.disks.get(i).ok_or(Error::new(ENOENT))?;
|
||||
if disk.pt.is_none()
|
||||
|| disk
|
||||
.pt
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.partitions
|
||||
.get(p as usize)
|
||||
.is_none()
|
||||
{
|
||||
return Err(Error::new(ENOENT));
|
||||
}
|
||||
|
||||
self.check_locks(i, Some(p))?;
|
||||
|
||||
Handle::Partition(i, p)
|
||||
} else {
|
||||
let i = path_str.parse::<usize>().or(Err(Error::new(ENOENT)))?;
|
||||
|
||||
if self.disks.get(i).is_none() {
|
||||
return Err(Error::new(ENOENT));
|
||||
}
|
||||
self.check_locks(i, None)?;
|
||||
|
||||
Handle::Disk(i)
|
||||
};
|
||||
let id = self.next_id;
|
||||
self.next_id += 1;
|
||||
self.handles.insert(id, handle);
|
||||
Ok(Some(OpenResult::ThisScheme {
|
||||
number: id,
|
||||
flags: NewFdFlags::POSITIONED,
|
||||
}))
|
||||
}
|
||||
|
||||
fn fstat(&mut self, id: usize, stat: &mut Stat) -> Result<Option<usize>> {
|
||||
match *self.handles.get(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::List(ref data) => {
|
||||
stat.st_mode = MODE_DIR;
|
||||
stat.st_size = data.len() as u64;
|
||||
Ok(Some(0))
|
||||
}
|
||||
Handle::Disk(number) => {
|
||||
let disk = self.disks.get_mut(number).ok_or(Error::new(EBADF))?;
|
||||
stat.st_mode = MODE_FILE;
|
||||
stat.st_size = disk.size();
|
||||
stat.st_blksize = disk.block_size();
|
||||
Ok(Some(0))
|
||||
}
|
||||
Handle::Partition(disk_id, part_num) => {
|
||||
let disk = self.disks.get_mut(disk_id).ok_or(Error::new(EBADF))?;
|
||||
let size = {
|
||||
let pt = disk.pt.as_ref().ok_or(Error::new(EBADF))?;
|
||||
let partition = pt
|
||||
.partitions
|
||||
.get(part_num as usize)
|
||||
.ok_or(Error::new(EBADF))?;
|
||||
partition.size
|
||||
};
|
||||
|
||||
stat.st_mode = MODE_FILE; // TODO: Block device?
|
||||
stat.st_size = size * u64::from(disk.block_size());
|
||||
stat.st_blksize = disk.block_size();
|
||||
stat.st_blocks = size;
|
||||
Ok(Some(0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result<Option<usize>> {
|
||||
let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
let mut i = 0;
|
||||
|
||||
let scheme_name = self.scheme_name.as_bytes();
|
||||
let mut j = 0;
|
||||
while i < buf.len() && j < scheme_name.len() {
|
||||
buf[i] = scheme_name[j];
|
||||
i += 1;
|
||||
j += 1;
|
||||
}
|
||||
|
||||
if i < buf.len() {
|
||||
buf[i] = b':';
|
||||
i += 1;
|
||||
}
|
||||
|
||||
match *handle {
|
||||
Handle::List(_) => (),
|
||||
Handle::Disk(number) => {
|
||||
let number_str = format!("{}", number);
|
||||
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;
|
||||
}
|
||||
}
|
||||
Handle::Partition(disk_num, part_num) => {
|
||||
let path = format!("{}p{}", disk_num, part_num);
|
||||
let path_bytes = path.as_bytes();
|
||||
j = 0;
|
||||
while i < buf.len() && j < path_bytes.len() {
|
||||
buf[i] = path_bytes[j];
|
||||
i += 1;
|
||||
j += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Some(i))
|
||||
}
|
||||
|
||||
fn read(
|
||||
&mut self,
|
||||
id: usize,
|
||||
buf: &mut [u8],
|
||||
offset: u64,
|
||||
_fcntl_flags: u32,
|
||||
) -> Result<Option<usize>> {
|
||||
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::List(ref handle) => {
|
||||
let src = usize::try_from(offset)
|
||||
.ok()
|
||||
.and_then(|o| handle.get(o..))
|
||||
.unwrap_or(&[]);
|
||||
|
||||
let byte_count = core::cmp::min(src.len(), buf.len());
|
||||
buf[..byte_count].copy_from_slice(&src[..byte_count]);
|
||||
Ok(Some(byte_count))
|
||||
}
|
||||
Handle::Disk(number) => {
|
||||
let disk = self.disks.get_mut(number).ok_or(Error::new(EBADF))?;
|
||||
let block = offset / u64::from(disk.block_size());
|
||||
disk.read(None, block, buf)
|
||||
}
|
||||
Handle::Partition(disk_num, part_num) => {
|
||||
let disk = self.disks.get_mut(disk_num).ok_or(Error::new(EBADF))?;
|
||||
let block = offset / u64::from(disk.block_size());
|
||||
disk.read(Some(part_num as usize), block, buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write(
|
||||
&mut self,
|
||||
id: usize,
|
||||
buf: &[u8],
|
||||
offset: u64,
|
||||
_fcntl_flags: u32,
|
||||
) -> Result<Option<usize>> {
|
||||
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::List(_) => Err(Error::new(EBADF)),
|
||||
Handle::Disk(number) => {
|
||||
let disk = self.disks.get_mut(number).ok_or(Error::new(EBADF))?;
|
||||
let block = offset / u64::from(disk.block_size());
|
||||
disk.write(None, block, buf)
|
||||
}
|
||||
Handle::Partition(disk_num, part_num) => {
|
||||
let disk = self.disks.get_mut(disk_num).ok_or(Error::new(EBADF))?;
|
||||
let block = offset / u64::from(disk.block_size());
|
||||
disk.write(Some(part_num as usize), block, buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fsize(&mut self, id: usize) -> Result<Option<u64>> {
|
||||
Ok(Some(
|
||||
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::List(ref mut handle) => handle.len() as u64,
|
||||
Handle::Disk(number) => {
|
||||
let disk = self.disks.get_mut(number).ok_or(Error::new(EBADF))?;
|
||||
disk.size()
|
||||
}
|
||||
Handle::Partition(disk_num, part_num) => {
|
||||
let disk = self.disks.get_mut(disk_num).ok_or(Error::new(EBADF))?;
|
||||
let block_count = disk
|
||||
.pt
|
||||
.as_ref()
|
||||
.ok_or(Error::new(EBADF))?
|
||||
.partitions
|
||||
.get(part_num as usize)
|
||||
.ok_or(Error::new(EBADF))?
|
||||
.size;
|
||||
u64::from(disk.block_size()) * block_count
|
||||
}
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
fn close(&mut self, id: usize) -> Result<Option<usize>> {
|
||||
self.handles
|
||||
.remove(&id)
|
||||
.ok_or(Error::new(EBADF))
|
||||
.and(Ok(Some(0)))
|
||||
}
|
||||
}
|
||||
@@ -13,5 +13,4 @@ driver-block = { path = "../driver-block" }
|
||||
redox-daemon = "0.1"
|
||||
libredox = "0.1.3"
|
||||
redox_syscall = { version = "0.5", features = ["std"] }
|
||||
redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git" }
|
||||
redox_event = "0.4"
|
||||
|
||||
@@ -1,27 +1,11 @@
|
||||
#![feature(let_chains)]
|
||||
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{Read, Write},
|
||||
process,
|
||||
};
|
||||
use std::process;
|
||||
|
||||
use driver_block::Disk;
|
||||
use driver_block::{Disk, DiskScheme};
|
||||
use event::{EventFlags, RawEventQueue};
|
||||
use fdt::{node::FdtNode, Fdt};
|
||||
use redox_scheme::{RequestKind, Response, SignalBehavior, Socket};
|
||||
use syscall::{
|
||||
data::{Event, Packet},
|
||||
error::{Error, ENODEV},
|
||||
flag::EVENT_READ,
|
||||
io::Io,
|
||||
scheme::SchemeBlockMut,
|
||||
EAGAIN, EINTR, EWOULDBLOCK,
|
||||
};
|
||||
use fdt::Fdt;
|
||||
|
||||
use crate::scheme::DiskScheme;
|
||||
|
||||
mod scheme;
|
||||
mod sd;
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
@@ -114,82 +98,32 @@ fn daemon(daemon: redox_daemon::Daemon) -> ! {
|
||||
*/
|
||||
}
|
||||
|
||||
let scheme_name = "disk.mmc";
|
||||
let socket_fd = Socket::create(&scheme_name).expect("mmcd: failed to create disk scheme");
|
||||
let mut disks = Vec::new();
|
||||
disks.push(Box::new(sdhci) as Box<dyn Disk>);
|
||||
let mut scheme = DiskScheme::new(
|
||||
"disk.mmc".to_string(),
|
||||
disks
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i, disk)| (i as u32, disk))
|
||||
.collect(),
|
||||
);
|
||||
|
||||
let mut event_queue = RawEventQueue::new().expect("mmcd: failed to open event file");
|
||||
let event_queue = RawEventQueue::new().expect("mmcd: failed to open event file");
|
||||
event_queue
|
||||
.subscribe(socket_fd.inner().raw(), 0, EventFlags::READ)
|
||||
.subscribe(scheme.event_handle().raw(), 0, EventFlags::READ)
|
||||
.expect("mmcd: failed to event disk scheme");
|
||||
|
||||
libredox::call::setrens(0, 0).expect("mmcd: failed to enter null namespace");
|
||||
daemon.ready().expect("mmcd: failed to notify parent");
|
||||
|
||||
let mut todo = Vec::new();
|
||||
let mut disks = Vec::new();
|
||||
|
||||
disks.push(Box::new(sdhci) as Box<dyn Disk>);
|
||||
let mut scheme = DiskScheme::new(scheme_name.to_string(), disks);
|
||||
|
||||
'outer: loop {
|
||||
let Some(event) = event_queue
|
||||
.next()
|
||||
.transpose()
|
||||
.expect("mmcd: failed to read event file")
|
||||
else {
|
||||
break;
|
||||
};
|
||||
if event.fd == socket_fd.inner().raw() {
|
||||
loop {
|
||||
let req = match socket_fd.next_request(SignalBehavior::Interrupt) {
|
||||
Ok(None) => break 'outer,
|
||||
Ok(Some(r)) => {
|
||||
if let RequestKind::Call(c) = r.kind() {
|
||||
c
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
if matches!(err.errno, EAGAIN | EWOULDBLOCK | EINTR) {
|
||||
break;
|
||||
} else {
|
||||
panic!("mmcd: failed to read disk scheme: {}", err);
|
||||
}
|
||||
}
|
||||
};
|
||||
if let Some(resp) = req.handle_scheme_block(&mut scheme) {
|
||||
socket_fd
|
||||
.write_response(resp, SignalBehavior::Restart)
|
||||
.expect("mmcd: failed to write disk scheme");
|
||||
} else {
|
||||
todo.push(req);
|
||||
}
|
||||
}
|
||||
for event in event_queue {
|
||||
let event = event.unwrap();
|
||||
if event.fd == scheme.event_handle().raw() {
|
||||
scheme.tick().unwrap();
|
||||
} else {
|
||||
println!("Unknown event {}", event.fd);
|
||||
}
|
||||
|
||||
// Handle todos to start new packets if possible
|
||||
let mut i = 0;
|
||||
while i < todo.len() {
|
||||
if let Some(resp) = todo[i].handle_scheme_block(&mut scheme) {
|
||||
socket_fd
|
||||
.write_response(resp, SignalBehavior::Restart)
|
||||
.expect("mmcd: failed to write disk scheme");
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
for req in todo.drain(..) {
|
||||
socket_fd
|
||||
.write_response(
|
||||
Response::new(&req, Err(Error::new(ENODEV))),
|
||||
SignalBehavior::Restart,
|
||||
)
|
||||
.expect("mmcd: failed to write disk scheme");
|
||||
}
|
||||
}
|
||||
process::exit(0);
|
||||
}
|
||||
|
||||
@@ -1,309 +0,0 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt::Write;
|
||||
use std::str;
|
||||
|
||||
use driver_block::{Disk, DiskWrapper};
|
||||
use syscall::schemev2::NewFdFlags;
|
||||
use syscall::{
|
||||
Error, Result, Stat, EACCES, EBADF, EISDIR, ENOENT, ENOLCK, MODE_DIR, MODE_FILE, O_DIRECTORY,
|
||||
O_STAT,
|
||||
};
|
||||
|
||||
use redox_scheme::{CallerCtx, OpenResult, SchemeBlock};
|
||||
|
||||
enum Handle {
|
||||
List(Vec<u8>), // Dir contents buffer
|
||||
Disk(usize), // Disk index
|
||||
Partition(usize, u32), // Disk index, partition index
|
||||
}
|
||||
|
||||
pub struct DiskScheme {
|
||||
scheme_name: String,
|
||||
disks: Box<[DiskWrapper<Box<dyn Disk>>]>,
|
||||
handles: BTreeMap<usize, Handle>,
|
||||
next_id: usize,
|
||||
}
|
||||
|
||||
impl DiskScheme {
|
||||
pub fn new(scheme_name: String, disks: Vec<Box<dyn Disk>>) -> DiskScheme {
|
||||
DiskScheme {
|
||||
scheme_name,
|
||||
disks: disks
|
||||
.into_iter()
|
||||
.map(DiskWrapper::new)
|
||||
.collect::<Vec<_>>()
|
||||
.into_boxed_slice(),
|
||||
handles: BTreeMap::new(),
|
||||
next_id: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// Checks if any conflicting handles already exist
|
||||
fn check_locks(&self, disk_i: usize, part_i_opt: Option<u32>) -> Result<()> {
|
||||
for (_, handle) in self.handles.iter() {
|
||||
match handle {
|
||||
Handle::Disk(i) => {
|
||||
if disk_i == *i {
|
||||
return Err(Error::new(ENOLCK));
|
||||
}
|
||||
}
|
||||
Handle::Partition(i, p) => {
|
||||
if disk_i == *i {
|
||||
match part_i_opt {
|
||||
Some(part_i) => {
|
||||
if part_i == *p {
|
||||
return Err(Error::new(ENOLCK));
|
||||
}
|
||||
}
|
||||
None => {
|
||||
return Err(Error::new(ENOLCK));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl SchemeBlock for DiskScheme {
|
||||
fn xopen(&mut self, path: &str, flags: usize, ctx: &CallerCtx) -> Result<Option<OpenResult>> {
|
||||
if ctx.uid == 0 {
|
||||
let path_str = path.trim_matches('/');
|
||||
if path_str.is_empty() {
|
||||
if flags & O_DIRECTORY == O_DIRECTORY || flags & O_STAT == O_STAT {
|
||||
let mut list = String::new();
|
||||
|
||||
for (disk_index, disk) in self.disks.iter().enumerate() {
|
||||
write!(list, "{}\n", disk_index).unwrap();
|
||||
|
||||
if disk.pt.is_none() {
|
||||
continue;
|
||||
}
|
||||
for part_index in 0..disk.pt.as_ref().unwrap().partitions.len() {
|
||||
write!(list, "{}p{}\n", disk_index, part_index).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
let id = self.next_id;
|
||||
self.next_id += 1;
|
||||
self.handles.insert(id, Handle::List(list.into_bytes()));
|
||||
Ok(Some(OpenResult::ThisScheme {
|
||||
number: id,
|
||||
flags: NewFdFlags::POSITIONED,
|
||||
}))
|
||||
} else {
|
||||
Err(Error::new(EISDIR))
|
||||
}
|
||||
} else if let Some(p_pos) = path_str.chars().position(|c| c == 'p') {
|
||||
let disk_id_str = &path_str[..p_pos];
|
||||
if p_pos + 1 >= path_str.len() {
|
||||
return Err(Error::new(ENOENT));
|
||||
}
|
||||
let part_id_str = &path_str[p_pos + 1..];
|
||||
let i = disk_id_str.parse::<usize>().or(Err(Error::new(ENOENT)))?;
|
||||
let p = part_id_str.parse::<u32>().or(Err(Error::new(ENOENT)))?;
|
||||
|
||||
if let Some(disk) = self.disks.get(i) {
|
||||
if disk.pt.is_none()
|
||||
|| disk
|
||||
.pt
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.partitions
|
||||
.get(p as usize)
|
||||
.is_none()
|
||||
{
|
||||
return Err(Error::new(ENOENT));
|
||||
}
|
||||
|
||||
self.check_locks(i, Some(p))?;
|
||||
|
||||
let id = self.next_id;
|
||||
self.next_id += 1;
|
||||
self.handles.insert(id, Handle::Partition(i, p));
|
||||
Ok(Some(OpenResult::ThisScheme {
|
||||
number: id,
|
||||
flags: NewFdFlags::POSITIONED,
|
||||
}))
|
||||
} else {
|
||||
Err(Error::new(ENOENT))
|
||||
}
|
||||
} else {
|
||||
let i = path_str.parse::<usize>().or(Err(Error::new(ENOENT)))?;
|
||||
|
||||
if self.disks.get(i).is_some() {
|
||||
self.check_locks(i, None)?;
|
||||
|
||||
let id = self.next_id;
|
||||
self.next_id += 1;
|
||||
self.handles.insert(id, Handle::Disk(i));
|
||||
Ok(Some(OpenResult::ThisScheme {
|
||||
number: id,
|
||||
flags: NewFdFlags::POSITIONED,
|
||||
}))
|
||||
} else {
|
||||
Err(Error::new(ENOENT))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Err(Error::new(EACCES))
|
||||
}
|
||||
}
|
||||
|
||||
fn fstat(&mut self, id: usize, stat: &mut Stat) -> Result<Option<usize>> {
|
||||
match *self.handles.get(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::List(ref data) => {
|
||||
stat.st_mode = MODE_DIR;
|
||||
stat.st_size = data.len() as u64;
|
||||
Ok(Some(0))
|
||||
}
|
||||
Handle::Disk(number) => {
|
||||
let disk = self.disks.get_mut(number).ok_or(Error::new(EBADF))?;
|
||||
stat.st_mode = MODE_FILE;
|
||||
stat.st_size = disk.size();
|
||||
stat.st_blksize = disk.block_size();
|
||||
Ok(Some(0))
|
||||
}
|
||||
Handle::Partition(disk_id, part_num) => {
|
||||
let disk = self.disks.get_mut(disk_id).ok_or(Error::new(EBADF))?;
|
||||
let size = {
|
||||
let pt = disk.pt.as_ref().ok_or(Error::new(EBADF))?;
|
||||
let partition = pt
|
||||
.partitions
|
||||
.get(part_num as usize)
|
||||
.ok_or(Error::new(EBADF))?;
|
||||
partition.size
|
||||
};
|
||||
|
||||
stat.st_mode = MODE_FILE; // TODO: Block device?
|
||||
stat.st_size = size * u64::from(disk.block_size());
|
||||
stat.st_blksize = disk.block_size();
|
||||
stat.st_blocks = size;
|
||||
Ok(Some(0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result<Option<usize>> {
|
||||
let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
let mut i = 0;
|
||||
|
||||
let scheme_name = self.scheme_name.as_bytes();
|
||||
let mut j = 0;
|
||||
while i < buf.len() && j < scheme_name.len() {
|
||||
buf[i] = scheme_name[j];
|
||||
i += 1;
|
||||
j += 1;
|
||||
}
|
||||
|
||||
if i < buf.len() {
|
||||
buf[i] = b':';
|
||||
i += 1;
|
||||
}
|
||||
|
||||
match *handle {
|
||||
Handle::List(_) => (),
|
||||
Handle::Disk(number) => {
|
||||
let number_str = format!("{}", number);
|
||||
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;
|
||||
}
|
||||
}
|
||||
Handle::Partition(disk_num, part_num) => {
|
||||
let path = format!("{}p{}", disk_num, part_num);
|
||||
let path_bytes = path.as_bytes();
|
||||
j = 0;
|
||||
while i < buf.len() && j < path_bytes.len() {
|
||||
buf[i] = path_bytes[j];
|
||||
i += 1;
|
||||
j += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Some(i))
|
||||
}
|
||||
|
||||
fn read(
|
||||
&mut self,
|
||||
id: usize,
|
||||
buf: &mut [u8],
|
||||
offset: u64,
|
||||
_flags: u32,
|
||||
) -> Result<Option<usize>> {
|
||||
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::List(ref handle) => {
|
||||
let src = usize::try_from(offset)
|
||||
.ok()
|
||||
.and_then(|o| handle.get(o..))
|
||||
.unwrap_or(&[]);
|
||||
let bytes = src.len().min(buf.len());
|
||||
buf[..bytes].copy_from_slice(&src[..bytes]);
|
||||
Ok(Some(bytes))
|
||||
}
|
||||
Handle::Disk(number) => {
|
||||
let disk = self.disks.get_mut(number).ok_or(Error::new(EBADF))?;
|
||||
let block = offset / u64::from(disk.block_size());
|
||||
disk.read(None, block, buf)
|
||||
}
|
||||
Handle::Partition(disk_num, part_num) => {
|
||||
let disk = self.disks.get_mut(disk_num).ok_or(Error::new(EBADF))?;
|
||||
let block = offset / u64::from(disk.block_size());
|
||||
disk.read(Some(part_num as usize), block, buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, id: usize, buf: &[u8], offset: u64, _flags: u32) -> Result<Option<usize>> {
|
||||
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::List(_) => Err(Error::new(EBADF)),
|
||||
Handle::Disk(number) => {
|
||||
let disk = self.disks.get_mut(number).ok_or(Error::new(EBADF))?;
|
||||
let block = offset / u64::from(disk.block_size());
|
||||
disk.write(None, block, buf)
|
||||
}
|
||||
Handle::Partition(disk_num, part_num) => {
|
||||
let disk = self.disks.get_mut(disk_num).ok_or(Error::new(EBADF))?;
|
||||
let block = offset / u64::from(disk.block_size());
|
||||
disk.write(Some(part_num as usize), block, buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fsize(&mut self, id: usize) -> Result<Option<u64>> {
|
||||
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::List(ref mut handle) => Ok(Some(handle.len() as u64)),
|
||||
Handle::Disk(number) => {
|
||||
let disk = self.disks.get_mut(number).ok_or(Error::new(EBADF))?;
|
||||
Ok(Some(disk.size()))
|
||||
}
|
||||
Handle::Partition(disk_num, part_num) => {
|
||||
let disk = self.disks.get_mut(disk_num).ok_or(Error::new(EBADF))?;
|
||||
let block_count = disk
|
||||
.pt
|
||||
.as_ref()
|
||||
.ok_or(Error::new(EBADF))?
|
||||
.partitions
|
||||
.get(part_num as usize)
|
||||
.ok_or(Error::new(EBADF))?
|
||||
.size;
|
||||
Ok(Some(u64::from(disk.block_size()) * block_count))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn close(&mut self, id: usize) -> Result<Option<usize>> {
|
||||
self.handles
|
||||
.remove(&id)
|
||||
.ok_or(Error::new(EBADF))
|
||||
.and(Ok(Some(0)))
|
||||
}
|
||||
}
|
||||
@@ -6,4 +6,6 @@ edition = "2021"
|
||||
[dependencies]
|
||||
partitionlib = { path = "../partitionlib" }
|
||||
|
||||
libredox = "0.1.3"
|
||||
redox_syscall = "0.5"
|
||||
redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git" }
|
||||
|
||||
@@ -1,9 +1,21 @@
|
||||
use std::cmp;
|
||||
use std::io::Error;
|
||||
use std::io::{self, Read, Seek, SeekFrom};
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt::Write;
|
||||
use std::str;
|
||||
|
||||
use libredox::Fd;
|
||||
use partitionlib::{LogicalBlockSize, PartitionTable};
|
||||
use syscall::{EBADF, EOVERFLOW};
|
||||
use redox_scheme::{
|
||||
CallRequest, CallerCtx, OpenResult, RequestKind, Response, SchemeBlock, SignalBehavior, Socket,
|
||||
};
|
||||
use syscall::schemev2::NewFdFlags;
|
||||
use syscall::{
|
||||
Error, Result, Stat, EACCES, EAGAIN, EBADF, EISDIR, ENOENT, ENOLCK, EOPNOTSUPP, EOVERFLOW,
|
||||
MODE_DIR, MODE_FILE, O_DIRECTORY, O_STAT,
|
||||
};
|
||||
|
||||
/// Split the read operation into a series of block reads.
|
||||
/// `read_fn` will be called with a block number to be read, and a buffer to be filled.
|
||||
@@ -13,8 +25,8 @@ fn block_read(
|
||||
offset: u64,
|
||||
blksize: u32,
|
||||
buf: &mut [u8],
|
||||
mut read_fn: impl FnMut(u64, &mut [u8]) -> Result<(), Error>,
|
||||
) -> Result<usize, Error> {
|
||||
mut read_fn: impl FnMut(u64, &mut [u8]) -> io::Result<()>,
|
||||
) -> io::Result<usize> {
|
||||
// TODO: Yield sometimes, perhaps after a few blocks or something.
|
||||
|
||||
if buf.len() == 0 {
|
||||
@@ -57,6 +69,8 @@ pub trait Disk {
|
||||
fn block_size(&self) -> u32;
|
||||
fn size(&self) -> u64;
|
||||
|
||||
// These operate on a whole multiple of the block size
|
||||
// FIXME maybe only operate on a single block worth of data?
|
||||
fn read(&mut self, block: u64, buffer: &mut [u8]) -> syscall::Result<Option<usize>>;
|
||||
fn write(&mut self, block: u64, buffer: &[u8]) -> syscall::Result<Option<usize>>;
|
||||
}
|
||||
@@ -231,3 +245,370 @@ impl<T: Disk> DiskWrapper<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum Handle {
|
||||
List(Vec<u8>), // entries
|
||||
Disk(u32), // disk num
|
||||
Partition(u32, u32), // disk num, part num
|
||||
}
|
||||
|
||||
pub struct DiskScheme<T> {
|
||||
scheme_name: String,
|
||||
socket: Socket,
|
||||
disks: BTreeMap<u32, DiskWrapper<T>>,
|
||||
handles: BTreeMap<usize, Handle>,
|
||||
next_id: usize,
|
||||
blocked: Vec<CallRequest>,
|
||||
}
|
||||
|
||||
impl<T: Disk> DiskScheme<T> {
|
||||
pub fn new(scheme_name: String, disks: BTreeMap<u32, T>) -> Self {
|
||||
assert!(scheme_name.starts_with("disk"));
|
||||
let socket = Socket::nonblock(&scheme_name).expect("failed to create disk scheme");
|
||||
|
||||
Self {
|
||||
scheme_name,
|
||||
socket,
|
||||
disks: disks
|
||||
.into_iter()
|
||||
.map(|(k, disk)| (k, DiskWrapper::new(disk)))
|
||||
.collect(),
|
||||
next_id: 0,
|
||||
handles: BTreeMap::new(),
|
||||
blocked: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn event_handle(&self) -> &Fd {
|
||||
self.socket.inner()
|
||||
}
|
||||
|
||||
/// Process pending and new requests.
|
||||
///
|
||||
/// This needs to be called each time there is a new event on the scheme
|
||||
/// file and each time a read or write operation has completed.
|
||||
// FIXME maybe split into one method for events on the scheme fd and one
|
||||
// to call when an irq is received to indicate that blocked packets can
|
||||
// be processed.
|
||||
pub fn tick(&mut self) -> io::Result<()> {
|
||||
// Handle any blocked requests
|
||||
let mut i = 0;
|
||||
while i < self.blocked.len() {
|
||||
if let Some(resp) = self.blocked[i].handle_scheme_block(self) {
|
||||
self.socket
|
||||
.write_response(resp, SignalBehavior::Restart)
|
||||
.expect("driver-block: failed to write scheme");
|
||||
self.blocked.remove(i);
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle new scheme requests
|
||||
loop {
|
||||
let request = match self.socket.next_request(SignalBehavior::Restart) {
|
||||
Ok(Some(request)) => request,
|
||||
Ok(None) => {
|
||||
// Scheme likely got unmounted
|
||||
std::process::exit(0);
|
||||
}
|
||||
Err(err) if err.errno == EAGAIN => break,
|
||||
Err(err) => return Err(err.into()),
|
||||
};
|
||||
|
||||
match request.kind() {
|
||||
RequestKind::Call(call_request) => {
|
||||
if let Some(resp) = call_request.handle_scheme_block(self) {
|
||||
self.socket.write_response(resp, SignalBehavior::Restart)?;
|
||||
} else {
|
||||
self.blocked.push(call_request);
|
||||
}
|
||||
}
|
||||
RequestKind::SendFd(sendfd_request) => {
|
||||
self.socket.write_response(
|
||||
Response::for_sendfd(&sendfd_request, Err(syscall::Error::new(EOPNOTSUPP))),
|
||||
SignalBehavior::Restart,
|
||||
)?;
|
||||
}
|
||||
RequestKind::Cancellation(_cancellation_request) => {
|
||||
// FIXME implement cancellation
|
||||
}
|
||||
RequestKind::MsyncMsg | RequestKind::MunmapMsg | RequestKind::MmapMsg => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Checks if any conflicting handles already exist
|
||||
fn check_locks(&self, disk_i: u32, part_i_opt: Option<u32>) -> Result<()> {
|
||||
for (_, handle) in self.handles.iter() {
|
||||
match handle {
|
||||
Handle::Disk(i) => {
|
||||
if disk_i == *i {
|
||||
return Err(Error::new(ENOLCK));
|
||||
}
|
||||
}
|
||||
Handle::Partition(i, p) => {
|
||||
if disk_i == *i {
|
||||
match part_i_opt {
|
||||
Some(part_i) => {
|
||||
if part_i == *p {
|
||||
return Err(Error::new(ENOLCK));
|
||||
}
|
||||
}
|
||||
None => {
|
||||
return Err(Error::new(ENOLCK));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Disk> SchemeBlock for DiskScheme<T> {
|
||||
fn xopen(
|
||||
&mut self,
|
||||
path_str: &str,
|
||||
flags: usize,
|
||||
ctx: &CallerCtx,
|
||||
) -> Result<Option<OpenResult>> {
|
||||
if ctx.uid != 0 {
|
||||
return Err(Error::new(EACCES));
|
||||
}
|
||||
let path_str = path_str.trim_matches('/');
|
||||
|
||||
let handle = if path_str.is_empty() {
|
||||
if flags & O_DIRECTORY == O_DIRECTORY || flags & O_STAT == O_STAT {
|
||||
let mut list = String::new();
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
Handle::List(list.into_bytes())
|
||||
} else {
|
||||
return 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::<u32>().or(Err(Error::new(ENOENT)))?;
|
||||
let part_num = part_num_str.parse::<u32>().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()
|
||||
{
|
||||
self.check_locks(nsid, Some(part_num))?;
|
||||
|
||||
Handle::Partition(nsid, part_num)
|
||||
} else {
|
||||
return Err(Error::new(ENOENT));
|
||||
}
|
||||
} else {
|
||||
return Err(Error::new(ENOENT));
|
||||
}
|
||||
} else {
|
||||
let nsid = path_str.parse::<u32>().or(Err(Error::new(ENOENT)))?;
|
||||
|
||||
if self.disks.contains_key(&nsid) {
|
||||
self.check_locks(nsid, None)?;
|
||||
Handle::Disk(nsid)
|
||||
} else {
|
||||
return Err(Error::new(ENOENT));
|
||||
}
|
||||
};
|
||||
let id = self.next_id;
|
||||
self.next_id += 1;
|
||||
self.handles.insert(id, handle);
|
||||
Ok(Some(OpenResult::ThisScheme {
|
||||
number: id,
|
||||
flags: NewFdFlags::POSITIONED,
|
||||
}))
|
||||
}
|
||||
|
||||
fn fstat(&mut self, id: usize, stat: &mut Stat) -> Result<Option<usize>> {
|
||||
match *self.handles.get(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::List(ref data) => {
|
||||
stat.st_mode = MODE_DIR;
|
||||
stat.st_size = data.len() as u64;
|
||||
Ok(Some(0))
|
||||
}
|
||||
Handle::Disk(number) => {
|
||||
let disk = self.disks.get_mut(&number).ok_or(Error::new(EBADF))?;
|
||||
stat.st_mode = MODE_FILE;
|
||||
stat.st_blocks = disk.disk().size() / u64::from(disk.block_size());
|
||||
stat.st_blksize = disk.block_size();
|
||||
stat.st_size = disk.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 * u64::from(disk.block_size());
|
||||
stat.st_blocks = part.size;
|
||||
stat.st_blksize = disk.block_size();
|
||||
Ok(Some(0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result<Option<usize>> {
|
||||
let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
let mut i = 0;
|
||||
|
||||
let scheme_name = self.scheme_name.as_bytes();
|
||||
let mut j = 0;
|
||||
while i < buf.len() && j < scheme_name.len() {
|
||||
buf[i] = scheme_name[j];
|
||||
i += 1;
|
||||
j += 1;
|
||||
}
|
||||
|
||||
if i < buf.len() {
|
||||
buf[i] = b':';
|
||||
i += 1;
|
||||
}
|
||||
|
||||
match *handle {
|
||||
Handle::List(_) => (),
|
||||
Handle::Disk(number) => {
|
||||
let number_str = format!("{}", number);
|
||||
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;
|
||||
}
|
||||
}
|
||||
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))
|
||||
}
|
||||
|
||||
fn read(
|
||||
&mut self,
|
||||
id: usize,
|
||||
buf: &mut [u8],
|
||||
offset: u64,
|
||||
_fcntl_flags: u32,
|
||||
) -> Result<Option<usize>> {
|
||||
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::List(ref handle) => {
|
||||
let src = usize::try_from(offset)
|
||||
.ok()
|
||||
.and_then(|o| handle.get(o..))
|
||||
.unwrap_or(&[]);
|
||||
let count = core::cmp::min(src.len(), buf.len());
|
||||
buf[..count].copy_from_slice(&src[..count]);
|
||||
Ok(Some(count))
|
||||
}
|
||||
Handle::Disk(number) => {
|
||||
let disk = self.disks.get_mut(&number).ok_or(Error::new(EBADF))?;
|
||||
let block = offset / u64::from(disk.block_size());
|
||||
disk.read(None, block, buf)
|
||||
}
|
||||
Handle::Partition(disk_num, part_num) => {
|
||||
let disk = self.disks.get_mut(&disk_num).ok_or(Error::new(EBADF))?;
|
||||
let block = offset / u64::from(disk.block_size());
|
||||
disk.read(Some(part_num as usize), block, buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write(
|
||||
&mut self,
|
||||
id: usize,
|
||||
buf: &[u8],
|
||||
offset: u64,
|
||||
_fcntl_flags: u32,
|
||||
) -> Result<Option<usize>> {
|
||||
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::List(_) => Err(Error::new(EBADF)),
|
||||
Handle::Disk(number) => {
|
||||
let disk = self.disks.get_mut(&number).ok_or(Error::new(EBADF))?;
|
||||
let block = offset / u64::from(disk.block_size());
|
||||
disk.write(None, block, buf)
|
||||
}
|
||||
Handle::Partition(disk_num, part_num) => {
|
||||
let disk = self.disks.get_mut(&disk_num).ok_or(Error::new(EBADF))?;
|
||||
let block = offset / u64::from(disk.block_size());
|
||||
disk.write(Some(part_num as usize), block, buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fsize(&mut self, id: usize) -> Result<Option<u64>> {
|
||||
Ok(Some(
|
||||
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::List(ref handle) => handle.len() as u64,
|
||||
Handle::Disk(number) => {
|
||||
let disk = self.disks.get_mut(&number).ok_or(Error::new(EBADF))?;
|
||||
disk.size()
|
||||
}
|
||||
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))?;
|
||||
|
||||
part.size * u64::from(disk.block_size())
|
||||
}
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
fn close(&mut self, id: usize) -> Result<Option<usize>> {
|
||||
self.handles
|
||||
.remove(&id)
|
||||
.ok_or(Error::new(EBADF))
|
||||
.and(Ok(Some(0)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,5 +11,4 @@ log = "0.4"
|
||||
pcid = { path = "../../pcid" }
|
||||
redox-daemon = "0.1"
|
||||
redox_syscall = { version = "0.5", features = ["std"] }
|
||||
redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git" }
|
||||
redox_event = "0.4"
|
||||
|
||||
+30
-102
@@ -1,10 +1,9 @@
|
||||
use common::io::Io as _;
|
||||
use driver_block::Disk;
|
||||
use driver_block::{Disk, DiskScheme};
|
||||
use event::{EventFlags, RawEventQueue};
|
||||
use libredox::flag;
|
||||
use log::{error, info};
|
||||
use pcid_interface::PciFunctionHandle;
|
||||
use redox_scheme::{RequestKind, Response, SignalBehavior, Socket};
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{Read, Write},
|
||||
@@ -13,15 +12,10 @@ use std::{
|
||||
thread::{self, sleep},
|
||||
time::Duration,
|
||||
};
|
||||
use syscall::error::{Error, EAGAIN, EINTR, ENODEV, EWOULDBLOCK};
|
||||
|
||||
use crate::{
|
||||
ide::{AtaCommand, AtaDisk, Channel},
|
||||
scheme::DiskScheme,
|
||||
};
|
||||
use crate::ide::{AtaCommand, AtaDisk, Channel};
|
||||
|
||||
pub mod ide;
|
||||
pub mod scheme;
|
||||
|
||||
fn main() {
|
||||
redox_daemon::Daemon::new(daemon).expect("ided: failed to daemonize");
|
||||
@@ -193,7 +187,14 @@ fn daemon(daemon: redox_daemon::Daemon) -> ! {
|
||||
}
|
||||
|
||||
let scheme_name = format!("disk.{}", name);
|
||||
let socket_fd = Socket::nonblock(&scheme_name).expect("ided: failed to create disk scheme");
|
||||
let mut scheme = DiskScheme::new(
|
||||
scheme_name,
|
||||
disks
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i, disk)| (i as u32, disk))
|
||||
.collect(),
|
||||
);
|
||||
|
||||
let primary_irq_fd = libredox::call::open(
|
||||
&format!("/scheme/irq/{}", primary_irq),
|
||||
@@ -211,14 +212,14 @@ fn daemon(daemon: redox_daemon::Daemon) -> ! {
|
||||
.expect("ided: failed to open irq file");
|
||||
let mut secondary_irq_file = unsafe { File::from_raw_fd(secondary_irq_fd as RawFd) };
|
||||
|
||||
let mut event_queue = RawEventQueue::new().expect("ided: failed to open event file");
|
||||
let event_queue = RawEventQueue::new().expect("ided: failed to open event file");
|
||||
|
||||
libredox::call::setrens(0, 0).expect("ided: failed to enter null namespace");
|
||||
|
||||
daemon.ready().expect("ided: failed to notify parent");
|
||||
|
||||
event_queue
|
||||
.subscribe(socket_fd.inner().raw(), 0, EventFlags::READ)
|
||||
.subscribe(scheme.event_handle().raw(), 0, EventFlags::READ)
|
||||
.expect("ided: failed to event disk scheme");
|
||||
|
||||
event_queue
|
||||
@@ -229,44 +230,10 @@ fn daemon(daemon: redox_daemon::Daemon) -> ! {
|
||||
.subscribe(secondary_irq_fd, 0, EventFlags::READ)
|
||||
.expect("ided: failed to event irq scheme");
|
||||
|
||||
let mut scheme = DiskScheme::new(scheme_name, chans, disks);
|
||||
|
||||
let mut todo = Vec::new();
|
||||
'outer: loop {
|
||||
let Some(event) = event_queue
|
||||
.next()
|
||||
.transpose()
|
||||
.expect("ided: failed to read event file")
|
||||
else {
|
||||
break;
|
||||
};
|
||||
if event.fd == socket_fd.inner().raw() {
|
||||
loop {
|
||||
let req = match socket_fd.next_request(SignalBehavior::Interrupt) {
|
||||
Ok(None) => break 'outer,
|
||||
Ok(Some(r)) => {
|
||||
if let RequestKind::Call(c) = r.kind() {
|
||||
c
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
if matches!(err.errno, EAGAIN | EWOULDBLOCK | EINTR) {
|
||||
break;
|
||||
} else {
|
||||
panic!("ided: failed to read disk scheme: {}", err);
|
||||
}
|
||||
}
|
||||
};
|
||||
if let Some(resp) = req.handle_scheme_block(&mut scheme) {
|
||||
socket_fd
|
||||
.write_response(resp, SignalBehavior::Restart)
|
||||
.expect("ided: failed to write disk scheme");
|
||||
} else {
|
||||
todo.push(req);
|
||||
}
|
||||
}
|
||||
for event in event_queue {
|
||||
let event = event.unwrap();
|
||||
if event.fd == scheme.event_handle().raw() {
|
||||
scheme.tick().unwrap();
|
||||
} else if event.fd == primary_irq_fd {
|
||||
let mut irq = [0; 8];
|
||||
if primary_irq_file
|
||||
@@ -274,23 +241,14 @@ fn daemon(daemon: redox_daemon::Daemon) -> ! {
|
||||
.expect("ided: failed to read irq file")
|
||||
>= irq.len()
|
||||
{
|
||||
if scheme.irq(0) {
|
||||
primary_irq_file
|
||||
.write(&irq)
|
||||
.expect("ided: failed to write irq file");
|
||||
let _chan = chans[0].lock().unwrap();
|
||||
//TODO: check chan for irq
|
||||
|
||||
// Handle todos in order to finish previous packets if possible
|
||||
let mut i = 0;
|
||||
while i < todo.len() {
|
||||
if let Some(resp) = todo[i].handle_scheme_block(&mut scheme) {
|
||||
socket_fd
|
||||
.write_response(resp, SignalBehavior::Restart)
|
||||
.expect("ided: failed to write disk scheme");
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
primary_irq_file
|
||||
.write(&irq)
|
||||
.expect("ided: failed to write irq file");
|
||||
|
||||
scheme.tick().unwrap();
|
||||
}
|
||||
} else if event.fd == secondary_irq_fd {
|
||||
let mut irq = [0; 8];
|
||||
@@ -299,48 +257,18 @@ fn daemon(daemon: redox_daemon::Daemon) -> ! {
|
||||
.expect("ided: failed to read irq file")
|
||||
>= irq.len()
|
||||
{
|
||||
if scheme.irq(1) {
|
||||
secondary_irq_file
|
||||
.write(&irq)
|
||||
.expect("ided: failed to write irq file");
|
||||
let _chan = chans[1].lock().unwrap();
|
||||
//TODO: check chan for irq
|
||||
|
||||
// Handle todos in order to finish previous packets if possible
|
||||
let mut i = 0;
|
||||
while i < todo.len() {
|
||||
if let Some(resp) = todo[i].handle_scheme_block(&mut scheme) {
|
||||
socket_fd
|
||||
.write_response(resp, SignalBehavior::Restart)
|
||||
.expect("ided: failed to write disk scheme");
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
secondary_irq_file
|
||||
.write(&irq)
|
||||
.expect("ided: failed to write irq file");
|
||||
|
||||
scheme.tick().unwrap();
|
||||
}
|
||||
} else {
|
||||
error!("Unknown event {}", event.fd);
|
||||
}
|
||||
|
||||
// Handle todos to start new packets if possible
|
||||
let mut i = 0;
|
||||
while i < todo.len() {
|
||||
if let Some(resp) = todo[i].handle_scheme_block(&mut scheme) {
|
||||
socket_fd
|
||||
.write_response(resp, SignalBehavior::Restart)
|
||||
.expect("ided: failed to write disk scheme");
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
for req in todo.drain(..) {
|
||||
socket_fd
|
||||
.write_response(
|
||||
Response::new(&req, Err(Error::new(ENODEV))),
|
||||
SignalBehavior::Restart,
|
||||
)
|
||||
.expect("ided: failed to write disk scheme");
|
||||
}
|
||||
}
|
||||
|
||||
std::process::exit(0);
|
||||
|
||||
@@ -1,323 +0,0 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt::Write;
|
||||
use std::str;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use driver_block::{Disk, DiskWrapper};
|
||||
use syscall::schemev2::NewFdFlags;
|
||||
use syscall::{
|
||||
Error, Result, Stat, EACCES, EBADF, EISDIR, ENOENT, ENOLCK, MODE_DIR, MODE_FILE, O_DIRECTORY,
|
||||
O_STAT,
|
||||
};
|
||||
|
||||
use crate::ide::Channel;
|
||||
use redox_scheme::{CallerCtx, OpenResult, SchemeBlock};
|
||||
|
||||
enum Handle {
|
||||
List(Vec<u8>), // Dir contents buffer
|
||||
Disk(usize), // Disk index
|
||||
Partition(usize, u32), // Disk index, partition index
|
||||
}
|
||||
|
||||
pub struct DiskScheme {
|
||||
scheme_name: String,
|
||||
chans: Box<[Arc<Mutex<Channel>>]>,
|
||||
disks: Box<[DiskWrapper<Box<dyn Disk>>]>,
|
||||
handles: BTreeMap<usize, Handle>,
|
||||
next_id: usize,
|
||||
}
|
||||
|
||||
impl DiskScheme {
|
||||
pub fn new(
|
||||
scheme_name: String,
|
||||
chans: Vec<Arc<Mutex<Channel>>>,
|
||||
disks: Vec<Box<dyn Disk>>,
|
||||
) -> DiskScheme {
|
||||
DiskScheme {
|
||||
scheme_name,
|
||||
chans: chans.into_boxed_slice(),
|
||||
disks: disks
|
||||
.into_iter()
|
||||
.map(DiskWrapper::new)
|
||||
.collect::<Vec<_>>()
|
||||
.into_boxed_slice(),
|
||||
handles: BTreeMap::new(),
|
||||
next_id: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn irq(&mut self, chan_i: usize) -> bool {
|
||||
let _chan = self.chans[chan_i].lock().unwrap();
|
||||
//TODO: check chan for irq
|
||||
true
|
||||
}
|
||||
|
||||
// Checks if any conflicting handles already exist
|
||||
fn check_locks(&self, disk_i: usize, part_i_opt: Option<u32>) -> Result<()> {
|
||||
for (_, handle) in self.handles.iter() {
|
||||
match handle {
|
||||
Handle::Disk(i) => {
|
||||
if disk_i == *i {
|
||||
return Err(Error::new(ENOLCK));
|
||||
}
|
||||
}
|
||||
Handle::Partition(i, p) => {
|
||||
if disk_i == *i {
|
||||
match part_i_opt {
|
||||
Some(part_i) => {
|
||||
if part_i == *p {
|
||||
return Err(Error::new(ENOLCK));
|
||||
}
|
||||
}
|
||||
None => {
|
||||
return Err(Error::new(ENOLCK));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl SchemeBlock for DiskScheme {
|
||||
fn xopen(&mut self, path: &str, flags: usize, ctx: &CallerCtx) -> Result<Option<OpenResult>> {
|
||||
if ctx.uid == 0 {
|
||||
let path_str = path.trim_matches('/');
|
||||
if path_str.is_empty() {
|
||||
if flags & O_DIRECTORY == O_DIRECTORY || flags & O_STAT == O_STAT {
|
||||
let mut list = String::new();
|
||||
|
||||
for (disk_index, disk) in self.disks.iter().enumerate() {
|
||||
write!(list, "{}\n", disk_index).unwrap();
|
||||
|
||||
if disk.pt.is_none() {
|
||||
continue;
|
||||
}
|
||||
for part_index in 0..disk.pt.as_ref().unwrap().partitions.len() {
|
||||
write!(list, "{}p{}\n", disk_index, part_index).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
let id = self.next_id;
|
||||
self.next_id += 1;
|
||||
self.handles.insert(id, Handle::List(list.into_bytes()));
|
||||
Ok(Some(OpenResult::ThisScheme {
|
||||
number: id,
|
||||
flags: NewFdFlags::POSITIONED,
|
||||
}))
|
||||
} else {
|
||||
Err(Error::new(EISDIR))
|
||||
}
|
||||
} else if let Some(p_pos) = path_str.chars().position(|c| c == 'p') {
|
||||
let disk_id_str = &path_str[..p_pos];
|
||||
if p_pos + 1 >= path_str.len() {
|
||||
return Err(Error::new(ENOENT));
|
||||
}
|
||||
let part_id_str = &path_str[p_pos + 1..];
|
||||
let i = disk_id_str.parse::<usize>().or(Err(Error::new(ENOENT)))?;
|
||||
let p = part_id_str.parse::<u32>().or(Err(Error::new(ENOENT)))?;
|
||||
|
||||
if let Some(disk) = self.disks.get(i) {
|
||||
if disk.pt.is_none()
|
||||
|| disk
|
||||
.pt
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.partitions
|
||||
.get(p as usize)
|
||||
.is_none()
|
||||
{
|
||||
return Err(Error::new(ENOENT));
|
||||
}
|
||||
|
||||
self.check_locks(i, Some(p))?;
|
||||
|
||||
let id = self.next_id;
|
||||
self.next_id += 1;
|
||||
self.handles.insert(id, Handle::Partition(i, p));
|
||||
Ok(Some(OpenResult::ThisScheme {
|
||||
number: id,
|
||||
flags: NewFdFlags::POSITIONED,
|
||||
}))
|
||||
} else {
|
||||
Err(Error::new(ENOENT))
|
||||
}
|
||||
} else {
|
||||
let i = path_str.parse::<usize>().or(Err(Error::new(ENOENT)))?;
|
||||
|
||||
if self.disks.get(i).is_some() {
|
||||
self.check_locks(i, None)?;
|
||||
|
||||
let id = self.next_id;
|
||||
self.next_id += 1;
|
||||
self.handles.insert(id, Handle::Disk(i));
|
||||
Ok(Some(OpenResult::ThisScheme {
|
||||
number: id,
|
||||
flags: NewFdFlags::POSITIONED,
|
||||
}))
|
||||
} else {
|
||||
Err(Error::new(ENOENT))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Err(Error::new(EACCES))
|
||||
}
|
||||
}
|
||||
|
||||
fn fstat(&mut self, id: usize, stat: &mut Stat) -> Result<Option<usize>> {
|
||||
match *self.handles.get(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::List(ref data) => {
|
||||
stat.st_mode = MODE_DIR;
|
||||
stat.st_size = data.len() as u64;
|
||||
Ok(Some(0))
|
||||
}
|
||||
Handle::Disk(number) => {
|
||||
let disk = self.disks.get_mut(number).ok_or(Error::new(EBADF))?;
|
||||
stat.st_mode = MODE_FILE;
|
||||
stat.st_size = disk.size();
|
||||
stat.st_blksize = disk.block_size();
|
||||
Ok(Some(0))
|
||||
}
|
||||
Handle::Partition(disk_id, part_num) => {
|
||||
let disk = self.disks.get_mut(disk_id).ok_or(Error::new(EBADF))?;
|
||||
let size = {
|
||||
let pt = disk.pt.as_ref().ok_or(Error::new(EBADF))?;
|
||||
let partition = pt
|
||||
.partitions
|
||||
.get(part_num as usize)
|
||||
.ok_or(Error::new(EBADF))?;
|
||||
partition.size
|
||||
};
|
||||
|
||||
stat.st_mode = MODE_FILE; // TODO: Block device?
|
||||
stat.st_size = size * u64::from(disk.block_size());
|
||||
stat.st_blksize = disk.block_size();
|
||||
stat.st_blocks = size;
|
||||
Ok(Some(0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result<Option<usize>> {
|
||||
let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
let mut i = 0;
|
||||
|
||||
let scheme_name = self.scheme_name.as_bytes();
|
||||
let mut j = 0;
|
||||
while i < buf.len() && j < scheme_name.len() {
|
||||
buf[i] = scheme_name[j];
|
||||
i += 1;
|
||||
j += 1;
|
||||
}
|
||||
|
||||
if i < buf.len() {
|
||||
buf[i] = b':';
|
||||
i += 1;
|
||||
}
|
||||
|
||||
match *handle {
|
||||
Handle::List(_) => (),
|
||||
Handle::Disk(number) => {
|
||||
let number_str = format!("{}", number);
|
||||
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;
|
||||
}
|
||||
}
|
||||
Handle::Partition(disk_num, part_num) => {
|
||||
let path = format!("{}p{}", disk_num, part_num);
|
||||
let path_bytes = path.as_bytes();
|
||||
j = 0;
|
||||
while i < buf.len() && j < path_bytes.len() {
|
||||
buf[i] = path_bytes[j];
|
||||
i += 1;
|
||||
j += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Some(i))
|
||||
}
|
||||
|
||||
fn read(
|
||||
&mut self,
|
||||
id: usize,
|
||||
buf: &mut [u8],
|
||||
offset: u64,
|
||||
_flags: u32,
|
||||
) -> Result<Option<usize>> {
|
||||
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::List(ref handle) => {
|
||||
let src = usize::try_from(offset)
|
||||
.ok()
|
||||
.and_then(|o| handle.get(o..))
|
||||
.unwrap_or(&[]);
|
||||
let bytes = src.len().min(buf.len());
|
||||
buf[..bytes].copy_from_slice(&src[..bytes]);
|
||||
Ok(Some(bytes))
|
||||
}
|
||||
Handle::Disk(number) => {
|
||||
let disk = self.disks.get_mut(number).ok_or(Error::new(EBADF))?;
|
||||
let block = offset / u64::from(disk.block_size());
|
||||
disk.read(None, block, buf)
|
||||
}
|
||||
Handle::Partition(disk_num, part_num) => {
|
||||
let disk = self.disks.get_mut(disk_num).ok_or(Error::new(EBADF))?;
|
||||
let block = offset / u64::from(disk.block_size());
|
||||
disk.read(Some(part_num as usize), block, buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, id: usize, buf: &[u8], offset: u64, _flags: u32) -> Result<Option<usize>> {
|
||||
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::List(_) => Err(Error::new(EBADF)),
|
||||
Handle::Disk(number) => {
|
||||
let disk = self.disks.get_mut(number).ok_or(Error::new(EBADF))?;
|
||||
let block = offset / u64::from(disk.block_size());
|
||||
disk.write(None, block, buf)
|
||||
}
|
||||
Handle::Partition(disk_num, part_num) => {
|
||||
let disk = self.disks.get_mut(disk_num).ok_or(Error::new(EBADF))?;
|
||||
let block = offset / u64::from(disk.block_size());
|
||||
disk.write(Some(part_num as usize), block, buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fsize(&mut self, id: usize) -> Result<Option<u64>> {
|
||||
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::List(ref mut handle) => Ok(Some(handle.len() as u64)),
|
||||
Handle::Disk(number) => {
|
||||
let disk = self.disks.get_mut(number).ok_or(Error::new(EBADF))?;
|
||||
Ok(Some(disk.size()))
|
||||
}
|
||||
Handle::Partition(disk_num, part_num) => {
|
||||
let disk = self.disks.get_mut(disk_num).ok_or(Error::new(EBADF))?;
|
||||
let block_count = disk
|
||||
.pt
|
||||
.as_ref()
|
||||
.ok_or(Error::new(EBADF))?
|
||||
.partitions
|
||||
.get(part_num as usize)
|
||||
.ok_or(Error::new(EBADF))?
|
||||
.size;
|
||||
Ok(Some(u64::from(disk.block_size()) * block_count))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn close(&mut self, id: usize) -> Result<Option<usize>> {
|
||||
self.handles
|
||||
.remove(&id)
|
||||
.ok_or(Error::new(EBADF))
|
||||
.and(Ok(Some(0)))
|
||||
}
|
||||
}
|
||||
@@ -12,5 +12,5 @@ anyhow = "1"
|
||||
libredox = "0.1.3"
|
||||
redox-daemon = "0.1"
|
||||
redox_syscall = { version = "0.5", features = ["std"] }
|
||||
redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git" }
|
||||
redox_event = "0.4"
|
||||
driver-block = { path = "../driver-block" }
|
||||
|
||||
+62
-136
@@ -2,46 +2,26 @@
|
||||
|
||||
#![feature(int_roundings)]
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::fs::File;
|
||||
|
||||
use std::os::fd::AsRawFd;
|
||||
use std::str;
|
||||
|
||||
use driver_block::{Disk, DiskScheme};
|
||||
use libredox::call::MmapArgs;
|
||||
use libredox::flag;
|
||||
use redox_scheme::{CallerCtx, OpenResult, RequestKind, Scheme, SignalBehavior, Socket};
|
||||
|
||||
use syscall::data::Stat;
|
||||
use syscall::error::*;
|
||||
use syscall::flag::{MODE_DIR, MODE_FILE};
|
||||
use syscall::schemev2::NewFdFlags;
|
||||
use syscall::PAGE_SIZE;
|
||||
|
||||
use anyhow::{anyhow, bail, Context};
|
||||
|
||||
const LIST: [u8; 2] = *b"0\n";
|
||||
|
||||
#[repr(usize)]
|
||||
enum HandleType {
|
||||
TopLevel = 0,
|
||||
TheData = 1,
|
||||
}
|
||||
impl HandleType {
|
||||
fn try_from_raw(raw: usize) -> Option<Self> {
|
||||
Some(match raw {
|
||||
0 => Self::TopLevel,
|
||||
1 => Self::TheData,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DiskScheme {
|
||||
struct LiveDisk {
|
||||
the_data: &'static mut [u8],
|
||||
}
|
||||
|
||||
impl DiskScheme {
|
||||
pub fn new() -> anyhow::Result<DiskScheme> {
|
||||
impl LiveDisk {
|
||||
fn new() -> anyhow::Result<LiveDisk> {
|
||||
let mut phys = 0;
|
||||
let mut size = 0;
|
||||
|
||||
@@ -93,133 +73,79 @@ impl DiskScheme {
|
||||
std::slice::from_raw_parts_mut(base as *mut u8, size)
|
||||
};
|
||||
|
||||
Ok(DiskScheme { the_data })
|
||||
Ok(LiveDisk { the_data })
|
||||
}
|
||||
}
|
||||
|
||||
impl Scheme for DiskScheme {
|
||||
fn fsize(&mut self, id: usize) -> Result<u64> {
|
||||
Ok(
|
||||
match HandleType::try_from_raw(id).ok_or(Error::new(EBADF))? {
|
||||
HandleType::TopLevel => LIST.len() as u64,
|
||||
HandleType::TheData => self.the_data.len() as u64,
|
||||
},
|
||||
)
|
||||
impl Disk for LiveDisk {
|
||||
fn block_size(&self) -> u32 {
|
||||
512
|
||||
}
|
||||
|
||||
fn fcntl(&mut self, _id: usize, _cmd: usize, _arg: usize) -> Result<usize> {
|
||||
Ok(0)
|
||||
fn size(&self) -> u64 {
|
||||
self.the_data.len() as u64
|
||||
}
|
||||
|
||||
fn fsync(&mut self, _id: usize) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn close(&mut self, _id: usize) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
fn xopen(&mut self, path: &str, _flags: usize, ctx: &CallerCtx) -> Result<OpenResult> {
|
||||
if ctx.uid != 0 {
|
||||
return Err(Error::new(EACCES));
|
||||
fn read(&mut self, block: u64, buffer: &mut [u8]) -> syscall::Result<Option<usize>> {
|
||||
let block = block as usize;
|
||||
let block_size = self.block_size() as usize;
|
||||
if block * block_size + buffer.len() > self.size() as usize {
|
||||
return Err(syscall::Error::new(EOVERFLOW));
|
||||
}
|
||||
|
||||
let path_trimmed = path.trim_matches('/');
|
||||
|
||||
Ok(OpenResult::ThisScheme {
|
||||
number: match path_trimmed {
|
||||
"" => HandleType::TopLevel as usize,
|
||||
"0" => HandleType::TheData as usize,
|
||||
_ => return Err(Error::new(ENOENT)),
|
||||
},
|
||||
flags: NewFdFlags::POSITIONED,
|
||||
})
|
||||
}
|
||||
fn read(&mut self, id: usize, buf: &mut [u8], offset: u64, _flags: u32) -> Result<usize> {
|
||||
let data = match HandleType::try_from_raw(id).ok_or(Error::new(EBADF))? {
|
||||
HandleType::TheData => &*self.the_data,
|
||||
HandleType::TopLevel => &LIST,
|
||||
};
|
||||
|
||||
let src = usize::try_from(offset)
|
||||
.ok()
|
||||
.and_then(|o| data.get(o..))
|
||||
.unwrap_or(&[]);
|
||||
let byte_count = std::cmp::min(src.len(), buf.len());
|
||||
buf[..byte_count].copy_from_slice(&src[..byte_count]);
|
||||
Ok(byte_count)
|
||||
}
|
||||
fn write(&mut self, id: usize, buf: &[u8], offset: u64, _flags: u32) -> Result<usize> {
|
||||
let data = match HandleType::try_from_raw(id).ok_or(Error::new(EBADF))? {
|
||||
HandleType::TheData => &mut *self.the_data,
|
||||
HandleType::TopLevel => return Err(Error::new(EBADF)),
|
||||
};
|
||||
|
||||
let dst = usize::try_from(offset)
|
||||
.ok()
|
||||
.and_then(|o| data.get_mut(o..))
|
||||
.unwrap_or(&mut []);
|
||||
let byte_count = std::cmp::min(dst.len(), buf.len());
|
||||
dst[..byte_count].copy_from_slice(&buf[..byte_count]);
|
||||
Ok(byte_count)
|
||||
buffer
|
||||
.copy_from_slice(&self.the_data[block * block_size..block * block_size + buffer.len()]);
|
||||
Ok(Some(block_size))
|
||||
}
|
||||
|
||||
fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
let path = match HandleType::try_from_raw(id).ok_or(Error::new(EBADF))? {
|
||||
HandleType::TopLevel => "",
|
||||
HandleType::TheData => "0",
|
||||
};
|
||||
|
||||
let src = format!("disk.live:{}", path).into_bytes();
|
||||
|
||||
let byte_count = std::cmp::min(buf.len(), src.len());
|
||||
buf[..byte_count].copy_from_slice(&src[..byte_count]);
|
||||
|
||||
Ok(byte_count)
|
||||
}
|
||||
fn fstat(&mut self, id: usize, stat_buf: &mut Stat) -> Result<usize> {
|
||||
let (len, mode) = match HandleType::try_from_raw(id).ok_or(Error::new(EBADF))? {
|
||||
HandleType::TheData => (self.the_data.len(), MODE_FILE | 0o644),
|
||||
HandleType::TopLevel => (LIST.len(), MODE_DIR | 0o755),
|
||||
};
|
||||
|
||||
*stat_buf = Stat {
|
||||
st_mode: mode,
|
||||
st_uid: 0,
|
||||
st_gid: 0,
|
||||
st_size: len.try_into().map_err(|_| Error::new(EOVERFLOW))?,
|
||||
..Stat::default()
|
||||
};
|
||||
|
||||
Ok(0)
|
||||
fn write(&mut self, block: u64, buffer: &[u8]) -> syscall::Result<Option<usize>> {
|
||||
let block = block as usize;
|
||||
let block_size = self.block_size() as usize;
|
||||
if block * block_size + buffer.len() > self.size() as usize {
|
||||
return Err(syscall::Error::new(EOVERFLOW));
|
||||
}
|
||||
self.the_data[block * block_size..block * block_size + buffer.len()]
|
||||
.copy_from_slice(buffer);
|
||||
Ok(Some(block_size))
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
redox_daemon::Daemon::new(move |daemon| {
|
||||
let socket_fd = Socket::create("disk.live").expect("failed to open scheme");
|
||||
let mut scheme = DiskScheme::new().unwrap_or_else(|err| {
|
||||
eprintln!("failed to initialize livedisk scheme: {}", err);
|
||||
std::process::exit(1)
|
||||
});
|
||||
let event_queue = event::EventQueue::new().unwrap();
|
||||
|
||||
event::user_data! {
|
||||
enum Event {
|
||||
Scheme,
|
||||
}
|
||||
};
|
||||
|
||||
let mut scheme = DiskScheme::new(
|
||||
"disk.live".to_owned(),
|
||||
BTreeMap::from([(
|
||||
0,
|
||||
LiveDisk::new().unwrap_or_else(|err| {
|
||||
eprintln!("failed to initialize livedisk scheme: {}", err);
|
||||
std::process::exit(1)
|
||||
}),
|
||||
)]),
|
||||
);
|
||||
|
||||
libredox::call::setrens(0, 0).expect("nvmed: failed to enter null namespace");
|
||||
|
||||
event_queue
|
||||
.subscribe(
|
||||
scheme.event_handle().raw(),
|
||||
Event::Scheme,
|
||||
event::EventFlags::READ,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
daemon.ready().expect("failed to signal readiness");
|
||||
|
||||
loop {
|
||||
let req = match socket_fd
|
||||
.next_request(SignalBehavior::Restart)
|
||||
.expect("failed to get next request")
|
||||
{
|
||||
Some(r) => {
|
||||
if let RequestKind::Call(c) = r.kind() {
|
||||
c
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
None => break,
|
||||
};
|
||||
let resp = req.handle_scheme(&mut scheme);
|
||||
socket_fd
|
||||
.write_response(resp, SignalBehavior::Restart)
|
||||
.expect("failed to write packet");
|
||||
for event in event_queue {
|
||||
match event.unwrap().user_data {
|
||||
Event::Scheme => scheme.tick().unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
std::process::exit(0);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "nvmed"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
arrayvec = "0.5"
|
||||
@@ -11,7 +11,6 @@ futures = "0.3"
|
||||
log = "0.4"
|
||||
redox-daemon = "0.1"
|
||||
redox_syscall = { version = "0.5", features = ["std"] }
|
||||
redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git" }
|
||||
redox_event = "0.4"
|
||||
smallvec = "1"
|
||||
|
||||
|
||||
+48
-35
@@ -5,15 +5,15 @@ use std::ptr::NonNull;
|
||||
use std::sync::Arc;
|
||||
use std::{slice, usize};
|
||||
|
||||
use driver_block::{Disk, DiskScheme};
|
||||
use pcid_interface::{PciFeature, PciFeatureInfo, PciFunction, PciFunctionHandle};
|
||||
use redox_scheme::{RequestKind, SignalBehavior, Socket};
|
||||
use syscall::Result;
|
||||
|
||||
use crate::nvme::NvmeNamespace;
|
||||
|
||||
use self::nvme::{InterruptMethod, InterruptSources, Nvme};
|
||||
use self::scheme::DiskScheme;
|
||||
|
||||
mod nvme;
|
||||
mod scheme;
|
||||
|
||||
/// Get the most optimal yet functional interrupt mechanism: either (in the order of preference):
|
||||
/// MSI-X, MSI, and INTx# pin. Returns both runtime interrupt structures (MSI/MSI-X capability
|
||||
@@ -140,6 +140,26 @@ fn get_int_method(
|
||||
}
|
||||
}
|
||||
|
||||
struct NvmeDisk(Arc<Nvme>, NvmeNamespace);
|
||||
|
||||
impl Disk for NvmeDisk {
|
||||
fn block_size(&self) -> u32 {
|
||||
self.1.block_size.try_into().unwrap()
|
||||
}
|
||||
|
||||
fn size(&self) -> u64 {
|
||||
self.1.blocks * self.1.block_size
|
||||
}
|
||||
|
||||
fn read(&mut self, block: u64, buffer: &mut [u8]) -> syscall::Result<Option<usize>> {
|
||||
self.0.namespace_read(self.1, block, buffer)
|
||||
}
|
||||
|
||||
fn write(&mut self, block: u64, buffer: &[u8]) -> syscall::Result<Option<usize>> {
|
||||
self.0.namespace_write(self.1, block, buffer)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
redox_daemon::Daemon::new(daemon).expect("nvmed: failed to daemonize");
|
||||
}
|
||||
@@ -161,8 +181,6 @@ fn daemon(daemon: redox_daemon::Daemon) -> ! {
|
||||
|
||||
let address = unsafe { pcid_handle.map_bar(0).ptr };
|
||||
|
||||
let socket = Socket::create(&scheme_name).expect("nvmed: failed to create disk scheme");
|
||||
|
||||
daemon.ready().expect("nvmed: failed to signal readiness");
|
||||
|
||||
let (reactor_sender, reactor_receiver) = crossbeam_channel::unbounded();
|
||||
@@ -185,40 +203,35 @@ fn daemon(daemon: redox_daemon::Daemon) -> ! {
|
||||
reactor_receiver,
|
||||
);
|
||||
let namespaces = nvme.init_with_queues();
|
||||
let event_queue = event::EventQueue::new().unwrap();
|
||||
|
||||
event::user_data! {
|
||||
enum Event {
|
||||
Scheme,
|
||||
}
|
||||
};
|
||||
|
||||
let mut scheme = DiskScheme::new(
|
||||
scheme_name,
|
||||
namespaces
|
||||
.into_iter()
|
||||
.map(|(k, ns)| (k, NvmeDisk(nvme.clone(), ns)))
|
||||
.collect(),
|
||||
);
|
||||
|
||||
libredox::call::setrens(0, 0).expect("nvmed: failed to enter null namespace");
|
||||
|
||||
let mut scheme = DiskScheme::new(scheme_name, nvme, namespaces);
|
||||
let mut todo = Vec::new();
|
||||
loop {
|
||||
// TODO: Use a proper event queue once interrupt support is back.
|
||||
match socket
|
||||
.next_request(SignalBehavior::Restart)
|
||||
.expect("nvmed: failed to read disk scheme")
|
||||
{
|
||||
None => {
|
||||
break;
|
||||
}
|
||||
Some(req) => {
|
||||
if let RequestKind::Call(c) = req.kind() {
|
||||
todo.push(c);
|
||||
} else {
|
||||
// TODO: cancellation
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
event_queue
|
||||
.subscribe(
|
||||
scheme.event_handle().raw(),
|
||||
Event::Scheme,
|
||||
event::EventFlags::READ,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut i = 0;
|
||||
while i < todo.len() {
|
||||
if let Some(resp) = todo[i].handle_scheme_block(&mut scheme) {
|
||||
let _req = todo.remove(i);
|
||||
socket
|
||||
.write_response(resp, SignalBehavior::Restart)
|
||||
.expect("nvmed: failed to write disk scheme");
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
for event in event_queue {
|
||||
match event.unwrap().user_data {
|
||||
Event::Scheme => scheme.tick().unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,336 +0,0 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::fmt::Write;
|
||||
use std::str;
|
||||
use std::sync::Arc;
|
||||
|
||||
use driver_block::{Disk, DiskWrapper};
|
||||
use redox_scheme::{CallerCtx, OpenResult, SchemeBlock};
|
||||
use syscall::schemev2::NewFdFlags;
|
||||
use syscall::{
|
||||
Error, Result, Stat, EACCES, EBADF, EISDIR, ENOENT, ENOLCK, MODE_DIR, MODE_FILE, O_DIRECTORY,
|
||||
O_STAT,
|
||||
};
|
||||
|
||||
use crate::nvme::{Nvme, NvmeNamespace};
|
||||
|
||||
enum Handle {
|
||||
List(Vec<u8>), // entries
|
||||
Disk(u32), // disk num
|
||||
Partition(u32, u32), // disk num, part num
|
||||
}
|
||||
|
||||
struct NvmeDisk(Arc<Nvme>, NvmeNamespace);
|
||||
|
||||
impl Disk for NvmeDisk {
|
||||
fn block_size(&self) -> u32 {
|
||||
self.1.block_size.try_into().unwrap()
|
||||
}
|
||||
|
||||
fn size(&self) -> u64 {
|
||||
self.1.blocks * self.1.block_size
|
||||
}
|
||||
|
||||
fn read(&mut self, block: u64, buffer: &mut [u8]) -> syscall::Result<Option<usize>> {
|
||||
self.0.namespace_read(self.1, block, buffer)
|
||||
}
|
||||
|
||||
fn write(&mut self, block: u64, buffer: &[u8]) -> syscall::Result<Option<usize>> {
|
||||
self.0.namespace_write(self.1, block, buffer)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DiskScheme {
|
||||
scheme_name: String,
|
||||
disks: BTreeMap<u32, DiskWrapper<NvmeDisk>>,
|
||||
handles: BTreeMap<usize, Handle>,
|
||||
next_id: usize,
|
||||
}
|
||||
|
||||
impl DiskScheme {
|
||||
pub fn new(
|
||||
scheme_name: String,
|
||||
nvme: Arc<Nvme>,
|
||||
disks: BTreeMap<u32, NvmeNamespace>,
|
||||
) -> DiskScheme {
|
||||
DiskScheme {
|
||||
scheme_name,
|
||||
disks: disks
|
||||
.into_iter()
|
||||
.map(|(k, ns)| (k, DiskWrapper::new(NvmeDisk(nvme.clone(), ns))))
|
||||
.collect(),
|
||||
handles: BTreeMap::new(),
|
||||
next_id: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// Checks if any conflicting handles already exist
|
||||
fn check_locks(&self, disk_i: u32, part_i_opt: Option<u32>) -> Result<()> {
|
||||
for (_, handle) in self.handles.iter() {
|
||||
match handle {
|
||||
Handle::Disk(i) => {
|
||||
if disk_i == *i {
|
||||
return Err(Error::new(ENOLCK));
|
||||
}
|
||||
}
|
||||
Handle::Partition(i, p) => {
|
||||
if disk_i == *i {
|
||||
match part_i_opt {
|
||||
Some(part_i) => {
|
||||
if part_i == *p {
|
||||
return Err(Error::new(ENOLCK));
|
||||
}
|
||||
}
|
||||
None => {
|
||||
return Err(Error::new(ENOLCK));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl SchemeBlock for DiskScheme {
|
||||
fn xopen(
|
||||
&mut self,
|
||||
path_str: &str,
|
||||
flags: usize,
|
||||
ctx: &CallerCtx,
|
||||
) -> Result<Option<OpenResult>> {
|
||||
if ctx.uid != 0 {
|
||||
return Err(Error::new(EACCES));
|
||||
}
|
||||
let path_str = path_str.trim_matches('/');
|
||||
|
||||
let handle = if path_str.is_empty() {
|
||||
if flags & O_DIRECTORY == O_DIRECTORY || flags & O_STAT == O_STAT {
|
||||
let mut list = String::new();
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
Handle::List(list.into_bytes())
|
||||
} else {
|
||||
return 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::<u32>().or(Err(Error::new(ENOENT)))?;
|
||||
let part_num = part_num_str.parse::<u32>().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()
|
||||
{
|
||||
self.check_locks(nsid, Some(part_num))?;
|
||||
|
||||
Handle::Partition(nsid, part_num)
|
||||
} else {
|
||||
return Err(Error::new(ENOENT));
|
||||
}
|
||||
} else {
|
||||
return Err(Error::new(ENOENT));
|
||||
}
|
||||
} else {
|
||||
let nsid = path_str.parse::<u32>().or(Err(Error::new(ENOENT)))?;
|
||||
|
||||
if self.disks.contains_key(&nsid) {
|
||||
self.check_locks(nsid, None)?;
|
||||
Handle::Disk(nsid)
|
||||
} else {
|
||||
return Err(Error::new(ENOENT));
|
||||
}
|
||||
};
|
||||
let id = self.next_id;
|
||||
self.next_id += 1;
|
||||
self.handles.insert(id, handle);
|
||||
Ok(Some(OpenResult::ThisScheme {
|
||||
number: id,
|
||||
flags: NewFdFlags::POSITIONED,
|
||||
}))
|
||||
}
|
||||
|
||||
fn fstat(&mut self, id: usize, stat: &mut Stat) -> Result<Option<usize>> {
|
||||
match *self.handles.get(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::List(ref data) => {
|
||||
stat.st_mode = MODE_DIR;
|
||||
stat.st_size = data.len() as u64;
|
||||
Ok(Some(0))
|
||||
}
|
||||
Handle::Disk(number) => {
|
||||
let disk = self.disks.get_mut(&number).ok_or(Error::new(EBADF))?;
|
||||
stat.st_mode = MODE_FILE;
|
||||
stat.st_blocks = disk.disk().1.blocks;
|
||||
stat.st_blksize = disk.block_size();
|
||||
stat.st_size = disk.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 * u64::from(disk.block_size());
|
||||
stat.st_blocks = part.size;
|
||||
stat.st_blksize = disk.block_size();
|
||||
Ok(Some(0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result<Option<usize>> {
|
||||
let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
let mut i = 0;
|
||||
|
||||
let scheme_name = self.scheme_name.as_bytes();
|
||||
let mut j = 0;
|
||||
while i < buf.len() && j < scheme_name.len() {
|
||||
buf[i] = scheme_name[j];
|
||||
i += 1;
|
||||
j += 1;
|
||||
}
|
||||
|
||||
if i < buf.len() {
|
||||
buf[i] = b':';
|
||||
i += 1;
|
||||
}
|
||||
|
||||
match *handle {
|
||||
Handle::List(_) => (),
|
||||
Handle::Disk(number) => {
|
||||
let number_str = format!("{}", number);
|
||||
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;
|
||||
}
|
||||
}
|
||||
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))
|
||||
}
|
||||
|
||||
fn read(
|
||||
&mut self,
|
||||
id: usize,
|
||||
buf: &mut [u8],
|
||||
offset: u64,
|
||||
_fcntl_flags: u32,
|
||||
) -> Result<Option<usize>> {
|
||||
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::List(ref handle) => {
|
||||
let src = usize::try_from(offset)
|
||||
.ok()
|
||||
.and_then(|o| handle.get(o..))
|
||||
.unwrap_or(&[]);
|
||||
let count = core::cmp::min(src.len(), buf.len());
|
||||
buf[..count].copy_from_slice(&src[..count]);
|
||||
Ok(Some(count))
|
||||
}
|
||||
Handle::Disk(number) => {
|
||||
let disk = self.disks.get_mut(&number).ok_or(Error::new(EBADF))?;
|
||||
let block = offset / u64::from(disk.block_size());
|
||||
disk.read(None, block, buf)
|
||||
}
|
||||
Handle::Partition(disk_num, part_num) => {
|
||||
let disk = self.disks.get_mut(&disk_num).ok_or(Error::new(EBADF))?;
|
||||
let block = offset / u64::from(disk.block_size());
|
||||
disk.read(Some(part_num as usize), block, buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write(
|
||||
&mut self,
|
||||
id: usize,
|
||||
buf: &[u8],
|
||||
offset: u64,
|
||||
_fcntl_flags: u32,
|
||||
) -> Result<Option<usize>> {
|
||||
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::List(_) => Err(Error::new(EBADF)),
|
||||
Handle::Disk(number) => {
|
||||
let disk = self.disks.get_mut(&number).ok_or(Error::new(EBADF))?;
|
||||
let block = offset / u64::from(disk.block_size());
|
||||
disk.write(None, block, buf)
|
||||
}
|
||||
Handle::Partition(disk_num, part_num) => {
|
||||
let disk = self.disks.get_mut(&disk_num).ok_or(Error::new(EBADF))?;
|
||||
let block = offset / u64::from(disk.block_size());
|
||||
disk.write(Some(part_num as usize), block, buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fsize(&mut self, id: usize) -> Result<Option<u64>> {
|
||||
Ok(Some(
|
||||
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::List(ref handle) => handle.len() as u64,
|
||||
Handle::Disk(number) => {
|
||||
let disk = self.disks.get_mut(&number).ok_or(Error::new(EBADF))?;
|
||||
disk.size()
|
||||
}
|
||||
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))?;
|
||||
|
||||
part.size * u64::from(disk.block_size())
|
||||
}
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
fn close(&mut self, id: usize) -> Result<Option<usize>> {
|
||||
self.handles
|
||||
.remove(&id)
|
||||
.ok_or(Error::new(EBADF))
|
||||
.and(Ok(Some(0)))
|
||||
}
|
||||
}
|
||||
@@ -13,8 +13,8 @@ futures = { version = "0.3.28", features = ["executor"] }
|
||||
spin = "*"
|
||||
|
||||
redox-daemon = "0.1"
|
||||
redox_event = "0.4"
|
||||
redox_syscall = { version = "0.5", features = ["std"] }
|
||||
redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git" }
|
||||
|
||||
common = { path = "../../common" }
|
||||
driver-block = { path = "../driver-block" }
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#![deny(trivial_numeric_casts, unused_allocation)]
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::{Arc, Weak};
|
||||
|
||||
use redox_scheme::{RequestKind, SignalBehavior, Socket};
|
||||
use driver_block::DiskScheme;
|
||||
use static_assertions::const_assert_eq;
|
||||
|
||||
use pcid_interface::*;
|
||||
@@ -15,6 +16,8 @@ mod scheme;
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::scheme::VirtioDisk;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
#[error("capability {0:?} not found")]
|
||||
@@ -140,31 +143,37 @@ fn deamon(deamon: redox_daemon::Daemon) -> anyhow::Result<()> {
|
||||
|
||||
let scheme_name = format!("disk.{}", name);
|
||||
|
||||
let socket_fd = Socket::create(&scheme_name).map_err(Error::SyscallError)?;
|
||||
let event_queue = event::EventQueue::new().unwrap();
|
||||
|
||||
let mut scheme = scheme::DiskScheme::new(queue, device_space);
|
||||
event::user_data! {
|
||||
enum Event {
|
||||
Scheme,
|
||||
}
|
||||
};
|
||||
|
||||
let mut scheme = DiskScheme::new(
|
||||
scheme_name,
|
||||
BTreeMap::from([(0, VirtioDisk::new(queue, device_space))]),
|
||||
);
|
||||
|
||||
libredox::call::setrens(0, 0).expect("nvmed: failed to enter null namespace");
|
||||
|
||||
event_queue
|
||||
.subscribe(
|
||||
scheme.event_handle().raw(),
|
||||
Event::Scheme,
|
||||
event::EventFlags::READ,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
deamon.ready().expect("virtio-blkd: failed to deamonize");
|
||||
|
||||
loop {
|
||||
let req = match socket_fd
|
||||
.next_request(SignalBehavior::Restart)
|
||||
.expect("virtio-blkd: failed to read disk scheme")
|
||||
{
|
||||
Some(r) => {
|
||||
if let RequestKind::Call(c) = r.kind() {
|
||||
c
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
None => break,
|
||||
};
|
||||
let resp = req.handle_scheme_block(&mut scheme).expect("TODO: block?");
|
||||
socket_fd
|
||||
.write_response(resp, SignalBehavior::Restart)
|
||||
.expect("virtio-blkd: failed to write to disk scheme");
|
||||
for event in event_queue {
|
||||
match event.unwrap().user_data {
|
||||
Event::Scheme => scheme.tick().unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,6 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use std::fmt::Write;
|
||||
use std::sync::Arc;
|
||||
|
||||
use common::dma::Dma;
|
||||
use driver_block::DiskWrapper;
|
||||
|
||||
use redox_scheme::CallerCtx;
|
||||
use redox_scheme::OpenResult;
|
||||
use redox_scheme::SchemeBlock;
|
||||
use syscall::error::*;
|
||||
use syscall::flag::*;
|
||||
use syscall::schemev2::NewFdFlags;
|
||||
use virtio_core::spec::{Buffer, ChainBuilder, DescriptorFlags};
|
||||
use virtio_core::transport::Queue;
|
||||
|
||||
@@ -19,8 +8,6 @@ use crate::BlockDeviceConfig;
|
||||
use crate::BlockRequestTy;
|
||||
use crate::BlockVirtRequest;
|
||||
|
||||
const BLK_SIZE: u64 = 512;
|
||||
|
||||
trait BlkExtension {
|
||||
async fn read(&self, block: u64, target: &mut [u8]) -> usize;
|
||||
async fn write(&self, block: u64, target: &[u8]) -> usize;
|
||||
@@ -86,13 +73,13 @@ impl BlkExtension for Queue<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
struct VirtioDisk<'a> {
|
||||
pub(crate) struct VirtioDisk<'a> {
|
||||
queue: Arc<Queue<'a>>,
|
||||
cfg: BlockDeviceConfig,
|
||||
}
|
||||
|
||||
impl<'a> VirtioDisk<'a> {
|
||||
pub fn new(queue: Arc<Queue<'a>>, cfg: BlockDeviceConfig) -> Self {
|
||||
pub(crate) fn new(queue: Arc<Queue<'a>>, cfg: BlockDeviceConfig) -> Self {
|
||||
Self { queue, cfg }
|
||||
}
|
||||
}
|
||||
@@ -118,203 +105,3 @@ impl driver_block::Disk for VirtioDisk<'_> {
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Handle {
|
||||
Partition {
|
||||
/// Partition Number
|
||||
number: u32,
|
||||
},
|
||||
|
||||
List {
|
||||
entries: Vec<u8>,
|
||||
},
|
||||
|
||||
Disk,
|
||||
}
|
||||
|
||||
pub struct DiskScheme<'a> {
|
||||
disk: DiskWrapper<VirtioDisk<'a>>,
|
||||
next_id: usize,
|
||||
handles: BTreeMap<usize, Handle>,
|
||||
}
|
||||
|
||||
impl<'a> DiskScheme<'a> {
|
||||
pub fn new(queue: Arc<Queue<'a>>, cfg: BlockDeviceConfig) -> Self {
|
||||
Self {
|
||||
disk: DiskWrapper::new(VirtioDisk::new(queue, cfg)),
|
||||
next_id: 0,
|
||||
handles: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> SchemeBlock for DiskScheme<'a> {
|
||||
fn xopen(
|
||||
&mut self,
|
||||
path: &str,
|
||||
flags: usize,
|
||||
_ctx: &CallerCtx,
|
||||
) -> syscall::Result<Option<OpenResult>> {
|
||||
log::info!("virtiod: open: {}", path);
|
||||
|
||||
let path_str = path.trim_matches('/');
|
||||
if path_str.is_empty() {
|
||||
if flags & O_DIRECTORY == O_DIRECTORY || flags & O_STAT == O_STAT {
|
||||
let mut list = String::new();
|
||||
// FIXME: The zero is the disk identifier (look in the nvmed scheme, it set's this
|
||||
// to the namespace id).
|
||||
write!(list, "{}\n", 0).unwrap();
|
||||
|
||||
if let Some(part_table) = &self.disk.pt {
|
||||
for part_num in 0..part_table.partitions.len() {
|
||||
write!(list, "{}p{}\n", 0, part_num).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
let id = self.next_id;
|
||||
self.next_id += 1;
|
||||
self.handles.insert(
|
||||
id,
|
||||
Handle::List {
|
||||
entries: list.into_bytes(),
|
||||
},
|
||||
);
|
||||
|
||||
Ok(Some(OpenResult::ThisScheme {
|
||||
number: id,
|
||||
flags: NewFdFlags::POSITIONED,
|
||||
}))
|
||||
} else {
|
||||
return Err(syscall::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 part_num = part_num_str.parse::<u32>().unwrap();
|
||||
|
||||
let part_table = self.disk.pt.as_ref().unwrap();
|
||||
let _part = part_table.partitions.get(part_num as usize).unwrap();
|
||||
|
||||
let id = self.next_id;
|
||||
self.next_id += 1;
|
||||
self.handles
|
||||
.insert(id, Handle::Partition { number: part_num });
|
||||
|
||||
Ok(Some(OpenResult::ThisScheme {
|
||||
number: id,
|
||||
flags: NewFdFlags::POSITIONED,
|
||||
}))
|
||||
} else {
|
||||
let nsid = path_str.parse::<u32>().unwrap();
|
||||
assert_eq!(nsid, 0);
|
||||
|
||||
let id = self.next_id;
|
||||
self.next_id += 1;
|
||||
self.handles.insert(id, Handle::Disk);
|
||||
Ok(Some(OpenResult::ThisScheme {
|
||||
number: id,
|
||||
flags: NewFdFlags::POSITIONED,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
fn read(
|
||||
&mut self,
|
||||
id: usize,
|
||||
buf: &mut [u8],
|
||||
offset: u64,
|
||||
_fcntl_flags: u32,
|
||||
) -> syscall::Result<Option<usize>> {
|
||||
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::List { ref mut entries } => {
|
||||
let src = usize::try_from(offset)
|
||||
.ok()
|
||||
.and_then(|o| entries.get(o..))
|
||||
.unwrap_or(&[]);
|
||||
let count = core::cmp::min(src.len(), buf.len());
|
||||
buf[..count].copy_from_slice(&src[..count]);
|
||||
Ok(Some(count))
|
||||
}
|
||||
Handle::Disk => {
|
||||
let block = offset / u64::from(self.disk.block_size());
|
||||
self.disk.read(None, block, buf)
|
||||
}
|
||||
Handle::Partition { number } => {
|
||||
let block = offset / u64::from(self.disk.block_size());
|
||||
self.disk.read(Some(number as usize), block, buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write(
|
||||
&mut self,
|
||||
id: usize,
|
||||
buf: &[u8],
|
||||
offset: u64,
|
||||
_fcntl_flags: u32,
|
||||
) -> syscall::Result<Option<usize>> {
|
||||
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::List { .. } => Err(Error::new(EBADF)),
|
||||
Handle::Disk => {
|
||||
let block = offset / u64::from(self.disk.block_size());
|
||||
self.disk.write(None, block, buf)
|
||||
}
|
||||
Handle::Partition { number } => {
|
||||
let block = offset / u64::from(self.disk.block_size());
|
||||
self.disk.write(Some(number as usize), block, buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fsize(&mut self, id: usize) -> syscall::Result<Option<u64>> {
|
||||
Ok(Some(
|
||||
match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::List { ref entries } => {
|
||||
let len = entries.len() as u64;
|
||||
log::debug!("list: part_len={len:?}");
|
||||
|
||||
len
|
||||
}
|
||||
|
||||
Handle::Partition { number } => {
|
||||
let part_table = self.disk.pt.as_ref().unwrap();
|
||||
let part = part_table
|
||||
.partitions
|
||||
.get(number as usize)
|
||||
.ok_or(Error::new(EBADF))?;
|
||||
|
||||
// Partition size in bytes.
|
||||
let len = part.size * BLK_SIZE;
|
||||
|
||||
log::debug!("part: part_len={len:?}");
|
||||
|
||||
len
|
||||
}
|
||||
|
||||
Handle::Disk => self.disk.size(),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
fn fpath(&mut self, _id: usize, _buf: &mut [u8]) -> syscall::Result<Option<usize>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn fstat(&mut self, id: usize, _stat: &mut syscall::Stat) -> syscall::Result<Option<usize>> {
|
||||
match self.handles.get_mut(&id).ok_or(Error::new(EBADF))? {
|
||||
Handle::List { .. } => Ok(Some(0)),
|
||||
Handle::Disk { .. } | Handle::Partition { .. } => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn close(&mut self, id: usize) -> syscall::Result<Option<usize>> {
|
||||
self.handles
|
||||
.remove(&id)
|
||||
.ok_or(Error::new(EBADF))
|
||||
.and(Ok(Some(0)))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user