///|
pub(all) struct Timestamp {
seconds : UInt
nanoseconds : UInt
}
///|
pub fn Timestamp::validate(self : Timestamp) -> Unit raise NfsError {
if self.nanoseconds >= 1000000000U {
raise InvalidArgument("nanoseconds must be below one billion")
}
}
///|
fn read_timestamp(r : @xdr.Reader) -> Timestamp raise {
let seconds = r.read_uint()
let nanoseconds = r.read_uint()
if nanoseconds >= 1000000000U {
raise ProtocolError("invalid timestamp nanoseconds")
}
{ seconds, nanoseconds, }
}
///|
fn put_timestamp(w : @xdr.Writer, value : Timestamp) -> Unit raise {
value.validate()
w.write_uint(value.seconds)
w.write_uint(value.nanoseconds)
}
///|
pub(all) struct Metadata {
attributes : Attributes
device_major : UInt
device_minor : UInt
accessed : Timestamp
modified : Timestamp
changed : Timestamp
}
///|
pub(all) enum TimeUpdate {
KeepTime
ServerTime
ExactTime(Timestamp)
}
///|
pub(all) struct AttributeUpdate {
mode : UInt?
uid : UInt?
gid : UInt?
size : UInt64?
accessed : TimeUpdate
modified : TimeUpdate
}
///|
pub fn AttributeUpdate::default() -> AttributeUpdate {
{
mode: None,
uid: None,
gid: None,
size: None,
accessed: KeepTime,
modified: KeepTime,
}
}
///|
pub fn AttributeUpdate::validate(self : AttributeUpdate) -> Unit raise {
if self.mode is Some(mode) && mode > 4095U {
raise InvalidArgument("invalid Unix mode")
}
if self.accessed is ExactTime(time) {
time.validate()
}
if self.modified is ExactTime(time) {
time.validate()
}
}
///|
fn put_optional_uint(w : @xdr.Writer, value : UInt?) -> Unit raise {
w.write_bool(value is Some(_))
if value is Some(number) {
w.write_uint(number)
}
}
///|
fn put_time_update(w : @xdr.Writer, value : TimeUpdate) -> Unit raise {
match value {
KeepTime => w.write_uint(0U)
ServerTime => w.write_uint(1U)
ExactTime(time) => {
w.write_uint(2U)
put_timestamp(w, time)
}
}
}
///|
fn put_attribute_update(w : @xdr.Writer, value : AttributeUpdate) -> Unit raise {
value.validate()
put_optional_uint(w, value.mode)
put_optional_uint(w, value.uid)
put_optional_uint(w, value.gid)
w.write_bool(value.size is Some(_))
if value.size is Some(size) {
w.write_uhyper(size)
}
put_time_update(w, value.accessed)
put_time_update(w, value.modified)
}
///|
pub async fn Client::metadata(self : Client, handle : Handle) -> Metadata {
let w = @xdr.Writer::new()
put_handle(w, handle)
let r = self.invoke(1U, w)
let result = read_metadata(r)
r.finish()
result
}
///|
pub async fn Client::set_attributes(
self : Client,
handle : Handle,
update : AttributeUpdate,
ctime_guard? : Timestamp? = None,
) -> Unit {
let w = @xdr.Writer::new()
put_handle(w, handle)
put_attribute_update(w, update)
w.write_bool(ctime_guard is Some(_))
if ctime_guard is Some(time) {
put_timestamp(w, time)
}
let r = self.invoke(2U, w, modifying=true)
self.decode_mutation(r, reader => {
read_wcc(reader)
reader.finish()
})
}
///|
pub async fn Client::chmod(self : Client, path : String, mode : UInt) -> Unit {
let update = AttributeUpdate::{
..AttributeUpdate::default(),
mode: Some(mode),
}
update.validate()
self.set_attributes(self.resolve(path), update)
}
///|
pub async fn Client::chown(
self : Client,
path : String,
uid : UInt?,
gid : UInt?,
) -> Unit {
self.set_attributes(self.resolve(path), {
..AttributeUpdate::default(),
uid,
gid,
})
}
///|
pub async fn Client::truncate(
self : Client,
path : String,
size : UInt64,
) -> Unit {
self.set_attributes(self.resolve(path), {
..AttributeUpdate::default(),
size: Some(size),
})
}
///|
pub async fn Client::touch(self : Client, path : String) -> Unit {
self.set_attributes(self.resolve(path), {
..AttributeUpdate::default(),
accessed: ServerTime,
modified: ServerTime,
})
}
///|
pub fn file_kind_name(kind : UInt) -> String {
match kind {
1U => "file"
2U => "directory"
3U => "block-device"
4U => "character-device"
5U => "symlink"
6U => "socket"
7U => "fifo"
_ => "unknown"
}
}
///|
pub fn permission_string(kind : UInt, mode : UInt) -> String {
let prefix = match kind {
1U => "-"
2U => "d"
3U => "b"
4U => "c"
5U => "l"
6U => "s"
7U => "p"
_ => "?"
}
let parts = [prefix]
for shift in [6, 3, 0] {
parts.push(if (mode & (4U << shift)) != 0U { "r" } else { "-" })
parts.push(if (mode & (2U << shift)) != 0U { "w" } else { "-" })
let execute = (mode & (1U << shift)) != 0U
let special = mode &
(if shift == 6 { 2048U } else if shift == 3 { 1024U } else { 512U })
parts.push(
if special != 0U {
if shift == 0 {
if execute {
"t"
} else {
"T"
}
} else if execute {
"s"
} else {
"S"
}
} else if execute {
"x"
} else {
"-"
},
)
}
parts.join("")
}
///|
pub fn status_name(code : UInt) -> String {
match code {
0U => "OK"
1U => "PERM"
2U => "NOENT"
5U => "IO"
6U => "NXIO"
13U => "ACCES"
17U => "EXIST"
18U => "XDEV"
19U => "NODEV"
20U => "NOTDIR"
21U => "ISDIR"
22U => "INVAL"
27U => "FBIG"
28U => "NOSPC"
30U => "ROFS"
31U => "MLINK"
63U => "NAMETOOLONG"
66U => "NOTEMPTY"
69U => "DQUOT"
70U => "STALE"
71U => "REMOTE"
10001U => "BADHANDLE"
10002U => "NOT_SYNC"
10003U => "BAD_COOKIE"
10004U => "NOTSUPP"
10005U => "TOOSMALL"
10006U => "SERVERFAULT"
10007U => "BADTYPE"
10008U => "JUKEBOX"
_ => "UNKNOWN"
}
}