1#![allow(dead_code)]
7
8#[cfg(feature = "alloc")]
9extern crate alloc;
10
11#[cfg(feature = "alloc")]
12use alloc::{string::String, vec, vec::Vec};
13
14const NFS4_FHSIZE: usize = 128;
20
21#[derive(Debug, Clone, PartialEq, Eq)]
23pub struct NfsFileHandle {
24 data: [u8; NFS4_FHSIZE],
26 len: usize,
28}
29
30impl Default for NfsFileHandle {
31 fn default() -> Self {
32 Self::new()
33 }
34}
35
36impl NfsFileHandle {
37 pub fn new() -> Self {
39 Self {
40 data: [0u8; NFS4_FHSIZE],
41 len: 0,
42 }
43 }
44
45 pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
47 if bytes.len() > NFS4_FHSIZE {
48 return None;
49 }
50 let mut fh = Self::new();
51 fh.data[..bytes.len()].copy_from_slice(bytes);
52 fh.len = bytes.len();
53 Some(fh)
54 }
55
56 pub fn as_bytes(&self) -> &[u8] {
58 &self.data[..self.len]
59 }
60
61 pub fn is_empty(&self) -> bool {
63 self.len == 0
64 }
65}
66
67#[derive(Debug, Clone, Copy, PartialEq, Eq)]
73#[repr(u32)]
74pub enum NfsFtype {
75 Regular = 1,
76 Directory = 2,
77 BlockDevice = 3,
78 CharDevice = 4,
79 Symlink = 5,
80 Socket = 6,
81 Fifo = 7,
82}
83
84impl NfsFtype {
85 pub fn from_u32(v: u32) -> Option<Self> {
87 match v {
88 1 => Some(Self::Regular),
89 2 => Some(Self::Directory),
90 3 => Some(Self::BlockDevice),
91 4 => Some(Self::CharDevice),
92 5 => Some(Self::Symlink),
93 6 => Some(Self::Socket),
94 7 => Some(Self::Fifo),
95 _ => None,
96 }
97 }
98}
99
100#[derive(Debug, Clone)]
102pub struct NfsAttr {
103 pub file_type: NfsFtype,
104 pub mode: u32,
105 pub nlink: u32,
106 pub uid: u32,
107 pub gid: u32,
108 pub size: u64,
109 pub used: u64,
110 pub rdev: u64,
111 pub fsid: u64,
112 pub fileid: u64,
113 pub atime: u64,
114 pub mtime: u64,
115 pub ctime: u64,
116}
117
118impl Default for NfsAttr {
119 fn default() -> Self {
120 Self {
121 file_type: NfsFtype::Regular,
122 mode: 0,
123 nlink: 0,
124 uid: 0,
125 gid: 0,
126 size: 0,
127 used: 0,
128 rdev: 0,
129 fsid: 0,
130 fileid: 0,
131 atime: 0,
132 mtime: 0,
133 ctime: 0,
134 }
135 }
136}
137
138#[derive(Debug, Clone, Copy, PartialEq, Eq)]
144#[repr(u32)]
145pub enum NfsOpcode {
146 Access = 3,
147 Close = 4,
148 Commit = 5,
149 Create = 6,
150 GetAttr = 9,
151 GetFH = 10,
152 Lookup = 15,
153 Open = 18,
154 PutFH = 22,
155 PutRootFH = 24,
156 Read = 25,
157 ReadDir = 26,
158 Remove = 28,
159 Rename = 29,
160 SetAttr = 34,
161 Write = 38,
162}
163
164#[cfg(feature = "alloc")]
166#[derive(Debug, Clone)]
167pub enum NfsOperation {
168 Access {
169 access_mask: u32,
170 },
171 Close {
172 state_id: [u8; 16],
173 },
174 Commit {
175 offset: u64,
176 count: u32,
177 },
178 Create {
179 name: String,
180 file_type: NfsFtype,
181 },
182 GetAttr {
183 attr_request: u64,
184 },
185 GetFH,
186 Lookup {
187 name: String,
188 },
189 Open {
190 name: String,
191 access: u32,
192 deny: u32,
193 },
194 PutFH {
195 handle: NfsFileHandle,
196 },
197 PutRootFH,
198 Read {
199 state_id: [u8; 16],
200 offset: u64,
201 count: u32,
202 },
203 ReadDir {
204 cookie: u64,
205 count: u32,
206 },
207 Remove {
208 name: String,
209 },
210 Rename {
211 old_name: String,
212 new_name: String,
213 },
214 SetAttr {
215 state_id: [u8; 16],
216 attrs: NfsAttr,
217 },
218 Write {
219 state_id: [u8; 16],
220 offset: u64,
221 data: Vec<u8>,
222 stable: bool,
223 },
224}
225
226#[derive(Debug, Clone, Copy, PartialEq, Eq)]
228#[repr(u32)]
229pub enum NfsStatus {
230 Ok = 0,
231 Perm = 1,
232 NoEnt = 2,
233 IO = 5,
234 Access = 13,
235 Exist = 17,
236 NotDir = 20,
237 IsDir = 21,
238 Inval = 22,
239 FBig = 27,
240 NoSpc = 28,
241 RoFs = 30,
242 Stale = 70,
243 BadHandle = 10001,
244 NotSupp = 10004,
245 ServerFault = 10006,
246}
247
248impl NfsStatus {
249 pub fn from_u32(v: u32) -> Self {
251 match v {
252 0 => Self::Ok,
253 1 => Self::Perm,
254 2 => Self::NoEnt,
255 5 => Self::IO,
256 13 => Self::Access,
257 17 => Self::Exist,
258 20 => Self::NotDir,
259 21 => Self::IsDir,
260 22 => Self::Inval,
261 27 => Self::FBig,
262 28 => Self::NoSpc,
263 30 => Self::RoFs,
264 70 => Self::Stale,
265 10001 => Self::BadHandle,
266 10004 => Self::NotSupp,
267 10006 => Self::ServerFault,
268 _ => Self::ServerFault,
269 }
270 }
271}
272
273#[cfg(feature = "alloc")]
275#[derive(Debug, Clone)]
276pub enum NfsResult {
277 Access {
278 status: NfsStatus,
279 supported: u32,
280 access: u32,
281 },
282 Close {
283 status: NfsStatus,
284 },
285 Commit {
286 status: NfsStatus,
287 },
288 Create {
289 status: NfsStatus,
290 },
291 GetAttr {
292 status: NfsStatus,
293 attrs: Option<NfsAttr>,
294 },
295 GetFH {
296 status: NfsStatus,
297 handle: Option<NfsFileHandle>,
298 },
299 Lookup {
300 status: NfsStatus,
301 },
302 Open {
303 status: NfsStatus,
304 state_id: [u8; 16],
305 },
306 PutFH {
307 status: NfsStatus,
308 },
309 PutRootFH {
310 status: NfsStatus,
311 },
312 Read {
313 status: NfsStatus,
314 eof: bool,
315 data: Vec<u8>,
316 },
317 ReadDir {
318 status: NfsStatus,
319 entries: Vec<NfsDirEntry>,
320 },
321 Remove {
322 status: NfsStatus,
323 },
324 Rename {
325 status: NfsStatus,
326 },
327 SetAttr {
328 status: NfsStatus,
329 },
330 Write {
331 status: NfsStatus,
332 count: u32,
333 committed: bool,
334 },
335}
336
337#[cfg(feature = "alloc")]
339#[derive(Debug, Clone)]
340pub struct NfsDirEntry {
341 pub cookie: u64,
342 pub name: String,
343 pub fileid: u64,
344}
345
346#[cfg(feature = "alloc")]
352#[derive(Debug, Clone)]
353pub struct CompoundRequest {
354 pub tag: String,
355 pub minor_version: u32,
356 pub operations: Vec<NfsOperation>,
357}
358
359#[cfg(feature = "alloc")]
361#[derive(Debug, Clone)]
362pub struct CompoundResponse {
363 pub status: NfsStatus,
364 pub tag: String,
365 pub results: Vec<NfsResult>,
366}
367
368#[cfg(feature = "alloc")]
374pub struct XdrEncoder {
375 buf: Vec<u8>,
376}
377
378#[cfg(feature = "alloc")]
379impl Default for XdrEncoder {
380 fn default() -> Self {
381 Self::new()
382 }
383}
384
385#[cfg(feature = "alloc")]
386impl XdrEncoder {
387 pub fn new() -> Self {
389 Self { buf: Vec::new() }
390 }
391
392 pub fn with_capacity(cap: usize) -> Self {
394 Self {
395 buf: Vec::with_capacity(cap),
396 }
397 }
398
399 pub fn encode_u32(&mut self, v: u32) {
401 self.buf.extend_from_slice(&v.to_be_bytes());
402 }
403
404 pub fn encode_u64(&mut self, v: u64) {
406 self.buf.extend_from_slice(&v.to_be_bytes());
407 }
408
409 pub fn encode_bool(&mut self, v: bool) {
411 self.encode_u32(if v { 1 } else { 0 });
412 }
413
414 pub fn encode_opaque(&mut self, data: &[u8]) {
417 self.encode_u32(data.len() as u32);
418 self.buf.extend_from_slice(data);
419 let pad = (4 - (data.len() % 4)) % 4;
421 for _ in 0..pad {
422 self.buf.push(0);
423 }
424 }
425
426 pub fn encode_string(&mut self, s: &str) {
428 self.encode_opaque(s.as_bytes());
429 }
430
431 pub fn encode_opaque_fixed(&mut self, data: &[u8]) {
433 self.buf.extend_from_slice(data);
434 let pad = (4 - (data.len() % 4)) % 4;
435 for _ in 0..pad {
436 self.buf.push(0);
437 }
438 }
439
440 pub fn into_bytes(self) -> Vec<u8> {
442 self.buf
443 }
444
445 pub fn len(&self) -> usize {
447 self.buf.len()
448 }
449
450 pub fn is_empty(&self) -> bool {
452 self.buf.is_empty()
453 }
454}
455
456#[cfg(feature = "alloc")]
458pub struct XdrDecoder<'a> {
459 data: &'a [u8],
460 pos: usize,
461}
462
463#[cfg(feature = "alloc")]
464impl<'a> XdrDecoder<'a> {
465 pub fn new(data: &'a [u8]) -> Self {
467 Self { data, pos: 0 }
468 }
469
470 pub fn decode_u32(&mut self) -> Option<u32> {
472 if self.pos + 4 > self.data.len() {
473 return None;
474 }
475 let v = u32::from_be_bytes([
476 self.data[self.pos],
477 self.data[self.pos + 1],
478 self.data[self.pos + 2],
479 self.data[self.pos + 3],
480 ]);
481 self.pos += 4;
482 Some(v)
483 }
484
485 pub fn decode_u64(&mut self) -> Option<u64> {
487 let hi = self.decode_u32()? as u64;
488 let lo = self.decode_u32()? as u64;
489 Some((hi << 32) | lo)
490 }
491
492 pub fn decode_bool(&mut self) -> Option<bool> {
494 self.decode_u32().map(|v| v != 0)
495 }
496
497 pub fn decode_opaque(&mut self) -> Option<Vec<u8>> {
499 let len = self.decode_u32()? as usize;
500 if self.pos + len > self.data.len() {
501 return None;
502 }
503 let v = self.data[self.pos..self.pos + len].to_vec();
504 self.pos += len;
505 let pad = (4 - (len % 4)) % 4;
507 self.pos += pad;
508 Some(v)
509 }
510
511 pub fn decode_string(&mut self) -> Option<String> {
513 let bytes = self.decode_opaque()?;
514 String::from_utf8(bytes).ok()
515 }
516
517 pub fn decode_opaque_fixed(&mut self, len: usize) -> Option<Vec<u8>> {
519 if self.pos + len > self.data.len() {
520 return None;
521 }
522 let v = self.data[self.pos..self.pos + len].to_vec();
523 self.pos += len;
524 let pad = (4 - (len % 4)) % 4;
525 self.pos += pad;
526 Some(v)
527 }
528
529 pub fn remaining(&self) -> usize {
531 self.data.len().saturating_sub(self.pos)
532 }
533}
534
535#[cfg(feature = "alloc")]
541#[derive(Debug, Clone)]
542pub struct AuthSys {
543 pub stamp: u32,
544 pub machine_name: String,
545 pub uid: u32,
546 pub gid: u32,
547 pub gids: Vec<u32>,
548}
549
550#[cfg(feature = "alloc")]
551impl Default for AuthSys {
552 fn default() -> Self {
553 Self {
554 stamp: 0,
555 machine_name: String::from("veridian"),
556 uid: 0,
557 gid: 0,
558 gids: Vec::new(),
559 }
560 }
561}
562
563#[cfg(feature = "alloc")]
564impl AuthSys {
565 pub fn encode(&self, enc: &mut XdrEncoder) {
567 enc.encode_u32(self.stamp);
568 enc.encode_string(&self.machine_name);
569 enc.encode_u32(self.uid);
570 enc.encode_u32(self.gid);
571 enc.encode_u32(self.gids.len() as u32);
572 for &g in &self.gids {
573 enc.encode_u32(g);
574 }
575 }
576}
577
578#[derive(Debug, Clone, Copy, PartialEq, Eq)]
584pub enum NfsError {
585 Status(NfsStatus),
587 XdrError,
589 NoFileHandle,
591 TransportError,
593 AuthError,
595 NotConnected,
597 InvalidArgument,
599}
600
601#[cfg(feature = "alloc")]
603pub struct NfsClient {
604 server_addr: String,
606 root_fh: NfsFileHandle,
608 current_fh: NfsFileHandle,
610 auth: AuthSys,
612 connected: bool,
614 xid: u32,
616}
617
618#[cfg(feature = "alloc")]
619impl NfsClient {
620 pub fn new(server_addr: String) -> Self {
622 Self {
623 server_addr,
624 root_fh: NfsFileHandle::new(),
625 current_fh: NfsFileHandle::new(),
626 auth: AuthSys::default(),
627 connected: false,
628 xid: 1,
629 }
630 }
631
632 pub fn set_auth(&mut self, uid: u32, gid: u32, machine_name: String) {
634 self.auth.uid = uid;
635 self.auth.gid = gid;
636 self.auth.machine_name = machine_name;
637 }
638
639 pub fn mount(&mut self) -> Result<NfsFileHandle, NfsError> {
641 let request = CompoundRequest {
642 tag: String::from("mount"),
643 minor_version: 0,
644 operations: vec![NfsOperation::PutRootFH, NfsOperation::GetFH],
645 };
646
647 let response = self.send_compound(&request)?;
648
649 for result in &response.results {
651 if let NfsResult::GetFH {
652 status,
653 handle: Some(fh),
654 } = result
655 {
656 if *status != NfsStatus::Ok {
657 return Err(NfsError::Status(*status));
658 }
659 self.root_fh = fh.clone();
660 self.current_fh = fh.clone();
661 return Ok(fh.clone());
662 }
663 }
664
665 Err(NfsError::NoFileHandle)
666 }
667
668 pub fn lookup(
670 &mut self,
671 dir_fh: &NfsFileHandle,
672 name: &str,
673 ) -> Result<NfsFileHandle, NfsError> {
674 let request = CompoundRequest {
675 tag: String::from("lookup"),
676 minor_version: 0,
677 operations: vec![
678 NfsOperation::PutFH {
679 handle: dir_fh.clone(),
680 },
681 NfsOperation::Lookup {
682 name: String::from(name),
683 },
684 NfsOperation::GetFH,
685 ],
686 };
687
688 let response = self.send_compound(&request)?;
689
690 for result in &response.results {
691 if let NfsResult::GetFH {
692 status,
693 handle: Some(fh),
694 } = result
695 {
696 if *status != NfsStatus::Ok {
697 return Err(NfsError::Status(*status));
698 }
699 self.current_fh = fh.clone();
700 return Ok(fh.clone());
701 }
702 }
703
704 Err(NfsError::NoFileHandle)
705 }
706
707 pub fn read(
709 &mut self,
710 fh: &NfsFileHandle,
711 offset: u64,
712 count: u32,
713 ) -> Result<(Vec<u8>, bool), NfsError> {
714 let request = CompoundRequest {
715 tag: String::from("read"),
716 minor_version: 0,
717 operations: vec![
718 NfsOperation::PutFH { handle: fh.clone() },
719 NfsOperation::Read {
720 state_id: [0u8; 16],
721 offset,
722 count,
723 },
724 ],
725 };
726
727 let response = self.send_compound(&request)?;
728
729 for result in &response.results {
730 if let NfsResult::Read { status, eof, data } = result {
731 if *status != NfsStatus::Ok {
732 return Err(NfsError::Status(*status));
733 }
734 return Ok((data.clone(), *eof));
735 }
736 }
737
738 Err(NfsError::XdrError)
739 }
740
741 pub fn write(
743 &mut self,
744 fh: &NfsFileHandle,
745 offset: u64,
746 data: &[u8],
747 stable: bool,
748 ) -> Result<(u32, bool), NfsError> {
749 let request = CompoundRequest {
750 tag: String::from("write"),
751 minor_version: 0,
752 operations: vec![
753 NfsOperation::PutFH { handle: fh.clone() },
754 NfsOperation::Write {
755 state_id: [0u8; 16],
756 offset,
757 data: data.to_vec(),
758 stable,
759 },
760 ],
761 };
762
763 let response = self.send_compound(&request)?;
764
765 for result in &response.results {
766 if let NfsResult::Write {
767 status,
768 count,
769 committed,
770 } = result
771 {
772 if *status != NfsStatus::Ok {
773 return Err(NfsError::Status(*status));
774 }
775 return Ok((*count, *committed));
776 }
777 }
778
779 Err(NfsError::XdrError)
780 }
781
782 pub fn readdir(
784 &mut self,
785 dir_fh: &NfsFileHandle,
786 cookie: u64,
787 count: u32,
788 ) -> Result<Vec<NfsDirEntry>, NfsError> {
789 let request = CompoundRequest {
790 tag: String::from("readdir"),
791 minor_version: 0,
792 operations: vec![
793 NfsOperation::PutFH {
794 handle: dir_fh.clone(),
795 },
796 NfsOperation::ReadDir { cookie, count },
797 ],
798 };
799
800 let response = self.send_compound(&request)?;
801
802 for result in &response.results {
803 if let NfsResult::ReadDir { status, entries } = result {
804 if *status != NfsStatus::Ok {
805 return Err(NfsError::Status(*status));
806 }
807 return Ok(entries.clone());
808 }
809 }
810
811 Err(NfsError::XdrError)
812 }
813
814 pub fn create(
816 &mut self,
817 dir_fh: &NfsFileHandle,
818 name: &str,
819 file_type: NfsFtype,
820 ) -> Result<NfsFileHandle, NfsError> {
821 let request = CompoundRequest {
822 tag: String::from("create"),
823 minor_version: 0,
824 operations: vec![
825 NfsOperation::PutFH {
826 handle: dir_fh.clone(),
827 },
828 NfsOperation::Create {
829 name: String::from(name),
830 file_type,
831 },
832 NfsOperation::GetFH,
833 ],
834 };
835
836 let response = self.send_compound(&request)?;
837
838 for result in &response.results {
839 if let NfsResult::GetFH {
840 status,
841 handle: Some(fh),
842 } = result
843 {
844 if *status != NfsStatus::Ok {
845 return Err(NfsError::Status(*status));
846 }
847 return Ok(fh.clone());
848 }
849 }
850
851 Err(NfsError::NoFileHandle)
852 }
853
854 pub fn remove(&mut self, dir_fh: &NfsFileHandle, name: &str) -> Result<(), NfsError> {
856 let request = CompoundRequest {
857 tag: String::from("remove"),
858 minor_version: 0,
859 operations: vec![
860 NfsOperation::PutFH {
861 handle: dir_fh.clone(),
862 },
863 NfsOperation::Remove {
864 name: String::from(name),
865 },
866 ],
867 };
868
869 let response = self.send_compound(&request)?;
870
871 for result in &response.results {
872 if let NfsResult::Remove { status } = result {
873 if *status != NfsStatus::Ok {
874 return Err(NfsError::Status(*status));
875 }
876 return Ok(());
877 }
878 }
879
880 Err(NfsError::XdrError)
881 }
882
883 pub fn getattr(&mut self, fh: &NfsFileHandle) -> Result<NfsAttr, NfsError> {
885 let attr_request = 0xFFFF_FFFF_FFFF_FFFF;
887
888 let request = CompoundRequest {
889 tag: String::from("getattr"),
890 minor_version: 0,
891 operations: vec![
892 NfsOperation::PutFH { handle: fh.clone() },
893 NfsOperation::GetAttr { attr_request },
894 ],
895 };
896
897 let response = self.send_compound(&request)?;
898
899 for result in &response.results {
900 if let NfsResult::GetAttr {
901 status,
902 attrs: Some(a),
903 } = result
904 {
905 if *status != NfsStatus::Ok {
906 return Err(NfsError::Status(*status));
907 }
908 return Ok(a.clone());
909 }
910 }
911
912 Err(NfsError::XdrError)
913 }
914
915 pub fn setattr(&mut self, fh: &NfsFileHandle, attrs: NfsAttr) -> Result<(), NfsError> {
917 let request = CompoundRequest {
918 tag: String::from("setattr"),
919 minor_version: 0,
920 operations: vec![
921 NfsOperation::PutFH { handle: fh.clone() },
922 NfsOperation::SetAttr {
923 state_id: [0u8; 16],
924 attrs,
925 },
926 ],
927 };
928
929 let response = self.send_compound(&request)?;
930
931 for result in &response.results {
932 if let NfsResult::SetAttr { status } = result {
933 if *status != NfsStatus::Ok {
934 return Err(NfsError::Status(*status));
935 }
936 return Ok(());
937 }
938 }
939
940 Err(NfsError::XdrError)
941 }
942
943 pub fn build_compound(&self, req: &CompoundRequest) -> Vec<u8> {
945 let mut enc = XdrEncoder::with_capacity(256);
946
947 enc.encode_u32(self.xid);
949 enc.encode_u32(0); enc.encode_u32(2); enc.encode_u32(100003); enc.encode_u32(4); enc.encode_u32(1); enc.encode_u32(1); self.auth.encode(&mut enc);
958
959 enc.encode_string(&req.tag);
961 enc.encode_u32(req.minor_version);
962 enc.encode_u32(req.operations.len() as u32);
963
964 for op in &req.operations {
965 self.encode_operation(&mut enc, op);
966 }
967
968 enc.into_bytes()
969 }
970
971 pub fn parse_compound(&self, data: &[u8]) -> Result<CompoundResponse, NfsError> {
973 let mut dec = XdrDecoder::new(data);
974
975 let _xid = dec.decode_u32().ok_or(NfsError::XdrError)?;
977 let _msg_type = dec.decode_u32().ok_or(NfsError::XdrError)?;
978 let _reply_stat = dec.decode_u32().ok_or(NfsError::XdrError)?;
979
980 let _verf_flavor = dec.decode_u32().ok_or(NfsError::XdrError)?;
982 let _verf_body = dec.decode_opaque().ok_or(NfsError::XdrError)?;
983
984 let _accept_stat = dec.decode_u32().ok_or(NfsError::XdrError)?;
986
987 let status_val = dec.decode_u32().ok_or(NfsError::XdrError)?;
989 let status = NfsStatus::from_u32(status_val);
990 let tag = dec.decode_string().ok_or(NfsError::XdrError)?;
991 let num_results = dec.decode_u32().ok_or(NfsError::XdrError)? as usize;
992
993 let mut results = Vec::with_capacity(num_results);
994 for _ in 0..num_results {
995 let result = self.decode_result(&mut dec)?;
996 results.push(result);
997 }
998
999 Ok(CompoundResponse {
1000 status,
1001 tag,
1002 results,
1003 })
1004 }
1005
1006 fn encode_operation(&self, enc: &mut XdrEncoder, op: &NfsOperation) {
1008 match op {
1009 NfsOperation::Access { access_mask } => {
1010 enc.encode_u32(NfsOpcode::Access as u32);
1011 enc.encode_u32(*access_mask);
1012 }
1013 NfsOperation::Close { state_id } => {
1014 enc.encode_u32(NfsOpcode::Close as u32);
1015 enc.encode_u32(0); enc.encode_opaque_fixed(state_id);
1017 }
1018 NfsOperation::Commit { offset, count } => {
1019 enc.encode_u32(NfsOpcode::Commit as u32);
1020 enc.encode_u64(*offset);
1021 enc.encode_u32(*count);
1022 }
1023 NfsOperation::Create { name, file_type } => {
1024 enc.encode_u32(NfsOpcode::Create as u32);
1025 enc.encode_u32(*file_type as u32);
1026 enc.encode_string(name);
1027 }
1028 NfsOperation::GetAttr { attr_request } => {
1029 enc.encode_u32(NfsOpcode::GetAttr as u32);
1030 enc.encode_u64(*attr_request);
1031 }
1032 NfsOperation::GetFH => {
1033 enc.encode_u32(NfsOpcode::GetFH as u32);
1034 }
1035 NfsOperation::Lookup { name } => {
1036 enc.encode_u32(NfsOpcode::Lookup as u32);
1037 enc.encode_string(name);
1038 }
1039 NfsOperation::Open { name, access, deny } => {
1040 enc.encode_u32(NfsOpcode::Open as u32);
1041 enc.encode_u32(0); enc.encode_u32(*access);
1043 enc.encode_u32(*deny);
1044 enc.encode_string(name);
1045 }
1046 NfsOperation::PutFH { handle } => {
1047 enc.encode_u32(NfsOpcode::PutFH as u32);
1048 enc.encode_opaque(handle.as_bytes());
1049 }
1050 NfsOperation::PutRootFH => {
1051 enc.encode_u32(NfsOpcode::PutRootFH as u32);
1052 }
1053 NfsOperation::Read {
1054 state_id,
1055 offset,
1056 count,
1057 } => {
1058 enc.encode_u32(NfsOpcode::Read as u32);
1059 enc.encode_opaque_fixed(state_id);
1060 enc.encode_u64(*offset);
1061 enc.encode_u32(*count);
1062 }
1063 NfsOperation::ReadDir { cookie, count } => {
1064 enc.encode_u32(NfsOpcode::ReadDir as u32);
1065 enc.encode_u64(*cookie);
1066 enc.encode_u32(*count);
1067 }
1068 NfsOperation::Remove { name } => {
1069 enc.encode_u32(NfsOpcode::Remove as u32);
1070 enc.encode_string(name);
1071 }
1072 NfsOperation::Rename { old_name, new_name } => {
1073 enc.encode_u32(NfsOpcode::Rename as u32);
1074 enc.encode_string(old_name);
1075 enc.encode_string(new_name);
1076 }
1077 NfsOperation::SetAttr { state_id, attrs } => {
1078 enc.encode_u32(NfsOpcode::SetAttr as u32);
1079 enc.encode_opaque_fixed(state_id);
1080 enc.encode_u32(attrs.mode);
1081 enc.encode_u64(attrs.size);
1082 }
1083 NfsOperation::Write {
1084 state_id,
1085 offset,
1086 data,
1087 stable,
1088 } => {
1089 enc.encode_u32(NfsOpcode::Write as u32);
1090 enc.encode_opaque_fixed(state_id);
1091 enc.encode_u64(*offset);
1092 enc.encode_u32(if *stable { 2 } else { 0 }); enc.encode_opaque(data);
1094 }
1095 }
1096 }
1097
1098 fn decode_result(&self, dec: &mut XdrDecoder) -> Result<NfsResult, NfsError> {
1100 let opcode = dec.decode_u32().ok_or(NfsError::XdrError)?;
1101 let status_val = dec.decode_u32().ok_or(NfsError::XdrError)?;
1102 let status = NfsStatus::from_u32(status_val);
1103
1104 match opcode {
1105 3 => {
1106 let supported = if status == NfsStatus::Ok {
1108 dec.decode_u32().ok_or(NfsError::XdrError)?
1109 } else {
1110 0
1111 };
1112 let access = if status == NfsStatus::Ok {
1113 dec.decode_u32().ok_or(NfsError::XdrError)?
1114 } else {
1115 0
1116 };
1117 Ok(NfsResult::Access {
1118 status,
1119 supported,
1120 access,
1121 })
1122 }
1123 4 => Ok(NfsResult::Close { status }),
1124 5 => Ok(NfsResult::Commit { status }),
1125 6 => Ok(NfsResult::Create { status }),
1126 9 => {
1127 let attrs = if status == NfsStatus::Ok {
1129 Some(self.decode_fattr(dec)?)
1130 } else {
1131 None
1132 };
1133 Ok(NfsResult::GetAttr { status, attrs })
1134 }
1135 10 => {
1136 let handle = if status == NfsStatus::Ok {
1138 let fh_bytes = dec.decode_opaque().ok_or(NfsError::XdrError)?;
1139 NfsFileHandle::from_bytes(&fh_bytes)
1140 } else {
1141 None
1142 };
1143 Ok(NfsResult::GetFH { status, handle })
1144 }
1145 15 => Ok(NfsResult::Lookup { status }),
1146 18 => {
1147 let mut state_id = [0u8; 16];
1149 if status == NfsStatus::Ok {
1150 let sid = dec.decode_opaque_fixed(16).ok_or(NfsError::XdrError)?;
1151 state_id.copy_from_slice(&sid);
1152 }
1153 Ok(NfsResult::Open { status, state_id })
1154 }
1155 22 => Ok(NfsResult::PutFH { status }),
1156 24 => Ok(NfsResult::PutRootFH { status }),
1157 25 => {
1158 let (eof, data) = if status == NfsStatus::Ok {
1160 let eof = dec.decode_bool().ok_or(NfsError::XdrError)?;
1161 let d = dec.decode_opaque().ok_or(NfsError::XdrError)?;
1162 (eof, d)
1163 } else {
1164 (false, Vec::new())
1165 };
1166 Ok(NfsResult::Read { status, eof, data })
1167 }
1168 26 => {
1169 let entries = if status == NfsStatus::Ok {
1171 self.decode_dir_entries(dec)?
1172 } else {
1173 Vec::new()
1174 };
1175 Ok(NfsResult::ReadDir { status, entries })
1176 }
1177 28 => Ok(NfsResult::Remove { status }),
1178 29 => Ok(NfsResult::Rename { status }),
1179 34 => Ok(NfsResult::SetAttr { status }),
1180 38 => {
1181 let (count, committed) = if status == NfsStatus::Ok {
1183 let c = dec.decode_u32().ok_or(NfsError::XdrError)?;
1184 let com = dec.decode_u32().ok_or(NfsError::XdrError)?;
1185 (c, com == 2)
1186 } else {
1187 (0, false)
1188 };
1189 Ok(NfsResult::Write {
1190 status,
1191 count,
1192 committed,
1193 })
1194 }
1195 _ => Err(NfsError::XdrError),
1196 }
1197 }
1198
1199 fn decode_fattr(&self, dec: &mut XdrDecoder) -> Result<NfsAttr, NfsError> {
1201 let file_type_val = dec.decode_u32().ok_or(NfsError::XdrError)?;
1202 let file_type = NfsFtype::from_u32(file_type_val).ok_or(NfsError::XdrError)?;
1203 let mode = dec.decode_u32().ok_or(NfsError::XdrError)?;
1204 let nlink = dec.decode_u32().ok_or(NfsError::XdrError)?;
1205 let uid = dec.decode_u32().ok_or(NfsError::XdrError)?;
1206 let gid = dec.decode_u32().ok_or(NfsError::XdrError)?;
1207 let size = dec.decode_u64().ok_or(NfsError::XdrError)?;
1208 let used = dec.decode_u64().ok_or(NfsError::XdrError)?;
1209 let rdev = dec.decode_u64().ok_or(NfsError::XdrError)?;
1210 let fsid = dec.decode_u64().ok_or(NfsError::XdrError)?;
1211 let fileid = dec.decode_u64().ok_or(NfsError::XdrError)?;
1212 let atime = dec.decode_u64().ok_or(NfsError::XdrError)?;
1213 let mtime = dec.decode_u64().ok_or(NfsError::XdrError)?;
1214 let ctime = dec.decode_u64().ok_or(NfsError::XdrError)?;
1215
1216 Ok(NfsAttr {
1217 file_type,
1218 mode,
1219 nlink,
1220 uid,
1221 gid,
1222 size,
1223 used,
1224 rdev,
1225 fsid,
1226 fileid,
1227 atime,
1228 mtime,
1229 ctime,
1230 })
1231 }
1232
1233 fn decode_dir_entries(&self, dec: &mut XdrDecoder) -> Result<Vec<NfsDirEntry>, NfsError> {
1235 let count = dec.decode_u32().ok_or(NfsError::XdrError)? as usize;
1236 let mut entries = Vec::with_capacity(count);
1237 for _ in 0..count {
1238 let cookie = dec.decode_u64().ok_or(NfsError::XdrError)?;
1239 let name = dec.decode_string().ok_or(NfsError::XdrError)?;
1240 let fileid = dec.decode_u64().ok_or(NfsError::XdrError)?;
1241 entries.push(NfsDirEntry {
1242 cookie,
1243 name,
1244 fileid,
1245 });
1246 }
1247 Ok(entries)
1248 }
1249
1250 fn send_compound(&mut self, req: &CompoundRequest) -> Result<CompoundResponse, NfsError> {
1255 let _encoded = self.build_compound(req);
1256 self.xid += 1;
1257
1258 Err(NfsError::TransportError)
1261 }
1262
1263 pub fn root_fh(&self) -> &NfsFileHandle {
1265 &self.root_fh
1266 }
1267
1268 pub fn current_fh(&self) -> &NfsFileHandle {
1270 &self.current_fh
1271 }
1272
1273 pub fn server_addr(&self) -> &str {
1275 &self.server_addr
1276 }
1277}
1278
1279#[cfg(feature = "alloc")]
1285pub struct NfsMountPoint {
1286 pub mount_path: String,
1288 pub export_path: String,
1290 pub client: NfsClient,
1292}
1293
1294#[cfg(feature = "alloc")]
1295impl NfsMountPoint {
1296 pub fn new(server: &str, export: &str, mount_path: &str) -> Self {
1298 Self {
1299 mount_path: String::from(mount_path),
1300 export_path: String::from(export),
1301 client: NfsClient::new(String::from(server)),
1302 }
1303 }
1304
1305 pub fn mount(&mut self) -> Result<(), NfsError> {
1307 self.client.mount()?;
1308 Ok(())
1309 }
1310
1311 pub fn path(&self) -> &str {
1313 &self.mount_path
1314 }
1315}
1316
1317#[cfg(test)]
1322mod tests {
1323 use super::*;
1324
1325 #[test]
1326 fn test_file_handle_new() {
1327 let fh = NfsFileHandle::new();
1328 assert!(fh.is_empty());
1329 assert_eq!(fh.as_bytes().len(), 0);
1330 }
1331
1332 #[test]
1333 fn test_file_handle_from_bytes() {
1334 let data = [1, 2, 3, 4, 5];
1335 let fh = NfsFileHandle::from_bytes(&data).unwrap();
1336 assert_eq!(fh.as_bytes(), &data);
1337 assert!(!fh.is_empty());
1338 }
1339
1340 #[test]
1341 fn test_file_handle_too_large() {
1342 let data = [0u8; NFS4_FHSIZE + 1];
1343 assert!(NfsFileHandle::from_bytes(&data).is_none());
1344 }
1345
1346 #[test]
1347 fn test_file_handle_max_size() {
1348 let data = [0xAB; NFS4_FHSIZE];
1349 let fh = NfsFileHandle::from_bytes(&data).unwrap();
1350 assert_eq!(fh.as_bytes().len(), NFS4_FHSIZE);
1351 }
1352
1353 #[test]
1354 fn test_nfs_ftype_roundtrip() {
1355 assert_eq!(NfsFtype::from_u32(1), Some(NfsFtype::Regular));
1356 assert_eq!(NfsFtype::from_u32(2), Some(NfsFtype::Directory));
1357 assert_eq!(NfsFtype::from_u32(7), Some(NfsFtype::Fifo));
1358 assert_eq!(NfsFtype::from_u32(0), None);
1359 assert_eq!(NfsFtype::from_u32(8), None);
1360 }
1361
1362 #[test]
1363 fn test_nfs_status_roundtrip() {
1364 assert_eq!(NfsStatus::from_u32(0), NfsStatus::Ok);
1365 assert_eq!(NfsStatus::from_u32(2), NfsStatus::NoEnt);
1366 assert_eq!(NfsStatus::from_u32(70), NfsStatus::Stale);
1367 assert_eq!(NfsStatus::from_u32(10001), NfsStatus::BadHandle);
1368 }
1369
1370 #[test]
1371 fn test_xdr_encode_u32() {
1372 let mut enc = XdrEncoder::new();
1373 enc.encode_u32(0x12345678);
1374 let bytes = enc.into_bytes();
1375 assert_eq!(bytes, &[0x12, 0x34, 0x56, 0x78]);
1376 }
1377
1378 #[test]
1379 fn test_xdr_encode_u64() {
1380 let mut enc = XdrEncoder::new();
1381 enc.encode_u64(0x0102030405060708);
1382 let bytes = enc.into_bytes();
1383 assert_eq!(bytes, &[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]);
1384 }
1385
1386 #[test]
1387 fn test_xdr_encode_decode_string() {
1388 let mut enc = XdrEncoder::new();
1389 enc.encode_string("hello");
1390 let bytes = enc.into_bytes();
1391
1392 let mut dec = XdrDecoder::new(&bytes);
1393 let s = dec.decode_string().unwrap();
1394 assert_eq!(s, "hello");
1395 }
1396
1397 #[test]
1398 fn test_xdr_opaque_padding() {
1399 let mut enc = XdrEncoder::new();
1400 enc.encode_opaque(&[1, 2, 3]); let bytes = enc.into_bytes();
1402 assert_eq!(bytes.len(), 8);
1404
1405 let mut dec = XdrDecoder::new(&bytes);
1406 let data = dec.decode_opaque().unwrap();
1407 assert_eq!(data, &[1, 2, 3]);
1408 }
1409
1410 #[test]
1411 fn test_xdr_bool() {
1412 let mut enc = XdrEncoder::new();
1413 enc.encode_bool(true);
1414 enc.encode_bool(false);
1415 let bytes = enc.into_bytes();
1416
1417 let mut dec = XdrDecoder::new(&bytes);
1418 assert!(dec.decode_bool().unwrap());
1419 assert!(!dec.decode_bool().unwrap());
1420 }
1421
1422 #[test]
1423 fn test_xdr_decode_empty() {
1424 let dec_data: &[u8] = &[];
1425 let mut dec = XdrDecoder::new(dec_data);
1426 assert!(dec.decode_u32().is_none());
1427 assert_eq!(dec.remaining(), 0);
1428 }
1429
1430 #[test]
1431 fn test_nfs_client_new() {
1432 let client = NfsClient::new(String::from("192.168.1.100:2049"));
1433 assert_eq!(client.server_addr(), "192.168.1.100:2049");
1434 assert!(client.root_fh().is_empty());
1435 assert!(client.current_fh().is_empty());
1436 }
1437
1438 #[test]
1439 fn test_nfs_client_build_compound() {
1440 let client = NfsClient::new(String::from("10.0.0.1:2049"));
1441 let req = CompoundRequest {
1442 tag: String::from("test"),
1443 minor_version: 0,
1444 operations: vec![NfsOperation::PutRootFH, NfsOperation::GetFH],
1445 };
1446 let encoded = client.build_compound(&req);
1447 assert!(!encoded.is_empty());
1448 assert!(encoded.len() > 40);
1450 }
1451}