|
@@ -378,7 +378,6 @@ mod private {
|
|
|
handle_values: HashMap<Handle, HandleValue>,
|
|
|
next_handle: Handle,
|
|
|
lookup_counts: HashMap<Arc<BlockPath>, u64>,
|
|
|
- delete: bool,
|
|
|
}
|
|
|
|
|
|
impl InodeTableValue {
|
|
@@ -393,7 +392,6 @@ mod private {
|
|
|
handle_values: HashMap::new(),
|
|
|
next_handle: 1,
|
|
|
lookup_counts,
|
|
|
- delete: false,
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -973,8 +971,8 @@ mod private {
|
|
|
if 0 == lookup_count {
|
|
|
let entry = Arc::try_unwrap(inodes.remove(&inode).unwrap())
|
|
|
.map_err(|_| bterr!("LOGIC ERROR: entry for inode {inode} was still in use while it was being forgotten"))?;
|
|
|
- let delete = entry.into_inner().delete;
|
|
|
- if delete {
|
|
|
+ let value = entry.into_inner();
|
|
|
+ if value.block().meta_body().secrets()?.nlink == 0 {
|
|
|
self.delete_block_file(inode)?;
|
|
|
}
|
|
|
}
|
|
@@ -1087,6 +1085,10 @@ mod private {
|
|
|
let mut value_guard = table_guard.write(next_inode).await?;
|
|
|
let block = &mut value_guard.block;
|
|
|
block.write_proc_rec(&ProcRec::Valid(proc_rec))?;
|
|
|
+ block.mut_meta_body().access_secrets(|secrets| {
|
|
|
+ secrets.nlink += 1;
|
|
|
+ Ok(())
|
|
|
+ })?;
|
|
|
};
|
|
|
// We must ensure the reference count for the inode is decremented, otherwise the table
|
|
|
// entry will never be freed.
|
|
@@ -1167,6 +1169,67 @@ mod private {
|
|
|
let proc_rec = proc_rec.validate()?;
|
|
|
Ok(proc_rec.authz_attrs)
|
|
|
}
|
|
|
+
|
|
|
+ /// Decrements the link count of the given inode.
|
|
|
+ ///
|
|
|
+ /// If the link count goes to zero, the inode's file in the local filesystem is deleted.
|
|
|
+ /// The `block_path` parameter is the path to the block identified by the inode.
|
|
|
+ /// The `parent_key` is the block key of the inode's parent.
|
|
|
+ async fn decr_link_count<G>(
|
|
|
+ &self,
|
|
|
+ inode: Inode,
|
|
|
+ block_path: BlockPath,
|
|
|
+ parent_key: SymKey,
|
|
|
+ table_guard: &TableGuard<G>,
|
|
|
+ ) -> Result<()>
|
|
|
+ where
|
|
|
+ G: Deref<Target = InodeTable>,
|
|
|
+ {
|
|
|
+ fn decr_nlink(secrets: &mut BlockMetaSecrets) -> Result<u32> {
|
|
|
+ secrets.nlink -= 1;
|
|
|
+ Ok(secrets.nlink)
|
|
|
+ }
|
|
|
+ let delete = match table_guard.write(inode).await {
|
|
|
+ Ok(mut value) => {
|
|
|
+ value
|
|
|
+ .block_mut()
|
|
|
+ .mut_meta_body()
|
|
|
+ .access_secrets(decr_nlink)?;
|
|
|
+ value.block_mut().flush_meta()?;
|
|
|
+ // Since this block was already open, a client is keeping it alive. When they
|
|
|
+ // choose to forget this inode it will be deleted. Thus we return false here.
|
|
|
+ false
|
|
|
+ }
|
|
|
+ Err(err) => {
|
|
|
+ if let Some(Error::NotOpen(_)) = err.downcast_ref::<Error>() {
|
|
|
+ // It may be tempting to drop the table_guard here, but if this were done then
|
|
|
+ // another this block file could be opened concurrently.
|
|
|
+ let mut block = Self::open_block(
|
|
|
+ &self.path,
|
|
|
+ inode,
|
|
|
+ self.creds.clone(),
|
|
|
+ block_path,
|
|
|
+ Some(parent_key),
|
|
|
+ self.sb.inode_hash,
|
|
|
+ &self.sb.inode_key,
|
|
|
+ )?;
|
|
|
+ let nlink = block.mut_meta_body().access_secrets(decr_nlink)?;
|
|
|
+ if nlink > 0 {
|
|
|
+ block.flush_meta()?;
|
|
|
+ false
|
|
|
+ } else {
|
|
|
+ true
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ return Err(err);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+ if delete {
|
|
|
+ self.delete_block_file(inode)?;
|
|
|
+ }
|
|
|
+ Ok(())
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
unsafe impl<A: Sync> Sync for LocalFs<A> {}
|
|
@@ -1565,7 +1628,7 @@ mod private {
|
|
|
new_parent,
|
|
|
name,
|
|
|
} = msg;
|
|
|
- debug!("link: inode {inode}, new_parent {new_parent}, name {name}");
|
|
|
+ debug!("link: inode {inode}, new_parent {new_parent}, name '{name}'");
|
|
|
let authz_attrs = self.authz_attrs(from).await?;
|
|
|
let table_guard = self.table_guard().await;
|
|
|
let mut value_guard = table_guard.write(new_parent).await?;
|
|
@@ -1577,8 +1640,13 @@ mod private {
|
|
|
))?;
|
|
|
|
|
|
let mut dir = parent_block.read_dir()?;
|
|
|
- if dir.contains_entry(name) {
|
|
|
- return Err(io::Error::from_raw_os_error(libc::EEXIST).into());
|
|
|
+ if let Some(old_entry) = dir.entry(name) {
|
|
|
+ let parent_meta = parent_block.meta_body();
|
|
|
+ let mut block_path = parent_meta.path().to_owned();
|
|
|
+ block_path.push_component(name);
|
|
|
+ let parent_key = parent_meta.block_key()?.clone();
|
|
|
+ self.decr_link_count(old_entry.inode(), block_path, parent_key, &table_guard)
|
|
|
+ .await?;
|
|
|
}
|
|
|
|
|
|
let attr = {
|
|
@@ -1608,16 +1676,12 @@ mod private {
|
|
|
|
|
|
type UnlinkFut<'c> = impl 'c + Send + Future<Output = Result<()>>;
|
|
|
fn unlink<'c>(&'c self, from: &'c Arc<BlockPath>, msg: Unlink<'c>) -> Self::UnlinkFut<'c> {
|
|
|
- fn decr_nlink(secrets: &mut BlockMetaSecrets) -> Result<u32> {
|
|
|
- secrets.nlink -= 1;
|
|
|
- Ok(secrets.nlink)
|
|
|
- }
|
|
|
async move {
|
|
|
let Unlink { parent, name } = msg;
|
|
|
debug!("unlink: parent {parent}, name {name}");
|
|
|
let authz_attrs = self.authz_attrs(from).await?;
|
|
|
+ let table_guard = self.table_guard().await;
|
|
|
let (block_path, inode, parent_key) = {
|
|
|
- let table_guard = self.table_guard().await;
|
|
|
let mut value_guard = table_guard.write(parent).await?;
|
|
|
let parent_block = &mut value_guard.block;
|
|
|
|
|
@@ -1642,46 +1706,8 @@ mod private {
|
|
|
(block_path, inode, parent_key)
|
|
|
};
|
|
|
|
|
|
- let table_guard = self.inodes.read().await;
|
|
|
- let delete = if let Some(entry) = table_guard.get(&inode) {
|
|
|
- let mut value = entry.write().await;
|
|
|
- let nlink = value
|
|
|
- .block_mut()
|
|
|
- .mut_meta_body()
|
|
|
- .access_secrets(decr_nlink)?;
|
|
|
- value.delete = 0 == nlink;
|
|
|
- // If the block is about to be deleted then there's no point in flushing its
|
|
|
- // metadata.
|
|
|
- if !value.delete {
|
|
|
- value.block_mut().flush_meta()?;
|
|
|
- }
|
|
|
- // Since this block was already open, a client is keeping it alive. When they
|
|
|
- // choose to forget this inode it will be deleted. Thus we return false here.
|
|
|
- false
|
|
|
- } else {
|
|
|
- // It may be tempting to drop the table_guard here, but if this were done then
|
|
|
- // another this block file could be opened concurrently.
|
|
|
- let mut block = Self::open_block(
|
|
|
- &self.path,
|
|
|
- inode,
|
|
|
- self.creds.clone(),
|
|
|
- block_path,
|
|
|
- Some(parent_key),
|
|
|
- self.sb.inode_hash,
|
|
|
- &self.sb.inode_key,
|
|
|
- )?;
|
|
|
- let nlink = block.mut_meta_body().access_secrets(decr_nlink)?;
|
|
|
- if nlink > 0 {
|
|
|
- block.flush_meta()?;
|
|
|
- false
|
|
|
- } else {
|
|
|
- true
|
|
|
- }
|
|
|
- };
|
|
|
- if delete {
|
|
|
- self.delete_block_file(inode)?;
|
|
|
- }
|
|
|
- Ok(())
|
|
|
+ self.decr_link_count(inode, block_path, parent_key, &table_guard)
|
|
|
+ .await
|
|
|
}
|
|
|
}
|
|
|
|