Refracto error handling on rustcryptfs-linux
This commit is contained in:
parent
61d07910e6
commit
2032d861dd
|
@ -2,7 +2,7 @@ use std::{
|
|||
collections::BTreeMap,
|
||||
ffi::OsStr,
|
||||
fs::{File, FileType as StdFileType},
|
||||
io::{Read, Seek, SeekFrom},
|
||||
io::{Error as IoError, Read, Result as IoResult, Seek, SeekFrom},
|
||||
ops::Add,
|
||||
os::unix::prelude::{FileTypeExt, MetadataExt, OsStrExt, PermissionsExt},
|
||||
path::{Path, PathBuf},
|
||||
|
@ -13,10 +13,23 @@ use fuser::{FileAttr, FileType, Filesystem, FUSE_ROOT_ID};
|
|||
use rustcryptfs_lib::{content::ContentEnc, GocryptFs};
|
||||
|
||||
use crate::{
|
||||
error::Result,
|
||||
error::{ErrorExt, Result},
|
||||
inode_cache::{InodeCache, InodeCacheExt},
|
||||
};
|
||||
|
||||
trait OptionExt<R> {
|
||||
fn enoent(self) -> IoResult<R>;
|
||||
}
|
||||
|
||||
impl<R> OptionExt<R> for Option<R> {
|
||||
fn enoent(self) -> IoResult<R> {
|
||||
match self {
|
||||
Some(r) => Ok(r),
|
||||
None => Err(IoError::from_raw_os_error(libc::ENOENT)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const BLOCK_SIZE: u64 = 4096;
|
||||
|
||||
pub struct EncryptedFs {
|
||||
|
@ -42,11 +55,11 @@ impl EncryptedFs {
|
|||
Ok(Self { fs, inode_cache })
|
||||
}
|
||||
|
||||
pub fn mount<P>(self, mountpoint: P)
|
||||
pub fn mount<P>(self, mountpoint: P) -> std::io::Result<()>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
fuser::mount2(self, mountpoint, &[]).unwrap();
|
||||
fuser::mount2(self, mountpoint, &[])
|
||||
}
|
||||
|
||||
fn get_file_type(file_type: StdFileType) -> FileType {
|
||||
|
@ -73,11 +86,11 @@ impl EncryptedFs {
|
|||
self.inode_cache.get_path(ino)
|
||||
}
|
||||
|
||||
fn get_attr<P>(path: P, ino: u64) -> FileAttr
|
||||
fn get_attr<P>(path: P, ino: u64) -> std::io::Result<FileAttr>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let meta = std::fs::metadata(&path).unwrap();
|
||||
let meta = std::fs::metadata(&path)?;
|
||||
|
||||
let file_type = Self::get_file_type(meta.file_type());
|
||||
|
||||
|
@ -87,12 +100,12 @@ impl EncryptedFs {
|
|||
meta.size()
|
||||
};
|
||||
|
||||
FileAttr {
|
||||
Ok(FileAttr {
|
||||
ino,
|
||||
size: file_size,
|
||||
blocks: (file_size + BLOCK_SIZE - 1) / BLOCK_SIZE,
|
||||
atime: meta.accessed().unwrap(),
|
||||
mtime: meta.modified().unwrap(),
|
||||
atime: meta.accessed()?,
|
||||
mtime: meta.modified()?,
|
||||
ctime: UNIX_EPOCH.add(Duration::new(meta.ctime() as u64, 0)),
|
||||
crtime: UNIX_EPOCH.add(Duration::new(meta.ctime() as u64, 0)),
|
||||
kind: file_type,
|
||||
|
@ -103,41 +116,19 @@ impl EncryptedFs {
|
|||
rdev: 0,
|
||||
blksize: BLOCK_SIZE as u32,
|
||||
flags: 0,
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
impl Filesystem for EncryptedFs {
|
||||
fn access(&mut self, _req: &fuser::Request<'_>, ino: u64, mask: i32, reply: fuser::ReplyEmpty) {
|
||||
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) {
|
||||
if let Some(path) = self.get_path(ino) {
|
||||
reply.attr(&Duration::new(0, 0), &Self::get_attr(path, ino))
|
||||
} else {
|
||||
reply.error(libc::ENOENT)
|
||||
}
|
||||
}
|
||||
|
||||
fn lookup(
|
||||
fn lookup_impl(
|
||||
&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();
|
||||
) -> rustcryptfs_lib::error::Result<(Duration, FileAttr, u64)> {
|
||||
let parent = self.get_path(parent).enoent()?;
|
||||
let iv = std::fs::read(parent.join("gocryptfs.diriv"))?;
|
||||
let dir_decoder = self.fs.filename_decoder().get_cipher_for_dir(&iv);
|
||||
|
||||
let encrypted_name = dir_decoder
|
||||
.encrypt_filename(&name.to_string_lossy())
|
||||
.unwrap();
|
||||
let encrypted_name = dir_decoder.encrypt_filename(&name.to_string_lossy())?;
|
||||
|
||||
let encrypted_name = match encrypted_name {
|
||||
rustcryptfs_lib::filename::EncodedFilename::ShortFilename(s) => s,
|
||||
|
@ -149,25 +140,20 @@ impl Filesystem for EncryptedFs {
|
|||
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)
|
||||
Ok((Duration::new(0, 0), Self::get_attr(file_path, ino)?, 0))
|
||||
} else {
|
||||
reply.error(libc::ENOENT)
|
||||
}
|
||||
} else {
|
||||
reply.error(libc::ENOENT)
|
||||
Err(IoError::from_raw_os_error(libc::ENOENT).into())
|
||||
}
|
||||
}
|
||||
|
||||
fn readdir(
|
||||
fn read_dir_impl(
|
||||
&mut self,
|
||||
_req: &fuser::Request<'_>,
|
||||
ino: u64,
|
||||
_fh: u64,
|
||||
offset: i64,
|
||||
mut reply: fuser::ReplyDirectory,
|
||||
) {
|
||||
if let Some(folder_path) = &self.inode_cache.get_path(ino).cloned() {
|
||||
let iv = std::fs::read(folder_path.join("gocryptfs.diriv")).unwrap();
|
||||
reply: &mut fuser::ReplyDirectory,
|
||||
) -> rustcryptfs_lib::error::Result<()> {
|
||||
let folder_path = &self.inode_cache.get_path(ino).enoent()?.clone();
|
||||
let iv = std::fs::read(folder_path.join("gocryptfs.diriv"))?;
|
||||
|
||||
let dir_decoder = self.fs.filename_decoder().get_cipher_for_dir(&iv);
|
||||
|
||||
|
@ -175,28 +161,35 @@ impl Filesystem for EncryptedFs {
|
|||
let ino_parent = if ino == FUSE_ROOT_ID {
|
||||
FUSE_ROOT_ID
|
||||
} else {
|
||||
let parent = folder_path.parent().expect("Failed to get parent");
|
||||
let parent = folder_path.parent().enoent()?;
|
||||
self.inode_cache
|
||||
.iter()
|
||||
.find_map(|(ino, p)| if p == parent { Some(*ino) } else { None })
|
||||
.expect("Parent inode not found")
|
||||
.enoent()?
|
||||
};
|
||||
|
||||
if !reply.add(ino, 1, FileType::Directory, ".") {
|
||||
if reply.add(ino_parent, 2, FileType::Directory, "..") {
|
||||
reply.ok();
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
} else {
|
||||
reply.ok();
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
for (index, (meta, encrypted_name, name)) in std::fs::read_dir(folder_path)
|
||||
.unwrap()
|
||||
for (index, (meta, encrypted_name, name)) in std::fs::read_dir(folder_path)?
|
||||
.flat_map(|e| e.ok())
|
||||
.flat_map(|dir| extract_name(dir, folder_path, &dir_decoder))
|
||||
.flat_map(|dir| match extract_name(&dir, folder_path, &dir_decoder) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
log::error!(
|
||||
"Failed to extract name of entry {:?} : {}",
|
||||
dir.file_name(),
|
||||
e
|
||||
);
|
||||
None
|
||||
}
|
||||
})
|
||||
.skip(offset as usize)
|
||||
.enumerate()
|
||||
{
|
||||
|
@ -217,13 +210,126 @@ impl Filesystem for EncryptedFs {
|
|||
break;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_impl(
|
||||
&mut self,
|
||||
ino: u64,
|
||||
offset: i64,
|
||||
size: usize,
|
||||
) -> rustcryptfs_lib::error::Result<Vec<u8>> {
|
||||
let file_path = self.get_path(ino).enoent()?;
|
||||
let mut file = File::open(file_path)?;
|
||||
let decoder = self.fs.content_decoder();
|
||||
|
||||
let mut buf = [0u8; 18];
|
||||
let n = file.read(&mut buf)?;
|
||||
let id = if n < 18 { None } else { Some(&buf[2..]) };
|
||||
|
||||
let mut block_index = offset as u64 / 4096;
|
||||
|
||||
let mut buffer = Vec::with_capacity(size);
|
||||
|
||||
let mut rem = size;
|
||||
|
||||
let mut buf = [0u8; 4096 + 32];
|
||||
|
||||
file.seek(SeekFrom::Start(18 + block_index * (4096 + 32)))?;
|
||||
|
||||
{
|
||||
let n = file.read(&mut buf)?;
|
||||
|
||||
let res = decoder.decrypt_block(&buf[..n], block_index, id)?;
|
||||
|
||||
let seek = (offset as u64 - block_index * 4096) as usize;
|
||||
buffer.extend_from_slice(&res[seek..]);
|
||||
|
||||
block_index += 1;
|
||||
|
||||
rem -= res.len() - seek;
|
||||
}
|
||||
|
||||
while rem > 0 {
|
||||
let n = file.read(&mut buf)?;
|
||||
|
||||
if n == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
let res = decoder.decrypt_block(&buf[..n], block_index, id)?;
|
||||
|
||||
let size = res.len().min(rem);
|
||||
|
||||
buffer.extend_from_slice(&res[..size]);
|
||||
|
||||
block_index += 1;
|
||||
|
||||
rem -= size;
|
||||
}
|
||||
Ok(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
impl Filesystem for EncryptedFs {
|
||||
fn access(
|
||||
&mut self,
|
||||
_req: &fuser::Request<'_>,
|
||||
ino: u64,
|
||||
_mask: i32,
|
||||
reply: fuser::ReplyEmpty,
|
||||
) {
|
||||
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) {
|
||||
if let Some(path) = self.get_path(ino) {
|
||||
match Self::get_attr(path, ino) {
|
||||
Ok(attr) => reply.attr(&Duration::new(0, 0), &attr),
|
||||
Err(e) => reply.error(e.raw_os_error().unwrap()),
|
||||
}
|
||||
} else {
|
||||
reply.error(libc::ENOENT)
|
||||
}
|
||||
}
|
||||
|
||||
fn lookup(
|
||||
&mut self,
|
||||
_req: &fuser::Request<'_>,
|
||||
parent: u64,
|
||||
name: &std::ffi::OsStr,
|
||||
reply: fuser::ReplyEntry,
|
||||
) {
|
||||
match self.lookup_impl(parent, name) {
|
||||
Ok((ttl, attr, generation)) => reply.entry(&ttl, &attr, generation),
|
||||
Err(e) => {
|
||||
log::error!("lookup : {}", e);
|
||||
reply.error(e.to_raw_code())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn readdir(
|
||||
&mut self,
|
||||
_req: &fuser::Request<'_>,
|
||||
ino: u64,
|
||||
_fh: u64,
|
||||
offset: i64,
|
||||
mut reply: fuser::ReplyDirectory,
|
||||
) {
|
||||
match self.read_dir_impl(ino, offset, &mut reply) {
|
||||
Ok(()) => reply.ok(),
|
||||
Err(e) => {
|
||||
log::error!("readdir : {}", e);
|
||||
reply.error(e.to_raw_code())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read(
|
||||
&mut self,
|
||||
_req: &fuser::Request<'_>,
|
||||
|
@ -235,90 +341,42 @@ impl Filesystem for EncryptedFs {
|
|||
_lock_owner: Option<u64>,
|
||||
reply: fuser::ReplyData,
|
||||
) {
|
||||
if let Some(file_path) = &self.get_path(ino) {
|
||||
let mut file = File::open(file_path).unwrap();
|
||||
let decoder = self.fs.content_decoder();
|
||||
|
||||
let mut buf = [0u8; 18];
|
||||
let n = file.read(&mut buf).unwrap();
|
||||
let id = if n < 18 { None } else { Some(&buf[2..]) };
|
||||
|
||||
let mut block_index = offset as u64 / 4096;
|
||||
|
||||
let mut buffer = Vec::with_capacity(size as usize);
|
||||
|
||||
let mut rem = size as usize;
|
||||
|
||||
let mut buf = [0u8; 4096 + 32];
|
||||
|
||||
file.seek(SeekFrom::Start(18 + block_index * (4096 + 32)))
|
||||
.unwrap();
|
||||
|
||||
{
|
||||
let n = file.read(&mut buf).unwrap();
|
||||
|
||||
let res = decoder.decrypt_block(&buf[..n], block_index, id).unwrap();
|
||||
|
||||
let seek = (offset as u64 - block_index * 4096) as usize;
|
||||
buffer.extend_from_slice(&res[seek..]);
|
||||
|
||||
block_index += 1;
|
||||
|
||||
rem -= res.len() - seek;
|
||||
match self.read_impl(ino, offset, size as usize) {
|
||||
Ok(data) => reply.data(&data),
|
||||
Err(e) => {
|
||||
log::error!("read : {}", e);
|
||||
reply.error(e.to_raw_code())
|
||||
}
|
||||
|
||||
while rem > 0 {
|
||||
let n = file.read(&mut buf).unwrap();
|
||||
|
||||
if n == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
let res = decoder.decrypt_block(&buf[..n], block_index, id).unwrap();
|
||||
|
||||
let size = res.len().min(rem);
|
||||
|
||||
buffer.extend_from_slice(&res[..size]);
|
||||
|
||||
block_index += 1;
|
||||
|
||||
rem -= size;
|
||||
}
|
||||
|
||||
reply.data(&buffer);
|
||||
} else {
|
||||
reply.error(libc::ENOENT)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_name(
|
||||
dir: std::fs::DirEntry,
|
||||
folder_path: &PathBuf,
|
||||
dir: &std::fs::DirEntry,
|
||||
folder_path: &Path,
|
||||
dir_decoder: &rustcryptfs_lib::filename::DirFilenameCipher,
|
||||
) -> Option<(std::fs::Metadata, String, String)> {
|
||||
) -> rustcryptfs_lib::error::Result<Option<(std::fs::Metadata, String, String)>> {
|
||||
let filename = dir.file_name();
|
||||
let filename = filename.to_str().unwrap();
|
||||
let filename = filename.to_string_lossy();
|
||||
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()
|
||||
std::fs::read_to_string(folder_path.join(format!("{}.name", filename)))?;
|
||||
let decrypted_filename = dir_decoder.decode_filename(filename.as_str())?;
|
||||
Ok(Some((dir.metadata()?, filename, decrypted_filename)))
|
||||
} else {
|
||||
None
|
||||
Ok(None)
|
||||
}
|
||||
} else {
|
||||
dir_decoder
|
||||
.decode_filename(filename)
|
||||
.map(|n| (dir.metadata().unwrap(), filename.to_string(), n))
|
||||
.ok()
|
||||
let decrypted_filename = dir_decoder.decode_filename(&*filename)?;
|
||||
Ok(Some((
|
||||
dir.metadata()?,
|
||||
filename.to_string(),
|
||||
decrypted_filename,
|
||||
)))
|
||||
}
|
||||
} else {
|
||||
None
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,3 +14,20 @@ pub enum Error {
|
|||
#[error(transparent)]
|
||||
RustCryptFsFilenameError(#[from] FilenameCipherError),
|
||||
}
|
||||
|
||||
|
||||
pub(crate) trait ErrorExt {
|
||||
fn to_raw_code(&self) -> i32;
|
||||
}
|
||||
|
||||
impl ErrorExt for rustcryptfs_lib::error::Error {
|
||||
fn to_raw_code(&self) -> i32 {
|
||||
match self {
|
||||
rustcryptfs_lib::error::Error::FilenameCipherError(_) => libc::EIO,
|
||||
rustcryptfs_lib::error::Error::ContentCipherError(_) => libc::EIO,
|
||||
rustcryptfs_lib::error::Error::ConfigError(_) => todo!(),
|
||||
rustcryptfs_lib::error::Error::JsonError(_) => todo!(),
|
||||
rustcryptfs_lib::error::Error::IoError(e) => e.raw_os_error().unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -119,6 +119,7 @@ fn decrypt_file(c: &DecryptCommand) -> anyhow::Result<()> {
|
|||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn mount(mount: &MountCommand) -> anyhow::Result<()> {
|
||||
use anyhow::Context;
|
||||
use rustcryptfs_linux::EncryptedFs;
|
||||
|
||||
let password = if let Some(password) = &mount.password {
|
||||
|
@ -129,7 +130,8 @@ fn mount(mount: &MountCommand) -> anyhow::Result<()> {
|
|||
|
||||
let fs = EncryptedFs::new(&mount.path, &password)?;
|
||||
|
||||
fs.mount(&mount.mountpoint);
|
||||
fs.mount(&mount.mountpoint)
|
||||
.context("Failed to run fuse fs")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue