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)]
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! {
#[derive(Copy, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct LockType: u8 {
const RDLOCK = 0;
const WRLOCK = 1;
const UNLOCK = 2;
}
}
bitflags! {
#[derive(Copy, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct LockFlag: u32 {
#[doc = "Blocking request"]
const BLOCK = 1;
#[doc = "Reserved for future use"]
const RECLAIM = 2;
}
}
bitflags! {
#[derive(Copy, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct LockStatus: u8 {
const SUCCESS = 0;
const BLOCKED = 1;
const ERROR = 2;
const GRACE = 3;
}
}
bitflags! {
#[derive(Copy, Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
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! {
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
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! {
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
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)]
pub struct QId {
pub typ: QIdType,
pub version: u32,
pub path: u64,
}
impl QId {
pub fn size(&self) -> u32 {
(size_of::<QIdType>() + size_of::<u32>() + size_of::<u64>()) as u32
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
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(),
namelen: buf.name_max() as u32,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Time {
pub sec: u64,
pub nsec: u64,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
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(),
blksize: attr.blksize(),
blocks: attr.blocks(),
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)]
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)]
pub struct DirEntry {
pub qid: QId,
pub offset: u64,
pub typ: u8,
pub name: String,
}
impl DirEntry {
pub fn size(&self) -> u32 {
(self.qid.size() as usize
+ 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)]
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())
}
pub fn push(&mut self, entry: DirEntry) {
self.data.push(entry);
}
}
impl Default for DirEntryData {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Data(pub Vec<u8>);
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
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)]
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)]
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::*;
matches!(
*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
)
}
}
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)]
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)]
pub struct Msg {
pub tag: u16,
pub body: FCall,
}