Skip to main content

over_there/core/server/action/handler/
fs.rs

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                // Should be an OS-related error
1016                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}