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