|
@@ -4,7 +4,7 @@ use crate::{msg::*, server::FsProvider};
|
|
|
use btlib::{
|
|
|
accessor::Accessor,
|
|
|
bterr,
|
|
|
- crypto::{Creds, Decrypter, Signer, SymKey},
|
|
|
+ crypto::{rand_vec, Creds, Decrypter, HashKind, Signer, SymKey},
|
|
|
error::BtErr,
|
|
|
AuthzAttrs, BlockAccessor, BlockError, BlockMeta, BlockMetaSecrets, BlockOpenOptions,
|
|
|
BlockPath, BlockReader, DirEntry, Directory, Epoch, FileBlock, FlushMeta, IssuedProcRec,
|
|
@@ -35,11 +35,11 @@ use tokio::sync::{
|
|
|
Mutex, MutexGuard, OwnedMutexGuard, OwnedRwLockReadGuard, RwLock, RwLockReadGuard,
|
|
|
RwLockWriteGuard,
|
|
|
};
|
|
|
+use zeroize::ZeroizeOnDrop;
|
|
|
|
|
|
pub use private::{Authorizer, AuthzContext, Error, LocalFs, ModeAuthorizer};
|
|
|
|
|
|
mod private {
|
|
|
- use btlib::crypto::HashKind;
|
|
|
|
|
|
use super::*;
|
|
|
|
|
@@ -579,12 +579,18 @@ mod private {
|
|
|
}
|
|
|
|
|
|
/// Structure for metadata about a blocktree.
|
|
|
- #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
|
|
+ #[derive(Debug, Serialize, Deserialize, ZeroizeOnDrop)]
|
|
|
struct Superblock {
|
|
|
/// The generation number of the cluster this part of the blocktree is stored on.
|
|
|
generation: u64,
|
|
|
/// The next free inode available to the cluster.
|
|
|
- next_inode: u64,
|
|
|
+ #[zeroize(skip)]
|
|
|
+ next_inode: AtomicU64,
|
|
|
+ /// The hash algorithm to use when computing inode paths.
|
|
|
+ #[zeroize(skip)]
|
|
|
+ inode_hash: HashKind,
|
|
|
+ /// The key to use when hashing inodes to file paths.
|
|
|
+ inode_key: Vec<u8>,
|
|
|
}
|
|
|
|
|
|
/// Structure for managing the part of a blocktree which is stored in the local filesystem.
|
|
@@ -593,11 +599,8 @@ mod private {
|
|
|
path: PathBuf,
|
|
|
/// A map from inode numbers to their reference counts.
|
|
|
inodes: Arc<RwLock<InodeTable<C>>>,
|
|
|
- /// The next inode that will be assigned to a new block.
|
|
|
- next_inode: AtomicU64,
|
|
|
- /// The generation number of this filesystem. This is the same for every other server in
|
|
|
- /// the same cluster.
|
|
|
- generation: u64,
|
|
|
+ /// An in-memory copy of the superblock.
|
|
|
+ sb: Superblock,
|
|
|
/// The credentials this blocktree instance will use for all cryptographic operations.
|
|
|
creds: C,
|
|
|
authorizer: A,
|
|
@@ -623,17 +626,13 @@ mod private {
|
|
|
let root_principal = writecap.root_principal();
|
|
|
|
|
|
// Initialize the superblock.
|
|
|
- let mut sb_block = Self::open_block(
|
|
|
- &btdir,
|
|
|
- SpecInodes::Sb.into(),
|
|
|
- creds.clone(),
|
|
|
- root_block_path.clone(),
|
|
|
- None,
|
|
|
- &root_principal,
|
|
|
- )?;
|
|
|
+ let mut sb_block =
|
|
|
+ Self::open_superblock(&btdir, creds.clone(), root_block_path.clone())?;
|
|
|
let sb = Superblock {
|
|
|
generation,
|
|
|
- next_inode: SpecInodes::FirstFree.into(),
|
|
|
+ next_inode: AtomicU64::new(SpecInodes::FirstFree.into()),
|
|
|
+ inode_hash: HashKind::default(),
|
|
|
+ inode_key: rand_vec(HashKind::default().len())?,
|
|
|
};
|
|
|
write_to(&sb, &mut sb_block)?;
|
|
|
sb_block.mut_meta_body().access_secrets(|secrets| {
|
|
@@ -654,7 +653,8 @@ mod private {
|
|
|
creds.clone(),
|
|
|
root_block_path.clone(),
|
|
|
None,
|
|
|
- &root_principal,
|
|
|
+ sb.inode_hash,
|
|
|
+ &sb.inode_key,
|
|
|
)?;
|
|
|
write_to(&Directory::new(), &mut root_block)?;
|
|
|
root_block.mut_meta_body().access_secrets(|secrets| {
|
|
@@ -705,22 +705,17 @@ mod private {
|
|
|
let writecap = creds.writecap().ok_or(BlockError::MissingWritecap)?;
|
|
|
let root_block_path = writecap.root_block_path();
|
|
|
let root_principal = writecap.root_principal();
|
|
|
- let mut sb_block = Self::open_block(
|
|
|
- &btdir,
|
|
|
- SpecInodes::Sb.into(),
|
|
|
- creds.clone(),
|
|
|
- root_block_path.to_owned(),
|
|
|
- None,
|
|
|
- &root_principal,
|
|
|
- )?;
|
|
|
- let sb = read_from(&mut sb_block)?;
|
|
|
+ let mut sb_block =
|
|
|
+ Self::open_superblock(&btdir, creds.clone(), root_block_path.clone())?;
|
|
|
+ let sb: Superblock = read_from(&mut sb_block)?;
|
|
|
let root_block = Self::open_block(
|
|
|
&btdir,
|
|
|
SpecInodes::RootDir.into(),
|
|
|
creds.clone(),
|
|
|
root_block_path,
|
|
|
None,
|
|
|
- &root_principal,
|
|
|
+ sb.inode_hash,
|
|
|
+ &sb.inode_key,
|
|
|
)?;
|
|
|
Self::new(
|
|
|
btdir,
|
|
@@ -758,45 +753,55 @@ mod private {
|
|
|
Ok(LocalFs {
|
|
|
path: btdir,
|
|
|
inodes: Arc::new(RwLock::new(inodes)),
|
|
|
- next_inode: AtomicU64::new(sb.next_inode),
|
|
|
- generation: sb.generation,
|
|
|
+ sb,
|
|
|
creds,
|
|
|
authorizer,
|
|
|
root_principal,
|
|
|
})
|
|
|
}
|
|
|
|
|
|
- fn hex_encode<'a>(dest: &'a mut [u8], src: &[u8]) -> Result<&'a str> {
|
|
|
- if dest.len() < 2 * src.len() {
|
|
|
- return Err(bterr!(
|
|
|
- "destination slice must be at least twice source slice length"
|
|
|
- ));
|
|
|
- }
|
|
|
- for (index, byte) in src.iter().enumerate() {
|
|
|
- let upper = (byte & 0xF0) >> 4;
|
|
|
- let lower = byte & 0x0F;
|
|
|
- let first = char::from_digit(upper as u32, 16).unwrap();
|
|
|
- let second = char::from_digit(lower as u32, 16).unwrap();
|
|
|
- first.encode_utf8(&mut dest[2 * index..]);
|
|
|
- second.encode_utf8(&mut dest[2 * index + 1..]);
|
|
|
+ fn open_superblock<P: AsRef<Path>>(
|
|
|
+ btdir: P,
|
|
|
+ creds: C,
|
|
|
+ block_path: BlockPath,
|
|
|
+ ) -> Result<Accessor<FileBlock<C>>> {
|
|
|
+ let path = btdir.as_ref().join("super.blk");
|
|
|
+ let file = std::fs::OpenOptions::new()
|
|
|
+ .read(true)
|
|
|
+ .write(true)
|
|
|
+ .create(true)
|
|
|
+ .open(path)?;
|
|
|
+ let block = BlockOpenOptions::new()
|
|
|
+ .with_creds(creds)
|
|
|
+ .with_encrypt(true)
|
|
|
+ .with_inner(file)
|
|
|
+ .with_block_path(block_path)
|
|
|
+ .open()?;
|
|
|
+ Ok(block)
|
|
|
+ }
|
|
|
+
|
|
|
+ fn hex_encode(src: &[u8]) -> Result<String> {
|
|
|
+ use std::fmt::Write;
|
|
|
+ let mut string = String::with_capacity(2 * src.len());
|
|
|
+ for byte in src.iter() {
|
|
|
+ write!(string, "{byte:02x}")?;
|
|
|
}
|
|
|
- std::str::from_utf8(&dest[..2 * src.len()]).map_err(|err| err.into())
|
|
|
+ Ok(string)
|
|
|
}
|
|
|
|
|
|
/// Returns the path to the file storing the given inode's data.
|
|
|
fn block_path<P: AsRef<Path>>(
|
|
|
parent: P,
|
|
|
inode: Inode,
|
|
|
- root_principal: &Principal,
|
|
|
+ inode_hash: HashKind,
|
|
|
+ inode_key: &[u8],
|
|
|
) -> Result<PathBuf> {
|
|
|
- const HASH: HashKind = HashKind::Sha2_256;
|
|
|
- let mut buf = [0u8; HASH.len()];
|
|
|
- HASH.digest(
|
|
|
+ let mut buf = vec![0u8; inode_hash.len()];
|
|
|
+ inode_hash.digest(
|
|
|
&mut buf,
|
|
|
- [root_principal.as_slice(), inode.to_le_bytes().as_slice()].into_iter(),
|
|
|
+ [inode.to_le_bytes().as_slice(), inode_key].into_iter(),
|
|
|
)?;
|
|
|
- let mut hex_buf = [0u8; 2 * HASH.len()];
|
|
|
- let hex_str = Self::hex_encode(&mut hex_buf, &buf)?;
|
|
|
+ let hex_str = Self::hex_encode(&buf)?;
|
|
|
let mut path =
|
|
|
PathBuf::with_capacity(parent.as_ref().as_os_str().len() + 1 + hex_str.len() + 1);
|
|
|
path.push(parent);
|
|
@@ -811,9 +816,10 @@ mod private {
|
|
|
creds: C,
|
|
|
block_path: BlockPath,
|
|
|
parent_key: Option<SymKey>,
|
|
|
- root_principal: &Principal,
|
|
|
+ inode_hash: HashKind,
|
|
|
+ inode_key: &[u8],
|
|
|
) -> Result<Accessor<FileBlock<C>>> {
|
|
|
- let path = Self::block_path(&btdir, inode, root_principal)?;
|
|
|
+ let path = Self::block_path(&btdir, inode, inode_hash, inode_key)?;
|
|
|
let dir = path.ancestors().nth(1).unwrap();
|
|
|
if let Err(err) = std::fs::create_dir(dir) {
|
|
|
match err.kind() {
|
|
@@ -862,7 +868,8 @@ mod private {
|
|
|
self.creds.clone(),
|
|
|
block_path,
|
|
|
parent_key,
|
|
|
- &self.root_principal,
|
|
|
+ self.sb.inode_hash,
|
|
|
+ &self.sb.inode_key,
|
|
|
)?;
|
|
|
let value = Arc::new(RwLock::new(InodeTableValue::new(block, from)));
|
|
|
let mut inodes = self.inodes.write().await;
|
|
@@ -899,7 +906,8 @@ mod private {
|
|
|
}
|
|
|
|
|
|
fn delete_block_file(&self, inode: Inode) -> Result<()> {
|
|
|
- let mut path = Self::block_path(&self.path, inode, &self.root_principal)?;
|
|
|
+ let mut path =
|
|
|
+ Self::block_path(&self.path, inode, self.sb.inode_hash, &self.sb.inode_key)?;
|
|
|
std::fs::remove_file(&path)?;
|
|
|
path.pop();
|
|
|
let mut contents = std::fs::read_dir(&path)?;
|
|
@@ -948,13 +956,9 @@ mod private {
|
|
|
let mut block = &mut value_guard.block;
|
|
|
// We don't need strict ordering because the lock on the inode table value is already
|
|
|
// serializing access.
|
|
|
- let inode = self.next_inode.fetch_add(1, Ordering::Relaxed);
|
|
|
- let sb = Superblock {
|
|
|
- generation: self.generation,
|
|
|
- next_inode: inode + 1,
|
|
|
- };
|
|
|
+ let inode = self.sb.next_inode.fetch_add(1, Ordering::Relaxed);
|
|
|
block.rewind()?;
|
|
|
- write_to(&sb, &mut block)?;
|
|
|
+ write_to(&self.sb, &mut block)?;
|
|
|
block.flush()?;
|
|
|
Ok(inode)
|
|
|
}
|
|
@@ -1000,13 +1004,9 @@ mod private {
|
|
|
// superblock.
|
|
|
let mut value_guard = table_guard.write(SpecInodes::Sb.into()).await?;
|
|
|
let mut block = &mut value_guard.block;
|
|
|
- let next_inode = self.next_inode.fetch_add(1, Ordering::Relaxed);
|
|
|
- let sb = Superblock {
|
|
|
- generation: self.generation,
|
|
|
- next_inode: next_inode + 1,
|
|
|
- };
|
|
|
+ let next_inode = self.sb.next_inode.fetch_add(1, Ordering::Relaxed);
|
|
|
block.rewind()?;
|
|
|
- write_to(&sb, &mut block)?;
|
|
|
+ write_to(&self.sb, &mut block)?;
|
|
|
block
|
|
|
.mut_meta_body()
|
|
|
.add_readcap_for(principal.clone(), &proc_rec.pub_creds.enc)?;
|
|
@@ -1259,7 +1259,7 @@ mod private {
|
|
|
let entry = self.bt_entry(stat);
|
|
|
let reply = LookupReply {
|
|
|
inode,
|
|
|
- generation: self.generation,
|
|
|
+ generation: self.sb.generation,
|
|
|
entry,
|
|
|
};
|
|
|
Ok(reply)
|
|
@@ -1321,7 +1321,7 @@ mod private {
|
|
|
let stat = {
|
|
|
let mut block = value_guard.handle_guard_mut(from, handle).await?;
|
|
|
let stat = block.mut_meta_body().access_secrets(|secrets| {
|
|
|
- secrets.block_id.generation = self.generation;
|
|
|
+ secrets.block_id.generation = self.sb.generation;
|
|
|
secrets.block_id.inode = inode;
|
|
|
secrets.mode = mode & !umask;
|
|
|
if flags.directory() {
|
|
@@ -1637,7 +1637,8 @@ mod private {
|
|
|
self.creds.clone(),
|
|
|
block_path,
|
|
|
Some(parent_key),
|
|
|
- &self.root_principal,
|
|
|
+ self.sb.inode_hash,
|
|
|
+ &self.sb.inode_key,
|
|
|
)?;
|
|
|
let nlink = block.mut_meta_body().access_secrets(decr_nlink)?;
|
|
|
if nlink > 0 {
|