1use super::*;
2use ::vfs04::*;
3use std::io::Write;
4
5impl<IO: Read + Seek + Send + 'static> ZipReadOnly<IO> {
6 fn normalize_file<'s>(&self, orig: &'s str) -> VfsResult<&'s str> {
7 if orig.contains('\\') || orig.ends_with('/') {
8 return Err(VfsError::InvalidPath { path: orig.into() }); }
10 let path = if orig.starts_with('/') { &orig[1..] } else { orig };
11 if path.split('/').any(|c| c == "" || c == "." || c == "..") {
12 return Err(VfsError::InvalidPath { path: orig.into() });
13 }
14 Ok(path)
15 }
16
17 fn normalize_path_dir<'s>(&self, orig: &'s str) -> VfsResult<(&'s str, bool)> {
18 if orig == "" || orig == "/" {
19 Ok(("", true)) } else if orig.ends_with('/') {
21 Ok((self.normalize_file(&orig[..orig.len()-1])?, true))
22 } else {
23 Ok((self.normalize_file(orig)?, false))
24 }
25 }
26}
27
28impl<IO: Read + Seek + Send + 'static> FileSystem for ZipReadOnly<IO> {
29 fn read_dir(&self, orig: &str) -> VfsResult<Box<dyn Iterator<Item = String>>> {
30 let path = self.normalize_path_dir(orig)?.0;
31 if let Some(dir) = self.dirs.get(path) {
32 Ok(Box::new(dir.iter().cloned().collect::<Vec<_>>().into_iter())) } else if let Some(_file) = self.files.get(path) {
34 Err(VfsError::Other { message: format!("\"{}\" is a file, not a directory", orig) })
35 } else {
36 Err(VfsError::FileNotFound { path: orig.into() })
37 }
38 }
39
40 fn open_file(&self, orig: &str) -> VfsResult<Box<dyn SeekAndRead>> {
41 let path = self.normalize_file(orig)?;
42 if let Some(i) = self.files.get(path) {
43 let mut buf = Vec::new();
46 self.archive.lock().unwrap().by_index(*i).unwrap().read_to_end(&mut buf)?;
47 Ok(Box::new(std::io::Cursor::new(buf)))
48 } else if let Some(_) = self.dirs.get(path) {
49 Err(VfsError::Other { message: format!("\"{}\" is a directory, not a file", orig) })
50 } else {
51 Err(VfsError::FileNotFound { path: orig.into() })
52 }
53 }
54
55 fn metadata(&self, orig: &str) -> VfsResult<VfsMetadata> {
56 let (path, dir) = self.normalize_path_dir(orig)?;
57 if let Some(i) = self.files.get(path).filter(|_| !dir) {
58 Ok(VfsMetadata { file_type: VfsFileType::File, len: self.archive.lock().unwrap().by_index(*i).map(|f| f.size()).unwrap_or(0) })
59 } else if let Some(_) = self.dirs.get(path) {
60 Ok(VfsMetadata { file_type: VfsFileType::Directory, len: 0 })
61 } else {
62 Err(VfsError::FileNotFound { path: orig.into() })
63 }
64 }
65
66 fn exists(&self, path: &str) -> bool {
67 let (path, dir) = match self.normalize_path_dir(path) {
68 Ok(pd) => pd,
69 Err(_) => return false, };
71 (!dir && self.files.contains_key(path)) || self.dirs.contains_key(path.trim_end_matches('/'))
72 }
73
74 fn create_dir (&self, _path: &str) -> VfsResult<()> { Err(VfsError::NotSupported) }
76 fn create_file (&self, _path: &str) -> VfsResult<Box<dyn Write>> { Err(VfsError::NotSupported) }
77 fn append_file (&self, _path: &str) -> VfsResult<Box<dyn Write>> { Err(VfsError::NotSupported) }
78 fn remove_file (&self, _path: &str) -> VfsResult<()> { Err(VfsError::NotSupported) }
79 fn remove_dir (&self, _path: &str) -> VfsResult<()> { Err(VfsError::NotSupported) }
80 fn copy_file (&self, _src: &str, _dst: &str) -> VfsResult<()> { Err(VfsError::NotSupported) }
81 fn move_file (&self, _src: &str, _dst: &str) -> VfsResult<()> { Err(VfsError::NotSupported) }
82 fn move_dir (&self, _src: &str, _dst: &str) -> VfsResult<()> { Err(VfsError::NotSupported) }
83}
84
85#[cfg(test)] mod tests {
86 use super::*;
87 use std::fs::File;
88
89 fn is_empty_or_comment(line: &str) -> bool {
90 let line = line.trim_start_matches(char::is_whitespace);
91 line == "" || line.starts_with("#") || line.starts_with("//") || line.starts_with(";")
92 }
93
94 #[test] fn early_vfs_zip() {
95 let zip = ZipReadOnly::new_strict(File::open("test/data/early-vfs-zip.zip").unwrap()).unwrap();
96 let files = std::fs::read_to_string("test/data/early-vfs-zip.files.txt").unwrap();
97 let dirs = std::fs::read_to_string("test/data/early-vfs-zip.dirs.txt").unwrap();
98 let files = files.split(|ch| "\r\n".contains(ch)).filter(|l| !is_empty_or_comment(l));
99 let dirs = dirs .split(|ch| "\r\n".contains(ch)).filter(|l| !is_empty_or_comment(l));
100
101 for file in files {
102 for good in &[
103 format!("{}", file),
104 format!("/{}", file),
105 ] {
106 eprintln!("{}", good);
107 zip.read_dir(&good).err().unwrap();
108 zip.open_file(&good).unwrap();
109 zip.metadata(&good).unwrap();
110 assert_eq!(zip.exists(&good), true);
111 }
112
113 for bad in &[
114 format!("//{}", file),
115 format!("\\{}", file),
116 format!("nonexistant/{}", file),
117 format!("{}.nonexistant", file),
118 format!("{}/", file),
119 format!("/{}/", file),
120 format!("./{}/", file),
121 ] {
122 eprintln!("{}", bad);
123 zip.read_dir(&bad).err().unwrap();
124 zip.open_file(&bad).err().unwrap();
125 zip.metadata(&bad).err().unwrap();
126 assert_eq!(zip.exists(&bad), false);
127 }
128 }
129
130 for dir in dirs {
131 for good in &[
132 format!("{}", dir),
133 format!("/{}", dir),
134 format!("{}/", dir),
135 format!("/{}/", dir),
136 ] {
137 eprintln!("{}", good);
138 let _ = zip.read_dir(&good).unwrap().collect::<Vec<String>>();
139 zip.open_file(&good).err().unwrap();
140 zip.metadata(&good).unwrap();
141 assert_eq!(zip.exists(&good), true);
142 }
143
144 for bad in &[
145 format!("nonexistant/{}", dir),
146 format!("{}.nonexistant", dir),
147 format!("/{}/nonexistant", dir),
148 format!("./{}/", dir),
149 format!("//{}", dir),
150 format!("{}//", dir),
151 format!("{}\\", dir),
152 format!("\\{}", dir),
153 ] {
154 eprintln!("{}", bad);
155 zip.read_dir(&bad).err().unwrap();
156 zip.open_file(&bad).err().unwrap();
157 zip.metadata(&bad).err().unwrap();
158 assert_eq!(zip.exists(&bad), false);
159 }
160 }
161 }
162}