Implemented a few fuse functions
This commit is contained in:
parent
75540ba718
commit
057cefdb5c
|
@ -373,9 +373,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.126"
|
version = "0.2.134"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
|
checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
|
@ -556,6 +556,7 @@ name = "rustcryptfs-linux"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fuser",
|
"fuser",
|
||||||
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"rustcryptfs-lib",
|
"rustcryptfs-lib",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
use sha2::{Digest, Sha256};
|
||||||
|
|
||||||
/// EncodedFilename
|
/// EncodedFilename
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum EncodedFilename {
|
pub enum EncodedFilename {
|
||||||
|
@ -14,13 +16,18 @@ impl EncodedFilename {
|
||||||
{
|
{
|
||||||
let path = file.as_ref();
|
let path = file.as_ref();
|
||||||
|
|
||||||
let filename = path.file_name().unwrap().to_str().expect("Failed to get filename");
|
let filename = path
|
||||||
|
.file_name()
|
||||||
|
.unwrap()
|
||||||
|
.to_str()
|
||||||
|
.expect("Failed to get filename");
|
||||||
|
|
||||||
if filename.starts_with("gocryptfs.longname.") {
|
if filename.starts_with("gocryptfs.longname.") {
|
||||||
if !filename.ends_with(".name") {
|
if !filename.ends_with(".name") {
|
||||||
let long = std::fs::read_to_string(
|
let long = std::fs::read_to_string(
|
||||||
path.parent().unwrap().join(format!("{}.name", filename)),
|
path.parent().unwrap().join(format!("{}.name", filename)),
|
||||||
).unwrap();
|
)
|
||||||
|
.unwrap();
|
||||||
Ok(EncodedFilename::LongFilename(LongFilename {
|
Ok(EncodedFilename::LongFilename(LongFilename {
|
||||||
filename: filename.to_string(),
|
filename: filename.to_string(),
|
||||||
filename_content: long,
|
filename_content: long,
|
||||||
|
@ -43,7 +50,16 @@ pub struct LongFilename {
|
||||||
impl From<String> for EncodedFilename {
|
impl From<String> for EncodedFilename {
|
||||||
fn from(filename: String) -> Self {
|
fn from(filename: String) -> Self {
|
||||||
if filename.len() > 255 {
|
if filename.len() > 255 {
|
||||||
unimplemented!()
|
let mut hasher = Sha256::new();
|
||||||
|
hasher.update(filename.as_bytes());
|
||||||
|
|
||||||
|
Self::LongFilename(LongFilename {
|
||||||
|
filename: format!(
|
||||||
|
"gocryptfs.longname.{}.name",
|
||||||
|
base64::encode_config(hasher.finalize(), base64::URL_SAFE_NO_PAD)
|
||||||
|
),
|
||||||
|
filename_content: filename,
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
Self::ShortFilename(filename)
|
Self::ShortFilename(filename)
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ impl GocryptFs {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn filename_decoder(&self) -> &FilenameDecoder {
|
pub fn filename_decoder<'s> (&'s self) -> &'s FilenameDecoder {
|
||||||
&self.filename_decoder
|
&self.filename_decoder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,3 +11,4 @@ log = "0.4"
|
||||||
rustcryptfs-lib = { path = "../rustcryptfs-lib" }
|
rustcryptfs-lib = { path = "../rustcryptfs-lib" }
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
libc = "0.2"
|
|
@ -1,12 +1,26 @@
|
||||||
use std::path::Path;
|
use std::{
|
||||||
|
collections::BTreeMap,
|
||||||
|
ffi::OsStr,
|
||||||
|
fs::FileType as StdFileType,
|
||||||
|
os::unix::prelude::{FileTypeExt, MetadataExt, OsStrExt, PermissionsExt},
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
sync::atomic::{AtomicU64, Ordering},
|
||||||
|
time::{Duration, SystemTime},
|
||||||
|
};
|
||||||
|
|
||||||
use fuser::Filesystem;
|
use fuser::{FileAttr, FileType, Filesystem, FUSE_ROOT_ID};
|
||||||
use rustcryptfs_lib::GocryptFs;
|
use rustcryptfs_lib::GocryptFs;
|
||||||
|
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
|
|
||||||
|
const BLOCK_SIZE: u64 = 4096;
|
||||||
|
|
||||||
|
type InodeCache = BTreeMap<u64, PathBuf>;
|
||||||
|
|
||||||
pub struct EncryptedFs {
|
pub struct EncryptedFs {
|
||||||
fs: GocryptFs,
|
fs: GocryptFs,
|
||||||
|
inode_cache: InodeCache,
|
||||||
|
file_handle: AtomicU64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EncryptedFs {
|
impl EncryptedFs {
|
||||||
|
@ -14,9 +28,21 @@ impl EncryptedFs {
|
||||||
where
|
where
|
||||||
P: AsRef<Path>,
|
P: AsRef<Path>,
|
||||||
{
|
{
|
||||||
|
let path = path.as_ref();
|
||||||
|
|
||||||
|
log::info!("Opening dir ...");
|
||||||
let fs = GocryptFs::open(path, password)?;
|
let fs = GocryptFs::open(path, password)?;
|
||||||
|
|
||||||
Ok(Self { fs })
|
log::info!("Done");
|
||||||
|
|
||||||
|
let mut inode_cache = BTreeMap::new();
|
||||||
|
inode_cache.insert(FUSE_ROOT_ID, path.to_path_buf());
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
fs,
|
||||||
|
inode_cache,
|
||||||
|
file_handle: AtomicU64::new(0),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mount<P>(self, mountpoint: P)
|
pub fn mount<P>(self, mountpoint: P)
|
||||||
|
@ -25,6 +51,228 @@ impl EncryptedFs {
|
||||||
{
|
{
|
||||||
fuser::mount2(self, mountpoint, &[]).unwrap();
|
fuser::mount2(self, mountpoint, &[]).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_path(&self, ino: u64) -> Option<PathBuf> {
|
||||||
|
// TODO CHECK PERM
|
||||||
|
|
||||||
|
// TODO AVOID CLONE
|
||||||
|
self.inode_cache.get(&ino).map(|p| p.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Filesystem for EncryptedFs {}
|
fn get_real_size(size: u64) -> u64 {
|
||||||
|
let x = ((size as f64 - 48.0) / 4128.0).floor() as u64;
|
||||||
|
|
||||||
|
x * 4096 + size - 48 - x * 4096
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_file_type(file_type: StdFileType) -> FileType {
|
||||||
|
if file_type.is_file() {
|
||||||
|
FileType::RegularFile
|
||||||
|
} else if file_type.is_dir() {
|
||||||
|
FileType::Directory
|
||||||
|
} else if file_type.is_symlink() {
|
||||||
|
FileType::Symlink
|
||||||
|
} else if file_type.is_socket() {
|
||||||
|
FileType::Socket
|
||||||
|
} else if file_type.is_char_device() {
|
||||||
|
FileType::CharDevice
|
||||||
|
} else if file_type.is_block_device() {
|
||||||
|
FileType::BlockDevice
|
||||||
|
} else if file_type.is_fifo() {
|
||||||
|
FileType::NamedPipe
|
||||||
|
} else {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_attr<P>(path: P, ino: u64) -> FileAttr
|
||||||
|
where
|
||||||
|
P: AsRef<Path>,
|
||||||
|
{
|
||||||
|
let meta = std::fs::metadata(&path).unwrap();
|
||||||
|
|
||||||
|
let file_type = Self::get_file_type(meta.file_type());
|
||||||
|
|
||||||
|
FileAttr {
|
||||||
|
ino,
|
||||||
|
size: EncryptedFs::get_real_size(meta.size()),
|
||||||
|
blocks: BLOCK_SIZE / meta.size(),
|
||||||
|
atime: meta.accessed().unwrap(),
|
||||||
|
mtime: meta.modified().unwrap(),
|
||||||
|
ctime: SystemTime::now(), //SystemTime::from(meta.ctime()),
|
||||||
|
crtime: SystemTime::now(),
|
||||||
|
kind: file_type,
|
||||||
|
perm: meta.permissions().mode() as u16,
|
||||||
|
nlink: meta.nlink() as u32,
|
||||||
|
uid: meta.uid(),
|
||||||
|
gid: meta.gid(),
|
||||||
|
rdev: meta.rdev() as u32,
|
||||||
|
blksize: meta.blksize() as u32,
|
||||||
|
flags: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait InodeCacheExt {
|
||||||
|
fn get_or_insert_inode(&mut self, file_path: PathBuf) -> (u64, PathBuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InodeCacheExt for InodeCache {
|
||||||
|
// TODO Try to avoid clone
|
||||||
|
fn get_or_insert_inode(&mut self, file_path: PathBuf) -> (u64, PathBuf) {
|
||||||
|
if let Some((ino, path)) = {
|
||||||
|
self.iter()
|
||||||
|
.find_map(|(i, p)| if p.eq(&file_path) { Some((i, p)) } else { None })
|
||||||
|
} {
|
||||||
|
(*ino, path.clone())
|
||||||
|
} else {
|
||||||
|
let ino = self.len() as u64 + 1;
|
||||||
|
self.insert(ino, file_path);
|
||||||
|
|
||||||
|
(ino, self.get(&ino).unwrap().clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Filesystem for EncryptedFs {
|
||||||
|
fn access(&mut self, _req: &fuser::Request<'_>, ino: u64, mask: i32, reply: fuser::ReplyEmpty) {
|
||||||
|
log::debug!("access, ino : {}, mask : {}", ino, mask);
|
||||||
|
if let Some(path) = self.get_path(ino) {
|
||||||
|
reply.ok()
|
||||||
|
} else {
|
||||||
|
reply.error(libc::ENOENT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getattr(&mut self, _req: &fuser::Request<'_>, ino: u64, reply: fuser::ReplyAttr) {
|
||||||
|
log::debug!("getattr, ino : {}", ino);
|
||||||
|
if let Some(path) = self.get_path(ino) {
|
||||||
|
log::debug!("access, path = {:?}", path);
|
||||||
|
reply.attr(&Duration::new(0, 0), &Self::get_attr(path, ino))
|
||||||
|
} else {
|
||||||
|
reply.error(libc::ENOENT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lookup(
|
||||||
|
&mut self,
|
||||||
|
_req: &fuser::Request<'_>,
|
||||||
|
parent: u64,
|
||||||
|
name: &std::ffi::OsStr,
|
||||||
|
reply: fuser::ReplyEntry,
|
||||||
|
) {
|
||||||
|
if let Some(parent) = &self.get_path(parent) {
|
||||||
|
let iv = std::fs::read(parent.join("gocryptfs.diriv")).unwrap();
|
||||||
|
let dir_decoder = self.fs.filename_decoder().get_decoder_for_dir(&iv);
|
||||||
|
|
||||||
|
let encrypted_name = dir_decoder
|
||||||
|
.encrypt_filename(&name.to_string_lossy())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let encrypted_name = match encrypted_name {
|
||||||
|
rustcryptfs_lib::filename::EncodedFilename::ShortFilename(s) => s,
|
||||||
|
rustcryptfs_lib::filename::EncodedFilename::LongFilename(l) => l.filename,
|
||||||
|
};
|
||||||
|
|
||||||
|
let file_path = parent.join(encrypted_name);
|
||||||
|
|
||||||
|
if file_path.exists() {
|
||||||
|
let (ino, file_path) = self.inode_cache.get_or_insert_inode(file_path);
|
||||||
|
|
||||||
|
reply.entry(&Duration::new(0, 0), &Self::get_attr(file_path, ino), 0)
|
||||||
|
} else {
|
||||||
|
reply.error(libc::ENOENT)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reply.error(libc::ENOENT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn opendir(
|
||||||
|
&mut self,
|
||||||
|
_req: &fuser::Request<'_>,
|
||||||
|
ino: u64,
|
||||||
|
_flags: i32,
|
||||||
|
reply: fuser::ReplyOpen,
|
||||||
|
) {
|
||||||
|
let fh = self.file_handle.fetch_add(1, Ordering::SeqCst);
|
||||||
|
reply.opened(fh, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn readdir(
|
||||||
|
&mut self,
|
||||||
|
_req: &fuser::Request<'_>,
|
||||||
|
ino: u64,
|
||||||
|
_fh: u64,
|
||||||
|
offset: i64,
|
||||||
|
mut reply: fuser::ReplyDirectory,
|
||||||
|
) {
|
||||||
|
if let Some(folder_path) = &self.get_path(ino) {
|
||||||
|
log::debug!("folder_path :{:?}", folder_path);
|
||||||
|
|
||||||
|
let iv = std::fs::read(folder_path.join("gocryptfs.diriv")).unwrap();
|
||||||
|
|
||||||
|
let dir_decoder = self.fs.filename_decoder().get_decoder_for_dir(&iv);
|
||||||
|
|
||||||
|
for (index, (meta, encrypted_name, name)) in std::fs::read_dir(folder_path)
|
||||||
|
.unwrap()
|
||||||
|
.flat_map(|e| e.ok())
|
||||||
|
.flat_map(|dir| extract_name(dir, folder_path, &dir_decoder))
|
||||||
|
.skip(offset as usize)
|
||||||
|
.enumerate()
|
||||||
|
{
|
||||||
|
let (inode, _) = self
|
||||||
|
.inode_cache
|
||||||
|
.get_or_insert_inode(folder_path.join(&encrypted_name));
|
||||||
|
|
||||||
|
let file_type = Self::get_file_type(meta.file_type());
|
||||||
|
|
||||||
|
let buffer_full: bool = reply.add(
|
||||||
|
inode,
|
||||||
|
offset + index as i64 + 1,
|
||||||
|
file_type,
|
||||||
|
OsStr::from_bytes(name.as_bytes()),
|
||||||
|
);
|
||||||
|
|
||||||
|
if buffer_full {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reply.ok()
|
||||||
|
} else {
|
||||||
|
reply.error(libc::ENOENT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extract_name(
|
||||||
|
dir: std::fs::DirEntry,
|
||||||
|
folder_path: &PathBuf,
|
||||||
|
dir_decoder: &rustcryptfs_lib::filename::DirFilenameDecoder,
|
||||||
|
) -> Option<(std::fs::Metadata, String, String)> {
|
||||||
|
let filename = dir.file_name();
|
||||||
|
let filename = filename.to_str().unwrap();
|
||||||
|
if filename != "gocryptfs.conf" && filename != "gocryptfs.diriv" {
|
||||||
|
if filename.starts_with("gocryptfs.longname.") {
|
||||||
|
if !filename.ends_with(".name") {
|
||||||
|
let filename =
|
||||||
|
std::fs::read_to_string(folder_path.join(format!("{}.name", filename)))
|
||||||
|
.unwrap();
|
||||||
|
dir_decoder
|
||||||
|
.decode_filename(filename.as_str())
|
||||||
|
.map(|n| (dir.metadata().unwrap(), filename, n))
|
||||||
|
.ok()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dir_decoder
|
||||||
|
.decode_filename(filename)
|
||||||
|
.map(|n| (dir.metadata().unwrap(), filename.to_string(), n))
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue