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