Merge branch 'node_leak' into 'master'

Don't leak node block on node remove.

See merge request redox-os/redoxfs!81
This commit is contained in:
Jeremy Soller
2024-10-29 12:30:31 +00:00
3 changed files with 87 additions and 39 deletions
+1 -1
View File
@@ -14,7 +14,7 @@ pub fn mount<D, P, T, F>(filesystem: FileSystem<D>, mountpoint: P, mut callback:
where where
D: Disk, D: Disk,
P: AsRef<Path>, P: AsRef<Path>,
F: FnMut(&Path) -> T, F: FnOnce(&Path) -> T,
{ {
let mountpoint = mountpoint.as_ref(); let mountpoint = mountpoint.as_ref();
let socket = Socket::<V2>::create(&format!("{}", mountpoint.display()))?; let socket = Socket::<V2>::create(&format!("{}", mountpoint.display()))?;
+60 -28
View File
@@ -1,49 +1,61 @@
use std::ops::DerefMut;
use std::path::Path; use std::path::Path;
use std::process::Command; use std::process::Command;
use std::{fs, sync, thread, time}; use std::{fs, thread, time};
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::Relaxed;
use crate::{unmount_path, DiskSparse, FileSystem, Node, TreePtr};
use crate::{unmount_path, DiskSparse, FileSystem}; static IMAGE_SEQ: AtomicUsize = AtomicUsize::new(0);
fn with_redoxfs<T, F>(callback: F) -> T fn with_redoxfs<T, F>(callback: F) -> T
where where
T: Send + Sync + 'static, T: Send + Sync + 'static,
F: FnMut(&Path) -> T + Send + Sync + 'static, F: FnOnce(FileSystem<DiskSparse>) -> T + Send + Sync + 'static,
{ {
let disk_path = "image.bin"; let disk_path = format!("image{}.bin", IMAGE_SEQ.fetch_add(1, Relaxed));
let mount_path = "image";
let res = { let res = {
let disk = DiskSparse::create(dbg!(disk_path), 1024 * 1024 * 1024).unwrap(); let disk = DiskSparse::create(dbg!(&disk_path), 1024 * 1024 * 1024).unwrap();
if cfg!(not(target_os = "redox")) {
if !Path::new(mount_path).exists() {
dbg!(fs::create_dir(dbg!(mount_path))).unwrap();
}
}
let ctime = dbg!(time::SystemTime::now().duration_since(time::UNIX_EPOCH)).unwrap(); let ctime = dbg!(time::SystemTime::now().duration_since(time::UNIX_EPOCH)).unwrap();
let fs = FileSystem::create(disk, None, ctime.as_secs(), ctime.subsec_nanos()).unwrap(); let fs = FileSystem::create(disk, None, ctime.as_secs(), ctime.subsec_nanos()).unwrap();
let callback_mutex = sync::Arc::new(sync::Mutex::new(callback)); callback(fs)
};
dbg!(fs::remove_file(dbg!(disk_path))).unwrap();
res
}
fn with_mounted<T, F>(callback: F) -> T
where
T: Send + Sync + 'static,
F: FnOnce(&Path) -> T + Send + Sync + 'static,
{
let mount_path_o = format!("image{}", IMAGE_SEQ.fetch_add(1, Relaxed));
let mount_path = mount_path_o.clone();
let res = with_redoxfs(move |fs| {
if cfg!(not(target_os = "redox")) {
if !Path::new(&mount_path).exists() {
dbg!(fs::create_dir(dbg!(&mount_path))).unwrap();
}
}
let join_handle = crate::mount(fs, dbg!(mount_path), move |real_path| { let join_handle = crate::mount(fs, dbg!(mount_path), move |real_path| {
let callback_mutex = callback_mutex.clone();
let real_path = real_path.to_owned(); let real_path = real_path.to_owned();
thread::spawn(move || { thread::spawn(move || {
let res = { let res = callback(&real_path);
let mut callback_guard = callback_mutex.lock().unwrap(); let real_path = real_path.to_str().unwrap();
let callback = callback_guard.deref_mut();
callback(&real_path)
};
if cfg!(target_os = "redox") { if cfg!(target_os = "redox") {
dbg!(fs::remove_file(dbg!(format!(":{}", mount_path)))).unwrap(); dbg!(fs::remove_file(dbg!(format!(":{}", real_path)))).unwrap();
} else { } else {
if !dbg!(Command::new("sync").status()).unwrap().success() { if !dbg!(Command::new("sync").status()).unwrap().success() {
panic!("sync failed"); panic!("sync failed");
} }
if !unmount_path(mount_path).is_ok() { if !unmount_path(real_path).is_ok() {
panic!("umount failed"); panic!("umount failed");
} }
} }
@@ -54,12 +66,10 @@ where
.unwrap(); .unwrap();
join_handle.join().unwrap() join_handle.join().unwrap()
}; });
dbg!(fs::remove_file(dbg!(disk_path))).unwrap();
if cfg!(not(target_os = "redox")) { if cfg!(not(target_os = "redox")) {
dbg!(fs::remove_dir(dbg!(mount_path))).unwrap(); dbg!(fs::remove_dir(dbg!(mount_path_o))).unwrap();
} }
res res
@@ -67,7 +77,7 @@ where
#[test] #[test]
fn simple() { fn simple() {
with_redoxfs(|path| { with_mounted(|path| {
dbg!(fs::create_dir(&path.join("test"))).unwrap(); dbg!(fs::create_dir(&path.join("test"))).unwrap();
}) })
} }
@@ -78,7 +88,7 @@ fn mmap() {
use syscall; use syscall;
//TODO //TODO
with_redoxfs(|path| { with_mounted(|path| {
use std::slice; use std::slice;
let path = dbg!(path.join("test")); let path = dbg!(path.join("test"));
@@ -129,3 +139,25 @@ fn mmap() {
mmap_inner(false); mmap_inner(false);
}) })
} }
#[test]
fn create_remove_should_not_increase_size() {
with_redoxfs(|mut fs| {
let initially_free = fs.allocator().free();
let tree_ptr = TreePtr::<Node>::root();
let name = "test";
let _ = fs.tx(|tx| {
tx.create_node(
tree_ptr,
name,
Node::MODE_FILE | 0644,
1,
0,
)?;
tx.remove_node(tree_ptr, name, Node::MODE_FILE)
}).unwrap();
assert_eq!(fs.allocator().free(), initially_free);
});
}
+26 -10
View File
@@ -372,10 +372,10 @@ impl<'a, D: Disk> Transaction<'a, D> {
Ok(block.create_ptr()) Ok(block.create_ptr())
} }
pub fn read_tree<T: BlockTrait + DerefMut<Target = [u8]>>( fn read_tree_and_addr<T: BlockTrait + DerefMut<Target = [u8]>>(
&mut self, &mut self,
ptr: TreePtr<T>, ptr: TreePtr<T>,
) -> Result<TreeData<T>> { ) -> Result<(TreeData<T>, BlockAddr)> {
if ptr.is_null() { if ptr.is_null() {
// ID is invalid (should this return None?) // ID is invalid (should this return None?)
#[cfg(feature = "log")] #[cfg(feature = "log")]
@@ -401,7 +401,14 @@ impl<'a, D: Disk> Transaction<'a, D> {
}; };
data.copy_from_slice(raw.data()); data.copy_from_slice(raw.data());
Ok(TreeData::new(ptr.id(), data)) Ok((TreeData::new(ptr.id(), data), raw.addr()))
}
pub fn read_tree<T: BlockTrait + DerefMut<Target = [u8]>>(
&mut self,
ptr: TreePtr<T>,
) -> Result<TreeData<T>> {
Ok(self.read_tree_and_addr(ptr)?.0)
} }
//TODO: improve performance, reduce writes //TODO: improve performance, reduce writes
@@ -666,7 +673,7 @@ impl<'a, D: Disk> Transaction<'a, D> {
if let Some(entry_name) = entry.name() { if let Some(entry_name) = entry.name() {
if entry_name == name { if entry_name == name {
// Read node and test type against requested type // Read node and test type against requested type
let node = self.read_tree(node_ptr)?; let (node, addr) = self.read_tree_and_addr(node_ptr)?;
if node.data().mode() & Node::MODE_TYPE == mode { if node.data().mode() & Node::MODE_TYPE == mode {
if node.data().is_dir() if node.data().is_dir()
&& node.data().size() > 0 && node.data().size() > 0
@@ -677,7 +684,7 @@ impl<'a, D: Disk> Transaction<'a, D> {
} }
// Save node and clear entry // Save node and clear entry
node_opt = Some(node); node_opt = Some((node, addr));
*entry = DirEntry::default(); *entry = DirEntry::default();
break; break;
} else if node.data().is_dir() { } else if node.data().is_dir() {
@@ -691,14 +698,16 @@ impl<'a, D: Disk> Transaction<'a, D> {
} }
} }
if let Some(mut node) = node_opt { if let Some((mut node, addr)) = node_opt {
let links = node.data().links(); let links = node.data().links();
if links > 1 { let remove_node = if links > 1 {
node.data_mut().set_links(links - 1); node.data_mut().set_links(links - 1);
false
} else { } else {
node.data_mut().set_links(0); node.data_mut().set_links(0);
self.truncate_node_inner(&mut node, 0)?; self.truncate_node_inner(&mut node, 0)?;
} true
};
if record_offset == records - 1 && dir.data().is_empty() { if record_offset == records - 1 && dir.data().is_empty() {
let mut remove_record = record_offset; let mut remove_record = record_offset;
@@ -728,8 +737,15 @@ impl<'a, D: Disk> Transaction<'a, D> {
self.sync_node_record_ptr(&mut parent, record_offset, dir_record_ptr)?; self.sync_node_record_ptr(&mut parent, record_offset, dir_record_ptr)?;
} }
// Sync both parent and node at the same time if remove_node {
self.sync_trees(&[parent, node])?; self.sync_tree(parent)?;
unsafe {
self.deallocate(addr);
}
} else {
// Sync both parent and node at the same time
self.sync_trees(&[parent, node])?;
}
return Ok(()); return Ok(());
} }