use lawn_constants::Error;
use num_derive::FromPrimitive;
use std::any::Any;
use std::fs;
#[cfg(feature = "unix")]
use std::os::unix::fs::FileTypeExt;
use std::time::SystemTime;
#[cfg(feature = "unix")]
pub mod libc;
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum QIDKind {
Directory = 0o04,
Regular = 0o10,
FIFO = 0o01,
Symlink = 0o12,
BlockDevice = 0o06,
CharacterDevice = 0o02,
Socket = 0o14,
Authentication = 0xfe,
Unknown = 0xff,
}
impl QIDKind {
#[cfg(unix)]
fn from_filetype(ft: rustix::fs::FileType) -> Self {
match ft {
rustix::fs::FileType::Socket => Self::Socket,
rustix::fs::FileType::Symlink => Self::Symlink,
rustix::fs::FileType::BlockDevice => Self::BlockDevice,
rustix::fs::FileType::CharacterDevice => Self::CharacterDevice,
rustix::fs::FileType::Fifo => Self::FIFO,
rustix::fs::FileType::Directory => Self::Directory,
rustix::fs::FileType::RegularFile => Self::Regular,
_ => Self::Unknown,
}
}
#[cfg(unix)]
fn from_metadata(metadata: &std::fs::Metadata) -> Self {
let ft = metadata.file_type();
if ft.is_fifo() {
Self::FIFO
} else if ft.is_socket() {
Self::Socket
} else if ft.is_block_device() {
Self::BlockDevice
} else if ft.is_char_device() {
Self::CharacterDevice
} else if ft.is_dir() {
Self::Directory
} else if ft.is_symlink() {
Self::Symlink
} else {
Self::Unknown
}
}
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub struct QID {
kind: QIDKind,
dev: u64,
ino: u64,
}
impl Default for QID {
fn default() -> QID {
Self {
kind: QIDKind::Unknown,
dev: u64::MAX,
ino: u64::MAX,
}
}
}
impl QID {
pub fn new(kind: QIDKind, dev: u64, ino: u64) -> QID {
Self { kind, dev, ino }
}
pub fn kind(&self) -> QIDKind {
self.kind
}
pub fn dev(&self) -> u64 {
self.dev
}
pub fn ino(&self) -> u64 {
self.ino
}
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub struct FID {
id: u64,
}
impl FID {
pub const fn new(id: u64) -> FID {
Self { id }
}
pub const fn to_u64(&self) -> u64 {
self.id
}
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub struct Tag {
id: u64,
}
impl Tag {
pub const fn new(id: u64) -> Tag {
Self { id }
}
pub const fn to_u64(&self) -> u64 {
self.id
}
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
#[non_exhaustive]
pub enum Plan9Type {
Original,
Unix,
Linux,
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
#[non_exhaustive]
pub enum SFTPType {
V3,
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
#[non_exhaustive]
pub enum SFTPExtensions {
OpenSSHPosixRenameV1,
OpenSSHReversedSymlink,
OpenSSHHardlinkV1,
OpenSSHFsyncV1,
}
#[derive(Copy, Clone, Debug)]
pub enum ProtocolType<'a> {
Plan9(Plan9Type),
SFTP(SFTPType, &'a [SFTPExtensions]),
Other(Option<&'a dyn Any>),
}
#[derive(Clone, Debug)]
pub struct Metadata<'a> {
tag: Tag,
command: u64,
proto: ProtocolType<'a>,
proto_extra: Option<u128>,
efficient: bool,
}
impl<'a> Metadata<'a> {
pub fn new(
tag: Tag,
command: u64,
proto: ProtocolType<'a>,
proto_extra: Option<u128>,
efficient: bool,
) -> Metadata<'a> {
Self {
tag,
command,
proto,
proto_extra,
efficient,
}
}
pub fn command(&self) -> u64 {
self.command
}
pub fn tag(&self) -> Tag {
self.tag
}
pub fn protocol(&self) -> ProtocolType<'a> {
self.proto
}
pub fn protocol_extra_data(&self) -> Option<u128> {
self.proto_extra
}
pub fn efficient(&self) -> bool {
self.efficient
}
fn needs_valid_qid(&self) -> bool {
self.efficient
}
}
type Result<T> = std::result::Result<T, Error>;
bitflags! {
pub struct OpenMode: u32 {
const O_RDONLY = 0x00;
const O_WRONLY = 0x01;
const O_RDWR = 0x02;
const O_ACCMODE = 0x03;
const O_CREAT = 0o100;
const O_EXCL = 0o200;
const O_NOCTTY = 0o400;
const O_TRUNC = 0o1000;
const O_APPEND = 0o2000;
const O_NONBLOCK = 0o4000;
const O_LARGEFILE = 0o100000;
const O_DIRECTORY = 0o200000;
const O_NOFOLLOW = 0o400000;
}
pub struct FileType: u32 {
const S_IFMT = 0o170000;
const S_IFDIR = 0o040000;
const S_IFCHR = 0o020000;
const S_IFBLK = 0o060000;
const S_IFREG = 0o100000;
const S_IFIFO = 0o010000;
const S_IFLNK = 0o120000;
const S_IFSOCK = 0o140000;
const S_ISUID = 0o4000;
const S_ISGID = 0o2000;
const S_ISVTX = 0o1000;
const S_IRUSR = 0o0400;
const S_IWUSR = 0o0200;
const S_IXUSR = 0o0100;
const S_IRGRP = 0o0040;
const S_IWGRP = 0o0020;
const S_IXGRP = 0o0010;
const S_IROTH = 0o0004;
const S_IWOTH = 0o0002;
const S_IXOTH = 0o0001;
}
pub struct StatValidity: 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 INODE = 0x00000100;
const SIZE = 0x00000200;
const BLOCKS = 0x00000400;
const BTIME = 0x00000800;
const GEN = 0x00001000;
const DATA_VERSION = 0x00002000;
const BASIC = 0x000007ff;
const ALL = 0x00003fff;
}
}
impl OpenMode {
#[cfg(feature = "unix")]
fn to_unix(self) -> rustix::fs::OFlags {
let mut val = rustix::fs::OFlags::empty();
if self.contains(Self::O_RDONLY) {
val |= rustix::fs::OFlags::RDONLY;
}
if self.contains(Self::O_WRONLY) {
val |= rustix::fs::OFlags::WRONLY;
}
if self.contains(Self::O_RDWR) {
val |= rustix::fs::OFlags::RDWR;
}
if self.contains(Self::O_ACCMODE) {
val |= rustix::fs::OFlags::ACCMODE;
}
if self.contains(Self::O_CREAT) {
val |= rustix::fs::OFlags::CREATE;
}
if self.contains(Self::O_EXCL) {
val |= rustix::fs::OFlags::EXCL;
}
if self.contains(Self::O_NOCTTY) {
val |= rustix::fs::OFlags::NOCTTY;
}
if self.contains(Self::O_TRUNC) {
val |= rustix::fs::OFlags::TRUNC;
}
if self.contains(Self::O_APPEND) {
val |= rustix::fs::OFlags::APPEND;
}
if self.contains(Self::O_NONBLOCK) {
val |= rustix::fs::OFlags::NONBLOCK;
}
if self.contains(Self::O_DIRECTORY) {
val |= rustix::fs::OFlags::DIRECTORY;
}
if self.contains(Self::O_NOFOLLOW) {
val |= rustix::fs::OFlags::NOFOLLOW;
}
val
}
}
impl FileType {
#[cfg(feature = "unix")]
fn from_unix(mode: u32) -> Self {
let kind = match rustix::fs::FileType::from_raw_mode(mode as rustix::fs::RawMode) {
rustix::fs::FileType::Socket => Self::S_IFSOCK,
rustix::fs::FileType::Symlink => Self::S_IFLNK,
rustix::fs::FileType::BlockDevice => Self::S_IFBLK,
rustix::fs::FileType::CharacterDevice => Self::S_IFCHR,
rustix::fs::FileType::Fifo => Self::S_IFIFO,
rustix::fs::FileType::Directory => Self::S_IFDIR,
rustix::fs::FileType::RegularFile => Self::S_IFREG,
_ => Self::from_bits(0).unwrap(),
};
let dmode = Self::from_bits(mode & 0o7777).unwrap();
kind | dmode
}
#[allow(dead_code)]
#[cfg(feature = "unix")]
fn to_unix(self) -> rustix::fs::RawMode {
let ft = match self & Self::S_IFMT {
Self::S_IFSOCK => rustix::fs::FileType::Socket,
Self::S_IFLNK => rustix::fs::FileType::Symlink,
Self::S_IFBLK => rustix::fs::FileType::BlockDevice,
Self::S_IFCHR => rustix::fs::FileType::CharacterDevice,
Self::S_IFIFO => rustix::fs::FileType::Fifo,
Self::S_IFDIR => rustix::fs::FileType::Directory,
Self::S_IFREG => rustix::fs::FileType::RegularFile,
_ => rustix::fs::FileType::Unknown,
};
ft.as_raw_mode() | (self.bits() & 0o7777) as rustix::fs::RawMode
}
}
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub struct Stat {
pub qid: QID,
pub mode: u32,
pub uid: u32,
pub gid: u32,
pub nlink: Option<u64>,
pub rdev: Option<u64>,
pub length: u64,
pub blksize: u64,
pub blocks: Option<u64>,
pub atime_sec: Option<u64>,
pub atime_nsec: Option<u64>,
pub mtime_sec: Option<u64>,
pub mtime_nsec: Option<u64>,
pub ctime_sec: Option<u64>,
pub ctime_nsec: Option<u64>,
pub btime_sec: Option<u64>,
pub btime_nsec: Option<u64>,
pub gen: Option<u64>,
pub data_version: Option<u64>,
}
impl Stat {
#[allow(clippy::unnecessary_cast)]
#[cfg(all(feature = "unix", not(target_os = "netbsd")))]
fn from_unix(meta: &rustix::fs::Stat) -> Self {
let mode = FileType::from_unix(meta.st_mode as u32);
Self {
qid: QID::new(QIDKind::Unknown, u64::MAX, u64::MAX),
mode: mode.bits(),
uid: meta.st_uid as u32,
gid: meta.st_gid as u32,
nlink: Some(meta.st_nlink as u64),
rdev: Some(meta.st_rdev as u64),
length: meta.st_size as u64,
blksize: meta.st_blksize as u64,
blocks: Some(meta.st_blocks as u64),
atime_sec: Some(meta.st_atime as u64),
atime_nsec: Some(meta.st_atime_nsec as u64),
mtime_sec: Some(meta.st_mtime as u64),
mtime_nsec: Some(meta.st_mtime_nsec as u64),
ctime_sec: Some(meta.st_ctime as u64),
ctime_nsec: Some(meta.st_ctime_nsec as u64),
btime_sec: None,
btime_nsec: None,
gen: None,
data_version: None,
}
}
#[allow(clippy::unnecessary_cast)]
#[cfg(all(feature = "unix", target_os = "netbsd"))]
fn from_unix(meta: &rustix::fs::Stat) -> Self {
let mode = FileType::from_unix(meta.st_mode as u32);
Self {
qid: QID::new(QIDKind::Unknown, u64::MAX, u64::MAX),
mode: mode.bits(),
uid: meta.st_uid as u32,
gid: meta.st_gid as u32,
nlink: Some(meta.st_nlink as u64),
rdev: Some(meta.st_rdev as u64),
length: meta.st_size as u64,
blksize: meta.st_blksize as u64,
blocks: Some(meta.st_blocks as u64),
atime_sec: Some(meta.st_atime as u64),
atime_nsec: Some(meta.st_atimensec as u64),
mtime_sec: Some(meta.st_mtime as u64),
mtime_nsec: Some(meta.st_mtimensec as u64),
ctime_sec: Some(meta.st_ctime as u64),
ctime_nsec: Some(meta.st_ctimensec as u64),
btime_sec: None,
btime_nsec: None,
gen: None,
data_version: None,
}
}
}
#[derive(FromPrimitive, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum LockKind {
Read = 0,
Write = 1,
}
#[derive(FromPrimitive, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum LockCommand {
ReadLock = 0,
WriteLock = 1,
Unlock = 2,
}
impl From<LockKind> for LockCommand {
fn from(kind: LockKind) -> Self {
match kind {
LockKind::Read => Self::ReadLock,
LockKind::Write => Self::WriteLock,
}
}
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum LockStatus {
Ok = 0,
Blocked = 1,
Error = 2,
Grace = 3,
}
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub struct Lock {
pub kind: LockKind,
pub start: u64,
pub length: u64,
pub proc_id: u32,
pub client_id: Vec<u8>,
}
#[derive(Clone, Debug)]
pub struct DirEntry {
pub qid: QID,
pub offset: u64,
pub kind: u8,
pub name: Vec<u8>,
pub extension: Option<Vec<u8>>,
pub file_type: FileType,
pub size: u64,
pub metadata: fs::Metadata,
}
pub trait Backend {
fn auth(
&self,
meta: &Metadata,
afid: FID,
uname: &[u8],
aname: &[u8],
nuname: Option<u32>,
) -> Result<QID>;
fn attach(
&self,
meta: &Metadata,
fid: FID,
afid: Option<FID>,
uname: &[u8],
aname: &[u8],
nuname: Option<u32>,
) -> Result<QID>;
fn clunk(&self, meta: &Metadata, fid: FID) -> Result<()>;
fn clunk_all(&self, meta: &Metadata) -> Result<()>;
fn flush(&self, meta: &Metadata, tag: Tag) -> Result<()>;
fn open(&self, meta: &Metadata, fid: FID, mode: OpenMode) -> Result<(QID, Option<u32>)>;
fn is_open(&self, meta: &Metadata, fid: FID) -> Result<bool>;
#[allow(clippy::too_many_arguments)]
fn create(
&self,
meta: &Metadata,
fid: FID,
newfid: FID,
name: &[u8],
flags: OpenMode,
mode: FileType,
gid: Option<u32>,
) -> Result<(QID, Option<u32>)>;
fn read(&self, meta: &Metadata, fid: FID, offset: u64, data: &mut [u8]) -> Result<u32>;
fn write(&self, meta: &Metadata, fid: FID, offset: u64, data: &[u8]) -> Result<u32>;
fn remove(&self, meta: &Metadata, fid: FID) -> Result<()>;
fn fsync(&self, meta: &Metadata, fid: FID);
fn walk(&self, meta: &Metadata, fid: FID, newfid: FID, name: &[&[u8]]) -> Result<Vec<QID>>;
fn resolve(&self, meta: &Metadata, fid: FID, newfid: FID, name: &[&[u8]]) -> Result<()>;
fn symlink(
&self,
meta: &Metadata,
fid: FID,
name: &[u8],
target: &[u8],
gid: Option<u32>,
) -> Result<QID>;
#[allow(clippy::too_many_arguments)]
fn mknod(
&self,
meta: &Metadata,
fid: FID,
name: &[u8],
mode: FileType,
major: u32,
minor: u32,
gid: Option<u32>,
) -> Result<QID>;
fn rename(&self, meta: &Metadata, fid: FID, dfid: FID, name: &[u8]) -> Result<()>;
fn readlink(&self, meta: &Metadata, fid: FID) -> Result<Vec<u8>>;
fn realpath(&self, meta: &Metadata, fid: FID) -> Result<Vec<Vec<u8>>>;
fn pathname(&self, meta: &Metadata, fid: FID) -> Result<Vec<Vec<u8>>>;
fn getattr(&self, meta: &Metadata, fid: FID, mask: StatValidity) -> Result<Stat>;
#[allow(clippy::too_many_arguments)]
fn setattr(
&self,
meta: &Metadata,
fid: FID,
mode: Option<u32>,
uid: Option<u32>,
gid: Option<u32>,
size: Option<u64>,
atime: Option<SystemTime>,
mtime: Option<SystemTime>,
set_atime: bool,
set_mtime: bool,
) -> Result<()>;
fn xattrwalk(&self, meta: &Metadata, fid: FID, newfid: FID, name: &[u8]) -> Result<u64>;
fn xattrcreate(
&self,
meta: &Metadata,
fid: FID,
name: &[u8],
size: u64,
flags: u32,
) -> Result<()>;
fn readdir(
&self,
meta: &Metadata,
fid: FID,
offset: u64,
count: u32,
offsetf: Box<dyn FnMut(&DirEntry) -> usize>,
lenf: Box<dyn FnMut(&DirEntry) -> usize>,
) -> Result<Vec<DirEntry>>;
#[allow(clippy::too_many_arguments)]
fn lock(
&self,
meta: &Metadata,
fid: FID,
kind: LockCommand,
flags: u32,
start: u64,
length: u64,
proc_id: u32,
client_id: &[u8],
) -> Result<LockStatus>;
#[allow(clippy::too_many_arguments)]
fn getlock(
&self,
meta: &Metadata,
fid: FID,
kind: LockKind,
start: u64,
length: u64,
proc_id: u32,
client_id: &[u8],
) -> Result<Lock>;
fn link(&self, meta: &Metadata, dfid: FID, fid: FID, nane: &[u8]) -> Result<()>;
fn mkdir(
&self,
meta: &Metadata,
dfid: FID,
name: &[u8],
mode: FileType,
gid: Option<u32>,
) -> Result<QID>;
fn renameat(
&self,
meta: &Metadata,
olddirfid: FID,
oldname: &[u8],
newdirfid: FID,
newname: &[u8],
) -> Result<()>;
fn unlinkat(&self, meta: &Metadata, dirfd: FID, name: &[u8], flags: u32) -> Result<()>;
}