[go: up one dir, main page]

fuse/
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::{mem, ptr, slice};
10use std::convert::AsRef;
11use std::ffi::OsStr;
12use std::fmt;
13use std::marker::PhantomData;
14use std::os::unix::ffi::OsStrExt;
15use libc::{c_int, S_IFIFO, S_IFCHR, S_IFBLK, S_IFDIR, S_IFREG, S_IFLNK, S_IFSOCK, EIO};
16use time::Timespec;
17use kernel::{fuse_attr, fuse_kstatfs, fuse_file_lock, fuse_entry_out, fuse_attr_out};
18use kernel::{fuse_open_out, fuse_write_out, fuse_statfs_out, fuse_lk_out, fuse_bmap_out};
19use kernel::fuse_getxattr_out;
20#[cfg(target_os = "macos")]
21use kernel::fuse_getxtimes_out;
22use kernel::{fuse_out_header, fuse_dirent};
23use {FileType, FileAttr};
24
25/// Generic reply callback to send data
26pub trait ReplySender: Send + 'static {
27    /// Send data.
28    fn send(&self, data: &[&[u8]]);
29}
30
31impl fmt::Debug for Box<ReplySender> {
32    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
33        write!(f, "Box<ReplySender>")
34    }
35}
36
37/// Generic reply trait
38pub trait Reply {
39    /// Create a new reply for the given request
40    fn new<S: ReplySender>(unique: u64, sender: S) -> Self;
41}
42
43/// Serialize an arbitrary type to bytes (memory copy, useful for fuse_*_out types)
44fn as_bytes<T, U, F: FnOnce(&[&[u8]]) -> U>(data: &T, f: F) -> U {
45    let len = mem::size_of::<T>();
46    match len {
47        0 => f(&[]),
48        len => {
49            let p = data as *const T as *const u8;
50            let bytes = unsafe { slice::from_raw_parts(p, len) };
51            f(&[bytes])
52        }
53    }
54}
55
56// Some platforms like Linux x86_64 have mode_t = u32, and lint warns of a trivial_numeric_casts.
57// But others like macOS x86_64 have mode_t = u16, requiring a typecast.  So, just silence lint.
58#[allow(trivial_numeric_casts)]
59/// Returns the mode for a given file kind and permission
60fn mode_from_kind_and_perm(kind: FileType, perm: u16) -> u32 {
61    (match kind {
62        FileType::NamedPipe => S_IFIFO,
63        FileType::CharDevice => S_IFCHR,
64        FileType::BlockDevice => S_IFBLK,
65        FileType::Directory => S_IFDIR,
66        FileType::RegularFile => S_IFREG,
67        FileType::Symlink => S_IFLNK,
68        FileType::Socket => S_IFSOCK,
69    }) as u32 | perm as u32
70}
71
72/// Returns a fuse_attr from FileAttr
73#[cfg(target_os = "macos")]
74fn fuse_attr_from_attr(attr: &FileAttr) -> fuse_attr {
75    fuse_attr {
76        ino: attr.ino,
77        size: attr.size,
78        blocks: attr.blocks,
79        atime: attr.atime.sec,
80        mtime: attr.mtime.sec,
81        ctime: attr.ctime.sec,
82        crtime: attr.crtime.sec,
83        atimensec: attr.atime.nsec,
84        mtimensec: attr.mtime.nsec,
85        ctimensec: attr.ctime.nsec,
86        crtimensec: attr.crtime.nsec,
87        mode: mode_from_kind_and_perm(attr.kind, attr.perm),
88        nlink: attr.nlink,
89        uid: attr.uid,
90        gid: attr.gid,
91        rdev: attr.rdev,
92        flags: attr.flags,
93    }
94}
95
96/// Returns a fuse_attr from FileAttr
97#[cfg(not(target_os = "macos"))]
98fn fuse_attr_from_attr(attr: &FileAttr) -> fuse_attr {
99    fuse_attr {
100        ino: attr.ino,
101        size: attr.size,
102        blocks: attr.blocks,
103        atime: attr.atime.sec,
104        mtime: attr.mtime.sec,
105        ctime: attr.ctime.sec,
106        atimensec: attr.atime.nsec,
107        mtimensec: attr.mtime.nsec,
108        ctimensec: attr.ctime.nsec,
109        mode: mode_from_kind_and_perm(attr.kind, attr.perm),
110        nlink: attr.nlink,
111        uid: attr.uid,
112        gid: attr.gid,
113        rdev: attr.rdev,
114    }
115}
116
117///
118/// Raw reply
119///
120#[derive(Debug)]
121pub struct ReplyRaw<T> {
122    /// Unique id of the request to reply to
123    unique: u64,
124    /// Closure to call for sending the reply
125    sender: Option<Box<ReplySender>>,
126    /// Marker for being able to have T on this struct (which enforces
127    /// reply types to send the correct type of data)
128    marker: PhantomData<T>,
129}
130
131impl<T> Reply for ReplyRaw<T> {
132    fn new<S: ReplySender>(unique: u64, sender: S) -> ReplyRaw<T> {
133        let sender = Box::new(sender);
134        ReplyRaw { unique: unique, sender: Some(sender), marker: PhantomData }
135    }
136}
137
138impl<T> ReplyRaw<T> {
139    /// Reply to a request with the given error code and data. Must be called
140    /// only once (the `ok` and `error` methods ensure this by consuming `self`)
141    fn send(&mut self, err: c_int, bytes: &[&[u8]]) {
142        assert!(self.sender.is_some());
143        let len = bytes.iter().fold(0, |l, b| l + b.len());
144        let header = fuse_out_header {
145            len: (mem::size_of::<fuse_out_header>() + len) as u32,
146            error: -err,
147            unique: self.unique,
148        };
149        as_bytes(&header, |headerbytes| {
150            let sender = self.sender.take().unwrap();
151            let mut sendbytes = headerbytes.to_vec();
152            sendbytes.extend(bytes);
153            sender.send(&sendbytes);
154        });
155    }
156
157    /// Reply to a request with the given type
158    pub fn ok(mut self, data: &T) {
159        as_bytes(data, |bytes| {
160            self.send(0, bytes);
161        })
162    }
163
164    /// Reply to a request with the given error code
165    pub fn error(mut self, err: c_int) {
166        self.send(err, &[]);
167    }
168}
169
170impl<T> Drop for ReplyRaw<T> {
171    fn drop(&mut self) {
172        if self.sender.is_some() {
173            warn!("Reply not sent for operation {}, replying with I/O error", self.unique);
174            self.send(EIO, &[]);
175        }
176    }
177}
178
179///
180/// Empty reply
181///
182#[derive(Debug)]
183pub struct ReplyEmpty {
184    reply: ReplyRaw<()>,
185}
186
187impl Reply for ReplyEmpty {
188    fn new<S: ReplySender>(unique: u64, sender: S) -> ReplyEmpty {
189        ReplyEmpty { reply: Reply::new(unique, sender) }
190    }
191}
192
193impl ReplyEmpty {
194    /// Reply to a request with nothing
195    pub fn ok(mut self) {
196        self.reply.send(0, &[]);
197    }
198
199    /// Reply to a request with the given error code
200    pub fn error(self, err: c_int) {
201        self.reply.error(err);
202    }
203}
204
205///
206/// Data reply
207///
208#[derive(Debug)]
209pub struct ReplyData {
210    reply: ReplyRaw<()>,
211}
212
213impl Reply for ReplyData {
214    fn new<S: ReplySender>(unique: u64, sender: S) -> ReplyData {
215        ReplyData { reply: Reply::new(unique, sender) }
216    }
217}
218
219impl ReplyData {
220    /// Reply to a request with the given data
221    pub fn data(mut self, data: &[u8]) {
222        self.reply.send(0, &[data]);
223    }
224
225    /// Reply to a request with the given error code
226    pub fn error(self, err: c_int) {
227        self.reply.error(err);
228    }
229}
230
231///
232/// Entry reply
233///
234#[derive(Debug)]
235pub struct ReplyEntry {
236    reply: ReplyRaw<fuse_entry_out>,
237}
238
239impl Reply for ReplyEntry {
240    fn new<S: ReplySender>(unique: u64, sender: S) -> ReplyEntry {
241        ReplyEntry { reply: Reply::new(unique, sender) }
242    }
243}
244
245impl ReplyEntry {
246    /// Reply to a request with the given entry
247    pub fn entry(self, ttl: &Timespec, attr: &FileAttr, generation: u64) {
248        self.reply.ok(&fuse_entry_out {
249            nodeid: attr.ino,
250            generation: generation,
251            entry_valid: ttl.sec,
252            attr_valid: ttl.sec,
253            entry_valid_nsec: ttl.nsec,
254            attr_valid_nsec: ttl.nsec,
255            attr: fuse_attr_from_attr(attr),
256        });
257    }
258
259    /// Reply to a request with the given error code
260    pub fn error(self, err: c_int) {
261        self.reply.error(err);
262    }
263}
264
265///
266/// Attribute Reply
267///
268#[derive(Debug)]
269pub struct ReplyAttr {
270    reply: ReplyRaw<fuse_attr_out>,
271}
272
273impl Reply for ReplyAttr {
274    fn new<S: ReplySender>(unique: u64, sender: S) -> ReplyAttr {
275        ReplyAttr { reply: Reply::new(unique, sender) }
276    }
277}
278
279impl ReplyAttr {
280    /// Reply to a request with the given attribute
281    pub fn attr(self, ttl: &Timespec, attr: &FileAttr) {
282        self.reply.ok(&fuse_attr_out {
283            attr_valid: ttl.sec,
284            attr_valid_nsec: ttl.nsec,
285            dummy: 0,
286            attr: fuse_attr_from_attr(attr),
287        });
288    }
289
290    /// Reply to a request with the given error code
291    pub fn error(self, err: c_int) {
292        self.reply.error(err);
293    }
294}
295
296///
297/// XTimes Reply
298///
299#[cfg(target_os = "macos")]
300#[derive(Debug)]
301pub struct ReplyXTimes {
302    reply: ReplyRaw<fuse_getxtimes_out>,
303}
304
305#[cfg(target_os = "macos")]
306impl Reply for ReplyXTimes {
307    fn new<S: ReplySender>(unique: u64, sender: S) -> ReplyXTimes {
308        ReplyXTimes { reply: Reply::new(unique, sender) }
309    }
310}
311
312#[cfg(target_os = "macos")]
313impl ReplyXTimes {
314    /// Reply to a request with the given xtimes
315    pub fn xtimes(self, bkuptime: Timespec, crtime: Timespec) {
316        self.reply.ok(&fuse_getxtimes_out {
317            bkuptime: bkuptime.sec,
318            crtime: crtime.sec,
319            bkuptimensec: bkuptime.nsec,
320            crtimensec: crtime.nsec,
321        });
322    }
323
324    /// Reply to a request with the given error code
325    pub fn error(self, err: c_int) {
326        self.reply.error(err);
327    }
328}
329
330///
331/// Open Reply
332///
333#[derive(Debug)]
334pub struct ReplyOpen {
335    reply: ReplyRaw<fuse_open_out>,
336}
337
338impl Reply for ReplyOpen {
339    fn new<S: ReplySender>(unique: u64, sender: S) -> ReplyOpen {
340        ReplyOpen { reply: Reply::new(unique, sender) }
341    }
342}
343
344impl ReplyOpen {
345    /// Reply to a request with the given open result
346    pub fn opened(self, fh: u64, flags: u32) {
347        self.reply.ok(&fuse_open_out {
348            fh: fh,
349            open_flags: flags,
350            padding: 0,
351        });
352    }
353
354    /// Reply to a request with the given error code
355    pub fn error(self, err: c_int) {
356        self.reply.error(err);
357    }
358}
359
360///
361/// Write Reply
362///
363#[derive(Debug)]
364pub struct ReplyWrite {
365    reply: ReplyRaw<fuse_write_out>,
366}
367
368impl Reply for ReplyWrite {
369    fn new<S: ReplySender>(unique: u64, sender: S) -> ReplyWrite {
370        ReplyWrite { reply: Reply::new(unique, sender) }
371    }
372}
373
374impl ReplyWrite {
375    /// Reply to a request with the given open result
376    pub fn written(self, size: u32) {
377        self.reply.ok(&fuse_write_out {
378            size: size,
379            padding: 0,
380        });
381    }
382
383    /// Reply to a request with the given error code
384    pub fn error(self, err: c_int) {
385        self.reply.error(err);
386    }
387}
388
389///
390/// Statfs Reply
391///
392#[derive(Debug)]
393pub struct ReplyStatfs {
394    reply: ReplyRaw<fuse_statfs_out>,
395}
396
397impl Reply for ReplyStatfs {
398    fn new<S: ReplySender>(unique: u64, sender: S) -> ReplyStatfs {
399        ReplyStatfs { reply: Reply::new(unique, sender) }
400    }
401}
402
403impl ReplyStatfs {
404    /// Reply to a request with the given open result
405    pub fn statfs(self, blocks: u64, bfree: u64, bavail: u64, files: u64, ffree: u64, bsize: u32, namelen: u32, frsize: u32) {
406        self.reply.ok(&fuse_statfs_out {
407            st: fuse_kstatfs {
408                blocks: blocks,
409                bfree: bfree,
410                bavail: bavail,
411                files: files,
412                ffree: ffree,
413                bsize: bsize,
414                namelen: namelen,
415                frsize: frsize,
416                padding: 0,
417                spare: [0; 6],
418            },
419        });
420    }
421
422    /// Reply to a request with the given error code
423    pub fn error(self, err: c_int) {
424        self.reply.error(err);
425    }
426}
427
428///
429/// Create reply
430///
431#[derive(Debug)]
432pub struct ReplyCreate {
433    reply: ReplyRaw<(fuse_entry_out, fuse_open_out)>,
434}
435
436impl Reply for ReplyCreate {
437    fn new<S: ReplySender>(unique: u64, sender: S) -> ReplyCreate {
438        ReplyCreate { reply: Reply::new(unique, sender) }
439    }
440}
441
442impl ReplyCreate {
443    /// Reply to a request with the given entry
444    pub fn created(self, ttl: &Timespec, attr: &FileAttr, generation: u64, fh: u64, flags: u32) {
445        self.reply.ok(&(fuse_entry_out {
446            nodeid: attr.ino,
447            generation: generation,
448            entry_valid: ttl.sec,
449            attr_valid: ttl.sec,
450            entry_valid_nsec: ttl.nsec,
451            attr_valid_nsec: ttl.nsec,
452            attr: fuse_attr_from_attr(attr),
453        }, fuse_open_out {
454            fh: fh,
455            open_flags: flags,
456            padding: 0,
457        }));
458    }
459
460    /// Reply to a request with the given error code
461    pub fn error(self, err: c_int) {
462        self.reply.error(err);
463    }
464}
465
466///
467/// Lock Reply
468///
469#[derive(Debug)]
470pub struct ReplyLock {
471    reply: ReplyRaw<fuse_lk_out>,
472}
473
474impl Reply for ReplyLock {
475    fn new<S: ReplySender>(unique: u64, sender: S) -> ReplyLock {
476        ReplyLock { reply: Reply::new(unique, sender) }
477    }
478}
479
480impl ReplyLock {
481    /// Reply to a request with the given open result
482    pub fn locked(self, start: u64, end: u64, typ: u32, pid: u32) {
483        self.reply.ok(&fuse_lk_out {
484            lk: fuse_file_lock {
485                start: start,
486                end: end,
487                typ: typ,
488                pid: pid,
489            },
490        });
491    }
492
493    /// Reply to a request with the given error code
494    pub fn error(self, err: c_int) {
495        self.reply.error(err);
496    }
497}
498
499///
500/// Bmap Reply
501///
502#[derive(Debug)]
503pub struct ReplyBmap {
504    reply: ReplyRaw<fuse_bmap_out>,
505}
506
507impl Reply for ReplyBmap {
508    fn new<S: ReplySender>(unique: u64, sender: S) -> ReplyBmap {
509        ReplyBmap { reply: Reply::new(unique, sender) }
510    }
511}
512
513impl ReplyBmap {
514    /// Reply to a request with the given open result
515    pub fn bmap(self, block: u64) {
516        self.reply.ok(&fuse_bmap_out {
517            block: block,
518        });
519    }
520
521    /// Reply to a request with the given error code
522    pub fn error(self, err: c_int) {
523        self.reply.error(err);
524    }
525}
526
527///
528/// Directory reply
529///
530#[derive(Debug)]
531pub struct ReplyDirectory {
532    reply: ReplyRaw<()>,
533    data: Vec<u8>,
534}
535
536impl ReplyDirectory {
537    /// Creates a new ReplyDirectory with a specified buffer size.
538    pub fn new<S: ReplySender>(unique: u64, sender: S, size: usize) -> ReplyDirectory {
539        ReplyDirectory {
540            reply: Reply::new(unique, sender),
541            data: Vec::with_capacity(size),
542        }
543    }
544
545    /// Add an entry to the directory reply buffer. Returns true if the buffer is full.
546    /// A transparent offset value can be provided for each entry. The kernel uses these
547    /// value to request the next entries in further readdir calls
548    pub fn add<T: AsRef<OsStr>>(&mut self, ino: u64, offset: i64, kind: FileType, name: T) -> bool {
549        let name = name.as_ref().as_bytes();
550        let entlen = mem::size_of::<fuse_dirent>() + name.len();
551        let entsize = (entlen + mem::size_of::<u64>() - 1) & !(mem::size_of::<u64>() - 1); // 64bit align
552        let padlen = entsize - entlen;
553        if self.data.len() + entsize > self.data.capacity() { return true; }
554        unsafe {
555            let p = self.data.as_mut_ptr().offset(self.data.len() as isize);
556            let pdirent: *mut fuse_dirent = mem::transmute(p);
557            (*pdirent).ino = ino;
558            (*pdirent).off = offset;
559            (*pdirent).namelen = name.len() as u32;
560            (*pdirent).typ = mode_from_kind_and_perm(kind, 0) >> 12;
561            let p = p.offset(mem::size_of_val(&*pdirent) as isize);
562            ptr::copy_nonoverlapping(name.as_ptr(), p, name.len());
563            let p = p.offset(name.len() as isize);
564            ptr::write_bytes(p, 0u8, padlen);
565            let newlen = self.data.len() + entsize;
566            self.data.set_len(newlen);
567        }
568        false
569    }
570
571    /// Reply to a request with the filled directory buffer
572    pub fn ok(mut self) {
573        self.reply.send(0, &[&self.data]);
574    }
575
576    /// Reply to a request with the given error code
577    pub fn error(self, err: c_int) {
578        self.reply.error(err);
579    }
580}
581
582///
583/// Xattr reply
584///
585#[derive(Debug)]
586pub struct ReplyXattr {
587    reply: ReplyRaw<fuse_getxattr_out>,
588}
589
590impl Reply for ReplyXattr {
591    fn new<S: ReplySender>(unique: u64, sender: S) -> ReplyXattr {
592        ReplyXattr { reply: Reply::new(unique, sender) }
593    }
594}
595
596impl ReplyXattr {
597    /// Reply to a request with the size of the xattr.
598    pub fn size(self, size: u32) {
599        self.reply.ok(&fuse_getxattr_out {
600            size: size,
601            padding: 0,
602        });
603    }
604
605    /// Reply to a request with the data in the xattr.
606    pub fn data(mut self, data: &[u8]) {
607        self.reply.send(0, &[data]);
608    }
609
610    /// Reply to a request with the given error code.
611    pub fn error(self, err: c_int) {
612        self.reply.error(err);
613    }
614}
615
616#[cfg(test)]
617mod test {
618    use std::thread;
619    use std::sync::mpsc::{channel, Sender};
620    use time::Timespec;
621    use super::as_bytes;
622    use super::{Reply, ReplyRaw, ReplyEmpty, ReplyData, ReplyEntry, ReplyAttr, ReplyOpen};
623    use super::{ReplyWrite, ReplyStatfs, ReplyCreate, ReplyLock, ReplyBmap, ReplyDirectory};
624    use super::ReplyXattr;
625    #[cfg(target_os = "macos")]
626    use super::ReplyXTimes;
627    use {FileType, FileAttr};
628
629    #[allow(dead_code)]
630    #[repr(C)]
631    struct Data { a: u8, b: u8, c: u16 }
632
633    #[test]
634    fn serialize_empty() {
635        let data = ();
636        as_bytes(&data, |bytes| {
637            assert!(bytes.is_empty());
638        });
639    }
640
641    #[test]
642    fn serialize_slice() {
643        let data: [u8; 4] = [0x12, 0x34, 0x56, 0x78];
644        as_bytes(&data, |bytes| {
645            assert_eq!(bytes, [[0x12, 0x34, 0x56, 0x78]]);
646        });
647    }
648
649    #[test]
650    fn serialize_struct() {
651        let data = Data { a: 0x12, b: 0x34, c: 0x5678 };
652        as_bytes(&data, |bytes| {
653            assert_eq!(bytes, [[0x12, 0x34, 0x78, 0x56]]);
654        });
655    }
656
657    #[test]
658    fn serialize_tuple() {
659        let data = (Data { a: 0x12, b: 0x34, c: 0x5678 }, Data { a: 0x9a, b: 0xbc, c: 0xdef0 });
660        as_bytes(&data, |bytes| {
661            assert_eq!(bytes, [[0x12, 0x34, 0x78, 0x56, 0x9a, 0xbc, 0xf0, 0xde]]);
662        });
663    }
664
665
666    struct AssertSender {
667        expected: Vec<Vec<u8>>,
668    }
669
670    impl super::ReplySender for AssertSender {
671        fn send(&self, data: &[&[u8]]) {
672            assert_eq!(self.expected, data);
673        }
674    }
675
676    #[test]
677    fn reply_raw() {
678        let data = Data { a: 0x12, b: 0x34, c: 0x5678 };
679        let sender = AssertSender {
680            expected: vec![
681                vec![0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00],
682                vec![0x12, 0x34, 0x78, 0x56],
683            ]
684        };
685        let reply: ReplyRaw<Data> = Reply::new(0xdeadbeef, sender);
686        reply.ok(&data);
687    }
688
689    #[test]
690    fn reply_error() {
691        let sender = AssertSender {
692            expected: vec![
693                vec![0x10, 0x00, 0x00, 0x00, 0xbe, 0xff, 0xff, 0xff,  0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00],
694            ]
695        };
696        let reply: ReplyRaw<Data> = Reply::new(0xdeadbeef, sender);
697        reply.error(66);
698    }
699
700    #[test]
701    fn reply_empty() {
702        let sender = AssertSender {
703            expected: vec![
704                vec![0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00],
705            ]
706        };
707        let reply: ReplyEmpty = Reply::new(0xdeadbeef, sender);
708        reply.ok();
709    }
710
711    #[test]
712    fn reply_data() {
713        let sender = AssertSender {
714            expected: vec![
715                vec![0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00],
716                vec![0xde, 0xad, 0xbe, 0xef],
717            ]
718        };
719        let reply: ReplyData = Reply::new(0xdeadbeef, sender);
720        reply.data(&[0xde, 0xad, 0xbe, 0xef]);
721    }
722
723    #[test]
724    fn reply_entry() {
725        let sender = AssertSender {
726            expected: if cfg!(target_os = "macos") {
727                vec![
728                    vec![0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00],
729                    vec![0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
730                         0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
731                         0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00,  0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
732                         0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
733                         0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
734                         0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
735                         0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00,  0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00,
736                         0xa4, 0x81, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00,  0x66, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00,
737                         0x88, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00],
738                ]
739            } else {
740                vec![
741                    vec![0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00],
742                    vec![0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
743                         0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
744                         0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00,  0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
745                         0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
746                         0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
747                         0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00,
748                         0x78, 0x56, 0x00, 0x00, 0xa4, 0x81, 0x00, 0x00,  0x55, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00,
749                         0x77, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00],
750                ]
751            }
752        };
753        let reply: ReplyEntry = Reply::new(0xdeadbeef, sender);
754        let time = Timespec::new(0x1234, 0x5678);
755        let attr = FileAttr { ino: 0x11, size: 0x22, blocks: 0x33, atime: time, mtime: time, ctime: time, crtime: time,
756            kind: FileType::RegularFile, perm: 0o644, nlink: 0x55, uid: 0x66, gid: 0x77, rdev: 0x88, flags: 0x99 };
757        reply.entry(&time, &attr, 0xaa);
758    }
759
760    #[test]
761    fn reply_attr() {
762        let sender = AssertSender {
763            expected: if cfg!(target_os = "macos") {
764                vec![
765                    vec![0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00],
766                    vec![0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x78, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
767                         0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
768                         0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
769                         0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
770                         0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00,
771                         0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00,  0xa4, 0x81, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00,
772                         0x66, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00,  0x88, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00],
773                ]
774            } else {
775                vec![
776                    vec![0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00],
777                    vec![0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x78, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
778                         0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
779                         0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
780                         0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
781                         0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00,  0x78, 0x56, 0x00, 0x00, 0xa4, 0x81, 0x00, 0x00,
782                         0x55, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00,  0x77, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00],
783                ]
784            }
785        };
786        let reply: ReplyAttr = Reply::new(0xdeadbeef, sender);
787        let time = Timespec::new(0x1234, 0x5678);
788        let attr = FileAttr { ino: 0x11, size: 0x22, blocks: 0x33, atime: time, mtime: time, ctime: time, crtime: time,
789            kind: FileType::RegularFile, perm: 0o644, nlink: 0x55, uid: 0x66, gid: 0x77, rdev: 0x88, flags: 0x99 };
790        reply.attr(&time, &attr);
791    }
792
793    #[test]
794    #[cfg(target_os = "macos")]
795    fn reply_xtimes() {
796        let sender = AssertSender {
797            expected: vec![
798                vec![0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00],
799                vec![0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
800                     0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00],
801            ]
802        };
803        let reply: ReplyXTimes = Reply::new(0xdeadbeef, sender);
804        let time = Timespec::new(0x1234, 0x5678);
805        reply.xtimes(time, time);
806    }
807
808    #[test]
809    fn reply_open() {
810        let sender = AssertSender {
811            expected: vec![
812                vec![0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00],
813                vec![0x22, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
814            ]
815        };
816        let reply: ReplyOpen = Reply::new(0xdeadbeef, sender);
817        reply.opened(0x1122, 0x33);
818    }
819
820    #[test]
821    fn reply_write() {
822        let sender = AssertSender {
823            expected: vec![
824                vec![0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00],
825                vec![0x22, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
826            ]
827        };
828        let reply: ReplyWrite = Reply::new(0xdeadbeef, sender);
829        reply.written(0x1122);
830    }
831
832    #[test]
833    fn reply_statfs() {
834        let sender = AssertSender {
835            expected: vec![
836                vec![0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00],
837                vec![0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
838                     0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
839                     0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x66, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00,
840                     0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
841                     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
842            ]
843        };
844        let reply: ReplyStatfs = Reply::new(0xdeadbeef, sender);
845        reply.statfs(0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88);
846    }
847
848    #[test]
849    fn reply_create() {
850        let sender = AssertSender {
851            expected: if cfg!(target_os = "macos") {
852                vec![
853                    vec![0xa8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00],
854                    vec![0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
855                         0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
856                         0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00,  0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
857                         0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
858                         0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
859                         0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
860                         0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00,  0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00,
861                         0xa4, 0x81, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00,  0x66, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00,
862                         0x88, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00,  0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
863                         0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
864                ]
865            } else {
866                vec![
867                    vec![0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00],
868                    vec![0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
869                         0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
870                         0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00,  0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
871                         0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
872                         0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
873                         0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x78, 0x56, 0x00, 0x00, 0x78, 0x56, 0x00, 0x00,
874                         0x78, 0x56, 0x00, 0x00, 0xa4, 0x81, 0x00, 0x00,  0x55, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00,
875                         0x77, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00,  0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
876                         0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
877                ]
878            }
879        };
880        let reply: ReplyCreate = Reply::new(0xdeadbeef, sender);
881        let time = Timespec::new(0x1234, 0x5678);
882        let attr = FileAttr { ino: 0x11, size: 0x22, blocks: 0x33, atime: time, mtime: time, ctime: time, crtime: time,
883            kind: FileType::RegularFile, perm: 0o644, nlink: 0x55, uid: 0x66, gid: 0x77, rdev: 0x88, flags: 0x99 };
884        reply.created(&time, &attr, 0xaa, 0xbb, 0xcc);
885    }
886
887    #[test]
888    fn reply_lock() {
889        let sender = AssertSender {
890            expected: vec![
891                vec![0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00],
892                vec![0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
893                  0x33, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00],
894            ]
895        };
896        let reply: ReplyLock = Reply::new(0xdeadbeef, sender);
897        reply.locked(0x11, 0x22, 0x33, 0x44);
898    }
899
900    #[test]
901    fn reply_bmap() {
902        let sender = AssertSender {
903            expected: vec![
904                vec![0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00],
905                vec![0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
906            ]
907        };
908        let reply: ReplyBmap = Reply::new(0xdeadbeef, sender);
909        reply.bmap(0x1234);
910    }
911
912    #[test]
913    fn reply_directory() {
914        let sender = AssertSender {
915            expected: vec![
916                vec![0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00],
917                vec![0xbb, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
918                     0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,  0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x00 ,0x00, 0x00,
919                     0xdd, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
920                     0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,  0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x72, 0x73],
921            ]
922        };
923        let mut reply = ReplyDirectory::new(0xdeadbeef, sender, 4096);
924        reply.add(0xaabb, 1, FileType::Directory, "hello");
925        reply.add(0xccdd, 2, FileType::RegularFile, "world.rs");
926        reply.ok();
927    }
928
929    impl super::ReplySender for Sender<()> {
930        fn send(&self, _: &[&[u8]]) {
931            Sender::send(self, ()).unwrap()
932        }
933    }
934
935    #[test]
936    fn reply_xattr_size() {
937        let sender = AssertSender {
938            expected: vec![
939                vec![0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0xEF, 0xBE, 0xAD, 0xDE, 0x00, 0x00,  0x00, 0x00],
940                vec![0x78, 0x56, 0x34, 0x12, 0x00,0x00, 0x00, 0x00],
941            ]
942        };
943        let reply = ReplyXattr::new(0xdeadbeef, sender);
944        reply.size(0x12345678);
945    }
946
947    #[test]
948    fn reply_xattr_data() {
949        let sender = AssertSender {
950            expected: vec![
951                vec![0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  0xEF, 0xBE, 0xAD, 0xDE, 0x00, 0x00,  0x00, 0x00],
952                vec![0x11, 0x22, 0x33, 0x44],
953            ]
954        };
955        let reply = ReplyXattr::new(0xdeadbeef, sender);
956        reply.data(&vec![0x11, 0x22, 0x33, 0x44]);
957    }
958
959    #[test]
960    fn async_reply() {
961        let (tx, rx) = channel::<()>();
962        let reply: ReplyEmpty = Reply::new(0xdeadbeef, tx);
963        thread::spawn(move || {
964            reply.ok();
965        });
966        rx.recv().unwrap();
967    }
968}