Implement release list and add open node tracking to filesystem
This commit is contained in:
Generated
+25
-14
@@ -150,6 +150,12 @@ version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "cfg_aliases"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||
|
||||
[[package]]
|
||||
name = "cipher"
|
||||
version = "0.4.4"
|
||||
@@ -233,15 +239,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "fuser"
|
||||
version = "0.14.0"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e697f6f62c20b6fad1ba0f84ae909f25971cf16e735273524e3977c94604cf8"
|
||||
checksum = "0bb29a3ae32279fe3e79a958fe01899f5fb23eadccee919cf88e145b54ed9367"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"memchr",
|
||||
"nix",
|
||||
"page_size",
|
||||
"pkg-config",
|
||||
"smallvec",
|
||||
"zerocopy",
|
||||
]
|
||||
@@ -368,6 +374,18 @@ version = "2.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "numtoa"
|
||||
version = "0.2.4"
|
||||
@@ -396,12 +414,6 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "487f2ccd1e17ce8c1bfab3a65c89525af41cfad4c8659021a1e9a2aacd73b89b"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "1.11.1"
|
||||
@@ -816,19 +828,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.35"
|
||||
version = "0.8.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||
checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.35"
|
||||
version = "0.8.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
||||
+1
-1
@@ -82,7 +82,7 @@ std = [
|
||||
]
|
||||
|
||||
[target.'cfg(not(target_os = "redox"))'.dependencies]
|
||||
fuser = { version = "0.14", optional = true }
|
||||
fuser = { version = "0.16", optional = true }
|
||||
|
||||
[target.'cfg(target_os = "redox")'.dependencies]
|
||||
libredox = { version = "0.1.11", features = ["call"], optional = true }
|
||||
|
||||
Generated
+48
-63
@@ -191,15 +191,6 @@ dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e"
|
||||
dependencies = [
|
||||
"powerfmt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_arbitrary"
|
||||
version = "1.3.2"
|
||||
@@ -269,15 +260,15 @@ checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
|
||||
|
||||
[[package]]
|
||||
name = "fuser"
|
||||
version = "0.14.0"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e697f6f62c20b6fad1ba0f84ae909f25971cf16e735273524e3977c94604cf8"
|
||||
checksum = "0bb29a3ae32279fe3e79a958fe01899f5fb23eadccee919cf88e145b54ed9367"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"memchr",
|
||||
"nix",
|
||||
"page_size",
|
||||
"pkg-config",
|
||||
"smallvec",
|
||||
"zerocopy",
|
||||
]
|
||||
@@ -303,6 +294,15 @@ dependencies = [
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humansize"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7"
|
||||
dependencies = [
|
||||
"libm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inout"
|
||||
version = "0.1.4"
|
||||
@@ -369,14 +369,20 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.1.9"
|
||||
name = "libm"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3"
|
||||
checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"redox_syscall 0.7.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -415,12 +421,6 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-conv"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
||||
|
||||
[[package]]
|
||||
name = "numtoa"
|
||||
version = "0.2.4"
|
||||
@@ -444,10 +444,10 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.30"
|
||||
name = "parse-size"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
|
||||
checksum = "487f2ccd1e17ce8c1bfab3a65c89525af41cfad4c8659021a1e9a2aacd73b89b"
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
@@ -464,12 +464,6 @@ dependencies = [
|
||||
"portable-atomic",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "powerfmt"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.95"
|
||||
@@ -502,19 +496,28 @@ checksum = "436d45c2b6a5b159d43da708e62b25be3a4a3d5550d654b72216ade4c4bfd717"
|
||||
|
||||
[[package]]
|
||||
name = "redox-scheme"
|
||||
version = "0.7.0"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4da6a0251965958189cdfd5ebb66f99754db4aa165394300aa2b958525d94b64"
|
||||
checksum = "2cea2668a5932f878a4298a1d7f8950249bbbb77120fb263da252c589152f5ea"
|
||||
dependencies = [
|
||||
"libredox",
|
||||
"redox_syscall",
|
||||
"redox_syscall 0.6.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.17"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77"
|
||||
checksum = "ec96166dafa0886eb81fe1c0a388bece180fbef2135f97c1e2cf8302e74b43b5"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
@@ -527,26 +530,28 @@ checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb"
|
||||
|
||||
[[package]]
|
||||
name = "redoxfs"
|
||||
version = "0.7.1"
|
||||
version = "0.8.4"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"argon2",
|
||||
"base64ct",
|
||||
"bitflags",
|
||||
"endian-num",
|
||||
"env_logger",
|
||||
"fuser",
|
||||
"getrandom",
|
||||
"humansize",
|
||||
"libc",
|
||||
"libredox",
|
||||
"log",
|
||||
"lz4_flex",
|
||||
"parse-size",
|
||||
"range-tree",
|
||||
"redox-path",
|
||||
"redox-scheme",
|
||||
"redox_syscall",
|
||||
"redox_syscall 0.6.0",
|
||||
"seahash",
|
||||
"termion",
|
||||
"time",
|
||||
"uuid",
|
||||
"xts-mode",
|
||||
]
|
||||
@@ -679,25 +684,6 @@ dependencies = [
|
||||
"redox_termios",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"num-conv",
|
||||
"powerfmt",
|
||||
"serde",
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.17.0"
|
||||
@@ -853,19 +839,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.35"
|
||||
version = "0.8.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||
checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.35"
|
||||
version = "0.8.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ cargo-fuzz = true
|
||||
[dependencies]
|
||||
anyhow = "1.0.86"
|
||||
arbitrary = { version = "1.3.2", features = ["derive"] }
|
||||
fuser = { version = "0.14" }
|
||||
fuser = { version = "0.16" }
|
||||
libfuzzer-sys = "0.4"
|
||||
nix = { version = "0.29.0", features = ["fs"] }
|
||||
tempfile = "3.10.1"
|
||||
|
||||
@@ -99,7 +99,7 @@ enum Operation {
|
||||
/// Parameters for mounting the file system and operations to be performed afterwards.
|
||||
#[derive(Arbitrary, Clone, Debug)]
|
||||
struct MountSequence {
|
||||
squash: bool,
|
||||
cleanup: bool,
|
||||
operations: Vec<Operation>,
|
||||
}
|
||||
|
||||
@@ -133,13 +133,13 @@ fn create_redoxfs(disk: DiskSparse, reserved_size: u64) -> bool {
|
||||
}
|
||||
|
||||
/// Mounts an existing Redoxfs, runs the callback and performs the unmount.
|
||||
fn with_redoxfs_mount<F>(temp_path: &Path, disk: DiskSparse, squash: bool, callback: F)
|
||||
fn with_redoxfs_mount<F>(temp_path: &Path, disk: DiskSparse, cleanup: bool, callback: F)
|
||||
where
|
||||
F: FnOnce(&Path) + Send + 'static,
|
||||
{
|
||||
let password = None;
|
||||
let block = None;
|
||||
let mut fs = FileSystem::open(disk, password, block, squash).unwrap();
|
||||
let mut fs = FileSystem::open(disk, password, block, cleanup).unwrap();
|
||||
|
||||
let mount_path = temp_path.join("mount");
|
||||
fs::create_dir_all(&mount_path).unwrap();
|
||||
@@ -319,7 +319,7 @@ fuzz_target!(|test_case: TestCase| -> Corpus {
|
||||
|
||||
let disk = create_disk(temp_dir.path(), test_case.disk_size);
|
||||
let operations = mount_seq.operations.clone();
|
||||
with_redoxfs_mount(temp_dir.path(), disk, mount_seq.squash, move |fs_path| {
|
||||
with_redoxfs_mount(temp_dir.path(), disk, mount_seq.cleanup, move |fs_path| {
|
||||
for operation in operations.iter() {
|
||||
#[cfg(feature = "log")]
|
||||
eprintln!("do operation {operation:?}");
|
||||
|
||||
+20
-6
@@ -1,5 +1,9 @@
|
||||
use aes::Aes128;
|
||||
use alloc::{boxed::Box, collections::VecDeque, vec};
|
||||
use alloc::{
|
||||
boxed::Box,
|
||||
collections::{BTreeMap, VecDeque},
|
||||
vec,
|
||||
};
|
||||
use syscall::error::{Error, Result, EKEYREJECTED, ENOENT, ENOKEY};
|
||||
use xts_mode::{get_tweak_default, Xts128};
|
||||
|
||||
@@ -25,6 +29,7 @@ pub struct FileSystem<D: Disk> {
|
||||
pub(crate) allocator: Allocator,
|
||||
pub(crate) cipher_opt: Option<Xts128<Aes128>>,
|
||||
pub(crate) compress_cache: Box<[u8]>,
|
||||
pub node_usages: BTreeMap<u32, u64>,
|
||||
}
|
||||
|
||||
impl<D: Disk> FileSystem<D> {
|
||||
@@ -33,7 +38,7 @@ impl<D: Disk> FileSystem<D> {
|
||||
mut disk: D,
|
||||
password_opt: Option<&[u8]>,
|
||||
block_opt: Option<u64>,
|
||||
squash: bool,
|
||||
cleanup: bool,
|
||||
) -> Result<Self> {
|
||||
for ring_block in block_opt.map_or(0..65536, |x| x..x + 1) {
|
||||
let mut header = Header::default();
|
||||
@@ -90,12 +95,14 @@ impl<D: Disk> FileSystem<D> {
|
||||
allocator: Allocator::default(),
|
||||
cipher_opt,
|
||||
compress_cache: compress_cache(),
|
||||
node_usages: BTreeMap::new(),
|
||||
};
|
||||
|
||||
unsafe { fs.reset_allocator()? };
|
||||
|
||||
// Squash allocations and sync
|
||||
Transaction::new(&mut fs).commit(squash)?;
|
||||
if cleanup {
|
||||
fs.cleanup()?
|
||||
}
|
||||
|
||||
return Ok(fs);
|
||||
}
|
||||
@@ -171,6 +178,7 @@ impl<D: Disk> FileSystem<D> {
|
||||
allocator: Allocator::default(),
|
||||
cipher_opt,
|
||||
compress_cache: compress_cache(),
|
||||
node_usages: BTreeMap::new(),
|
||||
};
|
||||
|
||||
// Write header generation zero
|
||||
@@ -219,12 +227,18 @@ impl<D: Disk> FileSystem<D> {
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// Make sure everything is synced and squash allocations
|
||||
Transaction::new(&mut fs).commit(true)?;
|
||||
fs.cleanup()?;
|
||||
|
||||
Ok(fs)
|
||||
}
|
||||
|
||||
/// Release unused nodes and squash allocation log, happens on mount (with cleanup) and unmount
|
||||
pub fn cleanup(&mut self) -> Result<()> {
|
||||
let mut tx = Transaction::new(self);
|
||||
tx.release_unused_nodes()?;
|
||||
tx.commit(true)
|
||||
}
|
||||
|
||||
/// start a filesystem transaction, required for making any changes
|
||||
pub fn tx<F: FnOnce(&mut Transaction<D>) -> Result<T>, T>(&mut self, f: F) -> Result<T> {
|
||||
let mut tx = Transaction::new(self);
|
||||
|
||||
+34
-7
@@ -12,11 +12,11 @@ use self::fuser::TimeOrNow;
|
||||
use crate::mount::fuse::TimeOrNow::Now;
|
||||
use crate::mount::fuse::TimeOrNow::SpecificTime;
|
||||
|
||||
use crate::{filesystem, Disk, Node, Transaction, TreeData, TreePtr, BLOCK_SIZE};
|
||||
use crate::{filesystem, Disk, Node, TreeData, TreePtr, BLOCK_SIZE};
|
||||
|
||||
use self::fuser::{
|
||||
FileAttr, FileType, Filesystem, ReplyAttr, ReplyCreate, ReplyData, ReplyDirectory, ReplyEmpty,
|
||||
ReplyEntry, ReplyStatfs, ReplyWrite, Request, Session,
|
||||
ReplyEntry, ReplyOpen, ReplyStatfs, ReplyWrite, Request, Session,
|
||||
};
|
||||
use std::time::Duration;
|
||||
|
||||
@@ -62,8 +62,8 @@ where
|
||||
res
|
||||
};
|
||||
|
||||
// Squash allocations and sync on unmount
|
||||
let _ = Transaction::new(&mut filesystem).commit(true);
|
||||
// Cleanup on unmount
|
||||
filesystem.cleanup()?;
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
@@ -115,7 +115,7 @@ impl<D: Disk> Filesystem for Fuse<'_, D> {
|
||||
}
|
||||
}
|
||||
|
||||
fn getattr(&mut self, _req: &Request, node_id: u64, reply: ReplyAttr) {
|
||||
fn getattr(&mut self, _req: &Request, node_id: u64, _fh: Option<u64>, reply: ReplyAttr) {
|
||||
let node_ptr = TreePtr::<Node>::new(node_id as u32);
|
||||
match self.fs.tx(|tx| tx.read_tree(node_ptr)) {
|
||||
Ok(node) => {
|
||||
@@ -225,6 +225,14 @@ impl<D: Disk> Filesystem for Fuse<'_, D> {
|
||||
reply.attr(&TTL, &attr);
|
||||
}
|
||||
|
||||
fn open(&mut self, _req: &Request<'_>, node_id: u64, _flags: i32, reply: ReplyOpen) {
|
||||
let node_ptr = TreePtr::<Node>::new(node_id as u32);
|
||||
match self.fs.tx(|tx| tx.on_open_node(node_ptr)) {
|
||||
Ok(()) => reply.opened(0, 0),
|
||||
Err(err) => reply.error(err.errno),
|
||||
}
|
||||
}
|
||||
|
||||
fn read(
|
||||
&mut self,
|
||||
_req: &Request,
|
||||
@@ -295,6 +303,23 @@ impl<D: Disk> Filesystem for Fuse<'_, D> {
|
||||
reply.ok();
|
||||
}
|
||||
|
||||
fn release(
|
||||
&mut self,
|
||||
_req: &Request,
|
||||
node_id: u64,
|
||||
_fh: u64,
|
||||
_flags: i32,
|
||||
_lock_owner: Option<u64>,
|
||||
_flush: bool,
|
||||
reply: ReplyEmpty,
|
||||
) {
|
||||
let node_ptr = TreePtr::new(node_id as u32);
|
||||
match self.fs.tx(|tx| tx.on_close_node(node_ptr)) {
|
||||
Ok(()) => reply.ok(),
|
||||
Err(err) => reply.error(err.errno),
|
||||
}
|
||||
}
|
||||
|
||||
fn fsync(&mut self, _req: &Request, _ino: u64, _fh: u64, _datasync: bool, reply: ReplyEmpty) {
|
||||
reply.ok();
|
||||
}
|
||||
@@ -380,13 +405,15 @@ impl<D: Disk> Filesystem for Fuse<'_, D> {
|
||||
let parent_ptr = TreePtr::<Node>::new(parent_id as u32);
|
||||
let ctime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
|
||||
match self.fs.tx(|tx| {
|
||||
tx.create_node(
|
||||
let node = tx.create_node(
|
||||
parent_ptr,
|
||||
name.to_str().unwrap(),
|
||||
Node::MODE_FILE | (mode as u16 & Node::MODE_PERM),
|
||||
ctime.as_secs(),
|
||||
ctime.subsec_nanos(),
|
||||
)
|
||||
)?;
|
||||
tx.on_open_node(node.ptr())?;
|
||||
Ok(node)
|
||||
}) {
|
||||
Ok(node) => {
|
||||
// println!("Create {:?}:{:o}:{:o}", node.1.name(), node.1.mode, mode);
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::io;
|
||||
use std::path::Path;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use crate::{Disk, FileSystem, Transaction, IS_UMT};
|
||||
use crate::{Disk, FileSystem, IS_UMT};
|
||||
|
||||
use self::scheme::FileScheme;
|
||||
|
||||
@@ -42,6 +42,10 @@ where
|
||||
}
|
||||
continue;
|
||||
}
|
||||
RequestKind::OnClose { id } => {
|
||||
scheme.on_close(id);
|
||||
continue;
|
||||
}
|
||||
_ => {
|
||||
// TODO: Redoxfs does not yet support asynchronous file IO. It might still make
|
||||
// sense to implement cancellation for huge buffers, e.g. dd bs=1G
|
||||
@@ -57,8 +61,8 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
// Squash allocations and sync on unmount
|
||||
let _ = Transaction::new(&mut scheme.fs).commit(true);
|
||||
// Cleanup on unmount
|
||||
fs.cleanup()?;
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
@@ -359,6 +359,7 @@ pub struct FileResource {
|
||||
flags: usize,
|
||||
uid: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FileMmapInfo {
|
||||
base: *mut u8,
|
||||
@@ -366,13 +367,9 @@ pub struct FileMmapInfo {
|
||||
pub ranges: RangeTree<Fmap>,
|
||||
pub open_fds: usize,
|
||||
}
|
||||
|
||||
impl FileMmapInfo {
|
||||
pub fn in_use(&self) -> bool {
|
||||
self.open_fds > 0 || !self.ranges.is_empty()
|
||||
}
|
||||
}
|
||||
impl Default for FileMmapInfo {
|
||||
fn default() -> Self {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
base: core::ptr::null_mut(),
|
||||
size: 0,
|
||||
@@ -380,6 +377,18 @@ impl Default for FileMmapInfo {
|
||||
open_fds: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn in_use(&self) -> bool {
|
||||
self.open_fds > 0 || !self.ranges.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for FileMmapInfo {
|
||||
fn drop(&mut self) {
|
||||
if self.in_use() {
|
||||
log::error!("FileMmapInfo dropped while in use");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FileResource {
|
||||
@@ -588,7 +597,14 @@ impl<D: Disk> Resource<D> for FileResource {
|
||||
}
|
||||
//dbg!(&self.fmaps);
|
||||
|
||||
//TODO: allow release of node if not in use anymore
|
||||
// Allow release of node if not in use anymore
|
||||
if !fmap_info.in_use() {
|
||||
// Notify filesystem of close
|
||||
tx.on_close_node(self.node_ptr)?;
|
||||
|
||||
// Remove from fmaps list
|
||||
fmaps.remove(&self.node_ptr.id());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+29
-15
@@ -26,7 +26,7 @@ use redox_path::{
|
||||
|
||||
use crate::{Disk, FileSystem, Node, Transaction, TreeData, TreePtr, BLOCK_SIZE};
|
||||
|
||||
use super::resource::{DirResource, Entry, FileResource, Resource};
|
||||
use super::resource::{DirResource, Entry, FileMmapInfo, FileResource, Resource};
|
||||
|
||||
pub struct FileScheme<'sock, D: Disk> {
|
||||
scheme_name: String,
|
||||
@@ -333,9 +333,15 @@ impl<'sock, D: Disk> FileScheme<'sock, D> {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let node_ptr = resource.node_ptr();
|
||||
self.fmap
|
||||
.entry(resource.node_ptr().id())
|
||||
.or_insert_with(Default::default)
|
||||
.entry(node_ptr.id())
|
||||
.or_insert_with(|| {
|
||||
// Notify filesystem of open
|
||||
self.fs.tx(|tx| tx.on_open_node(node_ptr)).unwrap();
|
||||
FileMmapInfo::new()
|
||||
})
|
||||
.open_fds += 1;
|
||||
|
||||
let id = self.next_id.fetch_add(1, Ordering::Relaxed);
|
||||
@@ -487,13 +493,7 @@ impl<'sock, D: Disk> SchemeSync for FileScheme<'sock, D> {
|
||||
Node::MODE_FILE
|
||||
};
|
||||
|
||||
let in_use = if let Some(file_info) = self.fmap.get(&child.id()) {
|
||||
file_info.in_use()
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
tx.remove_node_in_use(parent.ptr(), &child_name, mode, in_use)
|
||||
tx.remove_node(parent.ptr(), &child_name, mode)
|
||||
} else {
|
||||
Err(Error::new(EISDIR))
|
||||
}
|
||||
@@ -912,7 +912,8 @@ impl<'sock, D: Disk> SchemeSync for FileScheme<'sock, D> {
|
||||
let Some(file) = self.files.remove(&id) else {
|
||||
return;
|
||||
};
|
||||
let Some(file_info) = self.fmap.get_mut(&file.node_ptr().id()) else {
|
||||
let node_ptr = file.node_ptr();
|
||||
let Some(file_info) = self.fmap.get_mut(&node_ptr.id()) else {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -921,8 +922,16 @@ impl<'sock, D: Disk> SchemeSync for FileScheme<'sock, D> {
|
||||
.checked_sub(1)
|
||||
.expect("open_fds not tracked correctly");
|
||||
|
||||
// TODO: If open_fds reaches zero and there are no hardlinks (directory entries) to any
|
||||
// particular inode, remove that inode here.
|
||||
// Check if node no longer in use
|
||||
if !file_info.in_use() {
|
||||
// Notify filesystem of close
|
||||
if let Err(err) = self.fs.tx(|tx| tx.on_close_node(node_ptr)) {
|
||||
log::error!("failed to close node {}: {}", node_ptr.id(), err);
|
||||
}
|
||||
|
||||
// Remove from fmap list
|
||||
self.fmap.remove(&node_ptr.id());
|
||||
}
|
||||
}
|
||||
|
||||
fn on_sendfd(&mut self, sendfd_request: &SendFdRequest) -> Result<usize> {
|
||||
@@ -1016,9 +1025,14 @@ impl<'sock, D: Disk> SchemeSync for FileScheme<'sock, D> {
|
||||
return Err(Error::new(EINVAL));
|
||||
};
|
||||
|
||||
let node_ptr = resource.node_ptr();
|
||||
self.fmap
|
||||
.entry(resource.node_ptr().id())
|
||||
.or_insert_with(Default::default)
|
||||
.entry(node_ptr.id())
|
||||
.or_insert_with(|| {
|
||||
// Notify filesystem of open
|
||||
self.fs.tx(|tx| tx.on_open_node(node_ptr)).unwrap();
|
||||
FileMmapInfo::new()
|
||||
})
|
||||
.open_fds += 1;
|
||||
|
||||
let id = self.next_id.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
+140
-19
@@ -16,8 +16,8 @@ use crate::{
|
||||
htree::{self, HTreeHash, HTreeNode, HTreePtr},
|
||||
AllocEntry, AllocList, Allocator, BlockAddr, BlockData, BlockLevel, BlockMeta, BlockPtr,
|
||||
BlockTrait, DirEntry, DirList, Disk, FileSystem, Header, Node, NodeFlags, NodeLevel,
|
||||
NodeLevelData, RecordRaw, TreeData, TreePtr, ALLOC_GC_THRESHOLD, ALLOC_LIST_ENTRIES,
|
||||
DIR_ENTRY_MAX_LENGTH, HEADER_RING,
|
||||
NodeLevelData, RecordRaw, ReleaseList, TreeData, TreePtr, ALLOC_GC_THRESHOLD,
|
||||
ALLOC_LIST_ENTRIES, DIR_ENTRY_MAX_LENGTH, HEADER_RING,
|
||||
};
|
||||
|
||||
pub(crate) fn level_data(node: &TreeData<Node>) -> Result<&NodeLevelData> {
|
||||
@@ -1086,16 +1086,6 @@ impl<'a, D: Disk> Transaction<'a, D> {
|
||||
parent_ptr: TreePtr<Node>,
|
||||
name: &str,
|
||||
mode: u16,
|
||||
) -> Result<Option<u32>> {
|
||||
self.remove_node_in_use(parent_ptr, name, mode, false)
|
||||
}
|
||||
|
||||
pub fn remove_node_in_use(
|
||||
&mut self,
|
||||
parent_ptr: TreePtr<Node>,
|
||||
name: &str,
|
||||
mode: u16,
|
||||
in_use: bool,
|
||||
) -> Result<Option<u32>> {
|
||||
#[cfg(feature = "log")]
|
||||
log::debug!(
|
||||
@@ -1201,7 +1191,7 @@ impl<'a, D: Disk> Transaction<'a, D> {
|
||||
|
||||
if remove_node {
|
||||
self.sync_tree(parent)?;
|
||||
self.release_node(node.ptr(), in_use)?;
|
||||
self.release_node(node.ptr())?;
|
||||
Ok(Some(node_id))
|
||||
} else {
|
||||
// Sync both parent and node at the same time
|
||||
@@ -1210,12 +1200,143 @@ impl<'a, D: Disk> Transaction<'a, D> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn release_node(&mut self, node_ptr: TreePtr<Node>, in_use: bool) -> Result<()> {
|
||||
if in_use {
|
||||
eprintln!(
|
||||
"LEAKING NODE {}, TODO PUT IT IN RELEASE LIST",
|
||||
node_ptr.id()
|
||||
);
|
||||
/// Notify of node open, for tracking node usage
|
||||
pub fn on_open_node(&mut self, node_ptr: TreePtr<Node>) -> Result<()> {
|
||||
let entry = self.fs.node_usages.entry(node_ptr.id()).or_insert(0);
|
||||
*entry = entry.checked_add(1).ok_or_else(|| {
|
||||
log::error!("node {} usage overflow", node_ptr.id());
|
||||
Error::new(EINVAL)
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Notify of node close, for tracking node usage. Delete node if it is in
|
||||
/// the release list.
|
||||
///
|
||||
/// Returns `Ok(true)` if the node was deleted
|
||||
pub fn on_close_node(&mut self, node_ptr: TreePtr<Node>) -> Result<()> {
|
||||
// Subtract from usages and return if not zero
|
||||
match self.fs.node_usages.get_mut(&node_ptr.id()) {
|
||||
Some(entry) => {
|
||||
*entry = entry.checked_sub(1).ok_or_else(|| {
|
||||
log::error!("node {} usage underflow", node_ptr.id());
|
||||
Error::new(EINVAL)
|
||||
})?;
|
||||
if *entry > 0 {
|
||||
// Node still in use
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
None => {
|
||||
log::error!(
|
||||
"tried to close node {} that is not already open",
|
||||
node_ptr.id()
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
// Remove node usages entry
|
||||
self.fs.node_usages.remove(&node_ptr.id());
|
||||
|
||||
// Check for node in release list and delete it
|
||||
self.release_unused_nodes()
|
||||
}
|
||||
|
||||
/// Check for unused nodes in release list and delete them
|
||||
pub fn release_unused_nodes(&mut self) -> Result<()> {
|
||||
// Read current release lists (going forward through list)
|
||||
let mut releases = VecDeque::<BlockData<ReleaseList>>::new();
|
||||
{
|
||||
let mut release_ptr = self.header.release;
|
||||
while !release_ptr.is_null() {
|
||||
let release = self.read_block(release_ptr)?;
|
||||
release_ptr = release.data().prev;
|
||||
releases.push_front(release);
|
||||
}
|
||||
}
|
||||
|
||||
// Find unused nodes and remove them (going backwards through list)
|
||||
let mut update_prev = None;
|
||||
let mut release_nodes = Vec::new();
|
||||
while let Some(mut release) = releases.pop_back() {
|
||||
if let Some(prev_ptr) = update_prev.take() {
|
||||
release.data_mut().prev = prev_ptr;
|
||||
}
|
||||
|
||||
let mut changed = false;
|
||||
let mut empty = true;
|
||||
for entry in release.data_mut().entries.iter_mut() {
|
||||
if !entry.is_null() {
|
||||
let usages = self.fs.node_usages.get(&entry.id()).copied().unwrap_or(0);
|
||||
if usages == 0 {
|
||||
release_nodes.push(*entry);
|
||||
*entry = TreePtr::default();
|
||||
changed = true;
|
||||
} else {
|
||||
empty = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if empty {
|
||||
// Deallocate this release list block
|
||||
unsafe {
|
||||
self.deallocate(&mut FsCtx, release.addr());
|
||||
}
|
||||
// Skip this block in the list
|
||||
update_prev = Some(release.data().prev);
|
||||
} else if changed {
|
||||
// Update this block
|
||||
update_prev = Some(self.sync_block(&mut FsCtx, release)?);
|
||||
} else {
|
||||
update_prev = None;
|
||||
}
|
||||
}
|
||||
if let Some(prev_ptr) = update_prev.take() {
|
||||
self.header.release = prev_ptr;
|
||||
self.header_changed = true;
|
||||
}
|
||||
|
||||
for node_ptr in release_nodes {
|
||||
self.release_node(node_ptr)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Removes node if usages are zero, adds it to release list if usages are
|
||||
/// greater than zero.
|
||||
pub fn release_node(&mut self, node_ptr: TreePtr<Node>) -> Result<()> {
|
||||
let usages = self
|
||||
.fs
|
||||
.node_usages
|
||||
.get(&node_ptr.id())
|
||||
.copied()
|
||||
.unwrap_or(0);
|
||||
if usages > 0 {
|
||||
let mut release = unsafe { self.read_block_or_empty(self.header.release)? };
|
||||
|
||||
// Try to insert into current release block
|
||||
let mut inserted = false;
|
||||
for entry in release.data_mut().entries.iter_mut() {
|
||||
if entry.is_null() {
|
||||
*entry = node_ptr;
|
||||
inserted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If not inserted, try to add another release block
|
||||
if !inserted {
|
||||
release = BlockData::empty(BlockAddr::null(BlockMeta::default())).unwrap();
|
||||
release.data_mut().prev = self.header.release;
|
||||
release.data_mut().entries[0] = node_ptr;
|
||||
}
|
||||
|
||||
// Update header
|
||||
self.header.release = self.sync_block(&mut FsCtx, release)?;
|
||||
self.header_changed = true;
|
||||
} else {
|
||||
let (mut node, node_addr) = self.read_tree_and_addr(node_ptr)?;
|
||||
self.truncate_node_inner(&mut node, 0)?;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use core::panic::AssertUnwindSafe;
|
||||
use redoxfs::{unmount_path, DirEntry, DiskMemory, DiskSparse, FileSystem, Node, TreePtr};
|
||||
|
||||
use std::io::{Read, Seek, SeekFrom, Write};
|
||||
use std::panic::catch_unwind;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
@@ -614,3 +615,25 @@ fn rename_works() {
|
||||
fs.tx(|tx| tx.rename_node(root, "dir", root, "newdir"))
|
||||
.expect("Renaming 'dir' to 'newdir' should succeed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn temporary_file() {
|
||||
with_mounted(|path| {
|
||||
let file_path = path.join("temp");
|
||||
let mut file = fs::File::create_new(&file_path).expect("failed to create temp file");
|
||||
|
||||
fs::remove_file(&file_path).expect("failed to unlink temp file");
|
||||
|
||||
let write_data = "Test\n";
|
||||
file.write_all(write_data.as_bytes())
|
||||
.expect("failed to write temp file");
|
||||
|
||||
let mut read_data = String::new();
|
||||
file.seek(SeekFrom::Start(0))
|
||||
.expect("failed to seek temp file");
|
||||
file.read_to_string(&mut read_data)
|
||||
.expect("failed to read temp file");
|
||||
|
||||
assert_eq!(read_data, write_data);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user