use alloc::string::String;
use core::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum NfsStatus {
Ok = 0,
Perm = 1,
Noent = 2,
Io = 5,
Nxio = 6,
Acces = 13,
Exist = 17,
Xdev = 18,
Notdir = 20,
Isdir = 21,
Inval = 22,
Fbig = 27,
Nospc = 28,
Rofs = 30,
Mlink = 31,
Nametoolong = 63,
Notempty = 66,
Dquot = 69,
Stale = 70,
Remote = 71,
Badhandle = 10001,
NotSync = 10002,
BadCookie = 10003,
Notsupp = 10004,
Toosmall = 10005,
Serverfault = 10006,
Badtype = 10007,
Delay = 10008,
Same = 10009,
Denied = 10010,
Resource = 10011,
Badxdr = 10036,
Locked = 10012,
Grace = 10013,
FhExpired = 10014,
ShareDenied = 10015,
Wrongsec = 10016,
ClidInuse = 10017,
Moved = 10019,
NoGrace = 10020,
ReclaimBad = 10021,
ReclaimConflict = 10022,
ReqTooBig = 10023,
BadSeqid = 10026,
NotSame = 10027,
LockRange = 10028,
Symlink = 10029,
Expired = 10035,
LockNotsupp = 10043,
OldStateid = 10024,
BadStateid = 10025,
AdminRevoked = 10047,
OpNotInSession = 10071,
ConnNotBoundToSession = 10055,
WrongCred = 10082,
WrongType = 10083,
DirDelegUnavail = 10084,
Reject = 10085,
Returnconflict = 10086,
DelegAlreadyWanted = 10087,
BadHighSlot = 10077,
Retry = 10049,
SeqMisordered = 10063,
SeqFalseRetry = 10076,
Nofilehandle = 10040,
BadSession = 10052,
Unknown = 0xFFFFFFFF,
}
impl NfsStatus {
pub fn from_u32(v: u32) -> Self {
match v {
0 => Self::Ok,
1 => Self::Perm,
2 => Self::Noent,
5 => Self::Io,
6 => Self::Nxio,
13 => Self::Acces,
17 => Self::Exist,
18 => Self::Xdev,
20 => Self::Notdir,
21 => Self::Isdir,
22 => Self::Inval,
27 => Self::Fbig,
28 => Self::Nospc,
30 => Self::Rofs,
31 => Self::Mlink,
63 => Self::Nametoolong,
66 => Self::Notempty,
69 => Self::Dquot,
70 => Self::Stale,
71 => Self::Remote,
10001 => Self::Badhandle,
10002 => Self::NotSync,
10003 => Self::BadCookie,
10004 => Self::Notsupp,
10005 => Self::Toosmall,
10006 => Self::Serverfault,
10007 => Self::Badtype,
10008 => Self::Delay,
10009 => Self::Same,
10010 => Self::Denied,
10011 => Self::Resource,
10012 => Self::Locked,
10013 => Self::Grace,
10014 => Self::FhExpired,
10015 => Self::ShareDenied,
10016 => Self::Wrongsec,
10017 => Self::ClidInuse,
10019 => Self::Moved,
10020 => Self::NoGrace,
10021 => Self::ReclaimBad,
10022 => Self::ReclaimConflict,
10023 => Self::ReqTooBig,
10024 => Self::OldStateid,
10025 => Self::BadStateid,
10026 => Self::BadSeqid,
10027 => Self::NotSame,
10028 => Self::LockRange,
10029 => Self::Symlink,
10035 => Self::Expired,
10036 => Self::Badxdr,
10043 => Self::LockNotsupp,
10047 => Self::AdminRevoked,
10049 => Self::Retry,
10055 => Self::ConnNotBoundToSession,
10063 => Self::SeqMisordered,
10071 => Self::OpNotInSession,
10076 => Self::SeqFalseRetry,
10077 => Self::BadHighSlot,
10082 => Self::WrongCred,
10083 => Self::WrongType,
10084 => Self::DirDelegUnavail,
10085 => Self::Reject,
10086 => Self::Returnconflict,
10087 => Self::DelegAlreadyWanted,
10040 => Self::Nofilehandle,
10052 => Self::BadSession,
_ => Self::Unknown,
}
}
pub fn code(&self) -> u32 {
*self as u32
}
pub fn is_ok(&self) -> bool {
matches!(self, Self::Ok)
}
pub fn is_err(&self) -> bool {
!self.is_ok()
}
pub fn message(&self) -> &'static str {
match self {
Self::Ok => "No error",
Self::Perm => "Permission denied",
Self::Noent => "No such file or directory",
Self::Io => "I/O error",
Self::Nxio => "No such device or address",
Self::Acces => "Permission denied",
Self::Exist => "File exists",
Self::Xdev => "Cross-device link",
Self::Notdir => "Not a directory",
Self::Isdir => "Is a directory",
Self::Inval => "Invalid argument",
Self::Fbig => "File too large",
Self::Nospc => "No space left on device",
Self::Rofs => "Read-only file system",
Self::Mlink => "Too many hard links",
Self::Nametoolong => "File name too long",
Self::Notempty => "Directory not empty",
Self::Dquot => "Disk quota exceeded",
Self::Stale => "Stale file handle",
Self::Remote => "Remote I/O error",
Self::Badhandle => "Bad file handle",
Self::NotSync => "Update synchronization mismatch",
Self::BadCookie => "READDIR cookie is stale",
Self::Notsupp => "Operation not supported",
Self::Toosmall => "Buffer or request is too small",
Self::Serverfault => "Server fault",
Self::Badtype => "Type not supported",
Self::Delay => "Delay occurred during processing",
Self::Same => "File is the same as source",
Self::Denied => "Lock denied",
Self::Resource => "Server ran out of resources",
Self::Badxdr => "XDR decode error",
Self::Locked => "Lock held by another",
Self::Grace => "Lock grace period in effect",
Self::FhExpired => "File handle expired",
Self::ShareDenied => "Share reservation conflict",
Self::Wrongsec => "Wrong security flavor",
Self::ClidInuse => "Client ID in use",
Self::Moved => "Operation moved to different server",
Self::NoGrace => "No matching grant for LOCKT",
Self::ReclaimBad => "Reclaim outside of grace period",
Self::ReclaimConflict => "Reclaim conflict",
Self::ReqTooBig => "Request too big",
Self::BadSeqid => "Bad sequence number",
Self::NotSame => "Not the same client",
Self::LockRange => "Lock range not supported",
Self::Symlink => "Symlink too many levels",
Self::Expired => "Lease expired",
Self::LockNotsupp => "Lock not supported",
Self::OldStateid => "Old state ID",
Self::BadStateid => "Bad state ID",
Self::AdminRevoked => "Admin revoked the state",
Self::OpNotInSession => "Operation not in session",
Self::ConnNotBoundToSession => "Connection not bound to session",
Self::WrongCred => "Wrong credential",
Self::WrongType => "Wrong type",
Self::DirDelegUnavail => "Dir delegation unavailable",
Self::Reject => "Reject",
Self::Returnconflict => "Return conflict",
Self::DelegAlreadyWanted => "Delegation already wanted",
Self::BadHighSlot => "Bad high slot",
Self::Retry => "Retry",
Self::SeqMisordered => "Sequence misordered",
Self::SeqFalseRetry => "Sequence false retry",
Self::Nofilehandle => "No file handle",
Self::BadSession => "Bad session",
Self::Unknown => "Unknown error",
}
}
}
impl fmt::Display for NfsStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.message())
}
}
#[derive(Debug, Clone)]
pub struct NfsError {
pub status: NfsStatus,
pub message: Option<String>,
}
impl NfsError {
pub fn new(status: NfsStatus) -> Self {
Self {
status,
message: None,
}
}
pub fn with_message(status: NfsStatus, msg: impl Into<String>) -> Self {
Self {
status,
message: Some(msg.into()),
}
}
pub fn perm() -> Self {
Self::new(NfsStatus::Perm)
}
pub fn noent() -> Self {
Self::new(NfsStatus::Noent)
}
pub fn io() -> Self {
Self::new(NfsStatus::Io)
}
pub fn access() -> Self {
Self::new(NfsStatus::Acces)
}
pub fn exist() -> Self {
Self::new(NfsStatus::Exist)
}
pub fn notdir() -> Self {
Self::new(NfsStatus::Notdir)
}
pub fn isdir() -> Self {
Self::new(NfsStatus::Isdir)
}
pub fn inval() -> Self {
Self::new(NfsStatus::Inval)
}
pub fn nospc() -> Self {
Self::new(NfsStatus::Nospc)
}
pub fn rofs() -> Self {
Self::new(NfsStatus::Rofs)
}
pub fn nametoolong() -> Self {
Self::new(NfsStatus::Nametoolong)
}
pub fn notempty() -> Self {
Self::new(NfsStatus::Notempty)
}
pub fn stale() -> Self {
Self::new(NfsStatus::Stale)
}
pub fn badhandle() -> Self {
Self::new(NfsStatus::Badhandle)
}
pub fn notsupp() -> Self {
Self::new(NfsStatus::Notsupp)
}
pub fn serverfault() -> Self {
Self::new(NfsStatus::Serverfault)
}
pub fn badxdr() -> Self {
Self::new(NfsStatus::Badxdr)
}
pub fn locked() -> Self {
Self::new(NfsStatus::Locked)
}
pub fn bad_stateid() -> Self {
Self::new(NfsStatus::BadStateid)
}
pub fn code(&self) -> u32 {
self.status.code()
}
}
impl fmt::Display for NfsError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(ref msg) = self.message {
write!(
f,
"NFS error {}: {} ({})",
self.status.code(),
self.status.message(),
msg
)
} else {
write!(
f,
"NFS error {}: {}",
self.status.code(),
self.status.message()
)
}
}
}
impl From<NfsStatus> for NfsError {
fn from(status: NfsStatus) -> Self {
Self::new(status)
}
}
pub type NfsResult<T> = core::result::Result<T, NfsError>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_nfs_status_from_u32() {
assert_eq!(NfsStatus::from_u32(0), NfsStatus::Ok);
assert_eq!(NfsStatus::from_u32(2), NfsStatus::Noent);
assert_eq!(NfsStatus::from_u32(10001), NfsStatus::Badhandle);
assert_eq!(NfsStatus::from_u32(99999), NfsStatus::Unknown);
}
#[test]
fn test_nfs_status_code() {
assert_eq!(NfsStatus::Ok.code(), 0);
assert_eq!(NfsStatus::Noent.code(), 2);
assert_eq!(NfsStatus::Badhandle.code(), 10001);
}
#[test]
fn test_nfs_status_is_ok() {
assert!(NfsStatus::Ok.is_ok());
assert!(!NfsStatus::Noent.is_ok());
assert!(NfsStatus::Noent.is_err());
}
#[test]
fn test_nfs_status_message() {
assert_eq!(NfsStatus::Ok.message(), "No error");
assert_eq!(NfsStatus::Noent.message(), "No such file or directory");
}
#[test]
fn test_nfs_error_creation() {
let err = NfsError::new(NfsStatus::Noent);
assert_eq!(err.status, NfsStatus::Noent);
assert!(err.message.is_none());
let err2 = NfsError::with_message(NfsStatus::Io, "disk failure");
assert_eq!(err2.status, NfsStatus::Io);
assert_eq!(err2.message, Some("disk failure".into()));
}
#[test]
fn test_nfs_error_convenience() {
assert_eq!(NfsError::noent().status, NfsStatus::Noent);
assert_eq!(NfsError::stale().status, NfsStatus::Stale);
assert_eq!(NfsError::inval().status, NfsStatus::Inval);
}
#[test]
fn test_nfs_error_from_status() {
let err: NfsError = NfsStatus::Perm.into();
assert_eq!(err.status, NfsStatus::Perm);
}
#[test]
fn test_nfs_error_display() {
let err = NfsError::new(NfsStatus::Noent);
let msg = alloc::format!("{}", err);
assert!(msg.contains("No such file or directory"));
let err2 = NfsError::with_message(NfsStatus::Io, "disk read failed");
let msg2 = alloc::format!("{}", err2);
assert!(msg2.contains("disk read failed"));
}
}