use crate::{Disk, FileSystem, Node, Transaction, TreePtr, BLOCK_SIZE}; fn tx_progress(tx: &mut Transaction, progress: &mut F) { let size = tx.header.size(); let free = tx.allocator.free() * BLOCK_SIZE; progress(size - free); } //TODO: handle hard links fn clone_at( tx_old: &mut Transaction, parent_ptr_old: TreePtr, tx: &mut Transaction, parent_ptr: TreePtr, buf: &mut [u8], progress: &mut F, ) -> syscall::Result<()> { let mut entries = Vec::new(); tx_old.child_nodes(parent_ptr_old, &mut entries)?; for entry in entries { //TODO: return error instead? let Some(name) = entry.name() else { continue; }; let node_ptr_old = entry.node_ptr(); let node_old = tx_old.read_tree(node_ptr_old)?; //TODO: this slows down the clone, but Redox has issues without this (Linux is fine) if tx.write_cache.len() > 64 { tx.sync(false)?; } let node_ptr = { let mode = node_old.data().mode(); let (ctime, ctime_nsec) = node_old.data().ctime(); let (mtime, mtime_nsec) = node_old.data().mtime(); let mut node = tx.create_node(parent_ptr, &name, mode, ctime, ctime_nsec)?; node.data_mut().set_uid(node_old.data().uid()); node.data_mut().set_gid(node_old.data().gid()); node.data_mut().set_mtime(mtime, mtime_nsec); if !node_old.data().is_dir() { let mut offset = 0; loop { let count = tx_old.read_node_inner(&node_old, offset, buf)?; if count == 0 { break; } tx.write_node_inner(&mut node, &mut offset, &buf[..count])?; } } let node_ptr = node.ptr(); tx.sync_tree(node)?; node_ptr }; tx_progress(tx, progress); if node_old.data().is_dir() { clone_at(tx_old, node_ptr_old, tx, node_ptr, buf, progress)?; } } Ok(()) } pub fn clone( fs_old: &mut FileSystem, fs: &mut FileSystem, mut progress: F, ) -> syscall::Result<()> { fs_old.tx(|tx_old| { let mut tx = Transaction::new(fs); // Clone at root node let mut buf = vec![0; 4 * 1024 * 1024]; clone_at( tx_old, TreePtr::root(), &mut tx, TreePtr::root(), &mut buf, &mut progress, )?; // Commit and squash alloc log tx.commit(true) }) }