use std::convert::AsRef;
use std::ffi::OsStr;
use std::io::IoSlice;
use std::os::fd::BorrowedFd;
use std::time::Duration;
#[cfg(target_os = "macos")]
use std::time::SystemTime;
use log::error;
use log::warn;
use crate::Errno;
use crate::FileAttr;
use crate::FileType;
use crate::PollEvents;
use crate::channel::ChannelSender;
use crate::ll::Generation;
use crate::ll::INodeNo;
use crate::ll::flags::fopen_flags::FopenFlags;
use crate::ll::reply::DirEntList;
use crate::ll::reply::DirEntOffset;
use crate::ll::reply::DirEntPlusList;
use crate::ll::reply::DirEntry;
use crate::ll::reply::DirEntryPlus;
use crate::ll::reply::Response;
use crate::ll::{self};
use crate::passthrough::BackingId;
#[derive(Debug)]
pub(crate) enum ReplySender {
Channel(ChannelSender),
#[cfg(test)]
Assert(AssertSender),
#[cfg(test)]
Sync(std::sync::mpsc::SyncSender<()>),
}
impl ReplySender {
pub(crate) fn send(&self, data: &[IoSlice<'_>]) -> std::io::Result<()> {
match self {
ReplySender::Channel(sender) => sender.send(data),
#[cfg(test)]
ReplySender::Assert(sender) => sender.send(data),
#[cfg(test)]
ReplySender::Sync(sender) => {
sender.send(()).unwrap();
Ok(())
}
}
}
pub(crate) fn open_backing(&self, fd: BorrowedFd<'_>) -> std::io::Result<BackingId> {
match self {
ReplySender::Channel(sender) => sender.open_backing(fd),
#[cfg(test)]
ReplySender::Assert(_) => unreachable!(),
#[cfg(test)]
ReplySender::Sync(_) => unreachable!(),
}
}
}
#[cfg(test)]
#[derive(Debug)]
pub(crate) struct AssertSender {
expected: Vec<u8>,
}
#[cfg(test)]
impl 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(())
}
}
pub(crate) trait Reply: Send + 'static {
fn new(unique: ll::RequestId, sender: ReplySender) -> Self;
}
#[derive(Debug)]
pub(crate) struct ReplyRaw {
unique: ll::RequestId,
sender: Option<ReplySender>,
}
impl Reply for ReplyRaw {
fn new(unique: ll::RequestId, sender: ReplySender) -> ReplyRaw {
ReplyRaw {
unique,
sender: Some(sender),
}
}
}
impl ReplyRaw {
pub(crate) fn send_ll_mut(&mut self, response: &impl 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}");
}
}
pub(crate) fn send_ll(mut self, response: &impl Response) {
self.send_ll_mut(response);
}
pub(crate) fn error(self, err: ll::Errno) {
self.send_ll(&ll::ResponseErrno(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::ResponseErrno(ll::Errno::EIO));
}
}
}
#[derive(Debug)]
pub struct ReplyEmpty {
reply: ReplyRaw,
}
impl Reply for ReplyEmpty {
fn new(unique: ll::RequestId, sender: ReplySender) -> ReplyEmpty {
ReplyEmpty {
reply: Reply::new(unique, sender),
}
}
}
impl ReplyEmpty {
pub fn ok(self) {
self.reply.send_ll(&ll::ResponseEmpty);
}
pub fn error(self, err: Errno) {
self.reply.error(err);
}
}
#[derive(Debug)]
pub struct ReplyData {
reply: ReplyRaw,
}
impl Reply for ReplyData {
fn new(unique: ll::RequestId, sender: ReplySender) -> ReplyData {
ReplyData {
reply: Reply::new(unique, sender),
}
}
}
impl ReplyData {
pub fn data(self, data: &[u8]) {
self.reply.send_ll(&ll::ResponseSlice(data));
}
pub fn error(self, err: Errno) {
self.reply.error(err);
}
}
#[derive(Debug)]
pub struct ReplyEntry {
reply: ReplyRaw,
}
impl Reply for ReplyEntry {
fn new(unique: ll::RequestId, sender: ReplySender) -> ReplyEntry {
ReplyEntry {
reply: Reply::new(unique, sender),
}
}
}
impl ReplyEntry {
pub fn entry(self, ttl: &Duration, attr: &FileAttr, generation: Generation) {
self.reply.send_ll(&ll::ResponseStruct::new_entry(
attr.ino,
generation,
&attr.into(),
*ttl,
*ttl,
));
}
pub fn error(self, err: Errno) {
self.reply.error(err);
}
}
#[derive(Debug)]
pub struct ReplyAttr {
reply: ReplyRaw,
}
impl Reply for ReplyAttr {
fn new(unique: ll::RequestId, sender: ReplySender) -> ReplyAttr {
ReplyAttr {
reply: Reply::new(unique, sender),
}
}
}
impl ReplyAttr {
pub fn attr(self, ttl: &Duration, attr: &FileAttr) {
self.reply
.send_ll(&ll::ResponseStruct::new_attr(ttl, &attr.into()));
}
pub fn error(self, err: Errno) {
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(unique: ll::RequestId, sender: ReplySender) -> 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::ResponseStruct::new_xtimes(bkuptime, crtime))
}
pub fn error(self, err: Errno) {
self.reply.error(err);
}
}
#[derive(Debug)]
pub struct ReplyOpen {
reply: ReplyRaw,
}
impl Reply for ReplyOpen {
fn new(unique: ll::RequestId, sender: ReplySender) -> ReplyOpen {
ReplyOpen {
reply: Reply::new(unique, sender),
}
}
}
impl ReplyOpen {
pub fn opened(self, fh: ll::FileHandle, flags: FopenFlags) {
assert!(!flags.contains(FopenFlags::FOPEN_PASSTHROUGH));
self.reply
.send_ll(&ll::ResponseStruct::new_open(fh, flags, 0));
}
pub fn open_backing(&self, fd: impl std::os::fd::AsFd) -> std::io::Result<BackingId> {
self.reply.sender.as_ref().unwrap().open_backing(fd.as_fd())
}
pub fn opened_passthrough(self, fh: ll::FileHandle, flags: FopenFlags, backing_id: &BackingId) {
let flags = flags | FopenFlags::FOPEN_PASSTHROUGH;
self.reply.send_ll(&ll::ResponseStruct::new_open(
fh,
flags,
backing_id.backing_id,
));
}
pub fn error(self, err: Errno) {
self.reply.error(err);
}
}
#[derive(Debug)]
pub struct ReplyWrite {
reply: ReplyRaw,
}
impl Reply for ReplyWrite {
fn new(unique: ll::RequestId, sender: ReplySender) -> ReplyWrite {
ReplyWrite {
reply: Reply::new(unique, sender),
}
}
}
impl ReplyWrite {
pub fn written(self, size: u32) {
self.reply.send_ll(&ll::ResponseStruct::new_write(size));
}
pub fn error(self, err: Errno) {
self.reply.error(err);
}
}
#[derive(Debug)]
pub struct ReplyStatfs {
reply: ReplyRaw,
}
impl Reply for ReplyStatfs {
fn new(unique: ll::RequestId, sender: ReplySender) -> 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::ResponseStruct::new_statfs(
blocks, bfree, bavail, files, ffree, bsize, namelen, frsize,
));
}
pub fn error(self, err: Errno) {
self.reply.error(err);
}
}
#[derive(Debug)]
pub struct ReplyCreate {
reply: ReplyRaw,
}
impl Reply for ReplyCreate {
fn new(unique: ll::RequestId, sender: ReplySender) -> ReplyCreate {
ReplyCreate {
reply: Reply::new(unique, sender),
}
}
}
impl ReplyCreate {
pub fn created(
self,
ttl: &Duration,
attr: &FileAttr,
generation: Generation,
fh: ll::FileHandle,
flags: FopenFlags,
) {
assert!(!flags.contains(FopenFlags::FOPEN_PASSTHROUGH));
self.reply.send_ll(&ll::ResponseStruct::new_create(
ttl,
&attr.into(),
generation,
fh,
flags,
0,
));
}
pub fn error(self, err: Errno) {
self.reply.error(err);
}
pub fn open_backing(&self, fd: impl std::os::fd::AsFd) -> std::io::Result<BackingId> {
self.reply.sender.as_ref().unwrap().open_backing(fd.as_fd())
}
pub fn created_passthrough(
self,
ttl: &Duration,
attr: &FileAttr,
generation: Generation,
fh: ll::FileHandle,
flags: FopenFlags,
backing_id: &BackingId,
) {
self.reply.send_ll(&ll::ResponseStruct::new_create(
ttl,
&attr.into(),
generation,
fh,
flags | FopenFlags::FOPEN_PASSTHROUGH,
backing_id.backing_id,
));
}
}
#[derive(Debug)]
pub struct ReplyLock {
reply: ReplyRaw,
}
impl Reply for ReplyLock {
fn new(unique: ll::RequestId, sender: ReplySender) -> 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::ResponseStruct::new_lock(&ll::Lock {
range: (start, end),
typ,
pid,
}));
}
pub fn error(self, err: Errno) {
self.reply.error(err);
}
}
#[derive(Debug)]
pub struct ReplyBmap {
reply: ReplyRaw,
}
impl Reply for ReplyBmap {
fn new(unique: ll::RequestId, sender: ReplySender) -> ReplyBmap {
ReplyBmap {
reply: Reply::new(unique, sender),
}
}
}
impl ReplyBmap {
pub fn bmap(self, block: u64) {
self.reply.send_ll(&ll::ResponseStruct::new_bmap(block));
}
pub fn error(self, err: Errno) {
self.reply.error(err);
}
}
#[derive(Debug)]
pub struct ReplyIoctl {
reply: ReplyRaw,
}
impl Reply for ReplyIoctl {
fn new(unique: ll::RequestId, sender: ReplySender) -> ReplyIoctl {
ReplyIoctl {
reply: Reply::new(unique, sender),
}
}
}
impl ReplyIoctl {
pub fn ioctl(self, result: i32, data: &[u8]) {
self.reply
.send_ll(&ll::ResponseIoctl::new_ioctl(result, &[IoSlice::new(data)]));
}
pub fn error(self, err: Errno) {
self.reply.error(err);
}
}
#[derive(Debug)]
pub struct ReplyPoll {
reply: ReplyRaw,
}
impl Reply for ReplyPoll {
fn new(unique: ll::RequestId, sender: ReplySender) -> ReplyPoll {
ReplyPoll {
reply: Reply::new(unique, sender),
}
}
}
impl ReplyPoll {
pub fn poll(self, revents: PollEvents) {
self.reply.send_ll(&ll::ResponseStruct::new_poll(revents));
}
pub fn error(self, err: Errno) {
self.reply.error(err);
}
}
#[derive(Debug)]
pub struct ReplyDirectory {
reply: ReplyRaw,
data: DirEntList,
}
impl ReplyDirectory {
pub(crate) fn new(unique: ll::RequestId, sender: ReplySender, size: usize) -> ReplyDirectory {
ReplyDirectory {
reply: Reply::new(unique, sender),
data: DirEntList::new(size),
}
}
#[must_use]
pub fn add<T: AsRef<OsStr>>(
&mut self,
ino: INodeNo,
offset: u64,
kind: FileType,
name: T,
) -> bool {
let name = name.as_ref();
self.data
.push(&DirEntry::new(ino, DirEntOffset(offset), kind, name))
}
pub fn ok(self) {
let response: ll::ResponseData = self.data.into();
self.reply.send_ll(&response);
}
pub fn error(self, err: Errno) {
self.reply.error(err);
}
}
#[derive(Debug)]
pub struct ReplyDirectoryPlus {
reply: ReplyRaw,
buf: DirEntPlusList,
}
impl ReplyDirectoryPlus {
pub(crate) fn new(
unique: ll::RequestId,
sender: ReplySender,
size: usize,
) -> ReplyDirectoryPlus {
ReplyDirectoryPlus {
reply: Reply::new(unique, sender),
buf: DirEntPlusList::new(size),
}
}
pub fn add<T: AsRef<OsStr>>(
&mut self,
ino: INodeNo,
offset: u64,
name: T,
ttl: &Duration,
attr: &FileAttr,
generation: Generation,
) -> bool {
let name = name.as_ref();
self.buf.push(&DirEntryPlus::new(
ino,
generation,
DirEntOffset(offset),
name,
*ttl,
attr.into(),
*ttl,
))
}
pub fn ok(self) {
let response: ll::ResponseData = self.buf.into();
self.reply.send_ll(&response);
}
pub fn error(self, err: Errno) {
self.reply.error(err);
}
}
#[derive(Debug)]
pub struct ReplyXattr {
reply: ReplyRaw,
}
impl Reply for ReplyXattr {
fn new(unique: ll::RequestId, sender: ReplySender) -> ReplyXattr {
ReplyXattr {
reply: Reply::new(unique, sender),
}
}
}
impl ReplyXattr {
pub fn size(self, size: u32) {
self.reply
.send_ll(&ll::ResponseStruct::new_xattr_size(size));
}
pub fn data(self, data: &[u8]) {
self.reply.send_ll(&ll::ResponseSlice(data));
}
pub fn error(self, err: Errno) {
self.reply.error(err);
}
}
#[derive(Debug)]
pub struct ReplyLseek {
reply: ReplyRaw,
}
impl Reply for ReplyLseek {
fn new(unique: ll::RequestId, sender: ReplySender) -> ReplyLseek {
ReplyLseek {
reply: Reply::new(unique, sender),
}
}
}
impl ReplyLseek {
pub fn offset(self, offset: i64) {
self.reply.send_ll(&ll::ResponseStruct::new_lseek(offset));
}
pub fn error(self, err: Errno) {
self.reply.error(err);
}
}
#[cfg(test)]
mod test {
use std::sync::mpsc::sync_channel;
use std::thread;
use std::time::Duration;
use std::time::UNIX_EPOCH;
use zerocopy::Immutable;
use zerocopy::IntoBytes;
use crate::FileAttr;
use crate::FileType;
use crate::reply::*;
#[derive(Debug, IntoBytes, Immutable)]
#[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]);
}
#[test]
fn reply_raw() {
let data = Data {
a: 0x12,
b: 0x34,
c: 0x5678,
};
let sender = ReplySender::Assert(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(ll::RequestId(0xdeadbeef), sender);
reply.send_ll(&ll::ResponseData::new_data(data.as_bytes()));
}
#[test]
fn reply_error() {
let sender = ReplySender::Assert(AssertSender {
expected: vec![
0x10, 0x00, 0x00, 0x00, 0xbe, 0xff, 0xff, 0xff, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00,
0x00, 0x00,
],
});
let reply: ReplyRaw = Reply::new(ll::RequestId(0xdeadbeef), sender);
reply.error(Errno::from_i32(66));
}
#[test]
fn reply_empty() {
let sender = ReplySender::Assert(AssertSender {
expected: vec![
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00,
0x00, 0x00,
],
});
let reply: ReplyEmpty = Reply::new(ll::RequestId(0xdeadbeef), sender);
reply.ok();
}
#[test]
fn reply_data() {
let sender = ReplySender::Assert(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(ll::RequestId(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,
]
};
expected.extend(vec![0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
expected[0] = (expected.len()) as u8;
let sender = ReplySender::Assert(AssertSender { expected });
let reply: ReplyEntry = Reply::new(ll::RequestId(0xdeadbeef), sender);
let time = UNIX_EPOCH + Duration::new(0x1234, 0x5678);
let ttl = Duration::new(0x8765, 0x4321);
let attr = FileAttr {
ino: INodeNo(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, ll::Generation(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,
]
};
expected.extend_from_slice(&[0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
expected[0] = expected.len() as u8;
let sender = ReplySender::Assert(AssertSender { expected });
let reply: ReplyAttr = Reply::new(ll::RequestId(0xdeadbeef), sender);
let time = UNIX_EPOCH + Duration::new(0x1234, 0x5678);
let ttl = Duration::new(0x8765, 0x4321);
let attr = FileAttr {
ino: INodeNo(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 = ReplySender::Assert(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(ll::RequestId(0xdeadbeef), sender);
let time = UNIX_EPOCH + Duration::new(0x1234, 0x5678);
reply.xtimes(time, time);
}
#[test]
fn reply_open() {
let sender = ReplySender::Assert(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(ll::RequestId(0xdeadbeef), sender);
reply.opened(ll::FileHandle(0x1122), FopenFlags::from_bits_retain(0x33));
}
#[test]
fn reply_write() {
let sender = ReplySender::Assert(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(ll::RequestId(0xdeadbeef), sender);
reply.written(0x1122);
}
#[test]
fn reply_statfs() {
let sender = ReplySender::Assert(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(ll::RequestId(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, 0x0c, 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, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]
};
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 = ReplySender::Assert(AssertSender { expected });
let reply: ReplyCreate = Reply::new(ll::RequestId(0xdeadbeef), sender);
let time = UNIX_EPOCH + Duration::new(0x1234, 0x5678);
let ttl = Duration::new(0x8765, 0x4321);
let attr = FileAttr {
ino: INodeNo(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,
ll::Generation(0xaa),
ll::FileHandle(0xbb),
FopenFlags::from_bits_retain(0x0c),
);
}
#[test]
fn reply_lock() {
let sender = ReplySender::Assert(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(ll::RequestId(0xdeadbeef), sender);
reply.locked(0x11, 0x22, 0x33, 0x44);
}
#[test]
fn reply_bmap() {
let sender = ReplySender::Assert(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(ll::RequestId(0xdeadbeef), sender);
reply.bmap(0x1234);
}
#[test]
fn reply_directory() {
let sender = ReplySender::Assert(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(ll::RequestId(0xdeadbeef), sender, 4096);
assert!(!reply.add(INodeNo(0xaabb), 1, FileType::Directory, "hello"));
assert!(!reply.add(INodeNo(0xccdd), 2, FileType::RegularFile, "world.rs"));
reply.ok();
}
#[test]
fn reply_xattr_size() {
let sender = ReplySender::Assert(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(ll::RequestId(0xdeadbeef), sender);
reply.size(0x12345678);
}
#[test]
fn reply_xattr_data() {
let sender = ReplySender::Assert(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(ll::RequestId(0xdeadbeef), sender);
reply.data(&[0x11, 0x22, 0x33, 0x44]);
}
#[test]
fn async_reply() {
let (tx, rx) = sync_channel::<()>(1);
let reply: ReplyEmpty = Reply::new(ll::RequestId(0xdeadbeef), ReplySender::Sync(tx));
thread::spawn(move || {
reply.ok();
});
rx.recv().unwrap();
}
}