2017-07-21 09:21:59 +00:00
|
|
|
use prelude::*;
|
2017-03-26 09:34:16 +00:00
|
|
|
|
|
|
|
use std::path::Path;
|
|
|
|
use std::ffi::OsStr;
|
|
|
|
use std::collections::HashMap;
|
|
|
|
use std::cell::RefCell;
|
|
|
|
use std::rc::Rc;
|
|
|
|
use std::mem;
|
|
|
|
use std::cmp::min;
|
|
|
|
|
|
|
|
use fuse;
|
2017-04-12 13:27:46 +00:00
|
|
|
use users::{self, Users, Groups};
|
2017-03-26 09:34:16 +00:00
|
|
|
use time::Timespec;
|
|
|
|
use libc;
|
|
|
|
|
|
|
|
|
|
|
|
macro_rules! fuse_try(
|
|
|
|
($val:expr, $reply:expr) => {
|
|
|
|
match $val {
|
|
|
|
Ok(val) => val,
|
|
|
|
Err(err) => {
|
|
|
|
info!("Error: {:?}", err);
|
|
|
|
return $reply.error(libc::EIO);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
macro_rules! str(
|
|
|
|
($val:expr, $reply:expr) => {
|
|
|
|
match $val.to_str() {
|
|
|
|
Some(val) => val,
|
|
|
|
None => {
|
|
|
|
info!("Error: Name is not valid unicode");
|
|
|
|
return $reply.error(libc::ENAMETOOLONG);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
macro_rules! inode(
|
|
|
|
($slf:expr, $num:expr, $reply:expr) => {
|
|
|
|
match $slf.get_inode($num) {
|
|
|
|
Some(inode) => inode,
|
|
|
|
None => {
|
|
|
|
info!("Error: Inode not found: {}", $num);
|
|
|
|
return $reply.error(libc::EBADF)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
macro_rules! lookup(
|
|
|
|
($slf:expr, $parent:expr, $name:expr, $reply:expr) => {
|
|
|
|
match fuse_try!($slf.get_child(&$parent, $name), $reply) {
|
|
|
|
Some(inode) => inode,
|
|
|
|
None => {
|
|
|
|
info!("Error: Child node not found: {} -> {}", $parent.borrow().num, $name);
|
|
|
|
return $reply.error(libc::ENOENT)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn convert_file_type(kind: FileType) -> fuse::FileType {
|
|
|
|
match kind {
|
|
|
|
FileType::Directory => fuse::FileType::Directory,
|
|
|
|
FileType::File => fuse::FileType::RegularFile,
|
|
|
|
FileType::Symlink => fuse::FileType::Symlink,
|
2017-06-20 10:38:16 +00:00
|
|
|
FileType::BlockDevice => fuse::FileType::BlockDevice,
|
2017-06-20 10:58:14 +00:00
|
|
|
FileType::CharDevice => fuse::FileType::CharDevice,
|
2017-07-21 09:21:59 +00:00
|
|
|
FileType::NamedPipe => fuse::FileType::NamedPipe,
|
2017-03-26 09:34:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type FuseInodeRef = Rc<RefCell<FuseInode>>;
|
|
|
|
|
|
|
|
pub struct FuseInode {
|
|
|
|
num: u64,
|
|
|
|
inode: Inode,
|
|
|
|
parent: Option<FuseInodeRef>,
|
|
|
|
children: HashMap<String, FuseInodeRef>,
|
2017-04-12 13:27:46 +00:00
|
|
|
chunks: Option<ChunkList>,
|
|
|
|
name_cache: Rc<users::UsersCache>,
|
|
|
|
user_names: Rc<HashMap<u32, String>>,
|
|
|
|
group_names: Rc<HashMap<u32, String>>
|
2017-03-26 09:34:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl FuseInode {
|
|
|
|
pub fn to_attrs(&self) -> fuse::FileAttr {
|
2017-04-12 13:27:46 +00:00
|
|
|
let mut uid = self.inode.user;
|
|
|
|
if let Some(name) = self.user_names.get(&self.inode.user) {
|
|
|
|
if let Some(user) = self.name_cache.get_user_by_name(name) {
|
|
|
|
uid = user.uid();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let mut gid = self.inode.group;
|
|
|
|
if let Some(name) = self.group_names.get(&self.inode.group) {
|
|
|
|
if let Some(group) = self.name_cache.get_group_by_name(name) {
|
|
|
|
gid = group.gid();
|
|
|
|
}
|
|
|
|
}
|
2017-03-26 09:34:16 +00:00
|
|
|
fuse::FileAttr {
|
|
|
|
ino: self.num,
|
|
|
|
size: self.inode.size,
|
|
|
|
blocks: self.inode.size / 512,
|
2017-04-02 16:55:53 +00:00
|
|
|
atime: Timespec::new(self.inode.timestamp, 0),
|
|
|
|
mtime: Timespec::new(self.inode.timestamp, 0),
|
2017-03-26 09:34:16 +00:00
|
|
|
ctime: Timespec::new(0, 0),
|
|
|
|
crtime: Timespec::new(0, 0),
|
|
|
|
kind: convert_file_type(self.inode.file_type),
|
|
|
|
perm: self.inode.mode as u16,
|
|
|
|
nlink: 1,
|
2018-03-03 16:25:05 +00:00
|
|
|
uid,
|
|
|
|
gid,
|
2017-07-21 09:21:59 +00:00
|
|
|
rdev: self.inode.device.map_or(
|
|
|
|
0,
|
|
|
|
|(major, minor)| (major << 8) + minor
|
|
|
|
),
|
2017-03-26 09:34:16 +00:00
|
|
|
flags: 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn dir_list(&self) -> Option<Vec<(u64, fuse::FileType, String)>> {
|
|
|
|
if self.inode.file_type != FileType::Directory {
|
2017-07-21 09:21:59 +00:00
|
|
|
return None;
|
2017-03-26 09:34:16 +00:00
|
|
|
}
|
2017-07-21 09:21:59 +00:00
|
|
|
let mut list = Vec::with_capacity(self.children.len() + 2);
|
2017-03-26 09:34:16 +00:00
|
|
|
list.push((self.num, fuse::FileType::Directory, ".".to_string()));
|
|
|
|
if let Some(ref parent) = self.parent {
|
|
|
|
let parent = parent.borrow();
|
|
|
|
list.push((parent.num, fuse::FileType::Directory, "..".to_string()));
|
|
|
|
} else {
|
|
|
|
list.push((self.num, fuse::FileType::Directory, "..".to_string()));
|
|
|
|
}
|
|
|
|
for ch in self.children.values() {
|
|
|
|
let child = ch.borrow();
|
2017-07-21 09:21:59 +00:00
|
|
|
list.push((
|
|
|
|
child.num,
|
|
|
|
convert_file_type(child.inode.file_type),
|
|
|
|
child.inode.name.clone()
|
|
|
|
));
|
2017-03-26 09:34:16 +00:00
|
|
|
}
|
|
|
|
Some(list)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pub struct FuseFilesystem<'a> {
|
|
|
|
next_id: u64,
|
|
|
|
repository: &'a mut Repository,
|
|
|
|
inodes: HashMap<u64, FuseInodeRef>
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> FuseFilesystem<'a> {
|
|
|
|
pub fn new(repository: &'a mut Repository) -> Result<Self, RepositoryError> {
|
|
|
|
Ok(FuseFilesystem {
|
|
|
|
next_id: 1,
|
2018-03-03 16:25:05 +00:00
|
|
|
repository,
|
2017-03-26 09:34:16 +00:00
|
|
|
inodes: HashMap::new()
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2017-07-21 09:21:59 +00:00
|
|
|
pub fn from_repository(
|
|
|
|
repository: &'a mut Repository,
|
|
|
|
path: Option<&str>,
|
|
|
|
) -> Result<Self, RepositoryError> {
|
2017-03-26 09:34:16 +00:00
|
|
|
let mut backups = vec![];
|
2017-04-13 12:24:58 +00:00
|
|
|
let backup_map = match path {
|
|
|
|
Some(path) => try!(repository.get_backups(path)),
|
2017-07-21 09:21:59 +00:00
|
|
|
None => try!(repository.get_all_backups()),
|
2017-04-13 12:24:58 +00:00
|
|
|
};
|
|
|
|
for (name, backup) in backup_map {
|
2017-03-26 09:34:16 +00:00
|
|
|
let inode = try!(repository.get_inode(&backup.root));
|
2017-04-12 13:47:26 +00:00
|
|
|
backups.push((name, backup, inode));
|
2017-03-26 09:34:16 +00:00
|
|
|
}
|
|
|
|
let mut fs = try!(FuseFilesystem::new(repository));
|
|
|
|
let root = fs.add_virtual_directory("".to_string(), None);
|
2017-04-12 13:47:26 +00:00
|
|
|
for (name, backup, mut inode) in backups {
|
2017-03-26 09:34:16 +00:00
|
|
|
let mut parent = root.clone();
|
|
|
|
for part in name.split('/') {
|
|
|
|
parent = match fs.get_child(&parent, part).unwrap() {
|
|
|
|
Some(child) => child,
|
2017-07-21 09:21:59 +00:00
|
|
|
None => fs.add_virtual_directory(part.to_string(), Some(parent)),
|
2017-03-26 09:34:16 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
let mut parent_mut = parent.borrow_mut();
|
2017-04-12 13:47:26 +00:00
|
|
|
inode.name = parent_mut.inode.name.clone();
|
|
|
|
parent_mut.inode = inode;
|
|
|
|
parent_mut.user_names = Rc::new(backup.user_names);
|
|
|
|
parent_mut.group_names = Rc::new(backup.group_names);
|
2017-03-26 09:34:16 +00:00
|
|
|
}
|
|
|
|
Ok(fs)
|
|
|
|
}
|
|
|
|
|
2017-07-21 09:21:59 +00:00
|
|
|
pub fn from_backup(
|
|
|
|
repository: &'a mut Repository,
|
|
|
|
backup: Backup,
|
|
|
|
) -> Result<Self, RepositoryError> {
|
2017-03-26 09:34:16 +00:00
|
|
|
let inode = try!(repository.get_inode(&backup.root));
|
|
|
|
let mut fs = try!(FuseFilesystem::new(repository));
|
2017-04-12 13:27:46 +00:00
|
|
|
fs.add_inode(inode, None, backup.user_names, backup.group_names);
|
2017-03-26 09:34:16 +00:00
|
|
|
Ok(fs)
|
|
|
|
}
|
|
|
|
|
2017-07-21 09:21:59 +00:00
|
|
|
pub fn from_inode(
|
|
|
|
repository: &'a mut Repository,
|
|
|
|
backup: Backup,
|
|
|
|
inode: Inode,
|
|
|
|
) -> Result<Self, RepositoryError> {
|
2017-03-26 09:34:16 +00:00
|
|
|
let mut fs = try!(FuseFilesystem::new(repository));
|
2017-04-12 13:27:46 +00:00
|
|
|
fs.add_inode(inode, None, backup.user_names, backup.group_names);
|
2017-03-26 09:34:16 +00:00
|
|
|
Ok(fs)
|
|
|
|
}
|
|
|
|
|
2017-07-21 09:21:59 +00:00
|
|
|
pub fn add_virtual_directory(
|
|
|
|
&mut self,
|
|
|
|
name: String,
|
|
|
|
parent: Option<FuseInodeRef>,
|
|
|
|
) -> FuseInodeRef {
|
|
|
|
self.add_inode(
|
|
|
|
Inode {
|
2018-03-03 16:25:05 +00:00
|
|
|
name,
|
2017-07-21 09:21:59 +00:00
|
|
|
file_type: FileType::Directory,
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
parent,
|
|
|
|
HashMap::default(),
|
|
|
|
HashMap::default()
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn add_inode(
|
|
|
|
&mut self,
|
|
|
|
inode: Inode,
|
|
|
|
parent: Option<FuseInodeRef>,
|
|
|
|
user_names: HashMap<u32, String>,
|
|
|
|
group_names: HashMap<u32, String>,
|
|
|
|
) -> FuseInodeRef {
|
2017-03-26 09:34:16 +00:00
|
|
|
let inode = FuseInode {
|
2018-03-03 16:25:05 +00:00
|
|
|
inode,
|
2017-03-26 09:34:16 +00:00
|
|
|
num: self.next_id,
|
|
|
|
parent: parent.clone(),
|
|
|
|
chunks: None,
|
2017-04-12 13:27:46 +00:00
|
|
|
children: HashMap::new(),
|
|
|
|
user_names: Rc::new(user_names),
|
|
|
|
group_names: Rc::new(group_names),
|
|
|
|
name_cache: Rc::new(users::UsersCache::new())
|
2017-03-26 09:34:16 +00:00
|
|
|
};
|
|
|
|
let name = inode.inode.name.clone();
|
|
|
|
let inode = Rc::new(RefCell::new(inode));
|
|
|
|
self.inodes.insert(self.next_id, inode.clone());
|
|
|
|
if let Some(parent) = parent {
|
|
|
|
parent.borrow_mut().children.insert(name, inode.clone());
|
|
|
|
}
|
|
|
|
self.next_id += 1;
|
|
|
|
inode
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn mount<P: AsRef<Path>>(self, mountpoint: P) -> Result<(), RepositoryError> {
|
2018-02-19 21:30:59 +00:00
|
|
|
try!(fuse::mount(
|
2017-07-21 09:21:59 +00:00
|
|
|
self,
|
|
|
|
&mountpoint,
|
|
|
|
&[
|
|
|
|
OsStr::new("default_permissions"),
|
|
|
|
OsStr::new("kernel_cache"),
|
|
|
|
OsStr::new("auto_cache"),
|
|
|
|
OsStr::new("readonly"),
|
|
|
|
]
|
2018-02-19 21:30:59 +00:00
|
|
|
));
|
|
|
|
Ok(())
|
2017-03-26 09:34:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_inode(&mut self, num: u64) -> Option<FuseInodeRef> {
|
|
|
|
self.inodes.get(&num).cloned()
|
|
|
|
}
|
|
|
|
|
2017-07-21 09:21:59 +00:00
|
|
|
pub fn get_child(
|
|
|
|
&mut self,
|
|
|
|
parent: &FuseInodeRef,
|
|
|
|
name: &str,
|
|
|
|
) -> Result<Option<FuseInodeRef>, RepositoryError> {
|
2017-03-26 09:34:16 +00:00
|
|
|
let mut parent_mut = parent.borrow_mut();
|
|
|
|
if let Some(child) = parent_mut.children.get(name) {
|
2017-07-21 09:21:59 +00:00
|
|
|
return Ok(Some(child.clone()));
|
2017-03-26 09:34:16 +00:00
|
|
|
}
|
|
|
|
let child;
|
|
|
|
if let Some(chunks) = parent_mut.inode.children.as_ref().and_then(|c| c.get(name)) {
|
|
|
|
child = Rc::new(RefCell::new(FuseInode {
|
|
|
|
num: self.next_id,
|
|
|
|
inode: try!(self.repository.get_inode(chunks)),
|
|
|
|
parent: Some(parent.clone()),
|
|
|
|
children: HashMap::new(),
|
2017-04-12 13:27:46 +00:00
|
|
|
chunks: None,
|
|
|
|
user_names: parent_mut.user_names.clone(),
|
|
|
|
group_names: parent_mut.group_names.clone(),
|
|
|
|
name_cache: parent_mut.name_cache.clone()
|
2017-03-26 09:34:16 +00:00
|
|
|
}));
|
|
|
|
self.inodes.insert(self.next_id, child.clone());
|
2017-07-21 09:21:59 +00:00
|
|
|
self.next_id += 1;
|
2017-03-26 09:34:16 +00:00
|
|
|
} else {
|
2017-07-21 09:21:59 +00:00
|
|
|
return Ok(None);
|
2017-03-26 09:34:16 +00:00
|
|
|
}
|
|
|
|
parent_mut.children.insert(name.to_string(), child.clone());
|
|
|
|
Ok(Some(child))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn fetch_children(&mut self, parent: &FuseInodeRef) -> Result<(), RepositoryError> {
|
|
|
|
let mut parent_mut = parent.borrow_mut();
|
|
|
|
let mut parent_children = HashMap::new();
|
|
|
|
mem::swap(&mut parent_children, &mut parent_mut.children);
|
|
|
|
if let Some(ref children) = parent_mut.inode.children {
|
|
|
|
for (name, chunks) in children {
|
|
|
|
if !parent_mut.children.contains_key(name) {
|
|
|
|
let child = Rc::new(RefCell::new(FuseInode {
|
|
|
|
num: self.next_id,
|
|
|
|
inode: try!(self.repository.get_inode(chunks)),
|
|
|
|
parent: Some(parent.clone()),
|
|
|
|
children: HashMap::new(),
|
2017-04-12 13:27:46 +00:00
|
|
|
chunks: None,
|
|
|
|
user_names: parent_mut.user_names.clone(),
|
|
|
|
group_names: parent_mut.group_names.clone(),
|
|
|
|
name_cache: parent_mut.name_cache.clone()
|
2017-03-26 09:34:16 +00:00
|
|
|
}));
|
|
|
|
self.inodes.insert(self.next_id, child.clone());
|
2017-07-21 09:21:59 +00:00
|
|
|
self.next_id += 1;
|
2017-03-26 09:34:16 +00:00
|
|
|
parent_children.insert(name.clone(), child);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mem::swap(&mut parent_children, &mut parent_mut.children);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn fetch_chunks(&mut self, inode: &FuseInodeRef) -> Result<(), RepositoryError> {
|
|
|
|
let mut inode = inode.borrow_mut();
|
|
|
|
let mut chunks = None;
|
2017-04-03 05:35:00 +00:00
|
|
|
match inode.inode.data {
|
2017-07-21 09:21:59 +00:00
|
|
|
None |
|
|
|
|
Some(FileData::Inline(_)) => (),
|
2017-04-03 05:35:00 +00:00
|
|
|
Some(FileData::ChunkedDirect(ref c)) => {
|
2017-03-26 09:34:16 +00:00
|
|
|
chunks = Some(c.clone());
|
2017-07-21 09:21:59 +00:00
|
|
|
}
|
2017-04-03 05:35:00 +00:00
|
|
|
Some(FileData::ChunkedIndirect(ref c)) => {
|
2017-03-26 09:34:16 +00:00
|
|
|
let chunk_data = try!(self.repository.get_data(c));
|
|
|
|
chunks = Some(ChunkList::read_from(&chunk_data));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
inode.chunks = chunks;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl<'a> fuse::Filesystem for FuseFilesystem<'a> {
|
|
|
|
/// Look up a directory entry by name and get its attributes.
|
2017-07-21 09:21:59 +00:00
|
|
|
fn lookup(&mut self, _req: &fuse::Request, parent: u64, name: &OsStr, reply: fuse::ReplyEntry) {
|
2017-03-26 09:34:16 +00:00
|
|
|
let sname = str!(name, reply);
|
|
|
|
let parent = inode!(self, parent, reply);
|
|
|
|
let child = lookup!(self, &parent, sname, reply);
|
|
|
|
let ttl = Timespec::new(60, 0);
|
|
|
|
let attrs = child.borrow().to_attrs();
|
|
|
|
reply.entry(&ttl, &attrs, 0)
|
|
|
|
}
|
|
|
|
|
2017-07-21 09:21:59 +00:00
|
|
|
fn destroy(&mut self, _req: &fuse::Request) {
|
2017-03-26 09:34:16 +00:00
|
|
|
info!("destroy");
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Forget about an inode
|
|
|
|
/// The nlookup parameter indicates the number of lookups previously performed on
|
|
|
|
/// this inode. If the filesystem implements inode lifetimes, it is recommended that
|
|
|
|
/// inodes acquire a single reference on each lookup, and lose nlookup references on
|
|
|
|
/// each forget. The filesystem may ignore forget calls, if the inodes don't need to
|
|
|
|
/// have a limited lifetime. On unmount it is not guaranteed, that all referenced
|
|
|
|
/// inodes will receive a forget message.
|
2017-07-21 09:21:59 +00:00
|
|
|
fn forget(&mut self, _req: &fuse::Request, ino: u64, _nlookup: u64) {
|
2017-03-26 09:34:16 +00:00
|
|
|
info!("forget {:?}", ino);
|
|
|
|
//self.fs.forget(ino).unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get file attributes
|
2017-07-21 09:21:59 +00:00
|
|
|
fn getattr(&mut self, _req: &fuse::Request, ino: u64, reply: fuse::ReplyAttr) {
|
2017-03-26 09:34:16 +00:00
|
|
|
let inode = inode!(self, ino, reply);
|
|
|
|
let ttl = Timespec::new(60, 0);
|
|
|
|
reply.attr(&ttl, &inode.borrow().to_attrs());
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Set file attributes
|
2017-07-21 09:21:59 +00:00
|
|
|
fn setattr(
|
|
|
|
&mut self,
|
|
|
|
_req: &fuse::Request,
|
|
|
|
_ino: u64,
|
|
|
|
_mode: Option<u32>,
|
|
|
|
_uid: Option<u32>,
|
|
|
|
_gid: Option<u32>,
|
|
|
|
_size: Option<u64>,
|
|
|
|
_atime: Option<Timespec>,
|
|
|
|
_mtime: Option<Timespec>,
|
|
|
|
_fh: Option<u64>,
|
|
|
|
_crtime: Option<Timespec>,
|
|
|
|
_chgtime: Option<Timespec>,
|
|
|
|
_bkuptime: Option<Timespec>,
|
|
|
|
_flags: Option<u32>,
|
|
|
|
reply: fuse::ReplyAttr,
|
|
|
|
) {
|
2017-03-26 09:34:16 +00:00
|
|
|
reply.error(libc::EROFS)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Read symbolic link
|
2017-07-21 09:21:59 +00:00
|
|
|
fn readlink(&mut self, _req: &fuse::Request, ino: u64, reply: fuse::ReplyData) {
|
2017-03-26 09:34:16 +00:00
|
|
|
let inode = inode!(self, ino, reply);
|
|
|
|
let inode = inode.borrow();
|
|
|
|
match inode.inode.symlink_target {
|
|
|
|
None => reply.error(libc::EINVAL),
|
2017-07-21 09:21:59 +00:00
|
|
|
Some(ref link) => reply.data(link.as_bytes()),
|
2017-03-26 09:34:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Create a hard link
|
2017-07-21 09:21:59 +00:00
|
|
|
fn link(
|
|
|
|
&mut self,
|
|
|
|
_req: &fuse::Request,
|
|
|
|
_ino: u64,
|
|
|
|
_newparent: u64,
|
|
|
|
_newname: &OsStr,
|
|
|
|
reply: fuse::ReplyEntry,
|
|
|
|
) {
|
2017-03-26 09:34:16 +00:00
|
|
|
reply.error(libc::EROFS)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Create file node
|
|
|
|
/// Create a regular file, character device, block device, fifo or socket node.
|
2017-07-21 09:21:59 +00:00
|
|
|
fn mknod(
|
|
|
|
&mut self,
|
|
|
|
_req: &fuse::Request,
|
|
|
|
_parent: u64,
|
|
|
|
_name: &OsStr,
|
|
|
|
_mode: u32,
|
|
|
|
_rdev: u32,
|
|
|
|
reply: fuse::ReplyEntry,
|
|
|
|
) {
|
2017-03-26 09:34:16 +00:00
|
|
|
reply.error(libc::EROFS)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Create a directory
|
2017-07-21 09:21:59 +00:00
|
|
|
fn mkdir(
|
|
|
|
&mut self,
|
|
|
|
_req: &fuse::Request,
|
|
|
|
_parent: u64,
|
|
|
|
_name: &OsStr,
|
|
|
|
_mode: u32,
|
|
|
|
reply: fuse::ReplyEntry,
|
|
|
|
) {
|
2017-03-26 09:34:16 +00:00
|
|
|
reply.error(libc::EROFS)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Remove a file
|
2017-07-21 09:21:59 +00:00
|
|
|
fn unlink(
|
|
|
|
&mut self,
|
|
|
|
_req: &fuse::Request,
|
|
|
|
_parent: u64,
|
|
|
|
_name: &OsStr,
|
|
|
|
reply: fuse::ReplyEmpty,
|
|
|
|
) {
|
2017-03-26 09:34:16 +00:00
|
|
|
reply.error(libc::EROFS)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Remove a directory
|
2017-07-21 09:21:59 +00:00
|
|
|
fn rmdir(
|
|
|
|
&mut self,
|
|
|
|
_req: &fuse::Request,
|
|
|
|
_parent: u64,
|
|
|
|
_name: &OsStr,
|
|
|
|
reply: fuse::ReplyEmpty,
|
|
|
|
) {
|
2017-03-26 09:34:16 +00:00
|
|
|
reply.error(libc::EROFS)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Create a symbolic link
|
2017-07-21 09:21:59 +00:00
|
|
|
fn symlink(
|
|
|
|
&mut self,
|
|
|
|
_req: &fuse::Request,
|
|
|
|
_parent: u64,
|
|
|
|
_name: &OsStr,
|
|
|
|
_link: &Path,
|
|
|
|
reply: fuse::ReplyEntry,
|
|
|
|
) {
|
2017-03-26 09:34:16 +00:00
|
|
|
reply.error(libc::EROFS)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Rename a file
|
2017-07-21 09:21:59 +00:00
|
|
|
fn rename(
|
|
|
|
&mut self,
|
|
|
|
_req: &fuse::Request,
|
|
|
|
_parent: u64,
|
|
|
|
_name: &OsStr,
|
|
|
|
_newparent: u64,
|
|
|
|
_newname: &OsStr,
|
|
|
|
reply: fuse::ReplyEmpty,
|
|
|
|
) {
|
2017-03-26 09:34:16 +00:00
|
|
|
reply.error(libc::EROFS)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Open a file
|
|
|
|
/// Open flags (with the exception of O_CREAT, O_EXCL, O_NOCTTY and O_TRUNC) are
|
|
|
|
/// available in flags. Filesystem may store an arbitrary file handle (pointer, index,
|
|
|
|
/// etc) in fh, and use this in other all other file operations (read, write, flush,
|
|
|
|
/// release, fsync). Filesystem may also implement stateless file I/O and not store
|
|
|
|
/// anything in fh. There are also some flags (direct_io, keep_cache) which the
|
|
|
|
/// filesystem may set, to change the way the file is opened. See fuse_file_info
|
|
|
|
/// structure in <fuse_common.h> for more details.
|
2017-07-21 09:21:59 +00:00
|
|
|
fn open(&mut self, _req: &fuse::Request, ino: u64, flags: u32, reply: fuse::ReplyOpen) {
|
2017-03-26 09:34:16 +00:00
|
|
|
if (flags & (libc::O_WRONLY | libc::O_RDWR | libc::O_TRUNC) as u32) != 0 {
|
|
|
|
return reply.error(libc::EROFS);
|
|
|
|
}
|
|
|
|
let inode = inode!(self, ino, reply);
|
|
|
|
fuse_try!(self.fetch_chunks(&inode), reply);
|
|
|
|
reply.opened(ino, libc::O_RDONLY as u32);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Read data
|
|
|
|
/// Read should send exactly the number of bytes requested except on EOF or error,
|
|
|
|
/// otherwise the rest of the data will be substituted with zeroes. An exception to
|
2018-02-24 12:19:51 +00:00
|
|
|
/// this is when the file has been opened in direct_io mode, in which case the
|
2017-03-26 09:34:16 +00:00
|
|
|
/// return value of the read system call will reflect the return value of this
|
|
|
|
/// operation. fh will contain the value set by the open method, or will be undefined
|
2018-02-24 12:19:51 +00:00
|
|
|
/// if the open method didnt set any value.
|
2017-07-21 09:21:59 +00:00
|
|
|
fn read(
|
|
|
|
&mut self,
|
|
|
|
_req: &fuse::Request,
|
|
|
|
ino: u64,
|
|
|
|
_fh: u64,
|
2018-02-19 21:42:44 +00:00
|
|
|
mut offset: i64,
|
2017-07-21 09:21:59 +00:00
|
|
|
mut size: u32,
|
|
|
|
reply: fuse::ReplyData,
|
|
|
|
) {
|
2017-03-26 09:34:16 +00:00
|
|
|
let inode = inode!(self, ino, reply);
|
|
|
|
let inode = inode.borrow();
|
2017-04-03 05:35:00 +00:00
|
|
|
match inode.inode.data {
|
2017-03-26 09:34:16 +00:00
|
|
|
None => return reply.data(&[]),
|
2017-07-21 09:21:59 +00:00
|
|
|
Some(FileData::Inline(ref data)) => {
|
|
|
|
return reply.data(
|
|
|
|
&data[min(offset as usize, data.len())..
|
|
|
|
min(offset as usize + size as usize, data.len())]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
_ => (),
|
2017-03-26 09:34:16 +00:00
|
|
|
}
|
|
|
|
if let Some(ref chunks) = inode.chunks {
|
|
|
|
let mut data = Vec::with_capacity(size as usize);
|
|
|
|
for &(hash, len) in chunks.iter() {
|
2018-02-19 21:42:44 +00:00
|
|
|
if i64::from(len) <= offset {
|
|
|
|
offset -= i64::from(len);
|
2017-07-21 09:21:59 +00:00
|
|
|
continue;
|
2017-03-26 09:34:16 +00:00
|
|
|
}
|
|
|
|
let chunk = match fuse_try!(self.repository.get_chunk(hash), reply) {
|
|
|
|
Some(chunk) => chunk,
|
2017-07-21 09:21:59 +00:00
|
|
|
None => return reply.error(libc::EIO),
|
2017-03-26 09:34:16 +00:00
|
|
|
};
|
|
|
|
assert_eq!(chunk.len() as u32, len);
|
2017-07-21 09:21:59 +00:00
|
|
|
data.extend_from_slice(
|
|
|
|
&chunk[offset as usize..min(offset as usize + size as usize, len as usize)]
|
|
|
|
);
|
2017-03-26 09:34:16 +00:00
|
|
|
if len - offset as u32 >= size {
|
2017-07-21 09:21:59 +00:00
|
|
|
break;
|
2017-03-26 09:34:16 +00:00
|
|
|
}
|
|
|
|
size -= len - offset as u32;
|
|
|
|
offset = 0;
|
|
|
|
}
|
|
|
|
reply.data(&data)
|
|
|
|
} else {
|
|
|
|
reply.error(libc::EBADF)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Write data
|
2017-07-21 09:21:59 +00:00
|
|
|
fn write(
|
|
|
|
&mut self,
|
|
|
|
_req: &fuse::Request,
|
|
|
|
_ino: u64,
|
|
|
|
_fh: u64,
|
2018-02-19 21:42:44 +00:00
|
|
|
_offset: i64,
|
2017-07-21 09:21:59 +00:00
|
|
|
_data: &[u8],
|
|
|
|
_flags: u32,
|
|
|
|
reply: fuse::ReplyWrite,
|
|
|
|
) {
|
2017-03-26 09:34:16 +00:00
|
|
|
reply.error(libc::EROFS)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Flush method
|
2017-07-21 09:21:59 +00:00
|
|
|
fn flush(
|
|
|
|
&mut self,
|
|
|
|
_req: &fuse::Request,
|
|
|
|
_ino: u64,
|
|
|
|
_fh: u64,
|
|
|
|
_lock_owner: u64,
|
|
|
|
reply: fuse::ReplyEmpty,
|
|
|
|
) {
|
2017-03-26 09:34:16 +00:00
|
|
|
reply.ok()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Release an open file
|
|
|
|
/// Release is called when there are no more references to an open file: all file
|
|
|
|
/// descriptors are closed and all memory mappings are unmapped. For every open
|
|
|
|
/// call there will be exactly one release call. The filesystem may reply with an
|
|
|
|
/// error, but error values are not returned to close() or munmap() which triggered
|
|
|
|
/// the release. fh will contain the value set by the open method, or will be undefined
|
2018-02-24 12:19:51 +00:00
|
|
|
/// if the open method didnt set any value. flags will contain the same flags as for
|
2017-03-26 09:34:16 +00:00
|
|
|
/// open.
|
2017-07-21 09:21:59 +00:00
|
|
|
fn release(
|
|
|
|
&mut self,
|
|
|
|
_req: &fuse::Request,
|
|
|
|
_ino: u64,
|
|
|
|
_fh: u64,
|
|
|
|
_flags: u32,
|
|
|
|
_lock_owner: u64,
|
|
|
|
_flush: bool,
|
|
|
|
reply: fuse::ReplyEmpty,
|
|
|
|
) {
|
2017-03-26 09:34:16 +00:00
|
|
|
/*if self.read_fds.remove(&fh).is_some() || self.write_fds.remove(&fh).is_some() {
|
|
|
|
reply.ok();
|
|
|
|
} else {
|
|
|
|
reply.error(libc::EBADF);
|
|
|
|
}*/
|
|
|
|
reply.error(libc::ENOSYS)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Synchronize file contents
|
2017-07-21 09:21:59 +00:00
|
|
|
fn fsync(
|
|
|
|
&mut self,
|
|
|
|
_req: &fuse::Request,
|
|
|
|
_ino: u64,
|
|
|
|
_fh: u64,
|
|
|
|
_datasync: bool,
|
|
|
|
reply: fuse::ReplyEmpty,
|
|
|
|
) {
|
2017-03-26 09:34:16 +00:00
|
|
|
reply.ok()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Open a directory, finished
|
2017-07-21 09:21:59 +00:00
|
|
|
fn opendir(&mut self, _req: &fuse::Request, ino: u64, _flags: u32, reply: fuse::ReplyOpen) {
|
2017-03-26 09:34:16 +00:00
|
|
|
let dir = inode!(self, ino, reply);
|
|
|
|
fuse_try!(self.fetch_children(&dir), reply);
|
|
|
|
reply.opened(ino, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Read directory, finished
|
2017-07-21 09:21:59 +00:00
|
|
|
fn readdir(
|
|
|
|
&mut self,
|
|
|
|
_req: &fuse::Request,
|
|
|
|
ino: u64,
|
|
|
|
_fh: u64,
|
2018-02-19 21:42:44 +00:00
|
|
|
offset: i64,
|
2017-07-21 09:21:59 +00:00
|
|
|
mut reply: fuse::ReplyDirectory,
|
|
|
|
) {
|
2017-03-26 09:34:16 +00:00
|
|
|
let dir = inode!(self, ino, reply);
|
|
|
|
let dir = dir.borrow();
|
|
|
|
if let Some(entries) = dir.dir_list() {
|
|
|
|
for (i, (num, file_type, name)) in entries.into_iter().enumerate() {
|
|
|
|
if i < offset as usize {
|
2017-07-21 09:21:59 +00:00
|
|
|
continue;
|
2017-03-26 09:34:16 +00:00
|
|
|
}
|
2018-02-19 21:42:44 +00:00
|
|
|
if reply.add(num, i as i64 + 1, file_type, &Path::new(&name)) {
|
2017-07-21 09:21:59 +00:00
|
|
|
break;
|
2017-03-26 09:34:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
reply.ok()
|
|
|
|
} else {
|
|
|
|
reply.error(libc::ENOTDIR)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Release an open directory, finished
|
2017-07-21 09:21:59 +00:00
|
|
|
fn releasedir(
|
|
|
|
&mut self,
|
|
|
|
_req: &fuse::Request,
|
|
|
|
_ino: u64,
|
|
|
|
_fh: u64,
|
|
|
|
_flags: u32,
|
|
|
|
reply: fuse::ReplyEmpty,
|
|
|
|
) {
|
2017-03-26 09:34:16 +00:00
|
|
|
reply.ok()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Synchronize directory contents, finished
|
2017-07-21 09:21:59 +00:00
|
|
|
fn fsyncdir(
|
|
|
|
&mut self,
|
|
|
|
_req: &fuse::Request,
|
|
|
|
_ino: u64,
|
|
|
|
_fh: u64,
|
|
|
|
_datasync: bool,
|
|
|
|
reply: fuse::ReplyEmpty,
|
|
|
|
) {
|
2017-03-26 09:34:16 +00:00
|
|
|
reply.ok()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get file system statistics
|
2017-07-21 09:21:59 +00:00
|
|
|
fn statfs(&mut self, _req: &fuse::Request, _ino: u64, reply: fuse::ReplyStatfs) {
|
2017-03-26 09:34:16 +00:00
|
|
|
let info = self.repository.info();
|
|
|
|
reply.statfs(
|
2017-07-21 09:21:59 +00:00
|
|
|
info.raw_data_size / 512 as u64, //total blocks
|
2017-03-26 09:34:16 +00:00
|
|
|
0, //free blocks for admin
|
|
|
|
0, //free blocks for users
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
512 as u32, //block size
|
|
|
|
255, //max name length
|
|
|
|
0
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Set an extended attribute
|
2017-07-21 09:21:59 +00:00
|
|
|
fn setxattr(
|
|
|
|
&mut self,
|
|
|
|
_req: &fuse::Request,
|
|
|
|
_ino: u64,
|
|
|
|
_name: &OsStr,
|
|
|
|
_value: &[u8],
|
|
|
|
_flags: u32,
|
|
|
|
_position: u32,
|
|
|
|
reply: fuse::ReplyEmpty,
|
|
|
|
) {
|
2017-03-26 09:34:16 +00:00
|
|
|
reply.error(libc::EROFS)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get an extended attribute
|
2017-07-21 09:21:59 +00:00
|
|
|
fn getxattr(
|
|
|
|
&mut self,
|
|
|
|
_req: &fuse::Request,
|
|
|
|
ino: u64,
|
|
|
|
name: &OsStr,
|
|
|
|
size: u32,
|
|
|
|
reply: fuse::ReplyXattr,
|
|
|
|
) {
|
2017-04-14 07:42:32 +00:00
|
|
|
let inode = inode!(self, ino, reply);
|
|
|
|
let inode = inode.borrow();
|
|
|
|
if let Some(val) = inode.inode.xattrs.get(&name.to_string_lossy() as &str) {
|
|
|
|
if size == 0 {
|
|
|
|
reply.size(val.len() as u32);
|
|
|
|
} else if size >= val.len() as u32 {
|
|
|
|
reply.data(val);
|
|
|
|
} else {
|
|
|
|
reply.error(libc::ERANGE);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
reply.error(libc::ENODATA);
|
|
|
|
}
|
2017-03-26 09:34:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// List extended attribute names
|
2017-07-21 09:21:59 +00:00
|
|
|
fn listxattr(&mut self, _req: &fuse::Request, ino: u64, size: u32, reply: fuse::ReplyXattr) {
|
2017-06-20 08:45:16 +00:00
|
|
|
let inode = inode!(self, ino, reply);
|
|
|
|
let inode = inode.borrow();
|
|
|
|
let mut names_str = String::new();
|
|
|
|
for name in inode.inode.xattrs.keys() {
|
|
|
|
names_str.push_str(name);
|
|
|
|
names_str.push('\0');
|
|
|
|
}
|
|
|
|
if size == 0 {
|
|
|
|
return reply.size(names_str.len() as u32);
|
|
|
|
}
|
|
|
|
if size < names_str.len() as u32 {
|
|
|
|
return reply.error(libc::ERANGE);
|
|
|
|
}
|
|
|
|
reply.data(names_str.as_bytes());
|
2017-03-26 09:34:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Remove an extended attribute
|
2017-07-21 09:21:59 +00:00
|
|
|
fn removexattr(
|
|
|
|
&mut self,
|
|
|
|
_req: &fuse::Request,
|
|
|
|
_ino: u64,
|
|
|
|
_name: &OsStr,
|
|
|
|
reply: fuse::ReplyEmpty,
|
|
|
|
) {
|
2017-03-26 09:34:16 +00:00
|
|
|
reply.error(libc::EROFS)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Check file access permissions
|
|
|
|
/// This will be called for the access() system call. If the 'default_permissions'
|
|
|
|
/// mount option is given, this method is not called. This method is not called
|
|
|
|
/// under Linux kernel versions 2.4.x
|
2017-07-21 09:21:59 +00:00
|
|
|
fn access(&mut self, _req: &fuse::Request, _ino: u64, _mask: u32, reply: fuse::ReplyEmpty) {
|
2017-03-26 09:34:16 +00:00
|
|
|
reply.error(libc::ENOSYS);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Create and open a file
|
2017-07-21 09:21:59 +00:00
|
|
|
fn create(
|
|
|
|
&mut self,
|
|
|
|
_req: &fuse::Request,
|
|
|
|
_parent: u64,
|
|
|
|
_name: &OsStr,
|
|
|
|
_mode: u32,
|
|
|
|
_flags: u32,
|
|
|
|
reply: fuse::ReplyCreate,
|
|
|
|
) {
|
2017-03-26 09:34:16 +00:00
|
|
|
reply.error(libc::EROFS)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Test for a POSIX file lock
|
2017-07-21 09:21:59 +00:00
|
|
|
fn getlk(
|
|
|
|
&mut self,
|
|
|
|
_req: &fuse::Request,
|
|
|
|
_ino: u64,
|
|
|
|
_fh: u64,
|
|
|
|
_lock_owner: u64,
|
|
|
|
_start: u64,
|
|
|
|
_end: u64,
|
|
|
|
_typ: u32,
|
|
|
|
_pid: u32,
|
|
|
|
reply: fuse::ReplyLock,
|
|
|
|
) {
|
2017-03-26 09:34:16 +00:00
|
|
|
reply.error(libc::ENOSYS);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Acquire, modify or release a POSIX file lock
|
2017-07-21 09:21:59 +00:00
|
|
|
fn setlk(
|
|
|
|
&mut self,
|
|
|
|
_req: &fuse::Request,
|
|
|
|
_ino: u64,
|
|
|
|
_fh: u64,
|
|
|
|
_lock_owner: u64,
|
|
|
|
_start: u64,
|
|
|
|
_end: u64,
|
|
|
|
_typ: u32,
|
|
|
|
_pid: u32,
|
|
|
|
_sleep: bool,
|
|
|
|
reply: fuse::ReplyEmpty,
|
|
|
|
) {
|
2017-03-26 09:34:16 +00:00
|
|
|
reply.error(libc::ENOSYS);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Map block index within file to block index within device
|
2017-07-21 09:21:59 +00:00
|
|
|
fn bmap(
|
|
|
|
&mut self,
|
|
|
|
_req: &fuse::Request,
|
|
|
|
_ino: u64,
|
|
|
|
_blocksize: u32,
|
|
|
|
_idx: u64,
|
|
|
|
reply: fuse::ReplyBmap,
|
|
|
|
) {
|
2017-03-26 09:34:16 +00:00
|
|
|
reply.error(libc::ENOSYS);
|
|
|
|
}
|
|
|
|
}
|