mirror of https://github.com/dswd/zvault
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
205 lines
6.5 KiB
205 lines
6.5 KiB
use std::collections::HashMap; |
|
use std::path::Path; |
|
use std::fs::{self, Metadata, File, Permissions}; |
|
use std::os::linux::fs::MetadataExt; |
|
use std::os::unix::fs::{PermissionsExt, symlink}; |
|
use std::io::{Read, Write}; |
|
|
|
use ::util::*; |
|
use super::{Repository, RepositoryError, Chunk}; |
|
use super::integrity::RepositoryIntegrityError; |
|
use ::bundle::BundleMode; |
|
|
|
|
|
#[derive(Debug, Eq, PartialEq)] |
|
pub enum FileType { |
|
File, |
|
Directory, |
|
Symlink |
|
} |
|
serde_impl!(FileType(u8) { |
|
File => 0, |
|
Directory => 1, |
|
Symlink => 2 |
|
}); |
|
|
|
|
|
#[derive(Debug)] |
|
pub enum FileContents { |
|
Inline(msgpack::Bytes), |
|
ChunkedDirect(Vec<Chunk>), |
|
ChunkedIndirect(Vec<Chunk>) |
|
} |
|
serde_impl!(FileContents(u8) { |
|
Inline(ByteBuf) => 0, |
|
ChunkedDirect(Vec<Chunk>) => 1, |
|
ChunkedIndirect(Vec<Chunk>) => 2 |
|
}); |
|
|
|
|
|
#[derive(Debug)] |
|
pub struct Inode { |
|
pub name: String, |
|
pub size: u64, |
|
pub file_type: FileType, |
|
pub mode: u32, |
|
pub user: u32, |
|
pub group: u32, |
|
pub access_time: i64, |
|
pub modify_time: i64, |
|
pub create_time: i64, |
|
pub symlink_target: Option<String>, |
|
pub contents: Option<FileContents>, |
|
pub children: Option<HashMap<String, Vec<Chunk>>> |
|
} |
|
impl Default for Inode { |
|
fn default() -> Self { |
|
Inode { |
|
name: "".to_string(), |
|
size: 0, |
|
file_type: FileType::File, |
|
mode: 0o644, |
|
user: 1000, |
|
group: 1000, |
|
access_time: 0, |
|
modify_time: 0, |
|
create_time: 0, |
|
symlink_target: None, |
|
contents: None, |
|
children: None |
|
} |
|
} |
|
} |
|
serde_impl!(Inode(u8) { |
|
name: String => 0, |
|
size: u64 => 1, |
|
file_type: FileType => 2, |
|
mode: u32 => 3, |
|
user: u32 => 4, |
|
group: u32 => 5, |
|
access_time: i64 => 6, |
|
modify_time: i64 => 7, |
|
create_time: i64 => 8, |
|
symlink_target: Option<String> => 9, |
|
contents: Option<FileContents> => 10, |
|
children: HashMap<String, Vec<Chunk>> => 11 |
|
}); |
|
|
|
impl Inode { |
|
#[inline] |
|
fn get_extended_attrs_from(&mut self, meta: &Metadata) -> Result<(), RepositoryError> { |
|
self.mode = meta.st_mode(); |
|
self.user = meta.st_uid(); |
|
self.group = meta.st_gid(); |
|
self.access_time = meta.st_atime(); |
|
self.modify_time = meta.st_mtime(); |
|
self.create_time = meta.st_ctime(); |
|
Ok(()) |
|
} |
|
|
|
pub fn get_from<P: AsRef<Path>>(path: P) -> Result<Self, RepositoryError> { |
|
let name = try!(path.as_ref().file_name() |
|
.ok_or_else(|| RepositoryError::InvalidFileType(path.as_ref().to_owned()))) |
|
.to_string_lossy().to_string(); |
|
let meta = try!(fs::symlink_metadata(path.as_ref())); |
|
let mut inode = Inode::default(); |
|
inode.name = name; |
|
inode.size = meta.len(); |
|
inode.file_type = if meta.is_file() { |
|
FileType::File |
|
} else if meta.is_dir() { |
|
FileType::Directory |
|
} else if meta.file_type().is_symlink() { |
|
FileType::Symlink |
|
} else { |
|
return Err(RepositoryError::InvalidFileType(path.as_ref().to_owned())); |
|
}; |
|
if meta.file_type().is_symlink() { |
|
inode.symlink_target = Some(try!(fs::read_link(path)).to_string_lossy().to_string()); |
|
} |
|
try!(inode.get_extended_attrs_from(&meta)); |
|
Ok(inode) |
|
} |
|
|
|
#[allow(dead_code)] |
|
pub fn create_at<P: AsRef<Path>>(&self, path: P) -> Result<Option<File>, RepositoryError> { |
|
let full_path = path.as_ref().join(&self.name); |
|
let mut file = None; |
|
match self.file_type { |
|
FileType::File => { |
|
file = Some(try!(File::create(&full_path))); |
|
}, |
|
FileType::Directory => { |
|
try!(fs::create_dir(&full_path)); |
|
}, |
|
FileType::Symlink => { |
|
if let Some(ref src) = self.symlink_target { |
|
try!(symlink(src, &full_path)); |
|
} else { |
|
return Err(RepositoryIntegrityError::SymlinkWithoutTarget.into()) |
|
} |
|
} |
|
} |
|
try!(fs::set_permissions(&full_path, Permissions::from_mode(self.mode))); |
|
//FIXME: set times and gid/uid |
|
// https://crates.io/crates/filetime |
|
Ok(file) |
|
} |
|
} |
|
|
|
|
|
impl Repository { |
|
pub fn create_inode<P: AsRef<Path>>(&mut self, path: P) -> Result<Inode, RepositoryError> { |
|
let mut inode = try!(Inode::get_from(path.as_ref())); |
|
if inode.file_type == FileType::File && inode.size > 0 { |
|
let mut file = try!(File::open(path)); |
|
if inode.size < 100 { |
|
let mut data = Vec::with_capacity(inode.size as usize); |
|
try!(file.read_to_end(&mut data)); |
|
inode.contents = Some(FileContents::Inline(data.into())); |
|
} else { |
|
let mut chunks = try!(self.put_stream(BundleMode::Content, &mut file)); |
|
if chunks.len() < 10 { |
|
inode.contents = Some(FileContents::ChunkedDirect(chunks)); |
|
} else { |
|
let chunks_data = try!(msgpack::encode(&chunks)); |
|
chunks = try!(self.put_data(BundleMode::Content, &chunks_data)); |
|
inode.contents = Some(FileContents::ChunkedIndirect(chunks)); |
|
} |
|
} |
|
} |
|
Ok(inode) |
|
} |
|
|
|
#[inline] |
|
pub fn put_inode(&mut self, inode: &Inode) -> Result<Vec<Chunk>, RepositoryError> { |
|
self.put_data(BundleMode::Meta, &try!(msgpack::encode(inode))) |
|
} |
|
|
|
#[inline] |
|
pub fn get_inode(&mut self, chunks: &[Chunk]) -> Result<Inode, RepositoryError> { |
|
Ok(try!(msgpack::decode(&try!(self.get_data(chunks))))) |
|
} |
|
|
|
#[inline] |
|
pub fn save_inode_at<P: AsRef<Path>>(&mut self, inode: &Inode, path: P) -> Result<(), RepositoryError> { |
|
if let Some(mut file) = try!(inode.create_at(path.as_ref())) { |
|
if let Some(ref contents) = inode.contents { |
|
match *contents { |
|
FileContents::Inline(ref data) => { |
|
try!(file.write_all(&data)); |
|
}, |
|
FileContents::ChunkedDirect(ref chunks) => { |
|
try!(self.get_stream(chunks, &mut file)); |
|
}, |
|
FileContents::ChunkedIndirect(ref chunks) => { |
|
let chunk_data = try!(self.get_data(chunks)); |
|
let chunks: Vec<Chunk> = try!(msgpack::decode(&chunk_data)); |
|
try!(self.get_stream(&chunks, &mut file)); |
|
} |
|
} |
|
} |
|
} |
|
Ok(()) |
|
} |
|
}
|
|
|