use crate::ll::{
self,
reply::{DirEntPlusList, DirEntryPlus},
Generation,
};
use crate::ll::{
reply::{DirEntList, DirEntOffset, DirEntry},
INodeNo,
};
use libc::c_int;
use log::{error, warn};
use std::convert::AsRef;
use std::ffi::OsStr;
use std::fmt;
use std::io::IoSlice;
use std::time::Duration;
#[cfg(target_os = "macos")]
use std::time::SystemTime;
use crate::{FileAttr, FileType};
pub trait ReplySender: Send + Sync + Unpin + 'static {
fn send(&self, data: &[IoSlice<'_>]) -> std::io::Result<()>;
}
impl fmt::Debug for Box<dyn ReplySender> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "Box<ReplySender>")
}
}
pub trait Reply {
fn new<S: ReplySender>(unique: u64, sender: S) -> Self;
}
#[derive(Debug)]
pub(crate) struct ReplyRaw {
unique: ll::RequestId,
sender: Option<Box<dyn ReplySender>>,
}
impl Reply for ReplyRaw {
fn new<S: ReplySender>(unique: u64, sender: S) -> ReplyRaw {
let sender = Box::new(sender);
ReplyRaw {
unique: ll::RequestId(unique),
sender: Some(sender),
}
}
}
impl ReplyRaw {
fn send_ll_mut(&mut self, response: &ll::Response) {
assert!(self.sender.is_some());
let sender = self.sender.take().unwrap();
let res = response.with_iovec(self.unique, |iov| sender.send(iov));
if let Err(err) = res {
error!("Failed to send FUSE reply: {}", err);
}
}
fn send_ll(mut self, response: &ll::Response) {
self.send_ll_mut(response)
}
pub fn error(self, err: c_int) {
assert_ne!(err, 0);
self.send_ll(&ll::Response::new_error(ll::Errno::from_i32(err)));
}
}
impl Drop for ReplyRaw {
fn drop(&mut self) {
if self.sender.is_some() {
warn!(
"Reply not sent for operation {}, replying with I/O error",
self.unique.0
);
self.send_ll_mut(&ll::Response::new_error(ll::Errno::EIO));
}
}
}
#[derive(Debug)]
pub struct ReplyEmpty {
reply: ReplyRaw,
}
impl Reply for ReplyEmpty {
fn new<S: ReplySender>(unique: u64, sender: S) -> ReplyEmpty {
ReplyEmpty {
reply: Reply::new(unique, sender),
}
}
}
impl ReplyEmpty {
pub fn ok(self) {
self.reply.send_ll(&ll::Response::new_empty());
}
pub fn error(self, err: c_int) {
self.reply.error(err);
}
}
#[derive(Debug)]
pub struct ReplyData {
reply: ReplyRaw,
}
impl Reply for ReplyData {
fn new<S: ReplySender>(unique: u64, sender: S) -> ReplyData {
ReplyData {
reply: Reply::new(unique, sender),
}
}
}
impl ReplyData {
pub fn data(self, data: &[u8]) {
self.reply.send_ll(&ll::Response::new_data(data));
}
pub fn error(self, err: c_int) {
self.reply.error(err);
}
}
#[derive(Debug)]
pub struct ReplyEntry {
reply: ReplyRaw,
}
impl Reply for ReplyEntry {
fn new<S: ReplySender>(unique: u64, sender: S) -> ReplyEntry {
ReplyEntry {
reply: Reply::new(unique, sender),
}
}
}
impl ReplyEntry {
pub fn entry(self, ttl: &Duration, attr: &FileAttr, generation: u64) {
self.reply.send_ll(&ll::Response::new_entry(
ll::INodeNo(attr.ino),
ll::Generation(generation),
&attr.into(),
*ttl,
*ttl,
));
}
pub fn error(self, err: c_int) {
self.reply.error(err);
}
}
#[derive(Debug)]
pub struct ReplyAttr {
reply: ReplyRaw,
}
impl Reply for ReplyAttr {
fn new<S: ReplySender>(unique: u64, sender: S) -> ReplyAttr {
ReplyAttr {
reply: Reply::new(unique, sender),
}
}
}
impl ReplyAttr {
pub fn attr(self, ttl: &Duration, attr: &FileAttr) {
self.reply
.send_ll(&ll::Response::new_attr(ttl, &attr.into()));
}
pub fn error(self, err: c_int) {
self.reply.error(err);
}
}
#[cfg(target_os = "macos")]
#[derive(Debug)]
pub struct ReplyXTimes {
reply: ReplyRaw,
}
#[cfg(target_os = "macos")]
impl Reply for ReplyXTimes {
fn new<S: ReplySender>(unique: u64, sender: S) -> ReplyXTimes {
ReplyXTimes {
reply: Reply::new(unique, sender),
}
}
}
#[cfg(target_os = "macos")]
impl ReplyXTimes {
pub fn xtimes(self, bkuptime: SystemTime, crtime: SystemTime) {
self.reply
.send_ll(&ll::Response::new_xtimes(bkuptime, crtime))
}
pub fn error(self, err: c_int) {
self.reply.error(err);
}
}
#[derive(Debug)]
pub struct ReplyOpen {
reply: ReplyRaw,
}
impl Reply for ReplyOpen {
fn new<S: ReplySender>(unique: u64, sender: S) -> ReplyOpen {
ReplyOpen {
reply: Reply::new(unique, sender),
}
}
}
impl ReplyOpen {
pub fn opened(self, fh: u64, flags: u32) {
self.reply
.send_ll(&ll::Response::new_open(ll::FileHandle(fh), flags))
}
pub fn error(self, err: c_int) {
self.reply.error(err);
}
}
#[derive(Debug)]
pub struct ReplyWrite {
reply: ReplyRaw,
}
impl Reply for ReplyWrite {
fn new<S: ReplySender>(unique: u64, sender: S) -> ReplyWrite {
ReplyWrite {
reply: Reply::new(unique, sender),
}
}
}
impl ReplyWrite {
pub fn written(self, size: u32) {
self.reply.send_ll(&ll::Response::new_write(size))
}
pub fn error(self, err: c_int) {
self.reply.error(err);
}
}
#[derive(Debug)]
pub struct ReplyStatfs {
reply: ReplyRaw,
}
impl Reply for ReplyStatfs {
fn new<S: ReplySender>(unique: u64, sender: S) -> ReplyStatfs {
ReplyStatfs {
reply: Reply::new(unique, sender),
}
}
}
impl ReplyStatfs {
#[allow(clippy::too_many_arguments)]
pub fn statfs(
self,
blocks: u64,
bfree: u64,
bavail: u64,
files: u64,
ffree: u64,
bsize: u32,
namelen: u32,
frsize: u32,
) {
self.reply.send_ll(&ll::Response::new_statfs(
blocks, bfree, bavail, files, ffree, bsize, namelen, frsize,
))
}
pub fn error(self, err: c_int) {
self.reply.error(err);
}
}
#[derive(Debug)]
pub struct ReplyCreate {
reply: ReplyRaw,
}
impl Reply for ReplyCreate {
fn new<S: ReplySender>(unique: u64, sender: S) -> ReplyCreate {
ReplyCreate {
reply: Reply::new(unique, sender),
}
}
}
impl ReplyCreate {
pub fn created(self, ttl: &Duration, attr: &FileAttr, generation: u64, fh: u64, flags: u32) {
self.reply.send_ll(&ll::Response::new_create(
ttl,
&attr.into(),
ll::Generation(generation),
ll::FileHandle(fh),
flags,
))
}
pub fn error(self, err: c_int) {
self.reply.error(err);
}
}
#[derive(Debug)]
pub struct ReplyLock {
reply: ReplyRaw,
}
impl Reply for ReplyLock {
fn new<S: ReplySender>(unique: u64, sender: S) -> ReplyLock {
ReplyLock {
reply: Reply::new(unique, sender),
}
}
}
impl ReplyLock {
pub fn locked(self, start: u64, end: u64, typ: i32, pid: u32) {
self.reply.send_ll(&ll::Response::new_lock(&ll::Lock {
range: (start, end),
typ,
pid,
}))
}
pub fn error(self, err: c_int) {
self.reply.error(err);
}
}
#[derive(Debug)]
pub struct ReplyBmap {
reply: ReplyRaw,
}
impl Reply for ReplyBmap {
fn new<S: ReplySender>(unique: u64, sender: S) -> ReplyBmap {
ReplyBmap {
reply: Reply::new(unique, sender),
}
}
}
impl ReplyBmap {
pub fn bmap(self, block: u64) {
self.reply.send_ll(&ll::Response::new_bmap(block))
}
pub fn error(self, err: c_int) {
self.reply.error(err);
}
}
#[derive(Debug)]
pub struct ReplyIoctl {
reply: ReplyRaw,
}
impl Reply for ReplyIoctl {
fn new<S: ReplySender>(unique: u64, sender: S) -> ReplyIoctl {
ReplyIoctl {
reply: Reply::new(unique, sender),
}
}
}
impl ReplyIoctl {
pub fn ioctl(self, result: i32, data: &[u8]) {
self.reply
.send_ll(&ll::Response::new_ioctl(result, &[IoSlice::new(data)]));
}
pub fn error(self, err: c_int) {
self.reply.error(err);
}
}
#[derive(Debug)]
pub struct ReplyDirectory {
reply: ReplyRaw,
data: DirEntList,
}
impl ReplyDirectory {
pub fn new<S: ReplySender>(unique: u64, sender: S, size: usize) -> ReplyDirectory {
ReplyDirectory {
reply: Reply::new(unique, sender),
data: DirEntList::new(size),
}
}
#[must_use]
pub fn add<T: AsRef<OsStr>>(&mut self, ino: u64, offset: i64, kind: FileType, name: T) -> bool {
let name = name.as_ref();
self.data.push(&DirEntry::new(
INodeNo(ino),
DirEntOffset(offset),
kind,
name,
))
}
pub fn ok(self) {
self.reply.send_ll(&self.data.into());
}
pub fn error(self, err: c_int) {
self.reply.error(err);
}
}
#[derive(Debug)]
pub struct ReplyDirectoryPlus {
reply: ReplyRaw,
buf: DirEntPlusList,
}
impl ReplyDirectoryPlus {
pub fn new<S: ReplySender>(unique: u64, sender: S, size: usize) -> ReplyDirectoryPlus {
ReplyDirectoryPlus {
reply: Reply::new(unique, sender),
buf: DirEntPlusList::new(size),
}
}
pub fn add<T: AsRef<OsStr>>(
&mut self,
ino: u64,
offset: i64,
name: T,
ttl: &Duration,
attr: &FileAttr,
generation: u64,
) -> bool {
let name = name.as_ref();
self.buf.push(&DirEntryPlus::new(
INodeNo(ino),
Generation(generation),
DirEntOffset(offset),
name,
*ttl,
attr.into(),
*ttl,
))
}
pub fn ok(self) {
self.reply.send_ll(&self.buf.into());
}
pub fn error(self, err: c_int) {
self.reply.error(err);
}
}
#[derive(Debug)]
pub struct ReplyXattr {
reply: ReplyRaw,
}
impl Reply for ReplyXattr {
fn new<S: ReplySender>(unique: u64, sender: S) -> ReplyXattr {
ReplyXattr {
reply: Reply::new(unique, sender),
}
}
}
impl ReplyXattr {
pub fn size(self, size: u32) {
self.reply.send_ll(&ll::Response::new_xattr_size(size))
}
pub fn data(self, data: &[u8]) {
self.reply.send_ll(&ll::Response::new_data(data))
}
pub fn error(self, err: c_int) {
self.reply.error(err);
}
}
#[derive(Debug)]
pub struct ReplyLseek {
reply: ReplyRaw,
}
impl Reply for ReplyLseek {
fn new<S: ReplySender>(unique: u64, sender: S) -> ReplyLseek {
ReplyLseek {
reply: Reply::new(unique, sender),
}
}
}
impl ReplyLseek {
pub fn offset(self, offset: i64) {
self.reply.send_ll(&ll::Response::new_lseek(offset))
}
pub fn error(self, err: c_int) {
self.reply.error(err);
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::{FileAttr, FileType};
use std::io::IoSlice;
use std::sync::mpsc::{sync_channel, SyncSender};
use std::thread;
use std::time::{Duration, UNIX_EPOCH};
use zerocopy::AsBytes;
#[derive(Debug, AsBytes)]
#[repr(C)]
struct Data {
a: u8,
b: u8,
c: u16,
}
#[test]
fn serialize_empty() {
assert!(().as_bytes().is_empty());
}
#[test]
fn serialize_slice() {
let data: [u8; 4] = [0x12, 0x34, 0x56, 0x78];
assert_eq!(data.as_bytes(), [0x12, 0x34, 0x56, 0x78]);
}
#[test]
fn serialize_struct() {
let data = Data {
a: 0x12,
b: 0x34,
c: 0x5678,
};
assert_eq!(data.as_bytes(), [0x12, 0x34, 0x78, 0x56]);
}
struct AssertSender {
expected: Vec<u8>,
}
impl super::ReplySender for AssertSender {
fn send(&self, data: &[IoSlice<'_>]) -> std::io::Result<()> {
let mut v = vec![];
for x in data {
v.extend_from_slice(x)
}
assert_eq!(self.expected, v);
Ok(())
}
}
#[test]
fn reply_raw() {
let data = Data {
a: 0x12,
b: 0x34,
c: 0x5678,
};
let sender = AssertSender {
expected: vec![
0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00,
0x00, 0x00, 0x12, 0x34, 0x78, 0x56,
],
};
let reply: ReplyRaw = Reply::new(0xdeadbeef, sender);
reply.send_ll(&ll::Response::new_data(data.as_bytes()));
}
#[test]
fn reply_error() {
let sender = AssertSender {
expected: vec![
0x10, 0x00, 0x00, 0x00, 0xbe, 0xff, 0xff, 0xff, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00,
0x00, 0x00,
],
};
let reply: ReplyRaw = Reply::new(0xdeadbeef, sender);
reply.error(66);
}
#[test]
fn reply_empty() {
let sender = AssertSender {
expected: vec![
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00,
0x00, 0x00,
],
};
let reply: ReplyEmpty = Reply::new(0xdeadbeef, sender);
reply.ok();
}
#[test]
fn reply_data() {
let sender = AssertSender {
expected: vec![
0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00,
0x00, 0x00, 0xde, 0xad, 0xbe, 0xef,
],
};
let reply: ReplyData = Reply::new(0xdeadbeef, sender);
reply.data(&[0xde, 0xad, 0xbe, 0xef]);
}
#[test]
fn reply_entry() {
let mut expected = if cfg!(target_os = "macos") {
vec![
0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00,
0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x65, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x87,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, 0x00, 0x00, 0x21, 0x43, 0x00, 0x00,
0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0x78, 0x56,
0x00, 0x00, 0xa4, 0x81, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00,
0x77, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00,
]
} else {
vec![
0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00,
0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x65, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x87,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, 0x00, 0x00, 0x21, 0x43, 0x00, 0x00,
0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00,
0x78, 0x56, 0x00, 0x00, 0xa4, 0x81, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x66, 0x00,
0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00,
]
};
if cfg!(feature = "abi-7-9") {
expected.extend(vec![0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
}
expected[0] = (expected.len()) as u8;
let sender = AssertSender { expected };
let reply: ReplyEntry = Reply::new(0xdeadbeef, sender);
let time = UNIX_EPOCH + Duration::new(0x1234, 0x5678);
let ttl = Duration::new(0x8765, 0x4321);
let attr = FileAttr {
ino: 0x11,
size: 0x22,
blocks: 0x33,
atime: time,
mtime: time,
ctime: time,
crtime: time,
kind: FileType::RegularFile,
perm: 0o644,
nlink: 0x55,
uid: 0x66,
gid: 0x77,
rdev: 0x88,
flags: 0x99,
blksize: 0xbb,
};
reply.entry(&ttl, &attr, 0xaa);
}
#[test]
fn reply_attr() {
let mut expected = if cfg!(target_os = "macos") {
vec![
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00,
0x00, 0x00, 0x65, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0x78, 0x56,
0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0xa4, 0x81, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00,
0x66, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x99, 0x00,
0x00, 0x00,
]
} else {
vec![
0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00,
0x00, 0x00, 0x65, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00,
0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0xa4, 0x81, 0x00, 0x00, 0x55, 0x00,
0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00,
]
};
if cfg!(feature = "abi-7-9") {
expected.extend_from_slice(&[0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
}
expected[0] = expected.len() as u8;
let sender = AssertSender { expected };
let reply: ReplyAttr = Reply::new(0xdeadbeef, sender);
let time = UNIX_EPOCH + Duration::new(0x1234, 0x5678);
let ttl = Duration::new(0x8765, 0x4321);
let attr = FileAttr {
ino: 0x11,
size: 0x22,
blocks: 0x33,
atime: time,
mtime: time,
ctime: time,
crtime: time,
kind: FileType::RegularFile,
perm: 0o644,
nlink: 0x55,
uid: 0x66,
gid: 0x77,
rdev: 0x88,
flags: 0x99,
blksize: 0xbb,
};
reply.attr(&ttl, &attr);
}
#[test]
#[cfg(target_os = "macos")]
fn reply_xtimes() {
let sender = AssertSender {
expected: vec![
0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00,
0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00,
],
};
let reply: ReplyXTimes = Reply::new(0xdeadbeef, sender);
let time = UNIX_EPOCH + Duration::new(0x1234, 0x5678);
reply.xtimes(time, time);
}
#[test]
fn reply_open() {
let sender = AssertSender {
expected: vec![
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00,
0x00, 0x00, 0x22, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
],
};
let reply: ReplyOpen = Reply::new(0xdeadbeef, sender);
reply.opened(0x1122, 0x33);
}
#[test]
fn reply_write() {
let sender = AssertSender {
expected: vec![
0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00,
0x00, 0x00, 0x22, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
],
};
let reply: ReplyWrite = Reply::new(0xdeadbeef, sender);
reply.written(0x1122);
}
#[test]
fn reply_statfs() {
let sender = AssertSender {
expected: vec![
0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00,
0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x66, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
],
};
let reply: ReplyStatfs = Reply::new(0xdeadbeef, sender);
reply.statfs(0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88);
}
#[test]
fn reply_create() {
let mut expected = if cfg!(target_os = "macos") {
vec![
0xa8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00,
0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x65, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x87,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, 0x00, 0x00, 0x21, 0x43, 0x00, 0x00,
0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0x78, 0x56,
0x00, 0x00, 0xa4, 0x81, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00,
0x77, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0xbb, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]
} else {
vec![
0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00,
0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x65, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x87,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, 0x00, 0x00, 0x21, 0x43, 0x00, 0x00,
0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00,
0x78, 0x56, 0x00, 0x00, 0xa4, 0x81, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x66, 0x00,
0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0xbb, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]
};
if cfg!(feature = "abi-7-9") {
let insert_at = expected.len() - 16;
expected.splice(
insert_at..insert_at,
vec![0xdd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
);
}
expected[0] = (expected.len()) as u8;
let sender = AssertSender { expected };
let reply: ReplyCreate = Reply::new(0xdeadbeef, sender);
let time = UNIX_EPOCH + Duration::new(0x1234, 0x5678);
let ttl = Duration::new(0x8765, 0x4321);
let attr = FileAttr {
ino: 0x11,
size: 0x22,
blocks: 0x33,
atime: time,
mtime: time,
ctime: time,
crtime: time,
kind: FileType::RegularFile,
perm: 0o644,
nlink: 0x55,
uid: 0x66,
gid: 0x77,
rdev: 0x88,
flags: 0x99,
blksize: 0xdd,
};
reply.created(&ttl, &attr, 0xaa, 0xbb, 0xcc);
}
#[test]
fn reply_lock() {
let sender = AssertSender {
expected: vec![
0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00,
0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00,
],
};
let reply: ReplyLock = Reply::new(0xdeadbeef, sender);
reply.locked(0x11, 0x22, 0x33, 0x44);
}
#[test]
fn reply_bmap() {
let sender = AssertSender {
expected: vec![
0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00,
0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
],
};
let reply: ReplyBmap = Reply::new(0xdeadbeef, sender);
reply.bmap(0x1234);
}
#[test]
fn reply_directory() {
let sender = AssertSender {
expected: vec![
0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00,
0x00, 0x00, 0xbb, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x68, 0x65,
0x6c, 0x6c, 0x6f, 0x00, 0x00, 0x00, 0xdd, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00,
0x00, 0x00, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x72, 0x73,
],
};
let mut reply = ReplyDirectory::new(0xdeadbeef, sender, 4096);
assert!(!reply.add(0xaabb, 1, FileType::Directory, "hello"));
assert!(!reply.add(0xccdd, 2, FileType::RegularFile, "world.rs"));
reply.ok();
}
#[test]
fn reply_xattr_size() {
let sender = AssertSender {
expected: vec![
0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEF, 0xBE, 0xAD, 0xDE, 0x00, 0x00,
0x00, 0x00, 0x78, 0x56, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00,
],
};
let reply = ReplyXattr::new(0xdeadbeef, sender);
reply.size(0x12345678);
}
#[test]
fn reply_xattr_data() {
let sender = AssertSender {
expected: vec![
0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEF, 0xBE, 0xAD, 0xDE, 0x00, 0x00,
0x00, 0x00, 0x11, 0x22, 0x33, 0x44,
],
};
let reply = ReplyXattr::new(0xdeadbeef, sender);
reply.data(&[0x11, 0x22, 0x33, 0x44]);
}
impl super::ReplySender for SyncSender<()> {
fn send(&self, _: &[IoSlice<'_>]) -> std::io::Result<()> {
self.send(()).unwrap();
Ok(())
}
}
#[test]
fn async_reply() {
let (tx, rx) = sync_channel::<()>(1);
let reply: ReplyEmpty = Reply::new(0xdeadbeef, tx);
thread::spawn(move || {
reply.ok();
});
rx.recv().unwrap();
}
}