~vertigo/forthbox

d102a85328e803bb1f5f79a14a115f2625e4f40b — Samuel A. Falvo II 7 months ago 1fba797
General clean-up tasks

Implement Tauth by letting default not-implemented behavior resolve it.
Rename FSObject trait to OpenFileHandlers, since FSObject makes more
sense as a struct.
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.