diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..bfaf1ae --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,25 @@ +name: Tests + +on: + push: + branches: + - main + pull_request: + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v5 + + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y fuse3 libfuse3-dev pkg-config + + - name: Check Formatting + run: cargo fmt --all -- --check + + - run: cargo test \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index cced1a2..4ac0cf5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -108,6 +108,12 @@ dependencies = [ "log", ] +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + [[package]] name = "funty" version = "2.0.0" @@ -238,6 +244,43 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" +dependencies = [ + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + [[package]] name = "regex" version = "1.12.2" @@ -267,6 +310,15 @@ version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + [[package]] name = "rusty-file-system" version = "0.1.0" @@ -275,6 +327,7 @@ dependencies = [ "env_logger", "fuser", "log", + "tempdir", ] [[package]] @@ -320,6 +373,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "tempdir" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" +dependencies = [ + "rand", + "remove_dir_all", +] + [[package]] name = "unicode-ident" version = "1.0.22" diff --git a/Cargo.toml b/Cargo.toml index 8b5000a..31d6784 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,9 @@ env_logger = "0.11" log = "0.4" bitvec = "1.0.1" +[dev-dependencies] +tempdir = "0.3.7" + [[bin]] name = "rusty-file-system" path = "src/main.rs" diff --git a/README.md b/README.md index 7a4d2c3..891980e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,11 @@ # Rusty-File-System- +## To Run +``` +sudo mkdir /tmp/nullfs +RUST_LOG=info cargo run -- /tmp/nullfs +``` + ## System Dependencies - fuse3 - libfuse3-dev diff --git a/src/main.rs b/src/main.rs index 79ef653..c260761 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,39 +1,41 @@ -use fuser::{Filesystem, MountOption, FileType}; -use std::env; use bitvec::prelude::*; - +use fuser::{FileType, Filesystem, MountOption}; +use log::{debug, error, info, log_enabled, Level}; +use std::env; struct NullFS; impl Filesystem for NullFS {} -const BLK_SIZE: usize = 4096; -const FS_SIZE: usize = 512usize * (1usize << 30); -const NUM_DATA_BLKS: usize = FS_SIZE / BLK_SIZE; +// This is the total capacity of the backing storage for the file system +// this includes the space used for the superblock, free object bitmaps, and file data and metadata +const FS_SIZE_BYTES: u64 = 1u64 * (0b1 << 30) as u64; // 1 GB, toy size +const BLK_SIZE_BYTES: u64 = 4096u64; +const NUM_DATA_BLKS: u32 = (FS_SIZE_BYTES / BLK_SIZE_BYTES) as u32; +const FREE_BLK_BMAP_SIZE_BYTES: usize = ((NUM_DATA_BLKS + 7) / 8) as usize; -const DATA_BLK_BITMAP_BYTES: usize = (NUM_DATA_BLKS + 7) / 8; -const NUM_INODES: usize = -const BITMAP_SIZE_BYTES: usize = 4096; -const NUM_DIRECT_PTR: usize = 12; +// Inodes +const MAX_NUM_INODES: u32 = 10; // toy size +const FREE_INODE_BMAP_SIZE_BYTES: usize = ((MAX_NUM_INODES + 7) / 8) as usize; +const NUM_INO_DIRECT_PTR: usize = 12; -// 28 bytes starting at offset 0 -// free inode bitmap can begin at offset 28 and inode table can follow immediately after +// free inode bitmap can begin right after this struct and inode table can follow immediately after struct SuperBlock { ino_count: u32, blk_count: u32, free_blk_count: u32, free_ino_count: u32, super_blk_no: u32, - mtime: u32, - wtime: u32, + mtime: u64, + wtime: u64, } impl Default for SuperBlock { fn default() -> Self { Self { - ino_count: 0, - blk_count: NUM_DATA_BLKS as u32, - free_blk_count: NUM_DATA_BLKS as u32, - free_ino_count: 0, + ino_count: MAX_NUM_INODES, + blk_count: NUM_DATA_BLKS, + free_blk_count: NUM_DATA_BLKS - 3u32, // first three blocks are reserved for FS metadata + free_ino_count: MAX_NUM_INODES - 1, // first inode is reserved for the root super_blk_no: 0, mtime: 0, wtime: 0, @@ -49,91 +51,53 @@ trait FreeObjectBitmap { } } +#[derive(Default)] struct FreeBlockBitmap { - map: BitArray<[u8; DATA_BLK_BITMAP_BYTES], Lsb0>, -} - -impl Default for FreeBlockBitmap { - fn default() -> Self { - Self { map: BitArray::ZERO } - } + map: BitArray<[u8; FREE_BLK_BMAP_SIZE_BYTES], Lsb0>, } -impl FreeBlockBitmap { - fn new() -> Self { - Self { map: BitArray::ZERO } - } -} - -impl FreeObjectBitmap for FreeBlockBitmap { - fn map(&self) -> &BitArray<[u8; DATA_BLK_BITMAP_BYTES], Lsb0> { +impl FreeObjectBitmap for FreeBlockBitmap { + fn map(&self) -> &BitArray<[u8; FREE_BLK_BMAP_SIZE_BYTES], Lsb0> { &self.map } } struct Inode { - ino_id: u64, // inode number - size: u64, // file size - blocks: u64, // num blocks allocated - mtime_secs: i64, // Easier to save to disk than SystemTime. Ignored the atime and ctime for now. - kind: FileType, - perm: u16, - direct_blks: [u32; NUM_DIRECT_PTR], + ino_id: u64, // inode number + size: u64, // file size + blocks: u64, // num blocks allocated + mtime_secs: i64, // Easier to save to disk than SystemTime. Ignored the atime and ctime for now. + kind: FileType, + perm: u16, + direct_blks: [u32; NUM_INO_DIRECT_PTR], indirect_blk: u32, dbl_indirect_blk: u32, - tri_indirect_blk: u32, -} - -struct InodeBitmap { - map: BitArray<[u8; BITMAP_SIZE_BYTES], Lsb0>, -} - -impl Default for InodeBitmap { - fn default() -> Self { - Self { map: BitArray::ZERO } - } + tri_indirect_blk: u32, } -impl InodeBitmap { - fn new() -> Self { - Self { map: BitArray::ZERO } - } +#[derive(Default)] +struct FreeInodeBitmap { + map: BitArray<[u8; FREE_INODE_BMAP_SIZE_BYTES], Lsb0>, } -impl FreeObjectBitmap for InodeBitmap { - fn map(&self) -> &BitArray<[u8; BITMAP_SIZE_BYTES], Lsb0> { +impl FreeObjectBitmap for FreeInodeBitmap { + fn map(&self) -> &BitArray<[u8; FREE_INODE_BMAP_SIZE_BYTES], Lsb0> { &self.map } } +#[derive(Default)] struct FSState { - superblock: SuperBlock, - free_blocks: FreeBlockBitmap, - inode_bitmap: InodeBitmap, + superblk: SuperBlock, + free_blks: FreeBlockBitmap, + inode_bitmap: FreeInodeBitmap, inodes: Vec, - blks: Vec, -} - -impl Default for FSState { - fn default() -> Self { - Self { - // TODO: Fix - superblock: SuperBlock::default(), - free_blocks: FreeBlockBitmap::default(), - inode_bitmap: InodeBitmap::default(), - inodes: Vec::default(), - blks: Vec::default(), - } - } + blks: Vec, } impl FSState { - fn new() -> Self { - Self::default() - } - fn alloc_inode(&mut self) -> Option { - // TODO: + None } } @@ -141,4 +105,4 @@ fn main() { env_logger::init(); let mountpoint = env::args_os().nth(1).unwrap(); fuser::mount2(NullFS, mountpoint, &[MountOption::AutoUnmount]).unwrap(); -} \ No newline at end of file +} diff --git a/tests/mount.rs b/tests/mount.rs new file mode 100644 index 0000000..f51940e --- /dev/null +++ b/tests/mount.rs @@ -0,0 +1,23 @@ +use std::process::{Command, Stdio}; +use std::thread; +use std::time::Duration; +use tempdir::TempDir; + +#[test] +fn mount() { + let tmp_dir = TempDir::new("testdir").unwrap(); + let mountpoint = tmp_dir.path(); + + let mut child = Command::new(env!("CARGO_BIN_EXE_rusty-file-system")) + .arg(mountpoint) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .spawn() + .expect("failed to mount filesystem"); + + thread::sleep(Duration::from_secs(3)); + + child.kill().ok(); + let status = child.wait().unwrap(); + assert!(!status.success(), "filesystem exited unexpectedly"); +}