forked from M-Labs/rust-fatfs
Do not panic if unexpected file type is found during path traversal
For example if in the middle of the path a file is found. Return error in such case instead of panicking.
This commit is contained in:
parent
632a371b0d
commit
18b9d8c285
39
src/dir.rs
39
src/dir.rs
@ -100,13 +100,18 @@ impl <'a, 'b> Dir<'a, 'b> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_entry(&mut self, name: &str, mut short_name_gen: Option<&mut ShortNameGenerator>) -> io::Result<DirEntry<'a, 'b>> {
|
fn find_entry(&mut self, name: &str, is_dir: Option<bool>, mut short_name_gen: Option<&mut ShortNameGenerator>) -> io::Result<DirEntry<'a, 'b>> {
|
||||||
for r in self.iter() {
|
for r in self.iter() {
|
||||||
let e = r?;
|
let e = r?;
|
||||||
// compare name ignoring case
|
// compare name ignoring case
|
||||||
if e.file_name().eq_ignore_ascii_case(name) || e.short_file_name().eq_ignore_ascii_case(name) {
|
if e.file_name().eq_ignore_ascii_case(name) || e.short_file_name().eq_ignore_ascii_case(name) {
|
||||||
|
// check if file or directory is expected
|
||||||
|
if is_dir.is_some() && Some(e.is_dir()) != is_dir {
|
||||||
|
return Err(io::Error::new(ErrorKind::NotFound, "unexpected file type in a path"))
|
||||||
|
}
|
||||||
return Ok(e);
|
return Ok(e);
|
||||||
}
|
}
|
||||||
|
// update short name generator state
|
||||||
if let Some(ref mut gen) = short_name_gen {
|
if let Some(ref mut gen) = short_name_gen {
|
||||||
gen.add_existing(e.raw_short_name());
|
gen.add_existing(e.raw_short_name());
|
||||||
}
|
}
|
||||||
@ -117,7 +122,7 @@ impl <'a, 'b> Dir<'a, 'b> {
|
|||||||
/// Opens existing directory
|
/// Opens existing directory
|
||||||
pub fn open_dir(&mut self, path: &str) -> io::Result<Self> {
|
pub fn open_dir(&mut self, path: &str) -> io::Result<Self> {
|
||||||
let (name, rest_opt) = split_path(path);
|
let (name, rest_opt) = split_path(path);
|
||||||
let e = self.find_entry(name, None)?;
|
let e = self.find_entry(name, Some(true), None)?;
|
||||||
match rest_opt {
|
match rest_opt {
|
||||||
Some(rest) => e.to_dir().open_dir(rest),
|
Some(rest) => e.to_dir().open_dir(rest),
|
||||||
None => Ok(e.to_dir()),
|
None => Ok(e.to_dir()),
|
||||||
@ -126,12 +131,15 @@ impl <'a, 'b> Dir<'a, 'b> {
|
|||||||
|
|
||||||
/// Opens existing file.
|
/// Opens existing file.
|
||||||
pub fn open_file(&mut self, path: &str) -> io::Result<File<'a, 'b>> {
|
pub fn open_file(&mut self, path: &str) -> io::Result<File<'a, 'b>> {
|
||||||
|
// traverse path
|
||||||
let (name, rest_opt) = split_path(path);
|
let (name, rest_opt) = split_path(path);
|
||||||
let e = self.find_entry(name, None)?;
|
if let Some(rest) = rest_opt {
|
||||||
match rest_opt {
|
let e = self.find_entry(name, Some(true), None)?;
|
||||||
Some(rest) => e.to_dir().open_file(rest),
|
return e.to_dir().open_file(rest);
|
||||||
None => Ok(e.to_file()),
|
|
||||||
}
|
}
|
||||||
|
// convert entry to a file
|
||||||
|
let e = self.find_entry(name, Some(false), None)?;
|
||||||
|
Ok(e.to_file())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates new file or opens existing without truncating.
|
/// Creates new file or opens existing without truncating.
|
||||||
@ -139,11 +147,11 @@ impl <'a, 'b> Dir<'a, 'b> {
|
|||||||
// traverse path
|
// traverse path
|
||||||
let (name, rest_opt) = split_path(path);
|
let (name, rest_opt) = split_path(path);
|
||||||
if let Some(rest) = rest_opt {
|
if let Some(rest) = rest_opt {
|
||||||
return self.find_entry(name, None)?.to_dir().create_file(rest);
|
return self.find_entry(name, Some(true), None)?.to_dir().create_file(rest);
|
||||||
}
|
}
|
||||||
// this is final filename in the path
|
// this is final filename in the path
|
||||||
let mut short_name_gen = ShortNameGenerator::new(name);
|
let mut short_name_gen = ShortNameGenerator::new(name);
|
||||||
let r = self.find_entry(name, Some(&mut short_name_gen));
|
let r = self.find_entry(name, Some(false), Some(&mut short_name_gen));
|
||||||
match r {
|
match r {
|
||||||
// file does not exist - create it
|
// file does not exist - create it
|
||||||
Err(ref err) if err.kind() == ErrorKind::NotFound => {
|
Err(ref err) if err.kind() == ErrorKind::NotFound => {
|
||||||
@ -163,11 +171,11 @@ impl <'a, 'b> Dir<'a, 'b> {
|
|||||||
// traverse path
|
// traverse path
|
||||||
let (name, rest_opt) = split_path(path);
|
let (name, rest_opt) = split_path(path);
|
||||||
if let Some(rest) = rest_opt {
|
if let Some(rest) = rest_opt {
|
||||||
return self.find_entry(name, None)?.to_dir().create_dir(rest);
|
return self.find_entry(name, Some(true), None)?.to_dir().create_dir(rest);
|
||||||
}
|
}
|
||||||
// this is final filename in the path
|
// this is final filename in the path
|
||||||
let mut short_name_gen = ShortNameGenerator::new(name);
|
let mut short_name_gen = ShortNameGenerator::new(name);
|
||||||
let r = self.find_entry(name, Some(&mut short_name_gen));
|
let r = self.find_entry(name, Some(false), Some(&mut short_name_gen));
|
||||||
match r {
|
match r {
|
||||||
// directory does not exist - create it
|
// directory does not exist - create it
|
||||||
Err(ref err) if err.kind() == ErrorKind::NotFound => {
|
Err(ref err) if err.kind() == ErrorKind::NotFound => {
|
||||||
@ -214,12 +222,13 @@ impl <'a, 'b> Dir<'a, 'b> {
|
|||||||
pub fn remove(&mut self, path: &str) -> io::Result<()> {
|
pub fn remove(&mut self, path: &str) -> io::Result<()> {
|
||||||
// traverse path
|
// traverse path
|
||||||
let (name, rest_opt) = split_path(path);
|
let (name, rest_opt) = split_path(path);
|
||||||
let e = self.find_entry(name, None)?;
|
|
||||||
if let Some(rest) = rest_opt {
|
if let Some(rest) = rest_opt {
|
||||||
|
let e = self.find_entry(name, Some(true), None)?;
|
||||||
return e.to_dir().remove(rest);
|
return e.to_dir().remove(rest);
|
||||||
}
|
}
|
||||||
trace!("removing {}", path);
|
trace!("removing {}", path);
|
||||||
// in case of directory check if it is empty
|
// in case of directory check if it is empty
|
||||||
|
let e = self.find_entry(name, None, None)?;
|
||||||
if e.is_dir() && !e.to_dir().is_empty()? {
|
if e.is_dir() && !e.to_dir().is_empty()? {
|
||||||
return Err(io::Error::new(ErrorKind::NotFound, "removing non-empty directory is denied"));
|
return Err(io::Error::new(ErrorKind::NotFound, "removing non-empty directory is denied"));
|
||||||
}
|
}
|
||||||
@ -250,13 +259,13 @@ impl <'a, 'b> Dir<'a, 'b> {
|
|||||||
// traverse source path
|
// traverse source path
|
||||||
let (name, rest_opt) = split_path(src_path);
|
let (name, rest_opt) = split_path(src_path);
|
||||||
if let Some(rest) = rest_opt {
|
if let Some(rest) = rest_opt {
|
||||||
let e = self.find_entry(name, None)?;
|
let e = self.find_entry(name, Some(true), None)?;
|
||||||
return e.to_dir().rename(rest, dst_dir, dst_path);
|
return e.to_dir().rename(rest, dst_dir, dst_path);
|
||||||
}
|
}
|
||||||
// traverse destination path
|
// traverse destination path
|
||||||
let (name, rest_opt) = split_path(dst_path);
|
let (name, rest_opt) = split_path(dst_path);
|
||||||
if let Some(rest) = rest_opt {
|
if let Some(rest) = rest_opt {
|
||||||
let e = dst_dir.find_entry(name, None)?;
|
let e = dst_dir.find_entry(name, Some(true), None)?;
|
||||||
return self.rename(src_path, &mut e.to_dir(), rest);
|
return self.rename(src_path, &mut e.to_dir(), rest);
|
||||||
}
|
}
|
||||||
// move/rename file
|
// move/rename file
|
||||||
@ -266,7 +275,7 @@ impl <'a, 'b> Dir<'a, 'b> {
|
|||||||
fn rename_internal(&mut self, src_name: &str, dst_dir: &mut Dir, dst_name: &str) -> io::Result<()> {
|
fn rename_internal(&mut self, src_name: &str, dst_dir: &mut Dir, dst_name: &str) -> io::Result<()> {
|
||||||
trace!("moving {} to {}", src_name, dst_name);
|
trace!("moving {} to {}", src_name, dst_name);
|
||||||
// find existing file
|
// find existing file
|
||||||
let e = self.find_entry(src_name, None)?;
|
let e = self.find_entry(src_name, None, None)?;
|
||||||
// free long and short name entries
|
// free long and short name entries
|
||||||
let mut stream = self.stream.clone();
|
let mut stream = self.stream.clone();
|
||||||
stream.seek(SeekFrom::Start(e.offset_range.0 as u64))?;
|
stream.seek(SeekFrom::Start(e.offset_range.0 as u64))?;
|
||||||
@ -280,7 +289,7 @@ impl <'a, 'b> Dir<'a, 'b> {
|
|||||||
}
|
}
|
||||||
// check if destionation filename is unused
|
// check if destionation filename is unused
|
||||||
let mut short_name_gen = ShortNameGenerator::new(dst_name);
|
let mut short_name_gen = ShortNameGenerator::new(dst_name);
|
||||||
let r = dst_dir.find_entry(dst_name, Some(&mut short_name_gen));
|
let r = dst_dir.find_entry(dst_name, None, Some(&mut short_name_gen));
|
||||||
if r.is_ok() {
|
if r.is_ok() {
|
||||||
return Err(io::Error::new(ErrorKind::AlreadyExists, "destination file already exists"))
|
return Err(io::Error::new(ErrorKind::AlreadyExists, "destination file already exists"))
|
||||||
}
|
}
|
||||||
|
@ -155,6 +155,14 @@ fn test_get_file_by_path(fs: FileSystem) {
|
|||||||
assert_eq!(str::from_utf8(&buf).unwrap(), TEST_TEXT);
|
assert_eq!(str::from_utf8(&buf).unwrap(), TEST_TEXT);
|
||||||
|
|
||||||
root_dir.open_file("VERY-L~1/VERY-L~1.TXT").unwrap();
|
root_dir.open_file("VERY-L~1/VERY-L~1.TXT").unwrap();
|
||||||
|
|
||||||
|
// try opening dir as file
|
||||||
|
assert!(root_dir.open_file("very/long/path").is_err());
|
||||||
|
// try opening file as dir
|
||||||
|
assert!(root_dir.open_dir("very/long/path/test.txt").is_err());
|
||||||
|
// try using invalid path containing file as non-last component
|
||||||
|
assert!(root_dir.open_file("very/long/path/test.txt/abc").is_err());
|
||||||
|
assert!(root_dir.open_dir("very/long/path/test.txt/abc").is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
Loading…
Reference in New Issue
Block a user