Skip to content
Snippets Groups Projects
Verified Commit d3b5dfc7 authored by Jeremy Soller's avatar Jeremy Soller
Browse files

Add symlink support

parent 54626d07
No related branches found
No related tags found
No related merge requests found
...@@ -7,9 +7,15 @@ use std::fs; ...@@ -7,9 +7,15 @@ use std::fs;
use std::io::{self, Read, Seek, SeekFrom, Write}; use std::io::{self, Read, Seek, SeekFrom, Write};
use std::mem; use std::mem;
use std::os::unix::ffi::OsStrExt; use std::os::unix::ffi::OsStrExt;
use std::os::unix::fs::{OpenOptionsExt, PermissionsExt}; use std::os::unix::fs::{symlink, OpenOptionsExt, PermissionsExt};
use std::path::{Component, Path}; use std::path::{Component, Path};
// This ensures that all platforms use the same mode defines
const MODE_PERM: u32 = 0o7777;
const MODE_KIND: u32 = 0o170000;
const MODE_FILE: u32 = 0o100000;
const MODE_SYMLINK: u32 = 0o120000;
fn folder_entries<P, Q>(base: P, path: Q, entries: &mut Vec<Entry>) -> io::Result<()> fn folder_entries<P, Q>(base: P, path: Q, entries: &mut Vec<Entry>) -> io::Result<()>
where P: AsRef<Path>, Q: AsRef<Path> where P: AsRef<Path>, Q: AsRef<Path>
{ {
...@@ -46,11 +52,23 @@ fn folder_entries<P, Q>(base: P, path: Q, entries: &mut Vec<Entry>) -> io::Resul ...@@ -46,11 +52,23 @@ fn folder_entries<P, Q>(base: P, path: Q, entries: &mut Vec<Entry>) -> io::Resul
} }
path_bytes[..relative_bytes.len()].copy_from_slice(relative_bytes); path_bytes[..relative_bytes.len()].copy_from_slice(relative_bytes);
let file_type = metadata.file_type();
let mut mode = metadata.permissions().mode() & MODE_PERM;
if file_type.is_file() {
mode |= MODE_FILE;
} else if file_type.is_symlink() {
mode |= MODE_SYMLINK;
} else {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("Unsupported entry at {:?}: {:?}", relative, metadata),
));
}
entries.push(Entry { entries.push(Entry {
blake3: [0; 32], blake3: [0; 32],
offset: 0, offset: 0,
size: metadata.len(), size: metadata.len(),
mode: metadata.permissions().mode(), mode,
path: path_bytes, path: path_bytes,
}); });
} }
...@@ -100,7 +118,7 @@ fn create(secret_path: &str, archive_path: &str, folder: &str) { ...@@ -100,7 +118,7 @@ fn create(secret_path: &str, archive_path: &str, folder: &str) {
for entry in &mut entries { for entry in &mut entries {
entry.offset = data_size; entry.offset = data_size;
data_size = data_size.checked_add(entry.size) data_size = data_size.checked_add(entry.size)
.expect("overflow when calculating entry offset"); .expect("overflow when calculating data size");
println!("{}: {:?}", { entry.offset }, ::std::str::from_utf8(entry.path())); println!("{}: {:?}", { entry.offset }, ::std::str::from_utf8(entry.path()));
} }
...@@ -118,22 +136,44 @@ fn create(secret_path: &str, archive_path: &str, folder: &str) { ...@@ -118,22 +136,44 @@ fn create(secret_path: &str, archive_path: &str, folder: &str) {
for entry in &mut entries { for entry in &mut entries {
let relative = Path::new(OsStr::from_bytes(entry.path())); let relative = Path::new(OsStr::from_bytes(entry.path()));
let path = Path::new(folder).join(relative); let path = Path::new(folder).join(relative);
let mut entry_file = fs::OpenOptions::new()
.read(true)
.open(path)
.expect("failed to open entry file");
let mut hasher = blake3::Hasher::new(); let mut hasher = blake3::Hasher::new();
loop { let mode_kind = entry.mode & MODE_KIND;
let count = entry_file.read(&mut buf) match mode_kind {
.expect("failed to read entry file"); MODE_FILE => {
if count == 0 { let mut entry_file = fs::OpenOptions::new()
break; .read(true)
.open(path)
.expect("failed to open entry file");
let mut total = 0;
loop {
let count = entry_file.read(&mut buf)
.expect("failed to read entry file");
if count == 0 {
break;
}
total += count as u64;
//TODO: Progress
archive_file.write_all(&buf[..count])
.expect("failed to write entry data");
hasher.update_with_join::<blake3::join::RayonJoin>(&buf[..count]);
}
assert_eq!(total, { entry.size });
},
MODE_SYMLINK => {
let destination = fs::read_link(path)
.expect("failed to read entry link");
let data = destination.as_os_str().as_bytes();
assert_eq!(data.len() as u64, { entry.size });
archive_file.write_all(&data)
.expect("failed to write entry data");
hasher.update_with_join::<blake3::join::RayonJoin>(&data);
},
_ => {
panic!("Unsupported mode {:#o}", { entry.mode });
} }
//TODO: Progress
archive_file.write_all(&buf[..count])
.expect("failed to write entry data");
hasher.update_with_join::<blake3::join::RayonJoin>(&buf[..count]);
} }
entry.blake3.copy_from_slice(hasher.finalize().as_bytes()); entry.blake3.copy_from_slice(hasher.finalize().as_bytes());
...@@ -143,6 +183,8 @@ fn create(secret_path: &str, archive_path: &str, folder: &str) { ...@@ -143,6 +183,8 @@ fn create(secret_path: &str, archive_path: &str, folder: &str) {
} }
header.blake3.copy_from_slice(header_hasher.finalize().as_bytes()); header.blake3.copy_from_slice(header_hasher.finalize().as_bytes());
//TODO: ensure file size matches
// Calculate signature // Calculate signature
let unsigned = header.clone(); let unsigned = header.clone();
sodalite::sign_attached( sodalite::sign_attached(
...@@ -246,15 +288,29 @@ fn extract(public_path: &str, archive_path: &str, folder: &str) { ...@@ -246,15 +288,29 @@ fn extract(public_path: &str, archive_path: &str, folder: &str) {
.expect("failed to create entry parent directory"); .expect("failed to create entry parent directory");
} }
fs::OpenOptions::new() let mode_kind = entry.mode & MODE_KIND;
.write(true) let mode_perm = entry.mode & MODE_PERM;
.create(true) match mode_kind {
.truncate(true) MODE_FILE => {
.mode(entry.mode) fs::OpenOptions::new()
.open(entry_path) .write(true)
.expect("failed to create entry file") .create(true)
.write_all(&data) .truncate(true)
.expect("failed to write entry file"); .mode(mode_perm)
.open(entry_path)
.expect("failed to create entry file")
.write_all(&data)
.expect("failed to write entry file");
},
MODE_SYMLINK => {
let os_str: &OsStr = OsStrExt::from_bytes(data.as_slice());
symlink(os_str, entry_path)
.expect("failed to create entry link");
},
_ => {
panic!("Unsupported mode {:#o}", { entry.mode });
}
}
} }
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment