|
@@ -8,7 +8,7 @@ mod private {
|
|
|
Context, DirEntry as FuseDirEntry, Entry, FileSystem, FsOptions, OpenOptions,
|
|
|
},
|
|
|
};
|
|
|
- use log::{debug, error};
|
|
|
+ use log::{debug, error, warn};
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
use std::{
|
|
|
collections::hash_map::{self, HashMap},
|
|
@@ -17,15 +17,14 @@ mod private {
|
|
|
io::{self, SeekFrom, Write},
|
|
|
path::{Path, PathBuf},
|
|
|
sync::{
|
|
|
- atomic::{AtomicU64, Ordering},
|
|
|
- RwLock,
|
|
|
+ atomic::{AtomicU64, Ordering}, RwLock,
|
|
|
},
|
|
|
time::Duration,
|
|
|
};
|
|
|
|
|
|
use crate::{
|
|
|
crypto::Creds, Block, BlockMeta, BlockOpenOptions, BlockPath, BoxInIoErr, DirEntry,
|
|
|
- Directory, Epoch, Error, Result,
|
|
|
+ Directory, Epoch, Error, Result, ToStringInIoErr,
|
|
|
};
|
|
|
|
|
|
type Inode = u64;
|
|
@@ -178,12 +177,78 @@ mod private {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ enum HandleValue {
|
|
|
+ File {
|
|
|
+ block: RwLock<Box<dyn Block>>,
|
|
|
+ },
|
|
|
+ Directory {
|
|
|
+ dir: Directory,
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl HandleValue {
|
|
|
+ fn new_file(block: Box<dyn Block>) -> HandleValue {
|
|
|
+ HandleValue::File {
|
|
|
+ block: RwLock::new(block),
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fn new_dir(dir: Directory) -> HandleValue {
|
|
|
+ HandleValue::Directory { dir }
|
|
|
+ }
|
|
|
+
|
|
|
+ fn not_file_err<T>() -> io::Result<T> {
|
|
|
+ Err(io::Error::new(io::ErrorKind::Other, "handle is not for a file"))
|
|
|
+ }
|
|
|
+
|
|
|
+ fn block_mut(&mut self) -> io::Result<&mut Box<dyn Block>> {
|
|
|
+ match self {
|
|
|
+ Self::File { block, .. } => block
|
|
|
+ .get_mut()
|
|
|
+ .map_err(|err| io::Error::new(io::ErrorKind::Other, err.to_string())),
|
|
|
+ _ => Self::not_file_err(),
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fn access_block<T, F: FnOnce(&Box<dyn Block>) -> io::Result<T>>(
|
|
|
+ &self,
|
|
|
+ cb: F,
|
|
|
+ ) -> io::Result<T> {
|
|
|
+ match self {
|
|
|
+ Self::File { block, .. } => {
|
|
|
+ let guard = block.read().err_to_string()?;
|
|
|
+ cb(&guard)
|
|
|
+ }
|
|
|
+ _ => Self::not_file_err(),
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fn access_block_mut<T, F: FnOnce(&mut Box<dyn Block>) -> io::Result<T>>(
|
|
|
+ &self,
|
|
|
+ cb: F,
|
|
|
+ ) -> io::Result<T> {
|
|
|
+ match self {
|
|
|
+ Self::File { block, .. } => {
|
|
|
+ let mut guard = block.write().err_to_string()?;
|
|
|
+ cb(&mut guard)
|
|
|
+ }
|
|
|
+ _ => Self::not_file_err(),
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fn directory(&self) -> io::Result<&Directory> {
|
|
|
+ match self {
|
|
|
+ Self::Directory { dir, .. } => Ok(dir),
|
|
|
+ _ => Err(io::Error::new(io::ErrorKind::Other, "handle is not for a directory"))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
struct InodeTableValue {
|
|
|
- blocks: HashMap<Handle, Box<dyn Block>>,
|
|
|
- dirs: HashMap<Handle, Directory>,
|
|
|
+ handle_values: HashMap<Handle, HandleValue>,
|
|
|
next_handle: Handle,
|
|
|
- lookup_count: AtomicU64,
|
|
|
unclaimed_handles: Vec<Handle>,
|
|
|
+ lookup_count: u64,
|
|
|
}
|
|
|
|
|
|
impl InodeTableValue {
|
|
@@ -193,22 +258,36 @@ mod private {
|
|
|
|
|
|
fn new(block: Box<dyn Block>) -> InodeTableValue {
|
|
|
const FIRST_HANDLE: Handle = 1;
|
|
|
- let mut blocks = HashMap::with_capacity(1);
|
|
|
- blocks.insert(FIRST_HANDLE, block);
|
|
|
+ let mut handles = HashMap::with_capacity(1);
|
|
|
+ handles.insert(FIRST_HANDLE, HandleValue::new_file(block));
|
|
|
Self {
|
|
|
- blocks,
|
|
|
- dirs: HashMap::new(),
|
|
|
+ handle_values: handles,
|
|
|
next_handle: FIRST_HANDLE + 1,
|
|
|
- lookup_count: AtomicU64::new(1),
|
|
|
+ lookup_count: 1,
|
|
|
unclaimed_handles: vec![FIRST_HANDLE],
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- fn block(&mut self, handle: Handle) -> io::Result<&mut Box<dyn Block>> {
|
|
|
- Ok(self
|
|
|
- .blocks
|
|
|
+ fn invalid_handle_err(handle: Handle) -> Error {
|
|
|
+ Error::custom(format!("invalid handle {handle}"))
|
|
|
+ }
|
|
|
+
|
|
|
+ fn block_mut(&mut self, handle: Handle) -> io::Result<&mut Box<dyn Block>> {
|
|
|
+ self.handle_values
|
|
|
.get_mut(&handle)
|
|
|
- .ok_or_else(|| Error::custom(format!("invalid handle {handle}")))?)
|
|
|
+ .ok_or_else(|| Self::invalid_handle_err(handle))?
|
|
|
+ .block_mut()
|
|
|
+ }
|
|
|
+
|
|
|
+ fn access_block_mut<T, F: FnOnce(&mut Box<dyn Block>) -> io::Result<T>>(
|
|
|
+ &self,
|
|
|
+ handle: Handle,
|
|
|
+ cb: F,
|
|
|
+ ) -> io::Result<T> {
|
|
|
+ self.handle_values
|
|
|
+ .get(&handle)
|
|
|
+ .ok_or_else(|| Self::invalid_handle_err(handle))?
|
|
|
+ .access_block_mut(cb)
|
|
|
}
|
|
|
|
|
|
fn try_borrow_block<T, F: FnOnce(&mut Box<dyn Block>) -> io::Result<T>>(
|
|
@@ -219,28 +298,32 @@ mod private {
|
|
|
.unclaimed_handles
|
|
|
.last()
|
|
|
.ok_or_else(|| Error::custom("no handles available"))?;
|
|
|
- let block = self.blocks.get_mut(handle).ok_or_else(|| {
|
|
|
- let err = io::Error::new(
|
|
|
- io::ErrorKind::Other,
|
|
|
+ let handle_value = self.handle_values.get_mut(handle).ok_or_else(|| {
|
|
|
+ let err = Error::custom(
|
|
|
"logic error: can't find block associated with handle",
|
|
|
);
|
|
|
error!("{err}");
|
|
|
err
|
|
|
})?;
|
|
|
+ let block = handle_value.block_mut()?;
|
|
|
cb(block)
|
|
|
}
|
|
|
|
|
|
fn insert(&mut self, block: Box<dyn Block>) {
|
|
|
let handle = self.next_handle;
|
|
|
self.next_handle += 1;
|
|
|
- self.blocks.insert(handle, block);
|
|
|
+ self.handle_values
|
|
|
+ .insert(handle, HandleValue::new_file(block));
|
|
|
self.unclaimed_handles.push(handle);
|
|
|
}
|
|
|
|
|
|
- fn insert_then_get_mut(&mut self, block: Box<dyn Block>) -> &mut Box<dyn Block> {
|
|
|
+ fn insert_then_get_mut(
|
|
|
+ &mut self,
|
|
|
+ block: Box<dyn Block>,
|
|
|
+ ) -> &mut Box<dyn Block> {
|
|
|
self.insert(block);
|
|
|
let handle = self.unclaimed_handles.last().unwrap();
|
|
|
- self.blocks.get_mut(handle).unwrap()
|
|
|
+ self.block_mut(*handle).unwrap()
|
|
|
}
|
|
|
|
|
|
fn take_handle(&mut self) -> Option<Handle> {
|
|
@@ -251,21 +334,25 @@ mod private {
|
|
|
self.unclaimed_handles.push(handle);
|
|
|
while self.unclaimed_handles.len() > Self::UNCLAIMED_HANDLE_LIMIT {
|
|
|
let handle = self.unclaimed_handles.pop().unwrap();
|
|
|
- self.blocks.remove(&handle);
|
|
|
+ self.handle_values.remove(&handle);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- fn incr_lookup_count(&self) -> u64 {
|
|
|
- self.lookup_count.fetch_add(1, Ordering::SeqCst)
|
|
|
+ fn incr_lookup_count(&mut self) -> u64 {
|
|
|
+ let prev = self.lookup_count;
|
|
|
+ self.lookup_count += 1;
|
|
|
+ prev
|
|
|
}
|
|
|
|
|
|
- fn decr_lookup_count(&self, count: u64) -> u64 {
|
|
|
- self.lookup_count.fetch_sub(count, Ordering::SeqCst)
|
|
|
+ fn decr_lookup_count(&mut self, count: u64) -> u64 {
|
|
|
+ let prev = self.lookup_count;
|
|
|
+ self.lookup_count -= count;
|
|
|
+ prev
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- type InodeTable = HashMap<Inode, InodeTableValue>;
|
|
|
- type InodeTableEntry<'a> = hash_map::Entry<'a, Inode, InodeTableValue>;
|
|
|
+ type InodeTable = HashMap<Inode, RwLock<InodeTableValue>>;
|
|
|
+ type InodeTableEntry<'a> = hash_map::Entry<'a, Inode, RwLock<InodeTableValue>>;
|
|
|
|
|
|
/// Structure for metadata about a blocktree.
|
|
|
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
|
@@ -379,8 +466,8 @@ mod private {
|
|
|
authorizer: A,
|
|
|
) -> Result<Blocktree<C, A>> {
|
|
|
let mut inodes = HashMap::with_capacity(1);
|
|
|
- inodes.insert(SpecInodes::Sb.into(), InodeTableValue::new(sb_block));
|
|
|
- inodes.insert(SpecInodes::RootDir.into(), InodeTableValue::new(root_block));
|
|
|
+ inodes.insert(SpecInodes::Sb.into(), RwLock::new(InodeTableValue::new(sb_block)));
|
|
|
+ inodes.insert(SpecInodes::RootDir.into(), RwLock::new(InodeTableValue::new(root_block)));
|
|
|
Ok(Blocktree {
|
|
|
path: btdir,
|
|
|
inodes: RwLock::new(inodes),
|
|
@@ -462,7 +549,7 @@ mod private {
|
|
|
let mut inodes = self
|
|
|
.inodes
|
|
|
.write()
|
|
|
- .map_err(|err| Error::custom(err.to_string()))?;
|
|
|
+ .err_to_string()?;
|
|
|
let entry = inodes.entry(inode);
|
|
|
cb(entry)
|
|
|
}
|
|
@@ -475,9 +562,9 @@ mod private {
|
|
|
let inodes = self
|
|
|
.inodes
|
|
|
.read()
|
|
|
- .map_err(|err| Error::custom(err.to_string()))?;
|
|
|
- let value = inodes.get(&inode).ok_or_else(|| Error::NotOpen(inode))?;
|
|
|
- cb(value)
|
|
|
+ .err_to_string()?;
|
|
|
+ let value = inodes.get(&inode).ok_or_else(|| Error::NotOpen(inode))?.read().err_to_string()?;
|
|
|
+ cb(&value)
|
|
|
}
|
|
|
|
|
|
fn access_value_mut<T, F: FnOnce(&mut InodeTableValue) -> io::Result<T>>(
|
|
@@ -485,24 +572,21 @@ mod private {
|
|
|
inode: Inode,
|
|
|
cb: F,
|
|
|
) -> io::Result<T> {
|
|
|
- self.access_entry(inode, |mut entry| match &mut entry {
|
|
|
- InodeTableEntry::Vacant(_) => Err(Error::NotOpen(inode).into()),
|
|
|
- InodeTableEntry::Occupied(entry) => cb(entry.get_mut()),
|
|
|
- })
|
|
|
+ let inodes = self
|
|
|
+ .inodes
|
|
|
+ .read()
|
|
|
+ .err_to_string()?;
|
|
|
+ let mut value = inodes.get(&inode).ok_or_else(|| Error::NotOpen(inode))?.write().err_to_string()?;
|
|
|
+ cb(&mut value)
|
|
|
}
|
|
|
|
|
|
- fn access_block<T, F: FnOnce(&mut Box<dyn Block>) -> io::Result<T>>(
|
|
|
+ fn access_block_mut<T, F: FnOnce(&mut Box<dyn Block>) -> io::Result<T>>(
|
|
|
&self,
|
|
|
inode: Inode,
|
|
|
handle: Handle,
|
|
|
cb: F,
|
|
|
) -> io::Result<T> {
|
|
|
- self.access_value_mut(inode, |value| {
|
|
|
- cb(value
|
|
|
- .blocks
|
|
|
- .get_mut(&handle)
|
|
|
- .ok_or(Error::InvalidHandle { handle, inode })?)
|
|
|
- })
|
|
|
+ self.access_value(inode, |value| value.access_block_mut(handle, cb))
|
|
|
}
|
|
|
|
|
|
fn access_meta<T, F: FnOnce(&BlockMeta) -> io::Result<T>>(
|
|
@@ -511,14 +595,14 @@ mod private {
|
|
|
cb: F,
|
|
|
) -> io::Result<T> {
|
|
|
self.access_value(inode, |value| {
|
|
|
- let block = value
|
|
|
- .blocks
|
|
|
+ let handle_value = value
|
|
|
+ .handle_values
|
|
|
.values()
|
|
|
.next()
|
|
|
.ok_or_else(|| Error::NotOpen(inode))?;
|
|
|
// Because we're using any of the meta data structs we need to ensure that any
|
|
|
// modification of meta data is performed on all open blocks.
|
|
|
- cb(block.meta())
|
|
|
+ handle_value.access_block(|block| cb(block.meta()))
|
|
|
})
|
|
|
}
|
|
|
|
|
@@ -529,14 +613,14 @@ mod private {
|
|
|
) -> io::Result<T> {
|
|
|
self.access_value_mut(inode, |value| {
|
|
|
let block = match value.unclaimed_handles.last() {
|
|
|
- Some(handle) => value.blocks.get_mut(handle).unwrap(),
|
|
|
+ Some(handle) => value.handle_values.get_mut(handle).unwrap().block_mut()?,
|
|
|
None => {
|
|
|
- let block = value
|
|
|
- .blocks
|
|
|
+ let block_path = value
|
|
|
+ .handle_values
|
|
|
.values()
|
|
|
.next()
|
|
|
- .ok_or_else(|| Error::NotOpen(inode))?;
|
|
|
- let block_path = block.meta_body().path.clone();
|
|
|
+ .ok_or_else(|| Error::NotOpen(inode))?
|
|
|
+ .access_block(|block| Ok(block.meta_body().path.clone()))?;
|
|
|
let block =
|
|
|
Self::open_block(&self.path, inode, self.creds.clone(), block_path)?;
|
|
|
value.insert_then_get_mut(block)
|
|
@@ -555,7 +639,7 @@ mod private {
|
|
|
let handle = value
|
|
|
.take_handle()
|
|
|
.ok_or_else(|| Error::NoHandlesAvailable(inode))?;
|
|
|
- let block = value.block(handle)?;
|
|
|
+ let block = value.block_mut(handle)?;
|
|
|
let result = cb(handle, block);
|
|
|
if result.is_err() {
|
|
|
value.give_handle(handle);
|
|
@@ -566,7 +650,7 @@ mod private {
|
|
|
|
|
|
fn give_handle(&self, inode: Inode, handle: Handle) -> io::Result<()> {
|
|
|
self.access_value_mut(inode, |value| {
|
|
|
- let block = value.block(handle)?;
|
|
|
+ let block = value.block_mut(handle)?;
|
|
|
// Be kind, rewind.
|
|
|
block.seek(SeekFrom::Start(0))?;
|
|
|
value.give_handle(handle);
|
|
@@ -586,11 +670,11 @@ mod private {
|
|
|
Self::open_block(&self.path, inode, self.creds.clone(), block_path)?;
|
|
|
let mut value = InodeTableValue::new(block);
|
|
|
let result = cb(&mut value);
|
|
|
- entry.insert(value);
|
|
|
+ entry.insert(RwLock::new(value));
|
|
|
result
|
|
|
}
|
|
|
InodeTableEntry::Occupied(mut entry) => {
|
|
|
- let value = entry.get_mut();
|
|
|
+ let value = entry.get_mut().get_mut().err_to_string()?;
|
|
|
if value.unclaimed_handles.is_empty() {
|
|
|
let block =
|
|
|
Self::open_block(&self.path, inode, self.creds.clone(), block_path)?;
|
|
@@ -614,23 +698,20 @@ mod private {
|
|
|
}
|
|
|
|
|
|
fn inode_forget(&self, inode: Inode, count: u64) -> io::Result<()> {
|
|
|
+ let mut inodes = self.inodes.write().err_to_string()?;
|
|
|
let prev = {
|
|
|
- let inodes = self
|
|
|
- .inodes
|
|
|
- .read()
|
|
|
- .map_err(|err| Error::custom(err.to_string()))?;
|
|
|
- let value = match inodes.get(&inode) {
|
|
|
- Some(value) => value,
|
|
|
- None => return Ok(()),
|
|
|
+ let inode_lock = match inodes.get_mut(&inode) {
|
|
|
+ Some(inode_lock) => inode_lock,
|
|
|
+ None => {
|
|
|
+ warn!("an attempt was made to forget non-existent inode {inode}");
|
|
|
+ return Ok(())
|
|
|
+ }
|
|
|
};
|
|
|
+ let mut value = inode_lock.write().err_to_string()?;
|
|
|
value.decr_lookup_count(count)
|
|
|
};
|
|
|
if 1 == prev {
|
|
|
- let mut inodes = self
|
|
|
- .inodes
|
|
|
- .write()
|
|
|
- .map_err(|err| Error::custom(err.to_string()))?;
|
|
|
- inodes.remove(&inode);
|
|
|
+ inodes.remove(&inode);
|
|
|
}
|
|
|
Ok(())
|
|
|
}
|
|
@@ -730,7 +811,9 @@ mod private {
|
|
|
ctx: &Context,
|
|
|
inode: Self::Inode,
|
|
|
flags: u32,
|
|
|
- fuse_flags: u32,
|
|
|
+ // This is the second field of the `fuse_open_in` struct, which is currently unused
|
|
|
+ // by the kernel. (https://man7.org/linux/man-pages/man4/fuse.4.html)
|
|
|
+ _fuse_flags: u32,
|
|
|
) -> io::Result<(Option<Self::Handle>, OpenOptions)> {
|
|
|
debug!("Blocktree::open called on inode {inode}");
|
|
|
let flags: i32 = flags.try_into().box_err()?;
|
|
@@ -740,17 +823,12 @@ mod private {
|
|
|
if flags & libc::O_CLOEXEC != 0 {
|
|
|
return Self::unsupported_flag("O_CLOEXEC");
|
|
|
}
|
|
|
+ if flags & libc::O_DIRECTORY != 0 {
|
|
|
+ return Self::unsupported_flag("O_DIRECTORY");
|
|
|
+ }
|
|
|
let handle = self.take_block_if_ok(inode, |handle, block| {
|
|
|
- let mode = block
|
|
|
- .mut_meta_body()
|
|
|
- .access_secrets(|secrets| Ok(secrets.mode))?;
|
|
|
- let file_type = FileType::from_value(mode)?;
|
|
|
- if (flags & libc::O_DIRECTORY) != 0 && FileType::Dir != file_type {
|
|
|
- return Err(io::Error::from_raw_os_error(libc::ENOTDIR));
|
|
|
- }
|
|
|
let ctx = AuthzContext::new(ctx, block.meta());
|
|
|
- let read_mask = libc::O_RDONLY | libc::O_RDWR;
|
|
|
- if read_mask & flags != 0 {
|
|
|
+ if flags == libc::O_RDONLY || (flags & libc::O_RDWR) != 0 {
|
|
|
self.authorizer.can_read(&ctx)?;
|
|
|
}
|
|
|
let write_mask = libc::O_WRONLY | libc::O_RDWR;
|
|
@@ -774,7 +852,7 @@ mod private {
|
|
|
) -> io::Result<()> {
|
|
|
debug!("Blocktree::release called on inode {inode}");
|
|
|
if flush {
|
|
|
- self.access_block(inode, handle, |block| block.flush())?;
|
|
|
+ self.access_block_mut(inode, handle, |block| block.flush())?;
|
|
|
};
|
|
|
self.give_handle(inode, handle)
|
|
|
}
|
|
@@ -796,7 +874,7 @@ mod private {
|
|
|
})?;
|
|
|
let handle = value.next_handle;
|
|
|
value.next_handle += 1;
|
|
|
- value.dirs.insert(handle, dir);
|
|
|
+ value.handle_values.insert(handle, HandleValue::new_dir(dir));
|
|
|
Ok(handle)
|
|
|
})?;
|
|
|
Ok((Some(handle), OpenOptions::empty()))
|
|
@@ -811,7 +889,7 @@ mod private {
|
|
|
) -> io::Result<()> {
|
|
|
debug!("Blocktree::releasedir called for inode {inode}");
|
|
|
self.access_value_mut(inode, |value| {
|
|
|
- value.dirs.remove(&handle);
|
|
|
+ value.handle_values.remove(&handle);
|
|
|
Ok(())
|
|
|
})
|
|
|
}
|
|
@@ -851,7 +929,7 @@ mod private {
|
|
|
let (handle, attr) =
|
|
|
self.open_then_take_handle(inode, block_path, |handle, value| {
|
|
|
value.incr_lookup_count();
|
|
|
- let block = value.block(handle)?;
|
|
|
+ let block = value.block_mut(handle)?;
|
|
|
Ok(block.mut_meta_body().access_secrets(|secrets| {
|
|
|
secrets.inode = inode;
|
|
|
secrets.mode = args.mode;
|
|
@@ -887,12 +965,17 @@ mod private {
|
|
|
offset: u64,
|
|
|
lock_owner: Option<u64>,
|
|
|
delayed_write: bool,
|
|
|
+ // `flags` and `fuse_flags` are the arguments that were passed to `open` when this
|
|
|
+ // handle was returned.
|
|
|
flags: u32,
|
|
|
- fuse_flags: u32,
|
|
|
+ _fuse_flags: u32,
|
|
|
) -> io::Result<usize> {
|
|
|
debug!("Blocktree::write called called on inode {inode}");
|
|
|
+ if flags as libc::c_int == libc::O_RDONLY {
|
|
|
+ return Err(io::Error::new(io::ErrorKind::PermissionDenied, "file is readonly"));
|
|
|
+ }
|
|
|
let mut size: usize = size.try_into().box_err()?;
|
|
|
- self.access_block(inode, handle, |block| {
|
|
|
+ self.access_block_mut(inode, handle, |block| {
|
|
|
let mut buf = [0u8; crate::SECTOR_SZ_DEFAULT];
|
|
|
let mut written = 0;
|
|
|
while size > 0 {
|
|
@@ -934,7 +1017,7 @@ mod private {
|
|
|
lock_owner: u64,
|
|
|
) -> io::Result<()> {
|
|
|
debug!("Blocktree::flush called for inode {inode}");
|
|
|
- self.access_block(inode, handle, |block| block.flush())
|
|
|
+ self.access_block_mut(inode, handle, |block| block.flush())
|
|
|
}
|
|
|
|
|
|
fn read(
|
|
@@ -950,7 +1033,7 @@ mod private {
|
|
|
) -> io::Result<usize> {
|
|
|
debug!("Blocktree::read called on inode {inode}");
|
|
|
let mut size: usize = size.try_into().box_err()?;
|
|
|
- self.access_block(inode, handle, |block| {
|
|
|
+ self.access_block_mut(inode, handle, |block| {
|
|
|
let mut buf = [0u8; crate::SECTOR_SZ_DEFAULT];
|
|
|
let mut read = 0;
|
|
|
while size > 0 {
|
|
@@ -996,9 +1079,13 @@ mod private {
|
|
|
) -> io::Result<usize>,
|
|
|
) -> io::Result<()> {
|
|
|
debug!("Blocktree::readdir called on inode {inode}");
|
|
|
+ let mut size: usize = size.try_into().box_err()?;
|
|
|
self.access_value(inode, |value| {
|
|
|
- let dir = value.dirs.get(&handle)
|
|
|
- .ok_or(Error::InvalidHandle { handle, inode })?;
|
|
|
+ let dir = value
|
|
|
+ .handle_values
|
|
|
+ .get(&handle)
|
|
|
+ .ok_or(Error::InvalidHandle { handle, inode })?
|
|
|
+ .directory()?;
|
|
|
let mut index: u64 = 0;
|
|
|
for (name, entry) in dir.entries() {
|
|
|
index += 1;
|
|
@@ -1015,7 +1102,10 @@ mod private {
|
|
|
type_: entry.kind() as u32,
|
|
|
name: name.as_bytes(),
|
|
|
};
|
|
|
- add_entry(dir_entry)?;
|
|
|
+ size -= add_entry(dir_entry)?;
|
|
|
+ if size <= 0 {
|
|
|
+ break;
|
|
|
+ }
|
|
|
}
|
|
|
Ok(())
|
|
|
})
|
|
@@ -1050,7 +1140,7 @@ mod private {
|
|
|
) -> io::Result<u64> {
|
|
|
debug!("Blocktree::lseek called for inode {inode}");
|
|
|
let seek_from = SeekFrom::whence_offset(whence, offset)?;
|
|
|
- self.access_block(inode, handle, |block| block.seek(seek_from))
|
|
|
+ self.access_block_mut(inode, handle, |block| block.seek(seek_from))
|
|
|
}
|
|
|
|
|
|
//////////////////////////////////
|
|
@@ -1334,10 +1424,13 @@ mod private {
|
|
|
|
|
|
#[cfg(test)]
|
|
|
mod tests {
|
|
|
- use std::ffi::CString;
|
|
|
use fuse_backend_rs::{
|
|
|
abi::fuse_abi::CreateIn,
|
|
|
- api::filesystem::{Context, FileSystem, FsOptions}
|
|
|
+ api::filesystem::{Context, FileSystem, FsOptions},
|
|
|
+ };
|
|
|
+ use std::{
|
|
|
+ io,
|
|
|
+ ffi::CString
|
|
|
};
|
|
|
use tempdir::TempDir;
|
|
|
|
|
@@ -1535,7 +1628,9 @@ mod tests {
|
|
|
}
|
|
|
|
|
|
fn context(&self) -> Context {
|
|
|
- let (stat, ..) = self.bt.getattr(&Default::default(), SpecInodes::RootDir.into(), None)
|
|
|
+ let (stat, ..) = self
|
|
|
+ .bt
|
|
|
+ .getattr(&Default::default(), SpecInodes::RootDir.into(), None)
|
|
|
.expect("getattr failed");
|
|
|
Context {
|
|
|
uid: stat.st_uid,
|
|
@@ -1552,13 +1647,19 @@ mod tests {
|
|
|
let bt = &case.bt;
|
|
|
let ctx = case.context();
|
|
|
let name = CString::new("README.md").unwrap();
|
|
|
+ let flags = libc::O_RDWR as u32;
|
|
|
|
|
|
let (entry, handle, ..) = bt
|
|
|
.create(
|
|
|
&ctx,
|
|
|
SpecInodes::RootDir.into(),
|
|
|
name.as_c_str(),
|
|
|
- CreateIn { mode: libc::S_IFREG | 0o644, umask: 0, flags: 0, fuse_flags: 0 },
|
|
|
+ CreateIn {
|
|
|
+ mode: libc::S_IFREG | 0o644,
|
|
|
+ umask: 0,
|
|
|
+ flags,
|
|
|
+ fuse_flags: 0,
|
|
|
+ },
|
|
|
)
|
|
|
.expect("failed to create file");
|
|
|
let inode = entry.inode;
|
|
@@ -1576,27 +1677,17 @@ mod tests {
|
|
|
0,
|
|
|
None,
|
|
|
false,
|
|
|
- 0,
|
|
|
+ flags,
|
|
|
0,
|
|
|
)
|
|
|
.expect("write failed");
|
|
|
assert_eq!(LEN, written);
|
|
|
|
|
|
- bt.lseek(&ctx, inode, handle, 0, 0)
|
|
|
- .expect("lseek failed");
|
|
|
+ bt.lseek(&ctx, inode, handle, 0, 0).expect("lseek failed");
|
|
|
|
|
|
let mut actual = BtCursor::new([0u8; LEN]);
|
|
|
let read = bt
|
|
|
- .read(
|
|
|
- &ctx,
|
|
|
- inode,
|
|
|
- handle,
|
|
|
- &mut actual,
|
|
|
- LEN as u32,
|
|
|
- 0,
|
|
|
- None,
|
|
|
- 0,
|
|
|
- )
|
|
|
+ .read(&ctx, inode, handle, &mut actual, LEN as u32, 0, None, flags)
|
|
|
.expect("failed to read");
|
|
|
assert_eq!(LEN, read);
|
|
|
|
|
@@ -1636,6 +1727,7 @@ mod tests {
|
|
|
let case = BtTestCase::new_empty();
|
|
|
let bt = &case.bt;
|
|
|
let ctx = case.context();
|
|
|
+ let flags = libc::O_RDWR as u32;
|
|
|
|
|
|
{
|
|
|
let (entry, handle, ..) = bt
|
|
@@ -1643,7 +1735,12 @@ mod tests {
|
|
|
&ctx,
|
|
|
SpecInodes::RootDir.into(),
|
|
|
name.as_c_str(),
|
|
|
- CreateIn { mode: libc::S_IFREG | 0o644, umask: 0, flags: 0, fuse_flags: 0 },
|
|
|
+ CreateIn {
|
|
|
+ mode: libc::S_IFREG | 0o644,
|
|
|
+ umask: 0,
|
|
|
+ flags,
|
|
|
+ fuse_flags: 0,
|
|
|
+ },
|
|
|
)
|
|
|
.expect("failed to create file");
|
|
|
let inode = entry.inode;
|
|
@@ -1662,7 +1759,7 @@ mod tests {
|
|
|
0,
|
|
|
None,
|
|
|
false,
|
|
|
- 0,
|
|
|
+ flags,
|
|
|
0,
|
|
|
)
|
|
|
.expect("write failed");
|
|
@@ -1695,10 +1792,63 @@ mod tests {
|
|
|
EXPECTED.len() as u32,
|
|
|
0,
|
|
|
None,
|
|
|
- 0,
|
|
|
+ flags,
|
|
|
)
|
|
|
.expect("read failed");
|
|
|
|
|
|
assert_eq!(EXPECTED, actual.into_inner().as_slice())
|
|
|
}
|
|
|
+
|
|
|
+ /// Tests that an error is returned by the `Blocktree::write` method if the file was opened
|
|
|
+ /// read-only.
|
|
|
+ #[test]
|
|
|
+ fn open_read_only_write_is_error() {
|
|
|
+ let name = CString::new("books.ods").unwrap();
|
|
|
+ let case = BtTestCase::new_empty();
|
|
|
+ let bt = &case.bt;
|
|
|
+ let ctx = case.context();
|
|
|
+
|
|
|
+ let (entry, handle, ..) = bt
|
|
|
+ .create(
|
|
|
+ &ctx,
|
|
|
+ SpecInodes::RootDir.into(),
|
|
|
+ name.as_c_str(),
|
|
|
+ CreateIn {
|
|
|
+ mode: libc::S_IFREG | 0o644,
|
|
|
+ umask: 0,
|
|
|
+ flags: 0,
|
|
|
+ fuse_flags: 0,
|
|
|
+ },
|
|
|
+ )
|
|
|
+ .expect("failed to create file");
|
|
|
+ let inode = entry.inode;
|
|
|
+ let handle = handle.unwrap();
|
|
|
+
|
|
|
+ bt.release(&ctx, inode, 0, handle, false, false, None)
|
|
|
+ .expect("release failed");
|
|
|
+
|
|
|
+ let flags = libc::O_RDONLY as u32;
|
|
|
+ let (handle, ..) = bt
|
|
|
+ .open(&ctx, inode, flags, 0)
|
|
|
+ .expect("open failed");
|
|
|
+ let handle = handle.unwrap();
|
|
|
+
|
|
|
+ const LEN: usize = 32;
|
|
|
+ let mut reader = BtCursor::new([1u8; LEN]);
|
|
|
+ let result = bt.write(
|
|
|
+ &ctx,
|
|
|
+ inode,
|
|
|
+ handle,
|
|
|
+ &mut reader,
|
|
|
+ LEN.try_into().unwrap(),
|
|
|
+ 0,
|
|
|
+ None,
|
|
|
+ false,
|
|
|
+ flags,
|
|
|
+ 0,
|
|
|
+ );
|
|
|
+
|
|
|
+ let err = result.err().unwrap();
|
|
|
+ assert_eq!(io::ErrorKind::PermissionDenied, err.kind());
|
|
|
+ }
|
|
|
}
|