From 9ec23f1234505fdd8c0677f034219e07a5563ac9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82?= Date: Fri, 27 Oct 2017 14:12:53 +0200 Subject: [PATCH] Add create_dir function. (#5) --- README.md | 5 +++-- src/dir.rs | 37 ++++++++++++++++++++++++++++++++++--- src/file.rs | 4 ++++ tests/write.rs | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 89 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 54f7bb6..ef60a3c 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,9 @@ Introduction FAT filesystem library implemented in Rust. Features: -* read file and directory, -* write file and directory, +* read file or directory, +* write file or directory, +* create new file or directory, * FAT12, FAT16, FAT32 compatibility, * LFN (Long File Names) extension supported. diff --git a/src/dir.rs b/src/dir.rs index a84daab..5417c5b 100644 --- a/src/dir.rs +++ b/src/dir.rs @@ -27,6 +27,13 @@ impl <'a, 'b> DirRawStream<'a, 'b> { &DirRawStream::Root(ref slice) => Some(slice.abs_pos()), } } + + pub(crate) fn first_cluster(&self) -> Option { + match self { + &DirRawStream::File(ref file) => file.first_cluster(), + &DirRawStream::Root(_) => None, + } + } } impl <'a, 'b> Read for DirRawStream<'a, 'b> { @@ -527,13 +534,35 @@ impl <'a, 'b> Dir<'a, 'b> { Some(rest) => r?.to_dir().create_file(rest), None => { match r { - Err(_) => Ok(self.create_file_entry(name)?.to_file()), + Err(_) => Ok(self.create_entry(name, FileAttributes::from_bits_truncate(0), None)?.to_file()), Ok(e) => Ok(e.to_file()) } } } } + /// Creates new directory or opens existing. + pub fn create_dir(&mut self, path: &str) -> io::Result> { + let (name, rest_opt) = Self::split_path(path); + let r = self.find_entry(name); + match rest_opt { + Some(rest) => r?.to_dir().create_dir(rest), + None => { + match r { + Err(_) => { + let cluster = self.fs.alloc_cluster(None)?; + let entry = self.create_entry(name, FileAttributes::DIRECTORY, Some(cluster))?; + let mut dir = entry.to_dir(); + dir.create_entry(".", FileAttributes::DIRECTORY, entry.first_cluster())?; + dir.create_entry("..", FileAttributes::DIRECTORY, self.stream.first_cluster())?; + Ok(dir) + }, + Ok(e) => Ok(e.to_dir()), + } + } + } + } + fn is_empty(&mut self) -> io::Result { for r in self.iter() { let e = r?; @@ -635,7 +664,7 @@ impl <'a, 'b> Dir<'a, 'b> { short_name } - fn create_file_entry(&mut self, name: &str) -> io::Result> { + fn create_entry(&mut self, name: &str, attrs: FileAttributes, first_cluster: Option) -> io::Result> { if name.len() > 255 { return Err(io::Error::new(ErrorKind::InvalidInput, "filename too long")); } @@ -671,10 +700,12 @@ impl <'a, 'b> Dir<'a, 'b> { lfn_entry.name_2.copy_from_slice(&lfn_part[11..11+2]); lfn_entry.serialize(&mut stream)?; } - let raw_entry = DirFileEntryData { + let mut raw_entry = DirFileEntryData { name: short_name, + attrs, ..Default::default() }; + raw_entry.set_first_cluster(first_cluster); raw_entry.serialize(&mut stream)?; let end_pos = stream.seek(io::SeekFrom::Current(0))?; let abs_pos = stream.abs_pos().map(|p| p - DIR_ENTRY_SIZE); diff --git a/src/file.rs b/src/file.rs index 8cbf5ee..81fcd37 100644 --- a/src/file.rs +++ b/src/file.rs @@ -130,6 +130,10 @@ impl <'a, 'b> File<'a, 'b> { } self.entry_dirty = true; } + + pub(crate) fn first_cluster(&self) -> Option { + self.first_cluster + } } impl<'a, 'b> Drop for File<'a, 'b> { diff --git a/tests/write.rs b/tests/write.rs index b0a87f2..433e08e 100644 --- a/tests/write.rs +++ b/tests/write.rs @@ -156,3 +156,51 @@ fn test_create_file_fat16() { fn test_create_file_fat32() { call_with_fs(&test_create_file, FAT32_IMG, 4) } + +fn test_create_dir(fs: FileSystem) { + let mut root_dir = fs.root_dir(); + let parent_dir = root_dir.open_dir("very/long/path").unwrap(); + let mut names = parent_dir.iter().map(|r| r.unwrap().file_name()).collect::>(); + assert_eq!(names, [".", "..", "test.txt"]); + { + let subdir = root_dir.create_dir("very/long/path/new-dir-with-long-name").unwrap(); + names = subdir.iter().map(|r| r.unwrap().file_name()).collect::>(); + assert_eq!(names, [".", ".."]); + } + // check if new entry is visible in parent + names = parent_dir.iter().map(|r| r.unwrap().file_name()).collect::>(); + assert_eq!(names, [".", "..", "test.txt", "new-dir-with-long-name"]); + { + // Check if new directory can be opened and read + let subdir = root_dir.open_dir("very/long/path/new-dir-with-long-name").unwrap(); + names = subdir.iter().map(|r| r.unwrap().file_name()).collect::>(); + assert_eq!(names, [".", ".."]); + } + // Check if '.' is alias for new directory + { + let subdir = root_dir.open_dir("very/long/path/new-dir-with-long-name/.").unwrap(); + names = subdir.iter().map(|r| r.unwrap().file_name()).collect::>(); + assert_eq!(names, [".", ".."]); + } + // Check if '..' is alias for parent directory + { + let subdir = root_dir.open_dir("very/long/path/new-dir-with-long-name/..").unwrap(); + names = subdir.iter().map(|r| r.unwrap().file_name()).collect::>(); + assert_eq!(names, [".", "..", "test.txt", "new-dir-with-long-name"]); + } +} + +#[test] +fn test_create_dir_fat12() { + call_with_fs(&test_create_dir, FAT12_IMG, 5) +} + +#[test] +fn test_create_dir_fat16() { + call_with_fs(&test_create_dir, FAT16_IMG, 5) +} + +#[test] +fn test_create_dir_fat32() { + call_with_fs(&test_create_dir, FAT32_IMG, 5) +}