M x/vdasd/src/control.rs => x/vdasd/src/control.rs +39 -2
@@ 2,16 2,21 @@
use std::collections::HashMap;
-use crate::constants::SECTOR_SIZE;
use crate::errors::Error;
use crate::messages::{RMsg, Request, TMsg};
-use crate::structs::{FidBinding, Qid};
+use crate::structs::{FidBinding, Qid, PathString, FSObject, QTDIR, QTFILE};
const NOFID: u32 = !0;
+// For our purposes, we arbitrarily pick path 1 for the root directory.
+const QID_ROOT_PATH: u64 = 1;
+const QID_0_PATH: u64 = 2;
+const QID_MEDIA_PATH: u64 = 3;
+
/// The state for the control plane.
pub struct Control {
fid_bindings: HashMap<u32, FidBinding>,
+ directory_tree: Vec<FSObject>,
}
/// Control-specific methods.
@@ 20,6 25,11 @@ impl Control {
pub fn new() -> Self {
Control {
fid_bindings: HashMap::new(),
+ directory_tree: vec![
+ FSObject { qid: Qid { qtype: QTDIR, version: 0, path: QID_ROOT_PATH }, parent: QID_ROOT_PATH, name: "/".to_string() },
+ FSObject { qid: Qid { qtype: QTDIR, version: 0, path: QID_0_PATH }, parent: QID_ROOT_PATH, name: "0".to_string() },
+ FSObject { qid: Qid { qtype: QTFILE, version: 0, path: QID_MEDIA_PATH }, parent: QID_0_PATH, name: "media".to_string() },
+ ],
}
}
@@ 76,6 86,7 @@ impl Control {
FidBinding {
fid: *fid,
is_open: false,
+ file_ref: PathString(vec![QID_ROOT_PATH]),
},
);
@@ 109,12 120,38 @@ impl Control {
#[cfg(test)]
mod tests {
use super::*;
+ use crate::constants::SECTOR_SIZE;
#[test]
fn basic_creation() {
let _control = Control::new();
}
+ #[test]
+ fn auth() {
+ let mut r: Request = Request {
+ t: TMsg::Auth {
+ tag: 1,
+ afid: NOFID,
+ uname: "".to_string(),
+ aname: "".to_string(),
+ },
+ r: None,
+ b: [0; SECTOR_SIZE],
+ };
+
+ let mut c: Control = Control::new();
+
+ c.dispatch(&mut r);
+
+ match r.r {
+ None => panic!("Control ignored message."),
+ Some(Err(Error::NotSupported)) => (),
+ Some(Err(e)) => panic!("Unexpected error {:?}", e),
+ Some(Ok(_)) => panic!("Unexpected success"),
+ }
+ }
+
fn try_attaching() -> (Control, Request) {
let mut r: Request = Request {
t: TMsg::Attach {
M x/vdasd/src/messages.rs => x/vdasd/src/messages.rs +8 -0
@@ 37,6 37,13 @@ pub enum TMsg {
aname: String,
},
+ Auth {
+ tag: u16,
+ afid: u32,
+ uname: String,
+ aname: String,
+ },
+
Clunk {
tag: u16,
fid: u32,
@@ 71,6 78,7 @@ pub enum TMsg {
#[derive(Debug, PartialEq)]
pub enum RMsg {
Attach { tag: u16, qid: Qid },
+ Auth { tag: u16, aqid: Qid }, // unused, but defined for completeness
Clunk { tag: u16 },
Read { tag: u16, count: u32 },
Write { tag: u16, count: u32 },
M x/vdasd/src/ramdisk.rs => x/vdasd/src/ramdisk.rs +2 -2
@@ 3,7 3,7 @@
use crate::constants::{NUM_SECTORS, SECTOR_SHIFT, SECTOR_SIZE};
use crate::errors::Error;
use crate::messages::{RMsg, Request, TMsg};
-use crate::traits::FSObject;
+use crate::traits::OpenFileHandlers;
/// The RAM disk is configured to be of a fixed size.
const RAMDISK_SIZE: usize = NUM_SECTORS * SECTOR_SIZE;
@@ 32,7 32,7 @@ fn on_sector_boundary(offset: u64) -> bool {
}
/// Filesystem Object methods
-impl FSObject for RamDiskMedium {
+impl OpenFileHandlers for RamDiskMedium {
/// Handler for the Tread 9P message.
fn read(&mut self, req: &mut Request) {
if let TMsg::Read {
M x/vdasd/src/structs.rs => x/vdasd/src/structs.rs +124 -0
@@ 21,6 21,42 @@ pub struct Qid {
pub path: u64,
}
+/// Qid.qtype that identifies a simple file.
+pub const QTFILE: u8 = 0x00;
+
+/// Qid.qtype that identifies a directory file.
+pub const QTDIR: u8 = 0x80;
+
+/// Modifier flag that indicates the file is append-only.
+/// Reads are not allowed; writes always happen at the end of the file,
+/// regardless of the offset field in the Twrite message.
+pub const QTAPPEND: u8 = 0x40;
+
+/// Modifier flag that indicates an exclusive lock on the file.
+/// While a Fid is open on a file marked exclusive, no other Fid can be
+/// opened on that file.
+pub const QTEXCL: u8 = 0x20;
+
+/// Currently reserved for future use.
+///
+/// The 9P man pages indicates that this bit was once used for something,
+/// but didn't say what. I reckon it used to signify a symbolic link before
+/// the authors realized that union mounts were equally useful as symlinks.
+pub const QTUNUSED0: u8 = 0x10;
+
+/// Qid.qtype that identifies an authentication file.
+pub const QTAUTH: u8 = 0x08;
+
+/// Modifier flag that indicates the file is a temporary file, and is not to
+/// be backed up.
+pub const QTTMP: u8 = 0x04;
+
+/// Currently reserved for future use.
+pub const QTUNUSED1: u8 = 0x02;
+
+/// Currently reserved for future use.
+pub const QTUNUSED2: u8 = 0x01;
+
/// A FidBinding associates a client-supplied fid (which is treated more or less
/// the same as a *variable name*) with some state related to traversing the filesystem.
///
@@ 28,4 64,92 @@ pub struct Qid {
pub struct FidBinding {
pub fid: u32,
pub is_open: bool,
+ pub file_ref: PathString,
+}
+
+/// A PathString is used to track which filesystem object a Fid refers to.
+/// The most recently pushed element is the path ID of the filesystem object the Fid refers to.
+/// Element 0 should always refer to / (the root directory).
+pub struct PathString(pub Vec<u64>);
+
+/// A FSObject provides a mapping between Qid path and a filename.
+/// Hierarchy is established through the parent field.
+///
+/// A vector ("table") of these records denotes the entire state of the filesystem
+/// served. For example, if we wanted to implement two RAM disk units instead of
+/// just one, where one is encrypted with a key and the other isn't, we might
+/// express a filesystem hierarchy like this:
+///
+/// ```text
+/// /
+/// |- 0/
+/// | |- media
+/// | `- key
+/// `- 1/
+/// `- media
+/// ```
+///
+/// In tabular format, this looks like this:
+///
+/// | Qid.Type | Qid.Path | Parent | Name |
+/// |:---------|:--------:|:------:|:--------|
+/// | QTDIR | 1 | 1 | "/" |
+/// | QTDIR | 2 | 1 | "0" |
+/// | QTFILE | 3 | 2 | "media" |
+/// | QTFILE | 4 | 2 | "key" |
+/// | QTDIR | 5 | 1 | "1" |
+/// | QTFILE | 6 | 5 | "media" |
+///
+/// Note how "/" (the root directory) is the only entry which is its own parent.
+///
+/// In code, we might represent the table above like so:
+///
+/// ```text
+/// let fstab: Vec<FSObject> = vec![
+/// FSObject { qid: { qtype: QTDIR , version: 0, path: 1 }, parent: 1, name: "/".to_string() },
+/// FSObject { qid: { qtype: QTDIR , version: 0, path: 2 }, parent: 1, name: "0".to_string() },
+/// FSObject { qid: { qtype: QTFILE, version: 0, path: 3 }, parent: 2, name: "media".to_string() },
+/// FSObject { qid: { qtype: QTFILE, version: 0, path: 4 }, parent: 2, name: "key".to_string() },
+/// FSObject { qid: { qtype: QTDIR , version: 0, path: 5 }, parent: 1, name: "1".to_string() },
+/// FSObject { qid: { qtype: QTFILE, version: 0, path: 6 }, parent: 5, name: "media".to_string() },
+/// ];
+/// ```
+///
+/// All manner of filesystem operations can be expressed as relational operations on this table. For example,
+/// a directory listing is just "select all FSObjects where the parent equals the current directory's qid.path".
+/// File deletion is just removal of a row from the table, while creation is simply creating a new row. As
+/// long as each row has a *unique* qid.path value, and as long as each file referenced by a parent field has
+/// qid.qtype = QTDIR, this system will work.
+pub struct FSObject {
+ /// Establishes the type and path ID of the filesystem object.
+ pub qid: Qid,
+ /// Establishes the containing directory for this object.
+ /// The parent this refers to **must** have the QTDIR bit set.
+ pub parent: u64,
+ /// The filename, which must not be zero-length nor can it contain
+ /// a slash (except for the root directory; it is the only FSObject
+ /// which may have a name of "/").
+ pub name: String,
}
+
+/// Open file for read-only access.
+pub const OREAD: u8 = 0;
+
+/// Open file for write-only access.
+pub const OWRITE: u8 = 1;
+
+/// Open file for read-and-write access.
+pub const ORDWR: u8 = 2;
+
+/// Open file for execute access (read-only with intent to execute software or script).
+pub const OEXEC: u8 = 3;
+
+pub const OUNUSED0: u8 = 0x04;
+pub const OUNUSED1: u8 = 0x08;
+/// After opening the file, truncate the file to zero length in preparation for writing.
+/// Requires write permission.
+pub const OTRUNC: u8 = 0x10;
+pub const OUNUSED2: u8 = 0x20;
+/// Remove file after closing/clunking it.
+pub const ORCLOSE: u8 = 0x40;
+pub const OUNUSED3: u8 = 0x80;
M x/vdasd/src/traits.rs => x/vdasd/src/traits.rs +1 -1
@@ 4,7 4,7 @@ use crate::messages::Request;
/// A filesystem object is anything which can react to 9P messages.
/// There's usually a correspondance between methods in this trait and message types in `TMsg`.
-pub trait FSObject {
+pub trait OpenFileHandlers {
/// Attempt to read bytes from the object.
fn read(&mut self, req: &mut Request);
/// Attempt to write bytes to the object.