1use crate::core::{
2 reply::*,
3 request::*,
4 server::{
5 fs::{LocalDirEntry, LocalFileError, LocalFileHandle},
6 state::ServerState,
7 },
8};
9use log::debug;
10use std::convert::TryFrom;
11use std::io;
12use std::sync::Arc;
13
14#[derive(Debug)]
15pub enum FileIoError {
16 Io(io::Error),
17 SigMismatch { id: u32, sig: u32 },
18}
19
20impl From<FileIoError> for ReplyError {
21 fn from(fie: FileIoError) -> ReplyError {
22 match fie {
23 FileIoError::Io(x) => ReplyError::Io(x.into()),
24 FileIoError::SigMismatch { id, sig } => {
25 ReplyError::FileSigChanged(FileSigChangedArgs { id, sig })
26 }
27 }
28 }
29}
30
31impl From<FileIoError> for Reply {
32 fn from(x: FileIoError) -> Self {
33 Self::Error(ReplyError::from(x))
34 }
35}
36
37pub async fn open_file(
38 state: Arc<ServerState>,
39 args: &OpenFileArgs,
40) -> Result<FileOpenedArgs, io::Error> {
41 debug!("handler::open_file: {:?}", args);
42
43 let handle = state
44 .fs_manager
45 .lock()
46 .await
47 .open_file(
48 &args.path,
49 args.create_if_missing,
50 args.write_access,
51 args.read_access,
52 )
53 .await?;
54
55 state.touch_file_id(handle.id).await;
56
57 Ok(FileOpenedArgs {
58 id: handle.id,
59 sig: handle.sig,
60 path: args.path.clone(),
61 read: args.read_access,
62 write: args.write_access,
63 })
64}
65
66pub async fn close_file(
67 state: Arc<ServerState>,
68 args: &CloseFileArgs,
69) -> Result<FileClosedArgs, io::Error> {
70 debug!("handler::close_file: {:?}", args);
71 state.touch_file_id(args.id).await;
72
73 let handle = LocalFileHandle {
74 id: args.id,
75 sig: args.sig,
76 };
77
78 let _ = state.fs_manager.lock().await.close_file(handle)?;
79
80 state.remove_file_id(args.id).await;
81 Ok(FileClosedArgs { id: args.id })
82}
83
84pub async fn rename_unopened_file(
85 state: Arc<ServerState>,
86 args: &RenameUnopenedFileArgs,
87) -> Result<UnopenedFileRenamedArgs, io::Error> {
88 debug!("handler::rename_unopened_file: {:?}", args);
89
90 state
91 .fs_manager
92 .lock()
93 .await
94 .rename_file(&args.from, &args.to)
95 .await?;
96
97 Ok(UnopenedFileRenamedArgs {
98 from: args.from.clone(),
99 to: args.to.clone(),
100 })
101}
102
103pub async fn rename_file(
104 state: Arc<ServerState>,
105 args: &RenameFileArgs,
106) -> Result<FileRenamedArgs, FileIoError> {
107 debug!("handler::rename_file: {:?}", args);
108 state.touch_file_id(args.id).await;
109
110 match state.fs_manager.lock().await.get_mut(args.id) {
111 Some(local_file) => match local_file.rename(args.sig, &args.to).await {
112 Ok(_) => Ok(FileRenamedArgs {
113 id: args.id,
114 sig: local_file.sig(),
115 }),
116 Err(LocalFileError::SigMismatch) => Err(FileIoError::SigMismatch {
117 id: args.id,
118 sig: local_file.sig(),
119 }),
120 Err(LocalFileError::IoError(x)) => Err(FileIoError::Io(x)),
121 },
122 None => Err(FileIoError::Io(
123 IoErrorArgs::invalid_file_id(args.id).into(),
124 )),
125 }
126}
127
128pub async fn remove_unopened_file(
129 state: Arc<ServerState>,
130 args: &RemoveUnopenedFileArgs,
131) -> Result<UnopenedFileRemovedArgs, io::Error> {
132 debug!("handler::remove_unopened_file: {:?}", args);
133
134 state
135 .fs_manager
136 .lock()
137 .await
138 .remove_file(&args.path)
139 .await?;
140
141 Ok(UnopenedFileRemovedArgs {
142 path: args.path.clone(),
143 })
144}
145
146pub async fn remove_file(
147 state: Arc<ServerState>,
148 args: &RemoveFileArgs,
149) -> Result<FileRemovedArgs, FileIoError> {
150 debug!("handler::remove_file: {:?}", args);
151 state.touch_file_id(args.id).await;
152
153 match state.fs_manager.lock().await.get_mut(args.id) {
154 Some(local_file) => match local_file.remove(args.sig).await {
155 Ok(_) => {
156 state.remove_file_id(args.id).await;
157 Ok(FileRemovedArgs {
158 id: args.id,
159 sig: local_file.sig(),
160 })
161 }
162 Err(LocalFileError::SigMismatch) => Err(FileIoError::SigMismatch {
163 id: args.id,
164 sig: local_file.sig(),
165 }),
166 Err(LocalFileError::IoError(x)) => Err(FileIoError::Io(x)),
167 },
168 None => Err(FileIoError::Io(
169 IoErrorArgs::invalid_file_id(args.id).into(),
170 )),
171 }
172}
173
174pub async fn read_file(
175 state: Arc<ServerState>,
176 args: &ReadFileArgs,
177) -> Result<FileContentsArgs, FileIoError> {
178 debug!("handler::read_file: {:?}", args);
179 state.touch_file_id(args.id).await;
180
181 match state.fs_manager.lock().await.get_mut(args.id) {
182 Some(local_file) => match local_file.read_all(args.sig).await {
183 Ok(contents) => Ok(FileContentsArgs {
184 id: args.id,
185 contents,
186 }),
187 Err(LocalFileError::SigMismatch) => Err(FileIoError::SigMismatch {
188 id: args.id,
189 sig: local_file.sig(),
190 }),
191 Err(LocalFileError::IoError(x)) => Err(FileIoError::Io(x)),
192 },
193 None => Err(FileIoError::Io(
194 IoErrorArgs::invalid_file_id(args.id).into(),
195 )),
196 }
197}
198
199pub async fn write_file(
200 state: Arc<ServerState>,
201 args: &WriteFileArgs,
202) -> Result<FileWrittenArgs, FileIoError> {
203 debug!("handler::write_file: {:?}", args);
204 state.touch_file_id(args.id).await;
205
206 match state.fs_manager.lock().await.get_mut(args.id) {
207 Some(local_file) => {
208 match local_file.write_all(args.sig, &args.contents).await {
209 Ok(_) => Ok(FileWrittenArgs {
210 id: args.id,
211 sig: local_file.sig(),
212 }),
213 Err(LocalFileError::SigMismatch) => {
214 Err(FileIoError::SigMismatch {
215 id: args.id,
216 sig: local_file.sig(),
217 })
218 }
219 Err(LocalFileError::IoError(x)) => Err(FileIoError::Io(x)),
220 }
221 }
222 None => Err(FileIoError::Io(
223 IoErrorArgs::invalid_file_id(args.id).into(),
224 )),
225 }
226}
227
228pub async fn create_dir(
229 state: Arc<ServerState>,
230 args: &CreateDirArgs,
231) -> Result<DirCreatedArgs, io::Error> {
232 debug!("handler::create_dir: {:?}", args);
233
234 state
235 .fs_manager
236 .lock()
237 .await
238 .create_dir(&args.path, args.include_components)
239 .await?;
240
241 Ok(DirCreatedArgs {
242 path: args.path.clone(),
243 })
244}
245
246pub async fn rename_dir(
247 state: Arc<ServerState>,
248 args: &RenameDirArgs,
249) -> Result<DirRenamedArgs, io::Error> {
250 debug!("handler::rename_dir: {:?}", args);
251
252 state
253 .fs_manager
254 .lock()
255 .await
256 .rename_dir(&args.from, &args.to)
257 .await?;
258
259 Ok(DirRenamedArgs {
260 from: args.from.clone(),
261 to: args.to.clone(),
262 })
263}
264
265pub async fn remove_dir(
266 state: Arc<ServerState>,
267 args: &RemoveDirArgs,
268) -> Result<DirRemovedArgs, io::Error> {
269 debug!("handler::remove_dir: {:?}", args);
270
271 state
272 .fs_manager
273 .lock()
274 .await
275 .remove_dir(&args.path, args.non_empty)
276 .await?;
277
278 Ok(DirRemovedArgs {
279 path: args.path.clone(),
280 })
281}
282
283pub async fn list_dir_contents(
284 state: Arc<ServerState>,
285 args: &ListDirContentsArgs,
286) -> Result<DirContentsListArgs, io::Error> {
287 debug!("handler::list_dir_contents: {:?}", args);
288
289 let local_entries = state
290 .fs_manager
291 .lock()
292 .await
293 .dir_entries(&args.path)
294 .await?;
295
296 let entries = local_entries
297 .into_iter()
298 .map(DirEntry::try_from)
299 .collect::<io::Result<Vec<DirEntry>>>()?;
300
301 Ok(DirContentsListArgs {
302 path: args.path.clone(),
303 entries,
304 })
305}
306
307impl TryFrom<LocalDirEntry> for DirEntry {
308 type Error = io::Error;
309
310 fn try_from(local_dir_entry: LocalDirEntry) -> Result<Self, Self::Error> {
311 Ok(Self {
312 path: local_dir_entry
313 .path
314 .into_os_string()
315 .into_string()
316 .map_err(|_| {
317 io::Error::new(
318 io::ErrorKind::InvalidData,
319 "OS String does not contain valid unicode",
320 )
321 })?,
322 is_file: local_dir_entry.is_file,
323 is_dir: local_dir_entry.is_dir,
324 is_symlink: local_dir_entry.is_symlink,
325 })
326 }
327}
328
329#[cfg(test)]
330mod tests {
331 use super::*;
332 use std::io;
333 use tokio::fs;
334
335 #[tokio::test]
336 async fn open_file_should_return_success_if_create_flag_set_and_opening_new_file(
337 ) {
338 let state = Arc::new(ServerState::default());
339 let tmp_path = tempfile::NamedTempFile::new()
340 .unwrap()
341 .into_temp_path()
342 .to_string_lossy()
343 .to_string();
344
345 let args = open_file(
346 Arc::clone(&state),
347 &OpenFileArgs {
348 path: tmp_path.clone(),
349 create_if_missing: true,
350 write_access: true,
351 read_access: true,
352 },
353 )
354 .await
355 .unwrap();
356
357 let x = state.fs_manager.lock().await;
358 let local_file = x.get(args.id).unwrap();
359 assert_eq!(args.sig, local_file.sig());
360 assert_eq!(args.path, tmp_path);
361 assert!(args.write);
362 assert!(args.read);
363 }
364
365 #[tokio::test]
366 async fn open_file_should_return_success_opening_existing_file() {
367 let state = Arc::new(ServerState::default());
368
369 let tmp_file = tempfile::NamedTempFile::new().unwrap();
370 let tmp_file_path = tmp_file.path().to_string_lossy().to_string();
371
372 let args = open_file(
373 Arc::clone(&state),
374 &OpenFileArgs {
375 path: tmp_file_path.clone(),
376 create_if_missing: false,
377 write_access: true,
378 read_access: true,
379 },
380 )
381 .await
382 .unwrap();
383
384 let x = state.fs_manager.lock().await;
385 let local_file = x.get(args.id).unwrap();
386 assert_eq!(args.sig, local_file.sig());
387 assert_eq!(args.path, tmp_file_path);
388 assert!(args.write);
389 assert!(args.read);
390 }
391
392 #[tokio::test]
393 async fn open_file_should_return_error_if_file_missing_and_create_flag_not_set(
394 ) {
395 let state = Arc::new(ServerState::default());
396
397 let tmp_path = tempfile::NamedTempFile::new()
398 .unwrap()
399 .into_temp_path()
400 .to_string_lossy()
401 .to_string();
402
403 let err = open_file(
404 Arc::clone(&state),
405 &OpenFileArgs {
406 path: tmp_path,
407 create_if_missing: false,
408 write_access: true,
409 read_access: true,
410 },
411 )
412 .await
413 .unwrap_err();
414
415 assert_eq!(err.kind(), io::ErrorKind::NotFound);
416 }
417
418 #[tokio::test]
419 async fn close_file_should_return_error_if_file_not_open() {
420 let state = Arc::new(ServerState::default());
421
422 let tmp_path = tempfile::NamedTempFile::new().unwrap().into_temp_path();
423 let handle = state
424 .fs_manager
425 .lock()
426 .await
427 .open_file(tmp_path, false, false, true)
428 .await
429 .expect("Failed to open file");
430
431 let id = handle.id + 1;
432 let sig = handle.sig;
433
434 let err = close_file(Arc::clone(&state), &CloseFileArgs { id, sig })
435 .await
436 .unwrap_err();
437
438 assert_eq!(err.kind(), io::ErrorKind::NotFound);
439 }
440
441 #[tokio::test]
442 async fn close_file_should_return_error_if_signature_different() {
443 let state = Arc::new(ServerState::default());
444
445 let tmp_path = tempfile::NamedTempFile::new().unwrap().into_temp_path();
446 let handle = state
447 .fs_manager
448 .lock()
449 .await
450 .open_file(tmp_path, false, false, true)
451 .await
452 .expect("Failed to open file");
453
454 let id = handle.id;
455 let sig = handle.sig + 1;
456
457 let err = close_file(Arc::clone(&state), &CloseFileArgs { id, sig })
458 .await
459 .unwrap_err();
460
461 assert_eq!(err.kind(), io::ErrorKind::InvalidInput);
462 }
463
464 #[tokio::test]
465 async fn close_file_should_return_confirmation_if_successful() {
466 let state = Arc::new(ServerState::default());
467
468 let tmp_path = tempfile::NamedTempFile::new().unwrap().into_temp_path();
469 let handle = state
470 .fs_manager
471 .lock()
472 .await
473 .open_file(tmp_path, false, false, true)
474 .await
475 .expect("Failed to open file");
476
477 let id = handle.id;
478 let sig = handle.sig;
479
480 let args = close_file(Arc::clone(&state), &CloseFileArgs { id, sig })
481 .await
482 .unwrap();
483
484 assert_eq!(args.id, id);
485 }
486
487 #[tokio::test]
488 async fn rename_unopened_file_should_return_error_if_file_open() {
489 let state = Arc::new(ServerState::default());
490
491 let file = tempfile::NamedTempFile::new().unwrap();
492 state
493 .fs_manager
494 .lock()
495 .await
496 .open_file(file.as_ref(), false, false, true)
497 .await
498 .expect("Failed to open file");
499
500 let err = rename_unopened_file(
501 Arc::clone(&state),
502 &RenameUnopenedFileArgs {
503 from: file.as_ref().to_string_lossy().to_string(),
504 to: file.as_ref().to_string_lossy().to_string(),
505 },
506 )
507 .await
508 .unwrap_err();
509
510 assert_eq!(err.kind(), io::ErrorKind::InvalidData);
511 }
512
513 #[tokio::test]
514 async fn rename_unopened_file_should_return_confirmation_if_file_renamed() {
515 let state = Arc::new(ServerState::default());
516
517 let file = tempfile::NamedTempFile::new().unwrap();
518 let from_path_str = file.as_ref().to_string_lossy().to_string();
519 let to_path_str = format!("{}.renamed", from_path_str);
520
521 let args = rename_unopened_file(
522 Arc::clone(&state),
523 &RenameUnopenedFileArgs {
524 from: from_path_str.clone(),
525 to: to_path_str.clone(),
526 },
527 )
528 .await
529 .unwrap();
530
531 assert!(
532 fs::metadata(from_path_str.clone()).await.is_err(),
533 "File not renamed"
534 );
535 assert!(
536 fs::metadata(to_path_str.clone()).await.is_ok(),
537 "File renamed incorrectly"
538 );
539
540 assert_eq!(args.from, from_path_str);
541 assert_eq!(args.to, to_path_str);
542 }
543
544 #[tokio::test]
545 async fn rename_file_should_return_error_if_file_not_open() {
546 let state = Arc::new(ServerState::default());
547
548 let file = tempfile::NamedTempFile::new().unwrap();
549 let handle = state
550 .fs_manager
551 .lock()
552 .await
553 .open_file(file.as_ref(), false, false, true)
554 .await
555 .expect("Failed to open file");
556 let new_path_str = String::from("new-file-name");
557
558 let err = rename_file(
559 Arc::clone(&state),
560 &RenameFileArgs {
561 id: handle.id + 1,
562 sig: handle.sig,
563 to: new_path_str.clone(),
564 },
565 )
566 .await
567 .unwrap_err();
568
569 assert!(
570 fs::metadata(file.as_ref()).await.is_ok(),
571 "File missing when rename failed"
572 );
573
574 assert!(
575 fs::metadata(&new_path_str).await.is_err(),
576 "Renamed file exists"
577 );
578
579 match err {
580 FileIoError::Io(x) => {
581 assert_eq!(x.kind(), io::ErrorKind::InvalidInput)
582 }
583 x => panic!("Unexpected error: {:?}", x),
584 }
585 }
586
587 #[tokio::test]
588 async fn rename_file_should_return_error_if_signature_different() {
589 let state = Arc::new(ServerState::default());
590
591 let file = tempfile::NamedTempFile::new().unwrap();
592 let handle = state
593 .fs_manager
594 .lock()
595 .await
596 .open_file(file.as_ref(), false, false, true)
597 .await
598 .expect("Failed to open file");
599 let new_path_str = String::from("new-file-name");
600
601 let err = rename_file(
602 Arc::clone(&state),
603 &RenameFileArgs {
604 id: handle.id,
605 sig: handle.sig + 1,
606 to: new_path_str.clone(),
607 },
608 )
609 .await
610 .unwrap_err();
611
612 assert!(
613 fs::metadata(file.as_ref()).await.is_ok(),
614 "File missing when rename failed"
615 );
616
617 assert!(
618 fs::metadata(&new_path_str).await.is_err(),
619 "Renamed file exists"
620 );
621
622 match err {
623 FileIoError::SigMismatch { id, sig } => {
624 assert_eq!(id, handle.id);
625 assert_eq!(sig, handle.sig);
626 }
627 x => panic!("Unexpected error: {:?}", x),
628 }
629 }
630
631 #[tokio::test]
632 async fn rename_file_should_return_confirmation_if_file_renamed() {
633 let state = Arc::new(ServerState::default());
634
635 let file = tempfile::NamedTempFile::new().unwrap();
636 let handle = state
637 .fs_manager
638 .lock()
639 .await
640 .open_file(file.as_ref(), false, false, true)
641 .await
642 .expect("Failed to open file");
643 let new_path_str =
644 format!("{}.2", file.as_ref().to_string_lossy().to_string());
645
646 let args = rename_file(
647 Arc::clone(&state),
648 &RenameFileArgs {
649 id: handle.id,
650 sig: handle.sig,
651 to: new_path_str.clone(),
652 },
653 )
654 .await
655 .unwrap();
656
657 assert!(
658 fs::metadata(file.as_ref()).await.is_err(),
659 "Renamed file still exists at original location"
660 );
661
662 assert!(
663 fs::metadata(&new_path_str).await.is_ok(),
664 "Renamed file missing"
665 );
666
667 assert_eq!(handle.id, args.id, "Wrong id returned");
668 assert_ne!(handle.sig, args.sig, "Signature returned is not different");
669 }
670
671 #[tokio::test]
672 async fn remove_unopened_file_should_return_error_if_file_open() {
673 let state = Arc::new(ServerState::default());
674
675 let file = tempfile::NamedTempFile::new().unwrap();
676 state
677 .fs_manager
678 .lock()
679 .await
680 .open_file(file.as_ref(), false, false, true)
681 .await
682 .expect("Failed to open file");
683
684 let err = remove_unopened_file(
685 Arc::clone(&state),
686 &RemoveUnopenedFileArgs {
687 path: file.as_ref().to_string_lossy().to_string(),
688 },
689 )
690 .await
691 .unwrap_err();
692
693 assert!(
694 fs::metadata(file.as_ref()).await.is_ok(),
695 "File unexpectedly removed"
696 );
697
698 assert_eq!(err.kind(), io::ErrorKind::InvalidData);
699 }
700
701 #[tokio::test]
702 async fn remove_unopened_file_should_return_confirmation_if_file_removed() {
703 let state = Arc::new(ServerState::default());
704
705 let file = tempfile::NamedTempFile::new().unwrap();
706
707 let args = remove_unopened_file(
708 Arc::clone(&state),
709 &RemoveUnopenedFileArgs {
710 path: file.as_ref().to_string_lossy().to_string(),
711 },
712 )
713 .await
714 .unwrap();
715
716 assert!(
717 fs::metadata(file.as_ref()).await.is_err(),
718 "File still exists"
719 );
720
721 assert_eq!(args.path, file.as_ref().to_string_lossy().to_string());
722 }
723
724 #[tokio::test]
725 async fn remove_file_should_return_error_if_file_not_open() {
726 let state = Arc::new(ServerState::default());
727
728 let file = tempfile::NamedTempFile::new().unwrap();
729 let handle = state
730 .fs_manager
731 .lock()
732 .await
733 .open_file(file.as_ref(), false, false, true)
734 .await
735 .expect("Failed to open file");
736
737 let err = remove_file(
738 Arc::clone(&state),
739 &RemoveFileArgs {
740 id: handle.id + 1,
741 sig: handle.sig,
742 },
743 )
744 .await
745 .unwrap_err();
746
747 assert!(
748 fs::metadata(file.as_ref()).await.is_ok(),
749 "File unexpectedly missing after failed remove"
750 );
751
752 match err {
753 FileIoError::Io(x) => {
754 assert_eq!(x.kind(), io::ErrorKind::InvalidInput)
755 }
756 x => panic!("Unexpected error: {:?}", x),
757 }
758 }
759
760 #[tokio::test]
761 async fn remove_file_should_return_error_if_signature_different() {
762 let state = Arc::new(ServerState::default());
763
764 let file = tempfile::NamedTempFile::new().unwrap();
765 let handle = state
766 .fs_manager
767 .lock()
768 .await
769 .open_file(file.as_ref(), false, false, true)
770 .await
771 .expect("Failed to open file");
772
773 let err = remove_file(
774 Arc::clone(&state),
775 &RemoveFileArgs {
776 id: handle.id,
777 sig: handle.sig + 1,
778 },
779 )
780 .await
781 .unwrap_err();
782
783 assert!(
784 fs::metadata(file.as_ref()).await.is_ok(),
785 "File unexpectedly missing after failed remove"
786 );
787
788 match err {
789 FileIoError::SigMismatch { id, sig } => {
790 assert_eq!(id, handle.id);
791 assert_eq!(sig, handle.sig);
792 }
793 x => panic!("Unexpected error: {:?}", x),
794 }
795 }
796
797 #[tokio::test]
798 async fn remove_file_should_return_confirmation_if_file_removed() {
799 let state = Arc::new(ServerState::default());
800
801 let file = tempfile::NamedTempFile::new().unwrap();
802 let handle = state
803 .fs_manager
804 .lock()
805 .await
806 .open_file(file.as_ref(), false, false, true)
807 .await
808 .expect("Failed to open file");
809
810 let args = remove_file(
811 Arc::clone(&state),
812 &RemoveFileArgs {
813 id: handle.id,
814 sig: handle.sig,
815 },
816 )
817 .await
818 .unwrap();
819
820 assert!(
821 fs::metadata(file.as_ref()).await.is_err(),
822 "File still exists"
823 );
824
825 assert_eq!(handle.id, args.id, "Wrong id returned");
826 assert_ne!(handle.sig, args.sig, "Signature returned is not different");
827 }
828
829 #[tokio::test]
830 async fn read_file_should_return_contents_if_read_successful() {
831 let state = Arc::new(ServerState::default());
832 let file_contents = vec![1, 2, 3, 4, 5, 6, 7, 8, 9];
833
834 let mut file = tempfile::NamedTempFile::new().unwrap();
835
836 use std::io::Write;
837 file.write_all(&file_contents).unwrap();
838 file.flush().unwrap();
839
840 let handle = state
841 .fs_manager
842 .lock()
843 .await
844 .open_file(file.as_ref(), true, true, true)
845 .await
846 .expect("Unable to open file");
847 let id = handle.id;
848 let sig = handle.sig;
849
850 let args = read_file(Arc::clone(&state), &ReadFileArgs { id, sig })
851 .await
852 .unwrap();
853
854 assert_eq!(args.id, id, "Wrong id returned");
855 assert_eq!(args.contents, file_contents);
856 }
857
858 #[tokio::test]
859 async fn read_file_should_return_error_if_file_not_open() {
860 let err = read_file(
861 Arc::new(ServerState::default()),
862 &ReadFileArgs { id: 0, sig: 0 },
863 )
864 .await
865 .unwrap_err();
866
867 match err {
868 FileIoError::Io(x) => {
869 assert_eq!(x.kind(), io::ErrorKind::InvalidInput);
870 }
871 x => panic!("Unexpected error: {:?}", x),
872 }
873 }
874
875 #[tokio::test]
876 async fn read_file_should_return_error_if_not_readable() {
877 let state = Arc::new(ServerState::default());
878
879 let tmp_file = tempfile::NamedTempFile::new().unwrap();
880
881 use std::io::Write;
882 let mut file = std::fs::OpenOptions::new()
883 .read(false)
884 .write(true)
885 .create(true)
886 .open(tmp_file.path())
887 .unwrap();
888 file.write_all(&vec![1, 2, 3, 4, 5, 6, 7, 8, 9]).unwrap();
889 file.flush().unwrap();
890
891 let handle = state
892 .fs_manager
893 .lock()
894 .await
895 .open_file(tmp_file.as_ref(), true, true, false)
896 .await
897 .expect("Unable to open file");
898 let id = handle.id;
899 let sig = handle.sig;
900
901 let err = read_file(Arc::clone(&state), &ReadFileArgs { id, sig })
902 .await
903 .unwrap_err();
904
905 match err {
906 FileIoError::Io(x) => {
907 assert!(x.raw_os_error().is_some());
908 }
909 x => panic!("Unexpected error: {:?}", x),
910 }
911 }
912
913 #[tokio::test]
914 async fn read_file_should_return_error_if_file_sig_has_changed() {
915 let state = Arc::new(ServerState::default());
916 let file = tempfile::NamedTempFile::new().unwrap();
917
918 let handle = state
919 .fs_manager
920 .lock()
921 .await
922 .open_file(file.as_ref(), true, true, true)
923 .await
924 .expect("Unable to open file");
925 let id = handle.id;
926 let sig = handle.sig;
927
928 let err =
929 read_file(Arc::clone(&state), &ReadFileArgs { id, sig: sig + 1 })
930 .await
931 .unwrap_err();
932
933 match err {
934 FileIoError::SigMismatch {
935 id: cur_id,
936 sig: cur_sig,
937 } => {
938 assert_eq!(cur_id, id);
939 assert_eq!(cur_sig, sig);
940 }
941 x => panic!("Unexpected error: {:?}", x),
942 }
943 }
944
945 #[tokio::test]
946 async fn write_file_should_return_success_if_write_successful() {
947 let state = Arc::new(ServerState::default());
948 let contents = vec![1, 2, 3, 4, 5, 6, 7, 8, 9];
949
950 let mut file = tempfile::NamedTempFile::new().unwrap();
951
952 let handle = state
953 .fs_manager
954 .lock()
955 .await
956 .open_file(file.as_ref(), true, true, true)
957 .await
958 .expect("Unable to open file");
959 let id = handle.id;
960 let sig = handle.sig;
961
962 let args = write_file(
963 Arc::clone(&state),
964 &WriteFileArgs {
965 id,
966 sig,
967 contents: contents.clone(),
968 },
969 )
970 .await
971 .unwrap();
972
973 assert_eq!(args.id, id, "Wrong id returned");
974 assert_ne!(args.sig, sig);
975
976 use std::io::{Seek, SeekFrom};
977 file.seek(SeekFrom::Start(0)).unwrap();
978
979 use std::io::Read;
980 let mut file_contents = Vec::new();
981 file.read_to_end(&mut file_contents).unwrap();
982
983 assert_eq!(
984 contents, file_contents,
985 "File does not match written content"
986 );
987 }
988
989 #[tokio::test]
990 async fn write_file_should_return_error_if_not_writeable() {
991 let state = Arc::new(ServerState::default());
992 let contents = vec![1, 2, 3, 4, 5, 6, 7, 8, 9];
993
994 let file = tempfile::NamedTempFile::new().unwrap();
995
996 let handle = state
997 .fs_manager
998 .lock()
999 .await
1000 .open_file(file.as_ref(), false, false, true)
1001 .await
1002 .expect("Unable to open file");
1003 let id = handle.id;
1004 let sig = handle.sig;
1005
1006 let err = write_file(
1007 Arc::clone(&state),
1008 &WriteFileArgs { id, sig, contents },
1009 )
1010 .await
1011 .unwrap_err();
1012
1013 match err {
1014 FileIoError::Io(x) => {
1015 assert!(x.raw_os_error().is_some());
1017 }
1018 x => panic!("Unexpected error: {:?}", x),
1019 }
1020 }
1021
1022 #[tokio::test]
1023 async fn write_file_should_return_error_if_file_sig_has_changed() {
1024 let state = Arc::new(ServerState::default());
1025 let contents = vec![1, 2, 3, 4, 5, 6, 7, 8, 9];
1026
1027 let file = tempfile::NamedTempFile::new().unwrap();
1028
1029 let handle = state
1030 .fs_manager
1031 .lock()
1032 .await
1033 .open_file(file.as_ref(), true, true, true)
1034 .await
1035 .expect("Unable to open file");
1036 let id = handle.id;
1037 let sig = handle.sig;
1038
1039 let err = write_file(
1040 Arc::clone(&state),
1041 &WriteFileArgs {
1042 id,
1043 sig: sig + 1,
1044 contents: contents.clone(),
1045 },
1046 )
1047 .await
1048 .unwrap_err();
1049
1050 match err {
1051 FileIoError::SigMismatch {
1052 id: cur_id,
1053 sig: cur_sig,
1054 } => {
1055 assert_eq!(cur_id, id, "Wrong id returned");
1056 assert_eq!(cur_sig, sig);
1057 }
1058 x => panic!("Unexpected error: {:?}", x),
1059 }
1060 }
1061
1062 #[tokio::test]
1063 async fn create_dir_should_return_error_if_part_of_path_missing_and_flag_not_set(
1064 ) {
1065 let root_path = tempfile::tempdir().unwrap();
1066 let state = Arc::new(ServerState::default());
1067
1068 let dir_path = root_path.as_ref().join("test").join("dir");
1069
1070 let err = create_dir(
1071 Arc::clone(&state),
1072 &CreateDirArgs {
1073 path: dir_path.as_path().to_string_lossy().to_string(),
1074 include_components: false,
1075 },
1076 )
1077 .await
1078 .unwrap_err();
1079
1080 assert!(
1081 fs::metadata(&dir_path).await.is_err(),
1082 "Dir was unexpectedly created"
1083 );
1084
1085 assert_eq!(err.kind(), io::ErrorKind::NotFound);
1086 }
1087
1088 #[tokio::test]
1089 async fn create_dir_should_return_confirmation_if_single_level_directory_created(
1090 ) {
1091 let root_path = tempfile::tempdir().unwrap();
1092 let state = Arc::new(ServerState::default());
1093
1094 let dir_path = root_path.as_ref().join("test");
1095
1096 let args = create_dir(
1097 Arc::clone(&state),
1098 &CreateDirArgs {
1099 path: dir_path.as_path().to_string_lossy().to_string(),
1100 include_components: false,
1101 },
1102 )
1103 .await
1104 .unwrap();
1105
1106 assert!(
1107 fs::metadata(&dir_path).await.is_ok(),
1108 "Dir was unexpectedly not created"
1109 );
1110
1111 assert_eq!(
1112 args.path,
1113 dir_path.to_string_lossy().to_string(),
1114 "Wrong path returned"
1115 );
1116 }
1117
1118 #[tokio::test]
1119 async fn create_dir_should_return_confirmation_if_multi_level_directory_created(
1120 ) {
1121 let root_path = tempfile::tempdir().unwrap();
1122 let state = Arc::new(ServerState::default());
1123
1124 let dir_path = root_path.as_ref().join("test").join("dir");
1125
1126 let args = create_dir(
1127 Arc::clone(&state),
1128 &CreateDirArgs {
1129 path: dir_path.as_path().to_string_lossy().to_string(),
1130 include_components: true,
1131 },
1132 )
1133 .await
1134 .unwrap();
1135
1136 assert!(
1137 fs::metadata(&dir_path).await.is_ok(),
1138 "Dir was unexpectedly not created"
1139 );
1140 assert_eq!(
1141 args.path,
1142 dir_path.to_string_lossy().to_string(),
1143 "Wrong path returned"
1144 );
1145 }
1146
1147 #[tokio::test]
1148 async fn rename_dir_should_return_error_if_a_file_open_in_directory() {
1149 let state = Arc::new(ServerState::default());
1150
1151 let from_dir = tempfile::tempdir().unwrap();
1152 let to_dir = tempfile::tempdir().unwrap().into_path();
1153 fs::remove_dir(to_dir.as_path())
1154 .await
1155 .expect("Failed to clean up temp dir");
1156
1157 let file_path = from_dir.as_ref().join("test-file");
1158 let _ = state
1159 .fs_manager
1160 .lock()
1161 .await
1162 .open_file(file_path.as_path(), true, true, true)
1163 .await
1164 .expect("Failed to open file");
1165
1166 let err = rename_dir(
1167 Arc::clone(&state),
1168 &RenameDirArgs {
1169 from: from_dir.as_ref().to_string_lossy().to_string(),
1170 to: to_dir.as_path().to_string_lossy().to_string(),
1171 },
1172 )
1173 .await
1174 .unwrap_err();
1175
1176 assert!(
1177 fs::metadata(file_path.as_path()).await.is_ok(),
1178 "Open file unexpectedly renamed to something else"
1179 );
1180
1181 assert!(
1182 fs::metadata(from_dir.as_ref()).await.is_ok(),
1183 "Dir was unexpectedly renamed"
1184 );
1185
1186 assert!(
1187 fs::metadata(to_dir.as_path()).await.is_err(),
1188 "Destination of rename unexpectedly exists"
1189 );
1190
1191 assert_eq!(err.kind(), io::ErrorKind::InvalidData);
1192 }
1193
1194 #[tokio::test]
1195 async fn rename_dir_should_return_confirmation_if_directory_renamed() {
1196 let state = Arc::new(ServerState::default());
1197
1198 let from_dir = tempfile::tempdir().unwrap();
1199 let to_dir = tempfile::tempdir().unwrap().into_path();
1200 fs::remove_dir(to_dir.as_path())
1201 .await
1202 .expect("Failed to clean up temp dir");
1203
1204 let args = rename_dir(
1205 Arc::clone(&state),
1206 &RenameDirArgs {
1207 from: from_dir.as_ref().to_string_lossy().to_string(),
1208 to: to_dir.as_path().to_string_lossy().to_string(),
1209 },
1210 )
1211 .await
1212 .unwrap();
1213
1214 assert!(
1215 fs::metadata(from_dir.as_ref()).await.is_err(),
1216 "Dir was unexpectedly not renamed"
1217 );
1218
1219 assert!(
1220 fs::metadata(to_dir.as_path()).await.is_ok(),
1221 "Destination of rename unexpectedly missing"
1222 );
1223
1224 assert_eq!(
1225 args.from,
1226 from_dir.as_ref().to_string_lossy().to_string(),
1227 "Wrong from path returned"
1228 );
1229 assert_eq!(
1230 args.to,
1231 to_dir.to_string_lossy().to_string(),
1232 "Wrong to path returned"
1233 );
1234 }
1235
1236 #[tokio::test]
1237 async fn remove_dir_should_return_error_if_a_file_open_in_directory() {
1238 let state = Arc::new(ServerState::default());
1239
1240 let dir = tempfile::tempdir().unwrap();
1241
1242 let file_path = dir.as_ref().join("test-file");
1243 let _ = state
1244 .fs_manager
1245 .lock()
1246 .await
1247 .open_file(file_path.as_path(), true, true, true)
1248 .await
1249 .expect("Failed to open file");
1250
1251 let err = remove_dir(
1252 Arc::clone(&state),
1253 &RemoveDirArgs {
1254 path: dir.as_ref().to_string_lossy().to_string(),
1255 non_empty: true,
1256 },
1257 )
1258 .await
1259 .unwrap_err();
1260
1261 assert!(
1262 fs::metadata(file_path.as_path()).await.is_ok(),
1263 "Open file unexpectedly removed"
1264 );
1265
1266 assert_eq!(err.kind(), io::ErrorKind::InvalidData);
1267 }
1268
1269 #[tokio::test]
1270 async fn remove_dir_should_return_confirmation_if_empty_directory_removed()
1271 {
1272 let state = Arc::new(ServerState::default());
1273
1274 let dir = tempfile::tempdir().unwrap();
1275
1276 let args = remove_dir(
1277 Arc::clone(&state),
1278 &RemoveDirArgs {
1279 path: dir.as_ref().to_string_lossy().to_string(),
1280 non_empty: false,
1281 },
1282 )
1283 .await
1284 .unwrap();
1285
1286 assert!(
1287 fs::metadata(dir.as_ref()).await.is_err(),
1288 "Dir still exists"
1289 );
1290
1291 assert_eq!(
1292 args.path,
1293 dir.as_ref().to_string_lossy().to_string(),
1294 "Wrong path returned"
1295 );
1296 }
1297
1298 #[tokio::test]
1299 async fn remove_dir_should_return_error_if_nonempty_directory_removed_with_flag_not_set(
1300 ) {
1301 let state = Arc::new(ServerState::default());
1302
1303 let dir = tempfile::tempdir().unwrap();
1304 let file = tempfile::NamedTempFile::new_in(dir.as_ref()).unwrap();
1305
1306 let err = remove_dir(
1307 Arc::clone(&state),
1308 &RemoveDirArgs {
1309 path: dir.as_ref().to_string_lossy().to_string(),
1310 non_empty: false,
1311 },
1312 )
1313 .await
1314 .unwrap_err();
1315
1316 assert!(
1317 fs::metadata(file.as_ref()).await.is_ok(),
1318 "File in dir unexpectedly removed"
1319 );
1320
1321 assert!(
1322 fs::metadata(dir.as_ref()).await.is_ok(),
1323 "Dir unexpected removed"
1324 );
1325
1326 assert_eq!(err.kind(), io::ErrorKind::Other);
1327 }
1328
1329 #[tokio::test]
1330 async fn remove_dir_should_return_confirmation_if_nonempty_directory_removed_with_flag_set(
1331 ) {
1332 let state = Arc::new(ServerState::default());
1333
1334 let dir = tempfile::tempdir().unwrap();
1335 let file = tempfile::NamedTempFile::new_in(dir.as_ref()).unwrap();
1336
1337 let args = remove_dir(
1338 Arc::clone(&state),
1339 &RemoveDirArgs {
1340 path: dir.as_ref().to_string_lossy().to_string(),
1341 non_empty: true,
1342 },
1343 )
1344 .await
1345 .unwrap();
1346
1347 assert!(
1348 fs::metadata(file.as_ref()).await.is_err(),
1349 "File in dir still exists"
1350 );
1351
1352 assert!(
1353 fs::metadata(dir.as_ref()).await.is_err(),
1354 "Dir still exists"
1355 );
1356
1357 assert_eq!(
1358 args.path,
1359 dir.as_ref().to_string_lossy().to_string(),
1360 "Wrong path returned"
1361 );
1362 }
1363
1364 #[tokio::test]
1365 async fn list_dir_contents_should_return_entries_if_successful() {
1366 let dir = tempfile::tempdir().unwrap();
1367 let dir_path = dir.path().to_string_lossy().to_string();
1368
1369 let tmp_file = tempfile::NamedTempFile::new_in(&dir).unwrap();
1370 let tmp_file_path = fs::canonicalize(tmp_file.path())
1371 .await
1372 .unwrap()
1373 .to_string_lossy()
1374 .to_string();
1375
1376 let tmp_dir = tempfile::tempdir_in(&dir).unwrap();
1377 let tmp_dir_path = fs::canonicalize(tmp_dir.path())
1378 .await
1379 .unwrap()
1380 .to_string_lossy()
1381 .to_string();
1382
1383 let args = list_dir_contents(
1384 Arc::new(ServerState::default()),
1385 &ListDirContentsArgs {
1386 path: dir_path.clone(),
1387 },
1388 )
1389 .await
1390 .unwrap();
1391
1392 std::fs::remove_dir_all(dir_path).unwrap();
1393
1394 assert_eq!(args.entries.len(), 2, "Unexpected number of entries");
1395
1396 assert!(args.entries.contains(&DirEntry {
1397 path: tmp_file_path,
1398 is_file: true,
1399 is_dir: false,
1400 is_symlink: false
1401 }));
1402
1403 assert!(args.entries.contains(&DirEntry {
1404 path: tmp_dir_path,
1405 is_file: false,
1406 is_dir: true,
1407 is_symlink: false
1408 }));
1409 }
1410
1411 #[tokio::test]
1412 async fn list_dir_contents_should_return_error_if_path_invalid() {
1413 let err = list_dir_contents(
1414 Arc::new(ServerState::default()),
1415 &ListDirContentsArgs {
1416 path: String::from(""),
1417 },
1418 )
1419 .await
1420 .unwrap_err();
1421
1422 assert_eq!(err.kind(), io::ErrorKind::NotFound);
1423 }
1424}