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