Skip to content

Commit f63f334

Browse files
authored
Merge pull request #45 from sireliah/directories-even-more-edge-cases
Directories even more edge cases
2 parents 82d07b1 + 931f5e5 commit f63f334

File tree

6 files changed

+84
-35
lines changed

6 files changed

+84
-35
lines changed

src/p2p/transfer/directory.rs

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ impl ZipStream {
4646
let entry = entry?;
4747
let file_path = entry.path();
4848
debug!("{:?}", file_path);
49+
50+
if !file_path.exists() {
51+
continue;
52+
}
53+
4954
let rel_path = match base_path {
5055
Some(base) => file_path
5156
.strip_prefix(base)
@@ -60,10 +65,16 @@ impl ZipStream {
6065

6166
// Only files and empty directories are supported for now. Symlinks are ignored.
6267
if file_path.is_file() {
63-
debug!("Writing file: {}", path_string);
64-
Self::write_file(&mut zip, path_string, &file_path).await?;
68+
if file_path.metadata()?.len() > 0 {
69+
debug!("Writing file: {}", path_string);
70+
Self::write_file(&mut zip, path_string, &file_path).await?;
71+
} else {
72+
debug!("Writing empty file: {}", path_string);
73+
Self::write_empty_file(&mut zip, path_string).await?;
74+
}
6575
} else {
6676
if file_path.read_dir()?.next().is_none() {
77+
debug!("Writing empty directory: {}", path_string);
6778
Self::write_empty_dir(&mut zip, path_string).await?;
6879
}
6980
}
@@ -95,6 +106,19 @@ impl ZipStream {
95106
Ok(())
96107
}
97108

109+
async fn write_empty_file(
110+
zip: &mut ZipFileWriter<&mut DuplexStream>,
111+
rel_path: String,
112+
) -> Result<(), Error> {
113+
// Trying to unzip the empty file at the reader end makes tokio error with "early eof".
114+
// This might be an async-zip bug, but as a workaround it's enough to create empty entry here.
115+
let opts = EntryOptions::new(rel_path, DEFAULT_COMPRESSION);
116+
zip.write_entry_whole(opts, &[])
117+
.await
118+
.map_err(|err| zip_error(err))?;
119+
Ok(())
120+
}
121+
98122
async fn write_file(
99123
zip: &mut ZipFileWriter<&mut DuplexStream>,
100124
rel_path: String,
@@ -141,13 +165,19 @@ async fn get_compression(path: &Path) -> Result<Compression, Error> {
141165
let mut file = File::open(&path).await?;
142166
file.read_exact(&mut buf).await?;
143167
match buf {
168+
// Zip
144169
[80, 75, 3, 4] => Ok(Compression::Bz),
145-
_ => Ok(DEFAULT_COMPRESSION),
170+
// Gzip
171+
[31, 139, 8, 0] => Ok(Compression::Bz),
172+
v => {
173+
debug!("Compression: {:?}, {:?}", v, path);
174+
Ok(DEFAULT_COMPRESSION)
175+
},
146176
}
147177
}
148178

149179
fn zip_error(err: ZipError) -> Error {
150-
Error::new(ErrorKind::Other, err.to_string())
180+
Error::new(ErrorKind::Other, format!("Zip error: {}", err.to_string()))
151181
}
152182

153183
fn is_zip_dir(path: &Path) -> bool {

src/p2p/transfer/file.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,10 @@ fn check_directory_size(path: &str) -> Result<u64, io::Error> {
168168
let mut total_size = 0;
169169
for entry in WalkDir::new(path) {
170170
let entry = entry?;
171-
let meta = metadata(entry.path())?;
172-
total_size += meta.len();
171+
match metadata(entry.path()) {
172+
Ok(meta) => total_size += meta.len(),
173+
Err(e) => warn!("Can't estimate size of {:?}, {}", entry.path(), e),
174+
};
173175
}
174176
Ok(total_size)
175177
}

src/user_data/mod.rs

Lines changed: 39 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -18,47 +18,40 @@ fn get_timestamp() -> u64 {
1818
.as_secs()
1919
}
2020

21-
fn extend_dir(path: &Path, time: u64) -> PathBuf {
22-
let dir = match path.file_name() {
23-
Some(dir_name) => dir_name.to_string_lossy().to_string(),
24-
None => "directory".to_string(),
25-
};
26-
match path.parent() {
27-
Some(parent_path) => parent_path.join(format!("{}_{}", dir, time)),
28-
// Probably not best idea to use this application to move your whole root dir (。•̀ᴗ-)
29-
None => Path::new(&format!("/directory_{}", time)).to_path_buf(),
30-
}
21+
fn extend_dir(path: &Path, dir_name: &str, time: u64) -> PathBuf {
22+
path.join(format!("{}_{}", dir_name, time))
3123
}
3224

33-
fn extend_file(path: &Path, time: u64) -> PathBuf {
34-
let extension: String = match path.extension() {
25+
fn extend_file(path: &Path, name: &str, time: u64) -> PathBuf {
26+
let file_name_path = Path::new(name);
27+
let extension: String = match file_name_path.extension() {
3528
Some(v) => v.to_string_lossy().to_string(),
3629
None => "".to_string(),
3730
};
38-
let basename = match path.file_stem() {
31+
let basename = match file_name_path.file_stem() {
3932
Some(v) => v.to_string_lossy().to_string(),
4033
None => "file".to_string(),
4134
};
42-
let name = format!("{}_{}", basename, time);
43-
let mut path = path.join(&name);
35+
let new_name = format!("{}_{}", basename, time);
36+
let mut path = path.join(&new_name);
4437
path.set_extension(extension);
4538
path
4639
}
4740

48-
fn generate_full_path<F>(name: &str, path: &Path, timestamp: F) -> Result<String, Error>
41+
fn generate_full_path<F>(path: &Path, name: &str, timestamp: F) -> Result<String, Error>
4942
where
5043
F: Fn() -> u64,
5144
{
5245
// If file or dir already exists in the target directory, create a path extended with a timestamp
53-
let path = Path::new(&path);
5446
let joined = path.join(&name);
5547
let time = timestamp();
5648

5749
let joined = if joined.exists() {
50+
info!("File already exists: {:?}", joined);
5851
if joined.is_file() {
59-
extend_file(&joined, time)
52+
extend_file(path, name, time)
6053
} else {
61-
extend_dir(&joined, time)
54+
extend_dir(path, name, time)
6255
}
6356
} else {
6457
joined
@@ -76,12 +69,12 @@ pub fn get_target_path(name: &str, target_path: Option<&String>) -> Result<Strin
7669
match target_path {
7770
Some(path) => {
7871
let path = Path::new(path);
79-
generate_full_path(name, path, get_timestamp)
72+
generate_full_path(path, name, get_timestamp)
8073
}
8174
None => {
8275
let config = UserConfig::new()?;
8376
let dir = config.get_downloads_dir();
84-
generate_full_path(name, dir.as_path(), get_timestamp)
77+
generate_full_path(dir.as_path(), name, get_timestamp)
8578
}
8679
}
8780
}
@@ -208,34 +201,51 @@ impl UserConfig {
208201
#[cfg(test)]
209202
mod tests {
210203
use crate::user_data::{extend_dir, generate_full_path};
204+
use std::fs::{create_dir_all, File};
211205
use std::path::Path;
206+
use tempfile::tempdir;
212207

213208
#[test]
214209
fn test_extend_dir_should_extend_name_with_timestamp() {
215-
let result = extend_dir(Path::new("/home/user/directory/"), 1111);
210+
let result = extend_dir(Path::new("/home/user/"), "directory", 1111);
216211

217212
assert_eq!(result, Path::new("/home/user/directory_1111"))
218213
}
219214

220215
#[test]
221-
fn test_extend_dir_root_edge_case() {
222-
let result = extend_dir(Path::new("/"), 1111);
216+
fn test_generate_full_file_path() {
217+
let result = generate_full_path(Path::new("/home/user/"), "a-file.txt", || 1111).unwrap();
223218

224-
assert_eq!(result, Path::new("/directory_1111"))
219+
assert_eq!(result, "/home/user/a-file.txt");
225220
}
226221

227222
#[test]
228-
fn test_generate_full_file_path() {
229-
let result = generate_full_path("a-file.txt", Path::new("/home/user/"), || 1111).unwrap();
223+
fn test_generate_full_file_path_when_file_exists() {
224+
let dir = tempdir().unwrap();
225+
let path = dir.path();
226+
let received_file_name = "a-file.txt";
227+
File::create(path.join(received_file_name)).unwrap();
230228

231-
assert_eq!(result, "/home/user/a-file.txt");
229+
let result = generate_full_path(path, received_file_name, || 1111).unwrap();
230+
231+
assert_eq!(result, path.join("a-file_1111.txt").to_string_lossy());
232232
}
233233

234234
#[test]
235235
fn test_generate_full_dir_path() {
236236
let result =
237-
generate_full_path("some_directory", Path::new("/home/user/"), || 1111).unwrap();
237+
generate_full_path(Path::new("/home/user/"), "some_directory", || 1111).unwrap();
238238

239239
assert_eq!(result, "/home/user/some_directory");
240240
}
241+
#[test]
242+
fn test_generate_full_dir_path_when_dir_exists() {
243+
let dir = tempdir().unwrap();
244+
let path = dir.path();
245+
let received_dir_name = "some_directory";
246+
create_dir_all(path.join(received_dir_name)).unwrap();
247+
let result = generate_full_path(path, received_dir_name, || 1111).unwrap();
248+
249+
assert_eq!(result, path.join("some_directory_1111").to_string_lossy());
250+
}
241251
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../deleted_symlink_file

tests/data/test_dir/empty_file

Whitespace-only changes.

tests/test_directory.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,12 @@ fn test_directory_transfer() {
144144
.len(),
145145
659903
146146
);
147+
assert_eq!(
148+
fs::metadata(Path::new(&path).join("test_dir/empty_file"))
149+
.unwrap()
150+
.len(),
151+
0
152+
);
147153
assert!(fs::metadata(Path::new(&path).join("test_dir/empty_dir/"))
148154
.unwrap()
149155
.is_dir());

0 commit comments

Comments
 (0)