~vertigo/forthbox

9c7df57086040203c597b3e99b27a67cd5a3dc65 — Samuel A. Falvo II 7 months ago d102a85
Support opening the root directory

This code should technically work with any directory.  However, we don't
implement Twalk yet.
4 files changed, 112 insertions(+), 3 deletions(-)

M x/vdasd/src/control.rs
M x/vdasd/src/errors.rs
M x/vdasd/src/messages.rs
M x/vdasd/src/structs.rs
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.)