Skip to main content

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