1use 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
25pub trait ReplySender: Send + 'static {
27 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
37pub trait Reply {
39 fn new<S: ReplySender>(unique: u64, sender: S) -> Self;
41}
42
43fn 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#[allow(trivial_numeric_casts)]
59fn 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#[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#[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#[derive(Debug)]
121pub struct ReplyRaw<T> {
122 unique: u64,
124 sender: Option<Box<ReplySender>>,
126 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 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 pub fn ok(mut self, data: &T) {
159 as_bytes(data, |bytes| {
160 self.send(0, bytes);
161 })
162 }
163
164 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#[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 pub fn ok(mut self) {
196 self.reply.send(0, &[]);
197 }
198
199 pub fn error(self, err: c_int) {
201 self.reply.error(err);
202 }
203}
204
205#[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 pub fn data(mut self, data: &[u8]) {
222 self.reply.send(0, &[data]);
223 }
224
225 pub fn error(self, err: c_int) {
227 self.reply.error(err);
228 }
229}
230
231#[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 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 pub fn error(self, err: c_int) {
261 self.reply.error(err);
262 }
263}
264
265#[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 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 pub fn error(self, err: c_int) {
292 self.reply.error(err);
293 }
294}
295
296#[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 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 pub fn error(self, err: c_int) {
326 self.reply.error(err);
327 }
328}
329
330#[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 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 pub fn error(self, err: c_int) {
356 self.reply.error(err);
357 }
358}
359
360#[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 pub fn written(self, size: u32) {
377 self.reply.ok(&fuse_write_out {
378 size: size,
379 padding: 0,
380 });
381 }
382
383 pub fn error(self, err: c_int) {
385 self.reply.error(err);
386 }
387}
388
389#[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 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 pub fn error(self, err: c_int) {
424 self.reply.error(err);
425 }
426}
427
428#[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 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 pub fn error(self, err: c_int) {
462 self.reply.error(err);
463 }
464}
465
466#[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 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 pub fn error(self, err: c_int) {
495 self.reply.error(err);
496 }
497}
498
499#[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 pub fn bmap(self, block: u64) {
516 self.reply.ok(&fuse_bmap_out {
517 block: block,
518 });
519 }
520
521 pub fn error(self, err: c_int) {
523 self.reply.error(err);
524 }
525}
526
527#[derive(Debug)]
531pub struct ReplyDirectory {
532 reply: ReplyRaw<()>,
533 data: Vec<u8>,
534}
535
536impl ReplyDirectory {
537 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 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); 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 pub fn ok(mut self) {
573 self.reply.send(0, &[&self.data]);
574 }
575
576 pub fn error(self, err: c_int) {
578 self.reply.error(err);
579 }
580}
581
582#[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 pub fn size(self, size: u32) {
599 self.reply.ok(&fuse_getxattr_out {
600 size: size,
601 padding: 0,
602 });
603 }
604
605 pub fn data(mut self, data: &[u8]) {
607 self.reply.send(0, &[data]);
608 }
609
610 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}