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