use std::fs;
use std::mem::{size_of, size_of_val};
use std::os::unix::fs::MetadataExt;
use bitflags::bitflags;
use enum_primitive::*;
pub const P92000: &str = "9P2000";
pub const P92000L: &str = "9P2000.L";
pub const VERSION_UNKNOWN: &str = "unknown";
pub const NOTAG: u16 = !0;
pub const NOFID: u32 = !0;
pub const NONUNAME: u32 = !0;
pub const IOHDRSZ: u32 = 24;
pub const READDIRHDRSZ: u32 = 24;
pub const V9FS_PORT: u16 = 564;
pub mod p92000 {
pub mod om {
pub const READ: u8 = 0;
pub const WRITE: u8 = 1;
pub const RDWR: u8 = 2;
pub const EXEC: u8 = 3;
pub const TRUNC: u8 = 16;
pub const CEXEC: u8 = 32;
pub const RCLOSE: u8 = 64;
}
pub mod dm {
pub const DIR: u32 = 0x80000000;
pub const APPEND: u32 = 0x40000000;
pub const EXCL: u32 = 0x20000000;
pub const MOUNT: u32 = 0x10000000;
pub const AUTH: u32 = 0x08000000;
pub const TMP: u32 = 0x04000000;
pub const READ: u32 = 0x4;
pub const WRITE: u32 = 0x2;
pub const EXEC: u32 = 0x1;
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Stat {
pub typ: u16,
pub dev: u32,
pub qid: super::Qid,
pub mode: u32,
pub atime: u32,
pub mtime: u32,
pub length: u64,
pub name: String,
pub uid: String,
pub gid: String,
pub muid: String,
}
impl Stat {
pub fn size(&self) -> u16 {
use std::mem::{size_of, size_of_val};
(size_of_val(&self.typ)
+ size_of_val(&self.dev)
+ size_of_val(&self.qid)
+ size_of_val(&self.mode)
+ size_of_val(&self.atime)
+ size_of_val(&self.mtime)
+ size_of_val(&self.length)
+ (size_of::<u16>() * 4)
+ self.name.len()
+ self.uid.len()
+ self.gid.len()
+ self.muid.len()) as u16
}
}
}
bitflags! {
pub struct LockType: u8 {
const RDLOCK = 0;
const WRLOCK = 1;
const UNLOCK = 2;
}
}
bitflags! {
pub struct LockFlag: u32 {
#[doc = "Blocking request"]
const BLOCK = 1;
#[doc = "Reserved for future use"]
const RECLAIM = 2;
}
}
bitflags! {
pub struct LockStatus: u8 {
const SUCCESS = 0;
const BLOCKED = 1;
const ERROR = 2;
const GRACE = 3;
}
}
bitflags! {
#[derive(Default)]
pub struct QidType: u8 {
#[doc = "Type bit for directories"]
const DIR = 0x80;
#[doc = "Type bit for append only files"]
const APPEND = 0x40;
#[doc = "Type bit for exclusive use files"]
const EXCL = 0x20;
#[doc = "Type bit for mounted channel"]
const MOUNT = 0x10;
#[doc = "Type bit for authentication file"]
const AUTH = 0x08;
#[doc = "Type bit for not-backed-up file"]
const TMP = 0x04;
#[doc = "Type bits for symbolic links (9P2000.u)"]
const SYMLINK = 0x02;
#[doc = "Type bits for hard-link (9P2000.u)"]
const LINK = 0x01;
#[doc = "Plain file"]
const FILE = 0x00;
}
}
impl From<::std::fs::FileType> for QidType {
fn from(typ: ::std::fs::FileType) -> Self {
From::from(&typ)
}
}
impl<'a> From<&'a ::std::fs::FileType> for QidType {
fn from(typ: &'a ::std::fs::FileType) -> Self {
let mut qid_type = QidType::FILE;
if typ.is_dir() {
qid_type.insert(QidType::DIR)
}
if typ.is_symlink() {
qid_type.insert(QidType::SYMLINK)
}
qid_type
}
}
bitflags! {
pub struct GetattrMask: u64 {
const MODE = 0x00000001;
const NLINK = 0x00000002;
const UID = 0x00000004;
const GID = 0x00000008;
const RDEV = 0x00000010;
const ATIME = 0x00000020;
const MTIME = 0x00000040;
const CTIME = 0x00000080;
const INO = 0x00000100;
const SIZE = 0x00000200;
const BLOCKS = 0x00000400;
const BTIME = 0x00000800;
const GEN = 0x00001000;
const DATA_VERSION = 0x00002000;
#[doc = "Mask for fields up to BLOCKS"]
const BASIC =0x000007ff;
#[doc = "Mask for All fields above"]
const ALL = 0x00003fff;
}
}
bitflags! {
pub struct SetattrMask: u32 {
const MODE = 0x00000001;
const UID = 0x00000002;
const GID = 0x00000004;
const SIZE = 0x00000008;
const ATIME = 0x00000010;
const MTIME = 0x00000020;
const CTIME = 0x00000040;
const ATIME_SET = 0x00000080;
const MTIME_SET = 0x00000100;
}
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Qid {
pub typ: QidType,
pub version: u32,
pub path: u64,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Statfs {
pub typ: u32,
pub bsize: u32,
pub blocks: u64,
pub bfree: u64,
pub bavail: u64,
pub files: u64,
pub ffree: u64,
pub fsid: u64,
pub namelen: u32,
}
impl From<nix::sys::statvfs::Statvfs> for Statfs {
fn from(buf: nix::sys::statvfs::Statvfs) -> Statfs {
Statfs {
typ: 0,
bsize: buf.block_size() as u32,
blocks: buf.blocks(),
bfree: buf.blocks_free(),
bavail: buf.blocks_available(),
files: buf.files(),
ffree: buf.files_free(),
fsid: buf.filesystem_id() as u64,
namelen: buf.name_max() as u32,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Time {
pub sec: u64,
pub nsec: u64,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Stat {
pub mode: u32,
pub uid: u32,
pub gid: u32,
pub nlink: u64,
pub rdev: u64,
pub size: u64,
pub blksize: u64,
pub blocks: u64,
pub atime: Time,
pub mtime: Time,
pub ctime: Time,
}
impl From<fs::Metadata> for Stat {
fn from(attr: fs::Metadata) -> Self {
From::from(&attr)
}
}
impl<'a> From<&'a fs::Metadata> for Stat {
fn from(attr: &'a fs::Metadata) -> Self {
Stat {
mode: attr.mode(),
uid: attr.uid(),
gid: attr.gid(),
nlink: attr.nlink(),
rdev: attr.rdev(),
size: attr.size() as u64,
blksize: attr.blksize() as u64,
blocks: attr.blocks() as u64,
atime: Time {
sec: attr.atime() as u64,
nsec: attr.atime_nsec() as u64,
},
mtime: Time {
sec: attr.mtime() as u64,
nsec: attr.mtime_nsec() as u64,
},
ctime: Time {
sec: attr.ctime() as u64,
nsec: attr.ctime_nsec() as u64,
},
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SetAttr {
pub mode: u32,
pub uid: u32,
pub gid: u32,
pub size: u64,
pub atime: Time,
pub mtime: Time,
}
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DirEntry {
pub qid: Qid,
pub offset: u64,
pub typ: u8,
pub name: String,
}
impl DirEntry {
pub fn size(&self) -> u32 {
(size_of_val(&self.qid)
+ size_of_val(&self.offset)
+ size_of_val(&self.typ)
+ size_of::<u16>()
+ self.name.len()) as u32
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DirEntryData {
pub data: Vec<DirEntry>,
}
impl DirEntryData {
pub fn new() -> DirEntryData {
Self::with(Vec::new())
}
pub fn with(v: Vec<DirEntry>) -> DirEntryData {
DirEntryData { data: v }
}
pub fn data(&self) -> &[DirEntry] {
&self.data
}
pub fn size(&self) -> u32 {
self.data.iter().fold(0, |a, e| a + e.size()) as u32
}
pub fn push(&mut self, entry: DirEntry) {
self.data.push(entry);
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Data(pub Vec<u8>);
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Flock {
pub typ: LockType,
pub flags: LockFlag,
pub start: u64,
pub length: u64,
pub proc_id: u32,
pub client_id: String,
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Getlock {
pub typ: LockType,
pub start: u64,
pub length: u64,
pub proc_id: u32,
pub client_id: String,
}
enum_from_primitive! {
#[doc = "Message type, 9P operations"]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum MsgType {
Tlerror = 6,
Rlerror,
Tstatfs = 8,
Rstatfs,
Tlopen = 12,
Rlopen,
Tlcreate = 14,
Rlcreate,
Tsymlink = 16,
Rsymlink,
Tmknod = 18,
Rmknod,
Trename = 20,
Rrename,
Treadlink = 22,
Rreadlink,
Tgetattr = 24,
Rgetattr,
Tsetattr = 26,
Rsetattr,
Txattrwalk = 30,
Rxattrwalk,
Txattrcreate = 32,
Rxattrcreate,
Treaddir = 40,
Rreaddir,
Tfsync = 50,
Rfsync,
Tlock = 52,
Rlock,
Tgetlock = 54,
Rgetlock,
Tlink = 70,
Rlink,
Tmkdir = 72,
Rmkdir,
Trenameat = 74,
Rrenameat,
Tunlinkat = 76,
Runlinkat,
Tversion = 100,
Rversion,
Tauth = 102,
Rauth,
Tattach = 104,
Rattach,
Tflush = 108,
Rflush,
Twalk = 110,
Rwalk,
Tread = 116,
Rread,
Twrite = 118,
Rwrite,
Tclunk = 120,
Rclunk,
Tremove = 122,
Rremove,
}
}
impl MsgType {
pub fn is_t(&self) -> bool {
!self.is_r()
}
pub fn is_r(&self) -> bool {
use crate::MsgType::*;
match *self {
Rlerror | Rstatfs | Rlopen | Rlcreate | Rsymlink | Rmknod | Rrename | Rreadlink
| Rgetattr | Rsetattr | Rxattrwalk | Rxattrcreate | Rreaddir | Rfsync | Rlock
| Rgetlock | Rlink | Rmkdir | Rrenameat | Runlinkat | Rversion | Rauth | Rattach
| Rflush | Rwalk | Rread | Rwrite | Rclunk | Rremove => true,
_ => false,
}
}
}
impl<'a> From<&'a Fcall> for MsgType {
fn from(fcall: &'a Fcall) -> MsgType {
match *fcall {
Fcall::Rlerror { .. } => MsgType::Rlerror,
Fcall::Tstatfs { .. } => MsgType::Tstatfs,
Fcall::Rstatfs { .. } => MsgType::Rstatfs,
Fcall::Tlopen { .. } => MsgType::Tlopen,
Fcall::Rlopen { .. } => MsgType::Rlopen,
Fcall::Tlcreate { .. } => MsgType::Tlcreate,
Fcall::Rlcreate { .. } => MsgType::Rlcreate,
Fcall::Tsymlink { .. } => MsgType::Tsymlink,
Fcall::Rsymlink { .. } => MsgType::Rsymlink,
Fcall::Tmknod { .. } => MsgType::Tmknod,
Fcall::Rmknod { .. } => MsgType::Rmknod,
Fcall::Trename { .. } => MsgType::Trename,
Fcall::Rrename => MsgType::Rrename,
Fcall::Treadlink { .. } => MsgType::Treadlink,
Fcall::Rreadlink { .. } => MsgType::Rreadlink,
Fcall::Tgetattr { .. } => MsgType::Tgetattr,
Fcall::Rgetattr { .. } => MsgType::Rgetattr,
Fcall::Tsetattr { .. } => MsgType::Tsetattr,
Fcall::Rsetattr => MsgType::Rsetattr,
Fcall::Txattrwalk { .. } => MsgType::Txattrwalk,
Fcall::Rxattrwalk { .. } => MsgType::Rxattrwalk,
Fcall::Txattrcreate { .. } => MsgType::Txattrcreate,
Fcall::Rxattrcreate => MsgType::Rxattrcreate,
Fcall::Treaddir { .. } => MsgType::Treaddir,
Fcall::Rreaddir { .. } => MsgType::Rreaddir,
Fcall::Tfsync { .. } => MsgType::Tfsync,
Fcall::Rfsync => MsgType::Rfsync,
Fcall::Tlock { .. } => MsgType::Tlock,
Fcall::Rlock { .. } => MsgType::Rlock,
Fcall::Tgetlock { .. } => MsgType::Tgetlock,
Fcall::Rgetlock { .. } => MsgType::Rgetlock,
Fcall::Tlink { .. } => MsgType::Tlink,
Fcall::Rlink => MsgType::Rlink,
Fcall::Tmkdir { .. } => MsgType::Tmkdir,
Fcall::Rmkdir { .. } => MsgType::Rmkdir,
Fcall::Trenameat { .. } => MsgType::Trenameat,
Fcall::Rrenameat => MsgType::Rrenameat,
Fcall::Tunlinkat { .. } => MsgType::Tunlinkat,
Fcall::Runlinkat => MsgType::Runlinkat,
Fcall::Tauth { .. } => MsgType::Tauth,
Fcall::Rauth { .. } => MsgType::Rauth,
Fcall::Tattach { .. } => MsgType::Tattach,
Fcall::Rattach { .. } => MsgType::Rattach,
Fcall::Tversion { .. } => MsgType::Tversion,
Fcall::Rversion { .. } => MsgType::Rversion,
Fcall::Tflush { .. } => MsgType::Tflush,
Fcall::Rflush => MsgType::Rflush,
Fcall::Twalk { .. } => MsgType::Twalk,
Fcall::Rwalk { .. } => MsgType::Rwalk,
Fcall::Tread { .. } => MsgType::Tread,
Fcall::Rread { .. } => MsgType::Rread,
Fcall::Twrite { .. } => MsgType::Twrite,
Fcall::Rwrite { .. } => MsgType::Rwrite,
Fcall::Tclunk { .. } => MsgType::Tclunk,
Fcall::Rclunk => MsgType::Rclunk,
Fcall::Tremove { .. } => MsgType::Tremove,
Fcall::Rremove => MsgType::Rremove,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Fcall {
Rlerror {
ecode: u32,
},
Tstatfs {
fid: u32,
},
Rstatfs {
statfs: Statfs,
},
Tlopen {
fid: u32,
flags: u32,
},
Rlopen {
qid: Qid,
iounit: u32,
},
Tlcreate {
fid: u32,
name: String,
flags: u32,
mode: u32,
gid: u32,
},
Rlcreate {
qid: Qid,
iounit: u32,
},
Tsymlink {
fid: u32,
name: String,
symtgt: String,
gid: u32,
},
Rsymlink {
qid: Qid,
},
Tmknod {
dfid: u32,
name: String,
mode: u32,
major: u32,
minor: u32,
gid: u32,
},
Rmknod {
qid: Qid,
},
Trename {
fid: u32,
dfid: u32,
name: String,
},
Rrename,
Treadlink {
fid: u32,
},
Rreadlink {
target: String,
},
Tgetattr {
fid: u32,
req_mask: GetattrMask,
},
Rgetattr {
valid: GetattrMask,
qid: Qid,
stat: Stat,
},
Tsetattr {
fid: u32,
valid: SetattrMask,
stat: SetAttr,
},
Rsetattr,
Txattrwalk {
fid: u32,
newfid: u32,
name: String,
},
Rxattrwalk {
size: u64,
},
Txattrcreate {
fid: u32,
name: String,
attr_size: u64,
flags: u32,
},
Rxattrcreate,
Treaddir {
fid: u32,
offset: u64,
count: u32,
},
Rreaddir {
data: DirEntryData,
},
Tfsync {
fid: u32,
},
Rfsync,
Tlock {
fid: u32,
flock: Flock,
},
Rlock {
status: LockStatus,
},
Tgetlock {
fid: u32,
flock: Getlock,
},
Rgetlock {
flock: Getlock,
},
Tlink {
dfid: u32,
fid: u32,
name: String,
},
Rlink,
Tmkdir {
dfid: u32,
name: String,
mode: u32,
gid: u32,
},
Rmkdir {
qid: Qid,
},
Trenameat {
olddirfid: u32,
oldname: String,
newdirfid: u32,
newname: String,
},
Rrenameat,
Tunlinkat {
dirfd: u32,
name: String,
flags: u32,
},
Runlinkat,
Tauth {
afid: u32,
uname: String,
aname: String,
n_uname: u32,
},
Rauth {
aqid: Qid,
},
Tattach {
fid: u32,
afid: u32,
uname: String,
aname: String,
n_uname: u32,
},
Rattach {
qid: Qid,
},
Tversion {
msize: u32,
version: String,
},
Rversion {
msize: u32,
version: String,
},
Tflush {
oldtag: u16,
},
Rflush,
Twalk {
fid: u32,
newfid: u32,
wnames: Vec<String>,
},
Rwalk {
wqids: Vec<Qid>,
},
Tread {
fid: u32,
offset: u64,
count: u32,
},
Rread {
data: Data,
},
Twrite {
fid: u32,
offset: u64,
data: Data,
},
Rwrite {
count: u32,
},
Tclunk {
fid: u32,
},
Rclunk,
Tremove {
fid: u32,
},
Rremove,
}
impl Fcall {
pub fn fids(&self) -> Vec<u32> {
match *self {
Fcall::Tstatfs { fid } => vec![fid],
Fcall::Tlopen { fid, .. } => vec![fid],
Fcall::Tlcreate { fid, .. } => vec![fid],
Fcall::Tsymlink { fid, .. } => vec![fid],
Fcall::Tmknod { dfid, .. } => vec![dfid],
Fcall::Trename { fid, dfid, .. } => vec![fid, dfid],
Fcall::Treadlink { fid } => vec![fid],
Fcall::Tgetattr { fid, .. } => vec![fid],
Fcall::Tsetattr { fid, .. } => vec![fid],
Fcall::Txattrwalk { fid, .. } => vec![fid],
Fcall::Txattrcreate { fid, .. } => vec![fid],
Fcall::Treaddir { fid, .. } => vec![fid],
Fcall::Tfsync { fid, .. } => vec![fid],
Fcall::Tlock { fid, .. } => vec![fid],
Fcall::Tgetlock { fid, .. } => vec![fid],
Fcall::Tlink { dfid, fid, .. } => vec![dfid, fid],
Fcall::Tmkdir { dfid, .. } => vec![dfid],
Fcall::Trenameat {
olddirfid,
newdirfid,
..
} => vec![olddirfid, newdirfid],
Fcall::Tunlinkat { dirfd, .. } => vec![dirfd],
Fcall::Tattach { afid, .. } if afid != NOFID => vec![afid],
Fcall::Twalk { fid, .. } => vec![fid],
Fcall::Tread { fid, .. } => vec![fid],
Fcall::Twrite { fid, .. } => vec![fid],
Fcall::Tclunk { fid, .. } => vec![fid],
Fcall::Tremove { fid } => vec![fid],
_ => Vec::new(),
}
}
pub fn newfid(&self) -> Option<u32> {
match *self {
Fcall::Txattrwalk { newfid, .. } => Some(newfid),
Fcall::Tauth { afid, .. } => Some(afid),
Fcall::Tattach { fid, .. } => Some(fid),
Fcall::Twalk { newfid, .. } => Some(newfid),
_ => None,
}
}
pub fn qids(&self) -> Vec<Qid> {
match *self {
Fcall::Rlopen { qid, .. } => vec![qid],
Fcall::Rlcreate { qid, .. } => vec![qid],
Fcall::Rsymlink { qid } => vec![qid],
Fcall::Rmknod { qid } => vec![qid],
Fcall::Rgetattr { qid, .. } => vec![qid],
Fcall::Rmkdir { qid } => vec![qid],
Fcall::Rauth { aqid } => vec![aqid],
Fcall::Rattach { qid } => vec![qid],
Fcall::Rwalk { ref wqids } => wqids.clone(),
_ => Vec::new(),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Msg {
pub tag: u16,
pub body: Fcall,
}