M x/vdasd/src/control.rs => x/vdasd/src/control.rs +100 -2
@@ 4,7 4,8 @@ use std::collections::HashMap;
use crate::errors::Error;
use crate::messages::{RMsg, Request, TMsg};
-use crate::structs::{FidBinding, Qid, PathString, FSObject, QTDIR, QTFILE};
+use crate::structs::{FidBinding, Qid, PathString, FSObject, QTDIR, QTFILE, QTAUTH, OREAD, ORCLOSE, OTRUNC};
+use crate::constants::SECTOR_SIZE;
const NOFID: u32 = !0;
@@ 37,6 38,7 @@ impl Control {
match &req.t {
TMsg::Attach { .. } => self.attach(req),
TMsg::Clunk { .. } => self.clunk(req),
+ TMsg::Open { .. } => self.open(req),
_ => req.r = Some(Err(Error::NotSupported)),
}
}
@@ 115,12 117,79 @@ impl Control {
req.r = Some(Err(Error::NotSupported));
}
}
+
+ fn open(&mut self, req: &mut Request) {
+ if let TMsg::Open { tag, fid, mode } = &req.t {
+ // Double-check that the Fid provided is valid. If it is,
+ // resolve the Fid to a filesystem object to work with.
+ let binding = match self.fid_bindings.get(fid) {
+ None => {
+ req.r = Some(Err(Error::Fid));
+ return;
+ },
+
+ Some(fid_binding) => fid_binding,
+ };
+
+ let fsobj_path = currently_addressed_object(binding);
+ let fsobj = *self.directory_tree.iter().filter(|&fso| fso.qid.path == fsobj_path).collect::<Vec<&FSObject>>().get(0).unwrap();
+
+ // What follows assumes we're opening the root directory
+ if is_directory(fsobj.qid.qtype) {
+ // Directories can only be opened in read mode.
+ // They cannot be removed upon closing. And, they cannot
+ // be truncated upon opening.
+ if (read_write_mode(*mode) != OREAD) || ((mode & (ORCLOSE | OTRUNC)) != 0) {
+ req.r = Some(Err(Error::Permission));
+ return;
+ }
+
+ // We don't check FSObject permissions or modes, because we only serve two kinds of objects:
+ // directories that everyone can read (0777), or files that everyone can read/write (0666).
+ // If we were implementing a real filesystem here, we would add checks for RWX permissions,
+ // username/groupname checks, etc. here.
+
+ req.r = Some(Ok(RMsg::Open { tag: *tag, qid: fsobj.qid, iounit: SECTOR_SIZE as u32}));
+ return;
+ }
+
+ // If we're here, it's neither file nor directory. We don't know
+ // how to handle this.
+ req.r = Some(Err(Error::NotFound));
+ } else {
+ req.r = Some(Err(Error::NotSupported));
+ }
+ }
+}
+
+const OMODEMASK: u8 = 0x03;
+
+fn kind_of_file(qtype: u8) -> u8 {
+ qtype & (QTDIR | QTAUTH)
+}
+
+fn is_directory(qtype: u8) -> bool {
+ kind_of_file(qtype) == QTDIR
+}
+
+fn read_write_mode(mode: u8) -> u8 {
+ mode & OMODEMASK
+}
+
+/// The object that the Fid is currently addressing is always defined to be the most
+/// recently pushed item in its file_ref PathString.
+fn currently_addressed_object(binding: &FidBinding) -> u64 {
+ let last = binding.file_ref.0.last();
+ match last {
+ Some(id) => *id,
+ None => unreachable!(),
+ }
}
#[cfg(test)]
mod tests {
use super::*;
- use crate::constants::SECTOR_SIZE;
+ use crate::structs::OREAD;
#[test]
fn basic_creation() {
@@ 236,4 305,33 @@ mod tests {
Some(Ok(_)) => panic!("Unexpected success"),
}
}
+
+ #[test]
+ fn open_root() {
+ let (mut c, _) = try_attaching();
+
+ let mut r = Request {
+ t: TMsg::Open {
+ tag: 1,
+ fid: 2,
+ mode: OREAD,
+ },
+ r: None,
+ b: [0; SECTOR_SIZE],
+ };
+
+ c.dispatch(&mut r);
+
+ match r.r {
+ None => panic!("Control ignored message"),
+ Some(Err(e)) => panic!("Unexpected error {:?}", e),
+ Some(Ok(RMsg::Open { tag, qid, iounit: _ })) => {
+ assert_eq!(tag, 1);
+ assert_eq!(qid.qtype, QTDIR);
+ assert_eq!(qid.version, 0);
+ assert_eq!(qid.path, QID_ROOT_PATH);
+ },
+ Some(Ok(_)) => panic!("Unexpected success"),
+ }
+ }
}
M x/vdasd/src/errors.rs => x/vdasd/src/errors.rs +4 -0
@@ 16,4 16,8 @@ pub enum Error {
DuplicateFid,
/// Missing or otherwise incorrect Fid.
Fid,
+ /// Permission denied.
+ Permission,
+ /// File Not Found.
+ NotFound,
}
M x/vdasd/src/messages.rs => x/vdasd/src/messages.rs +7 -0
@@ 49,6 49,12 @@ pub enum TMsg {
fid: u32,
},
+ Open {
+ tag: u16,
+ fid: u32,
+ mode: u8,
+ },
+
Read {
tag: u16,
fid: u32,
@@ 80,6 86,7 @@ pub enum RMsg {
Attach { tag: u16, qid: Qid },
Auth { tag: u16, aqid: Qid }, // unused, but defined for completeness
Clunk { tag: u16 },
+ Open { tag: u16, qid: Qid, iounit: u32 },
Read { tag: u16, count: u32 },
Write { tag: u16, count: u32 },
}
M x/vdasd/src/structs.rs => x/vdasd/src/structs.rs +1 -1
@@ 7,7 7,7 @@
/// (This rustdoc material comes from the 9P man pages, found online
/// at https://man.cat-v.org/plan_9/5/intro . Some changes were made
/// to better fit the rustdoc comment style.)
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Clone, Copy)]
pub struct Qid {
/// Specifies whether the file is a directory, append-only file, etc.
/// (The desired name for this field, type, is a reserved keyword in Rust.)