Skip to main content

fuser/
reply.rs

1//! Filesystem operation reply
2//!
3//! A reply is passed to filesystem operation implementations and must be used to send back the
4//! result of an operation. The reply can optionally be sent to another thread to asynchronously
5//! work on an operation and provide the result later. Also it allows replying with a block of
6//! data without cloning the data. A reply *must always* be used (by calling either `ok()` or
7//! `error()` exactly once).
8
9use std::convert::AsRef;
10use std::ffi::OsStr;
11use std::io::IoSlice;
12use std::os::fd::BorrowedFd;
13use std::time::Duration;
14#[cfg(target_os = "macos")]
15use std::time::SystemTime;
16
17use log::error;
18use log::warn;
19
20use crate::Errno;
21use crate::FileAttr;
22use crate::FileType;
23use crate::PollEvents;
24use crate::channel::ChannelSender;
25use crate::ll::Generation;
26use crate::ll::INodeNo;
27use crate::ll::flags::fopen_flags::FopenFlags;
28use crate::ll::reply::DirEntList;
29use crate::ll::reply::DirEntOffset;
30use crate::ll::reply::DirEntPlusList;
31use crate::ll::reply::DirEntry;
32use crate::ll::reply::DirEntryPlus;
33use crate::ll::reply::Response;
34use crate::ll::{self};
35use crate::passthrough::BackingId;
36
37/// Generic reply callback to send data
38#[derive(Debug)]
39pub(crate) enum ReplySender {
40    Channel(ChannelSender),
41    #[cfg(test)]
42    Assert(AssertSender),
43    #[cfg(test)]
44    Sync(std::sync::mpsc::SyncSender<()>),
45}
46
47impl ReplySender {
48    /// Send data.
49    pub(crate) fn send(&self, data: &[IoSlice<'_>]) -> std::io::Result<()> {
50        match self {
51            ReplySender::Channel(sender) => sender.send(data),
52            #[cfg(test)]
53            ReplySender::Assert(sender) => sender.send(data),
54            #[cfg(test)]
55            ReplySender::Sync(sender) => {
56                sender.send(()).unwrap();
57                Ok(())
58            }
59        }
60    }
61
62    /// Open a backing file
63    pub(crate) fn open_backing(&self, fd: BorrowedFd<'_>) -> std::io::Result<BackingId> {
64        match self {
65            ReplySender::Channel(sender) => sender.open_backing(fd),
66            #[cfg(test)]
67            ReplySender::Assert(_) => unreachable!(),
68            #[cfg(test)]
69            ReplySender::Sync(_) => unreachable!(),
70        }
71    }
72}
73
74#[cfg(test)]
75#[derive(Debug)]
76pub(crate) struct AssertSender {
77    expected: Vec<u8>,
78}
79
80#[cfg(test)]
81impl AssertSender {
82    fn send(&self, data: &[IoSlice<'_>]) -> std::io::Result<()> {
83        let mut v = vec![];
84        for x in data {
85            v.extend_from_slice(x);
86        }
87        assert_eq!(self.expected, v);
88        Ok(())
89    }
90}
91
92/// Generic reply trait
93pub(crate) trait Reply: Send + 'static {
94    /// Create a new reply for the given request
95    fn new(unique: ll::RequestId, sender: ReplySender) -> Self;
96}
97
98///
99/// Raw reply
100///
101#[derive(Debug)]
102pub(crate) struct ReplyRaw {
103    /// Unique id of the request to reply to
104    unique: ll::RequestId,
105    /// Closure to call for sending the reply
106    sender: Option<ReplySender>,
107}
108
109impl Reply for ReplyRaw {
110    fn new(unique: ll::RequestId, sender: ReplySender) -> ReplyRaw {
111        ReplyRaw {
112            unique,
113            sender: Some(sender),
114        }
115    }
116}
117
118impl ReplyRaw {
119    /// Reply to a request with the given error code and data. Must be called
120    /// only once (the `ok` and `error` methods ensure this by consuming `self`)
121    pub(crate) fn send_ll_mut(&mut self, response: &impl Response) {
122        assert!(self.sender.is_some());
123        let sender = self.sender.take().unwrap();
124        let res = response.with_iovec(self.unique, |iov| sender.send(iov));
125        if let Err(err) = res {
126            error!("Failed to send FUSE reply: {err}");
127        }
128    }
129    pub(crate) fn send_ll(mut self, response: &impl Response) {
130        self.send_ll_mut(response);
131    }
132
133    /// Reply to a request with the given error code
134    pub(crate) fn error(self, err: ll::Errno) {
135        self.send_ll(&ll::ResponseErrno(err));
136    }
137}
138
139impl Drop for ReplyRaw {
140    fn drop(&mut self) {
141        if self.sender.is_some() {
142            warn!(
143                "Reply not sent for operation {}, replying with I/O error",
144                self.unique.0
145            );
146            self.send_ll_mut(&ll::ResponseErrno(ll::Errno::EIO));
147        }
148    }
149}
150
151///
152/// Empty reply
153///
154#[derive(Debug)]
155pub struct ReplyEmpty {
156    reply: ReplyRaw,
157}
158
159impl Reply for ReplyEmpty {
160    fn new(unique: ll::RequestId, sender: ReplySender) -> ReplyEmpty {
161        ReplyEmpty {
162            reply: Reply::new(unique, sender),
163        }
164    }
165}
166
167impl ReplyEmpty {
168    /// Reply to a request with nothing
169    pub fn ok(self) {
170        self.reply.send_ll(&ll::ResponseEmpty);
171    }
172
173    /// Reply to a request with the given error code
174    pub fn error(self, err: Errno) {
175        self.reply.error(err);
176    }
177}
178
179///
180/// Data reply
181///
182#[derive(Debug)]
183pub struct ReplyData {
184    reply: ReplyRaw,
185}
186
187impl Reply for ReplyData {
188    fn new(unique: ll::RequestId, sender: ReplySender) -> ReplyData {
189        ReplyData {
190            reply: Reply::new(unique, sender),
191        }
192    }
193}
194
195impl ReplyData {
196    /// Reply to a request with the given data
197    pub fn data(self, data: &[u8]) {
198        self.reply.send_ll(&ll::ResponseSlice(data));
199    }
200
201    /// Reply to a request with the given error code
202    pub fn error(self, err: Errno) {
203        self.reply.error(err);
204    }
205}
206
207///
208/// Entry reply
209///
210#[derive(Debug)]
211pub struct ReplyEntry {
212    reply: ReplyRaw,
213}
214
215impl Reply for ReplyEntry {
216    fn new(unique: ll::RequestId, sender: ReplySender) -> ReplyEntry {
217        ReplyEntry {
218            reply: Reply::new(unique, sender),
219        }
220    }
221}
222
223impl ReplyEntry {
224    /// Reply to a request with the given entry
225    pub fn entry(self, ttl: &Duration, attr: &FileAttr, generation: Generation) {
226        self.reply.send_ll(&ll::ResponseStruct::new_entry(
227            attr.ino,
228            generation,
229            &attr.into(),
230            *ttl,
231            *ttl,
232        ));
233    }
234
235    /// Reply to a request with the given error code
236    pub fn error(self, err: Errno) {
237        self.reply.error(err);
238    }
239}
240
241///
242/// Attribute Reply
243///
244#[derive(Debug)]
245pub struct ReplyAttr {
246    reply: ReplyRaw,
247}
248
249impl Reply for ReplyAttr {
250    fn new(unique: ll::RequestId, sender: ReplySender) -> ReplyAttr {
251        ReplyAttr {
252            reply: Reply::new(unique, sender),
253        }
254    }
255}
256
257impl ReplyAttr {
258    /// Reply to a request with the given attribute
259    pub fn attr(self, ttl: &Duration, attr: &FileAttr) {
260        self.reply
261            .send_ll(&ll::ResponseStruct::new_attr(ttl, &attr.into()));
262    }
263
264    /// Reply to a request with the given error code
265    pub fn error(self, err: Errno) {
266        self.reply.error(err);
267    }
268}
269
270///
271/// XTimes Reply
272///
273#[cfg(target_os = "macos")]
274#[derive(Debug)]
275pub struct ReplyXTimes {
276    reply: ReplyRaw,
277}
278
279#[cfg(target_os = "macos")]
280impl Reply for ReplyXTimes {
281    fn new(unique: ll::RequestId, sender: ReplySender) -> ReplyXTimes {
282        ReplyXTimes {
283            reply: Reply::new(unique, sender),
284        }
285    }
286}
287
288#[cfg(target_os = "macos")]
289impl ReplyXTimes {
290    /// Reply to a request with the given xtimes
291    pub fn xtimes(self, bkuptime: SystemTime, crtime: SystemTime) {
292        self.reply
293            .send_ll(&ll::ResponseStruct::new_xtimes(bkuptime, crtime))
294    }
295
296    /// Reply to a request with the given error code
297    pub fn error(self, err: Errno) {
298        self.reply.error(err);
299    }
300}
301
302///
303/// Open Reply
304///
305#[derive(Debug)]
306pub struct ReplyOpen {
307    reply: ReplyRaw,
308}
309
310impl Reply for ReplyOpen {
311    fn new(unique: ll::RequestId, sender: ReplySender) -> ReplyOpen {
312        ReplyOpen {
313            reply: Reply::new(unique, sender),
314        }
315    }
316}
317
318impl ReplyOpen {
319    /// Reply to a request with the given open result
320    /// # Panics
321    /// When attempting to use kernel passthrough.
322    /// Use [`opened_passthrough()`](Self::opened_passthrough) instead.
323    pub fn opened(self, fh: ll::FileHandle, flags: FopenFlags) {
324        assert!(!flags.contains(FopenFlags::FOPEN_PASSTHROUGH));
325        self.reply
326            .send_ll(&ll::ResponseStruct::new_open(fh, flags, 0));
327    }
328
329    /// Registers a fd for passthrough, returning a `BackingId`.  Once you have the backing ID,
330    /// you can pass it as the 3rd parameter of [`ReplyOpen::opened_passthrough()`]. This is done in
331    /// two separate steps because it may make sense to reuse backing IDs (to avoid having to
332    /// repeatedly reopen the underlying file or potentially keep thousands of fds open).
333    pub fn open_backing(&self, fd: impl std::os::fd::AsFd) -> std::io::Result<BackingId> {
334        // TODO: assert passthrough capability is enabled.
335        self.reply.sender.as_ref().unwrap().open_backing(fd.as_fd())
336    }
337
338    /// Reply to a request with an opened backing id. Call [`ReplyOpen::open_backing()`]
339    /// to get one of these.
340    pub fn opened_passthrough(self, fh: ll::FileHandle, flags: FopenFlags, backing_id: &BackingId) {
341        // TODO: assert passthrough capability is enabled.
342        let flags = flags | FopenFlags::FOPEN_PASSTHROUGH;
343        self.reply.send_ll(&ll::ResponseStruct::new_open(
344            fh,
345            flags,
346            backing_id.backing_id,
347        ));
348    }
349
350    /// Reply to a request with the given error code
351    pub fn error(self, err: Errno) {
352        self.reply.error(err);
353    }
354}
355
356///
357/// Write Reply
358///
359#[derive(Debug)]
360pub struct ReplyWrite {
361    reply: ReplyRaw,
362}
363
364impl Reply for ReplyWrite {
365    fn new(unique: ll::RequestId, sender: ReplySender) -> ReplyWrite {
366        ReplyWrite {
367            reply: Reply::new(unique, sender),
368        }
369    }
370}
371
372impl ReplyWrite {
373    /// Reply to a request with the number of bytes written
374    pub fn written(self, size: u32) {
375        self.reply.send_ll(&ll::ResponseStruct::new_write(size));
376    }
377
378    /// Reply to a request with the given error code
379    pub fn error(self, err: Errno) {
380        self.reply.error(err);
381    }
382}
383
384///
385/// Statfs Reply
386///
387#[derive(Debug)]
388pub struct ReplyStatfs {
389    reply: ReplyRaw,
390}
391
392impl Reply for ReplyStatfs {
393    fn new(unique: ll::RequestId, sender: ReplySender) -> ReplyStatfs {
394        ReplyStatfs {
395            reply: Reply::new(unique, sender),
396        }
397    }
398}
399
400impl ReplyStatfs {
401    /// Reply to a statfs request with filesystem information
402    #[allow(clippy::too_many_arguments)]
403    pub fn statfs(
404        self,
405        blocks: u64,
406        bfree: u64,
407        bavail: u64,
408        files: u64,
409        ffree: u64,
410        bsize: u32,
411        namelen: u32,
412        frsize: u32,
413    ) {
414        self.reply.send_ll(&ll::ResponseStruct::new_statfs(
415            blocks, bfree, bavail, files, ffree, bsize, namelen, frsize,
416        ));
417    }
418
419    /// Reply to a request with the given error code
420    pub fn error(self, err: Errno) {
421        self.reply.error(err);
422    }
423}
424
425///
426/// Create reply
427///
428#[derive(Debug)]
429pub struct ReplyCreate {
430    reply: ReplyRaw,
431}
432
433impl Reply for ReplyCreate {
434    fn new(unique: ll::RequestId, sender: ReplySender) -> ReplyCreate {
435        ReplyCreate {
436            reply: Reply::new(unique, sender),
437        }
438    }
439}
440
441impl ReplyCreate {
442    /// Reply to a request with a newly created file entry and its newly open file handle
443    /// # Panics
444    /// When attempting to use kernel passthrough. Use `opened_passthrough()` instead.
445    pub fn created(
446        self,
447        ttl: &Duration,
448        attr: &FileAttr,
449        generation: Generation,
450        fh: ll::FileHandle,
451        flags: FopenFlags,
452    ) {
453        assert!(!flags.contains(FopenFlags::FOPEN_PASSTHROUGH));
454        self.reply.send_ll(&ll::ResponseStruct::new_create(
455            ttl,
456            &attr.into(),
457            generation,
458            fh,
459            flags,
460            0,
461        ));
462    }
463
464    /// Reply to a request with the given error code
465    pub fn error(self, err: Errno) {
466        self.reply.error(err);
467    }
468
469    /// Registers a fd for passthrough, returning a `BackingId`.  Once you have the backing ID,
470    /// you can pass it as the 6th parameter of `ReplyCreate::created_passthrough()`.  This is done in
471    /// two separate steps because it may make sense to reuse backing IDs (to avoid having to
472    /// repeatedly reopen the underlying file or potentially keep thousands of fds open).
473    pub fn open_backing(&self, fd: impl std::os::fd::AsFd) -> std::io::Result<BackingId> {
474        self.reply.sender.as_ref().unwrap().open_backing(fd.as_fd())
475    }
476
477    /// Reply to a request with an opened backing id. Call `ReplyCreate::open_backing()` to get one of
478    /// these.
479    pub fn created_passthrough(
480        self,
481        ttl: &Duration,
482        attr: &FileAttr,
483        generation: Generation,
484        fh: ll::FileHandle,
485        flags: FopenFlags,
486        backing_id: &BackingId,
487    ) {
488        self.reply.send_ll(&ll::ResponseStruct::new_create(
489            ttl,
490            &attr.into(),
491            generation,
492            fh,
493            flags | FopenFlags::FOPEN_PASSTHROUGH,
494            backing_id.backing_id,
495        ));
496    }
497}
498
499///
500/// Lock Reply
501///
502#[derive(Debug)]
503pub struct ReplyLock {
504    reply: ReplyRaw,
505}
506
507impl Reply for ReplyLock {
508    fn new(unique: ll::RequestId, sender: ReplySender) -> ReplyLock {
509        ReplyLock {
510            reply: Reply::new(unique, sender),
511        }
512    }
513}
514
515impl ReplyLock {
516    /// Reply to a request with a file lock
517    pub fn locked(self, start: u64, end: u64, typ: i32, pid: u32) {
518        self.reply.send_ll(&ll::ResponseStruct::new_lock(&ll::Lock {
519            range: (start, end),
520            typ,
521            pid,
522        }));
523    }
524
525    /// Reply to a request with the given error code
526    pub fn error(self, err: Errno) {
527        self.reply.error(err);
528    }
529}
530
531///
532/// Bmap Reply
533///
534#[derive(Debug)]
535pub struct ReplyBmap {
536    reply: ReplyRaw,
537}
538
539impl Reply for ReplyBmap {
540    fn new(unique: ll::RequestId, sender: ReplySender) -> ReplyBmap {
541        ReplyBmap {
542            reply: Reply::new(unique, sender),
543        }
544    }
545}
546
547impl ReplyBmap {
548    /// Reply to a request with a bmap
549    pub fn bmap(self, block: u64) {
550        self.reply.send_ll(&ll::ResponseStruct::new_bmap(block));
551    }
552
553    /// Reply to a request with the given error code
554    pub fn error(self, err: Errno) {
555        self.reply.error(err);
556    }
557}
558
559///
560/// Ioctl Reply
561///
562#[derive(Debug)]
563pub struct ReplyIoctl {
564    reply: ReplyRaw,
565}
566
567impl Reply for ReplyIoctl {
568    fn new(unique: ll::RequestId, sender: ReplySender) -> ReplyIoctl {
569        ReplyIoctl {
570            reply: Reply::new(unique, sender),
571        }
572    }
573}
574
575impl ReplyIoctl {
576    /// Reply to a request with an ioctl
577    pub fn ioctl(self, result: i32, data: &[u8]) {
578        self.reply
579            .send_ll(&ll::ResponseIoctl::new_ioctl(result, &[IoSlice::new(data)]));
580    }
581
582    /// Reply to a request with the given error code
583    pub fn error(self, err: Errno) {
584        self.reply.error(err);
585    }
586}
587
588///
589/// Poll Reply
590///
591#[derive(Debug)]
592pub struct ReplyPoll {
593    reply: ReplyRaw,
594}
595
596impl Reply for ReplyPoll {
597    fn new(unique: ll::RequestId, sender: ReplySender) -> ReplyPoll {
598        ReplyPoll {
599            reply: Reply::new(unique, sender),
600        }
601    }
602}
603
604impl ReplyPoll {
605    /// Reply to a request with ready poll events
606    pub fn poll(self, revents: PollEvents) {
607        self.reply.send_ll(&ll::ResponseStruct::new_poll(revents));
608    }
609
610    /// Reply to a request with the given error code
611    pub fn error(self, err: Errno) {
612        self.reply.error(err);
613    }
614}
615
616///
617/// Directory reply
618///
619#[derive(Debug)]
620pub struct ReplyDirectory {
621    reply: ReplyRaw,
622    data: DirEntList,
623}
624
625impl ReplyDirectory {
626    /// Creates a new `ReplyDirectory` with a specified buffer size.
627    pub(crate) fn new(unique: ll::RequestId, sender: ReplySender, size: usize) -> ReplyDirectory {
628        ReplyDirectory {
629            reply: Reply::new(unique, sender),
630            data: DirEntList::new(size),
631        }
632    }
633
634    /// Add an entry to the directory reply buffer. Returns true if the buffer is full.
635    /// A transparent offset value can be provided for each entry. The kernel uses these
636    /// value to request the next entries in further readdir calls
637    #[must_use]
638    pub fn add<T: AsRef<OsStr>>(
639        &mut self,
640        ino: INodeNo,
641        offset: u64,
642        kind: FileType,
643        name: T,
644    ) -> bool {
645        let name = name.as_ref();
646        self.data
647            .push(&DirEntry::new(ino, DirEntOffset(offset), kind, name))
648    }
649
650    /// Reply to a request with the filled directory buffer
651    pub fn ok(self) {
652        let response: ll::ResponseData = self.data.into();
653        self.reply.send_ll(&response);
654    }
655
656    /// Reply to a request with the given error code
657    pub fn error(self, err: Errno) {
658        self.reply.error(err);
659    }
660}
661
662///
663/// `DirectoryPlus` reply
664///
665#[derive(Debug)]
666pub struct ReplyDirectoryPlus {
667    reply: ReplyRaw,
668    buf: DirEntPlusList,
669}
670
671impl ReplyDirectoryPlus {
672    /// Creates a new `ReplyDirectory` with a specified buffer size.
673    pub(crate) fn new(
674        unique: ll::RequestId,
675        sender: ReplySender,
676        size: usize,
677    ) -> ReplyDirectoryPlus {
678        ReplyDirectoryPlus {
679            reply: Reply::new(unique, sender),
680            buf: DirEntPlusList::new(size),
681        }
682    }
683
684    /// Add an entry to the directory reply buffer. Returns true if the buffer is full.
685    /// A transparent offset value can be provided for each entry. The kernel uses these
686    /// value to request the next entries in further readdir calls
687    pub fn add<T: AsRef<OsStr>>(
688        &mut self,
689        ino: INodeNo,
690        offset: u64,
691        name: T,
692        ttl: &Duration,
693        attr: &FileAttr,
694        generation: Generation,
695    ) -> bool {
696        let name = name.as_ref();
697        self.buf.push(&DirEntryPlus::new(
698            ino,
699            generation,
700            DirEntOffset(offset),
701            name,
702            *ttl,
703            attr.into(),
704            *ttl,
705        ))
706    }
707
708    /// Reply to a request with the filled directory buffer
709    pub fn ok(self) {
710        let response: ll::ResponseData = self.buf.into();
711        self.reply.send_ll(&response);
712    }
713
714    /// Reply to a request with the given error code
715    pub fn error(self, err: Errno) {
716        self.reply.error(err);
717    }
718}
719
720///
721/// Xattr reply
722///
723#[derive(Debug)]
724pub struct ReplyXattr {
725    reply: ReplyRaw,
726}
727
728impl Reply for ReplyXattr {
729    fn new(unique: ll::RequestId, sender: ReplySender) -> ReplyXattr {
730        ReplyXattr {
731            reply: Reply::new(unique, sender),
732        }
733    }
734}
735
736impl ReplyXattr {
737    /// Reply to a request with the size of an extended attribute
738    pub fn size(self, size: u32) {
739        self.reply
740            .send_ll(&ll::ResponseStruct::new_xattr_size(size));
741    }
742
743    /// Reply to a request with the data of an extended attribute
744    pub fn data(self, data: &[u8]) {
745        self.reply.send_ll(&ll::ResponseSlice(data));
746    }
747
748    /// Reply to a request with the given error code.
749    pub fn error(self, err: Errno) {
750        self.reply.error(err);
751    }
752}
753
754///
755/// Lseek Reply
756///
757#[derive(Debug)]
758pub struct ReplyLseek {
759    reply: ReplyRaw,
760}
761
762impl Reply for ReplyLseek {
763    fn new(unique: ll::RequestId, sender: ReplySender) -> ReplyLseek {
764        ReplyLseek {
765            reply: Reply::new(unique, sender),
766        }
767    }
768}
769
770impl ReplyLseek {
771    /// Reply to a request with seeked offset
772    pub fn offset(self, offset: i64) {
773        self.reply.send_ll(&ll::ResponseStruct::new_lseek(offset));
774    }
775
776    /// Reply to a request with the given error code
777    pub fn error(self, err: Errno) {
778        self.reply.error(err);
779    }
780}
781
782#[cfg(test)]
783mod test {
784    use std::sync::mpsc::sync_channel;
785    use std::thread;
786    use std::time::Duration;
787    use std::time::UNIX_EPOCH;
788
789    use zerocopy::Immutable;
790    use zerocopy::IntoBytes;
791
792    use crate::FileAttr;
793    use crate::FileType;
794    use crate::reply::*;
795
796    #[derive(Debug, IntoBytes, Immutable)]
797    #[repr(C)]
798    struct Data {
799        a: u8,
800        b: u8,
801        c: u16,
802    }
803
804    #[test]
805    fn serialize_empty() {
806        assert!(().as_bytes().is_empty());
807    }
808
809    #[test]
810    fn serialize_slice() {
811        let data: [u8; 4] = [0x12, 0x34, 0x56, 0x78];
812        assert_eq!(data.as_bytes(), [0x12, 0x34, 0x56, 0x78]);
813    }
814
815    #[test]
816    fn serialize_struct() {
817        let data = Data {
818            a: 0x12,
819            b: 0x34,
820            c: 0x5678,
821        };
822        assert_eq!(data.as_bytes(), [0x12, 0x34, 0x78, 0x56]);
823    }
824
825    #[test]
826    fn reply_raw() {
827        let data = Data {
828            a: 0x12,
829            b: 0x34,
830            c: 0x5678,
831        };
832        let sender = ReplySender::Assert(AssertSender {
833            expected: vec![
834                0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00,
835                0x00, 0x00, 0x12, 0x34, 0x78, 0x56,
836            ],
837        });
838        let reply: ReplyRaw = Reply::new(ll::RequestId(0xdeadbeef), sender);
839        reply.send_ll(&ll::ResponseData::new_data(data.as_bytes()));
840    }
841
842    #[test]
843    fn reply_error() {
844        let sender = ReplySender::Assert(AssertSender {
845            expected: vec![
846                0x10, 0x00, 0x00, 0x00, 0xbe, 0xff, 0xff, 0xff, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00,
847                0x00, 0x00,
848            ],
849        });
850        let reply: ReplyRaw = Reply::new(ll::RequestId(0xdeadbeef), sender);
851        reply.error(Errno::from_i32(66));
852    }
853
854    #[test]
855    fn reply_empty() {
856        let sender = ReplySender::Assert(AssertSender {
857            expected: vec![
858                0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00,
859                0x00, 0x00,
860            ],
861        });
862        let reply: ReplyEmpty = Reply::new(ll::RequestId(0xdeadbeef), sender);
863        reply.ok();
864    }
865
866    #[test]
867    fn reply_data() {
868        let sender = ReplySender::Assert(AssertSender {
869            expected: vec![
870                0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00,
871                0x00, 0x00, 0xde, 0xad, 0xbe, 0xef,
872            ],
873        });
874        let reply: ReplyData = Reply::new(ll::RequestId(0xdeadbeef), sender);
875        reply.data(&[0xde, 0xad, 0xbe, 0xef]);
876    }
877
878    #[test]
879    fn reply_entry() {
880        let mut expected = if cfg!(target_os = "macos") {
881            vec![
882                0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00,
883                0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00,
884                0x00, 0x00, 0x00, 0x00, 0x65, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x87,
885                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, 0x00, 0x00, 0x21, 0x43, 0x00, 0x00,
886                0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00,
887                0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00,
888                0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12,
889                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
890                0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0x78, 0x56,
891                0x00, 0x00, 0xa4, 0x81, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00,
892                0x77, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00,
893            ]
894        } else {
895            vec![
896                0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00,
897                0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00,
898                0x00, 0x00, 0x00, 0x00, 0x65, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x87,
899                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, 0x00, 0x00, 0x21, 0x43, 0x00, 0x00,
900                0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00,
901                0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00,
902                0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12,
903                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00,
904                0x78, 0x56, 0x00, 0x00, 0xa4, 0x81, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x66, 0x00,
905                0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00,
906            ]
907        };
908
909        expected.extend(vec![0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
910        expected[0] = (expected.len()) as u8;
911
912        let sender = ReplySender::Assert(AssertSender { expected });
913        let reply: ReplyEntry = Reply::new(ll::RequestId(0xdeadbeef), sender);
914        let time = UNIX_EPOCH + Duration::new(0x1234, 0x5678);
915        let ttl = Duration::new(0x8765, 0x4321);
916        let attr = FileAttr {
917            ino: INodeNo(0x11),
918            size: 0x22,
919            blocks: 0x33,
920            atime: time,
921            mtime: time,
922            ctime: time,
923            crtime: time,
924            kind: FileType::RegularFile,
925            perm: 0o644,
926            nlink: 0x55,
927            uid: 0x66,
928            gid: 0x77,
929            rdev: 0x88,
930            flags: 0x99,
931            blksize: 0xbb,
932        };
933        reply.entry(&ttl, &attr, ll::Generation(0xaa));
934    }
935
936    #[test]
937    fn reply_attr() {
938        let mut expected = if cfg!(target_os = "macos") {
939            vec![
940                0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00,
941                0x00, 0x00, 0x65, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, 0x00, 0x00,
942                0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00,
943                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
944                0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00,
945                0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00,
946                0x00, 0x00, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0x78, 0x56,
947                0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0xa4, 0x81, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00,
948                0x66, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x99, 0x00,
949                0x00, 0x00,
950            ]
951        } else {
952            vec![
953                0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00,
954                0x00, 0x00, 0x65, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, 0x00, 0x00,
955                0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00,
956                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
957                0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00,
958                0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00,
959                0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0xa4, 0x81, 0x00, 0x00, 0x55, 0x00,
960                0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00,
961            ]
962        };
963
964        expected.extend_from_slice(&[0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
965        expected[0] = expected.len() as u8;
966
967        let sender = ReplySender::Assert(AssertSender { expected });
968        let reply: ReplyAttr = Reply::new(ll::RequestId(0xdeadbeef), sender);
969        let time = UNIX_EPOCH + Duration::new(0x1234, 0x5678);
970        let ttl = Duration::new(0x8765, 0x4321);
971        let attr = FileAttr {
972            ino: INodeNo(0x11),
973            size: 0x22,
974            blocks: 0x33,
975            atime: time,
976            mtime: time,
977            ctime: time,
978            crtime: time,
979            kind: FileType::RegularFile,
980            perm: 0o644,
981            nlink: 0x55,
982            uid: 0x66,
983            gid: 0x77,
984            rdev: 0x88,
985            flags: 0x99,
986            blksize: 0xbb,
987        };
988        reply.attr(&ttl, &attr);
989    }
990
991    #[test]
992    #[cfg(target_os = "macos")]
993    fn reply_xtimes() {
994        let sender = ReplySender::Assert(AssertSender {
995            expected: vec![
996                0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00,
997                0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00,
998                0x00, 0x00, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00,
999            ],
1000        });
1001        let reply: ReplyXTimes = Reply::new(ll::RequestId(0xdeadbeef), sender);
1002        let time = UNIX_EPOCH + Duration::new(0x1234, 0x5678);
1003        reply.xtimes(time, time);
1004    }
1005
1006    #[test]
1007    fn reply_open() {
1008        let sender = ReplySender::Assert(AssertSender {
1009            expected: vec![
1010                0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00,
1011                0x00, 0x00, 0x22, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00,
1012                0x00, 0x00, 0x00, 0x00,
1013            ],
1014        });
1015        let reply: ReplyOpen = Reply::new(ll::RequestId(0xdeadbeef), sender);
1016        reply.opened(ll::FileHandle(0x1122), FopenFlags::from_bits_retain(0x33));
1017    }
1018
1019    #[test]
1020    fn reply_write() {
1021        let sender = ReplySender::Assert(AssertSender {
1022            expected: vec![
1023                0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00,
1024                0x00, 0x00, 0x22, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1025            ],
1026        });
1027        let reply: ReplyWrite = Reply::new(ll::RequestId(0xdeadbeef), sender);
1028        reply.written(0x1122);
1029    }
1030
1031    #[test]
1032    fn reply_statfs() {
1033        let sender = ReplySender::Assert(AssertSender {
1034            expected: vec![
1035                0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00,
1036                0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
1037                0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00,
1038                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1039                0x66, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00,
1040                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1041                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1042            ],
1043        });
1044        let reply: ReplyStatfs = Reply::new(ll::RequestId(0xdeadbeef), sender);
1045        reply.statfs(0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88);
1046    }
1047
1048    #[test]
1049    fn reply_create() {
1050        let mut expected = if cfg!(target_os = "macos") {
1051            vec![
1052                0xa8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00,
1053                0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00,
1054                0x00, 0x00, 0x00, 0x00, 0x65, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x87,
1055                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, 0x00, 0x00, 0x21, 0x43, 0x00, 0x00,
1056                0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00,
1057                0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00,
1058                0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12,
1059                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1060                0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0x78, 0x56,
1061                0x00, 0x00, 0xa4, 0x81, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00,
1062                0x77, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0xbb, 0x00,
1063                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1064            ]
1065        } else {
1066            vec![
1067                0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00,
1068                0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00,
1069                0x00, 0x00, 0x00, 0x00, 0x65, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x87,
1070                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, 0x00, 0x00, 0x21, 0x43, 0x00, 0x00,
1071                0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00,
1072                0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00,
1073                0x00, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x12,
1074                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00,
1075                0x78, 0x56, 0x00, 0x00, 0xa4, 0x81, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x66, 0x00,
1076                0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0xbb, 0x00, 0x00, 0x00,
1077                0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1078            ]
1079        };
1080
1081        let insert_at = expected.len() - 16;
1082        expected.splice(
1083            insert_at..insert_at,
1084            vec![0xdd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
1085        );
1086        expected[0] = (expected.len()) as u8;
1087
1088        let sender = ReplySender::Assert(AssertSender { expected });
1089        let reply: ReplyCreate = Reply::new(ll::RequestId(0xdeadbeef), sender);
1090        let time = UNIX_EPOCH + Duration::new(0x1234, 0x5678);
1091        let ttl = Duration::new(0x8765, 0x4321);
1092        let attr = FileAttr {
1093            ino: INodeNo(0x11),
1094            size: 0x22,
1095            blocks: 0x33,
1096            atime: time,
1097            mtime: time,
1098            ctime: time,
1099            crtime: time,
1100            kind: FileType::RegularFile,
1101            perm: 0o644,
1102            nlink: 0x55,
1103            uid: 0x66,
1104            gid: 0x77,
1105            rdev: 0x88,
1106            flags: 0x99,
1107            blksize: 0xdd,
1108        };
1109        reply.created(
1110            &ttl,
1111            &attr,
1112            ll::Generation(0xaa),
1113            ll::FileHandle(0xbb),
1114            FopenFlags::from_bits_retain(0x0c),
1115        );
1116    }
1117
1118    #[test]
1119    fn reply_lock() {
1120        let sender = ReplySender::Assert(AssertSender {
1121            expected: vec![
1122                0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00,
1123                0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
1124                0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00,
1125            ],
1126        });
1127        let reply: ReplyLock = Reply::new(ll::RequestId(0xdeadbeef), sender);
1128        reply.locked(0x11, 0x22, 0x33, 0x44);
1129    }
1130
1131    #[test]
1132    fn reply_bmap() {
1133        let sender = ReplySender::Assert(AssertSender {
1134            expected: vec![
1135                0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00,
1136                0x00, 0x00, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1137            ],
1138        });
1139        let reply: ReplyBmap = Reply::new(ll::RequestId(0xdeadbeef), sender);
1140        reply.bmap(0x1234);
1141    }
1142
1143    #[test]
1144    fn reply_directory() {
1145        let sender = ReplySender::Assert(AssertSender {
1146            expected: vec![
1147                0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00,
1148                0x00, 0x00, 0xbb, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
1149                0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x68, 0x65,
1150                0x6c, 0x6c, 0x6f, 0x00, 0x00, 0x00, 0xdd, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1151                0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00,
1152                0x00, 0x00, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x72, 0x73,
1153            ],
1154        });
1155        let mut reply = ReplyDirectory::new(ll::RequestId(0xdeadbeef), sender, 4096);
1156        assert!(!reply.add(INodeNo(0xaabb), 1, FileType::Directory, "hello"));
1157        assert!(!reply.add(INodeNo(0xccdd), 2, FileType::RegularFile, "world.rs"));
1158        reply.ok();
1159    }
1160
1161    #[test]
1162    fn reply_xattr_size() {
1163        let sender = ReplySender::Assert(AssertSender {
1164            expected: vec![
1165                0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEF, 0xBE, 0xAD, 0xDE, 0x00, 0x00,
1166                0x00, 0x00, 0x78, 0x56, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00,
1167            ],
1168        });
1169        let reply = ReplyXattr::new(ll::RequestId(0xdeadbeef), sender);
1170        reply.size(0x12345678);
1171    }
1172
1173    #[test]
1174    fn reply_xattr_data() {
1175        let sender = ReplySender::Assert(AssertSender {
1176            expected: vec![
1177                0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEF, 0xBE, 0xAD, 0xDE, 0x00, 0x00,
1178                0x00, 0x00, 0x11, 0x22, 0x33, 0x44,
1179            ],
1180        });
1181        let reply = ReplyXattr::new(ll::RequestId(0xdeadbeef), sender);
1182        reply.data(&[0x11, 0x22, 0x33, 0x44]);
1183    }
1184
1185    #[test]
1186    fn async_reply() {
1187        let (tx, rx) = sync_channel::<()>(1);
1188        let reply: ReplyEmpty = Reply::new(ll::RequestId(0xdeadbeef), ReplySender::Sync(tx));
1189        thread::spawn(move || {
1190            reply.ok();
1191        });
1192        rx.recv().unwrap();
1193    }
1194}