diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index dd44cbe..303a185 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -10,7 +10,11 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + - name: Install Rust + uses: dtolnay/rust-toolchain@stable - name: Compile run: cargo build - name: Test - run: cargo test \ No newline at end of file + run: cargo test + - name: Install + run: make install diff --git a/.gitignore b/.gitignore index 1e7caa9..2f7896d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ -Cargo.lock target/ diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..dcecec1 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,237 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys", +] + +[[package]] +name = "anyhow" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "clap" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa8876b300ab35ba921adea3dfd70157a46249b33f95c9084ae5709785478946" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0797fb7aeb1406c84efac526901f7ec3ead2124f946b494e72879d4b54704d" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9c751b79415d4e559e3d1fcf128e09e720eb673a06d26cf6f392d37d75b66e0" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c133bc6a41be0d194c306b5506d15e6feeea7b1d6604bd3f8310dfb2ca96486" + +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + +[[package]] +name = "crc" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "3.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "2.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "writeboost-cli" +version = "2.0.0" +dependencies = [ + "anyhow", + "byteorder", + "clap", + "crc", + "thiserror", +] diff --git a/Cargo.toml b/Cargo.toml index 668303b..494696f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,5 @@ [workspace] -resolver = "2" +resolver = "3" members = [ - "wbtools", "writeboost-cli" -] \ No newline at end of file +] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..514d78f --- /dev/null +++ b/Makefile @@ -0,0 +1,17 @@ +CARGO ?= cargo +PREFIX ?= /usr/local +DESTDIR ?= +SUDO ?= sudo + +.PHONY: all install + +all: + $(CARGO) build --release --locked + +install: + @set -eu; \ + stage=$$(mktemp -d); \ + trap 'rm -rf -- "$$stage"' EXIT; \ + trap 'exit 1' HUP INT TERM; \ + $(CARGO) install --locked --path writeboost-cli --bin writeboost-cli --root "$$stage"; \ + $(SUDO) install -D -m 755 -- "$$stage/bin/writeboost-cli" "$(DESTDIR)$(PREFIX)/bin/writeboost-cli" diff --git a/wbtools/Cargo.toml b/wbtools/Cargo.toml deleted file mode 100644 index c3058d8..0000000 --- a/wbtools/Cargo.toml +++ /dev/null @@ -1,38 +0,0 @@ -[package] -name = "wbtools" -version = "1.1.0" -authors = ["Akira Hayakawa "] - -[dependencies] -crc = "3" -byteorder = "1" -clap = { version = "4", features = ["derive"] } -anyhow = "1" - -[[bin]] -name = "wbcheck" -path = "src/wbcheck.rs" - -[[bin]] -name = "wbcreate" -path = "src/wbcreate.rs" - -[[bin]] -name = "wbdump" -path = "src/wbdump.rs" - -[[bin]] -name = "wbmeta" -path = "src/wbmeta.rs" - -[[bin]] -name = "wbremove" -path = "src/wbremove.rs" - -[[bin]] -name = "wbstatus" -path = "src/wbstatus.rs" - -[lib] -name = "lib" -path = "src/lib.rs" diff --git a/wbtools/data/sample.cache.226 b/wbtools/data/sample.cache.226 deleted file mode 100644 index 00e4a58..0000000 Binary files a/wbtools/data/sample.cache.226 and /dev/null differ diff --git a/wbtools/data/sample.status.226 b/wbtools/data/sample.status.226 deleted file mode 100644 index fd41ea7..0000000 --- a/wbtools/data/sample.status.226 +++ /dev/null @@ -1 +0,0 @@ -0 204800 writeboost 2 762 6 73 73 67 391 12 224 0 0 15 9 1 0 15 6567 0 0 1 0 15 0 11 10 writeback_threshold 0 nr_cur_batched_writeback 1 sync_data_interval 0 update_sb_record_interval 0 read_cache_threshold 0 diff --git a/wbtools/data/sample.table.226 b/wbtools/data/sample.table.226 deleted file mode 100644 index 262516c..0000000 --- a/wbtools/data/sample.table.226 +++ /dev/null @@ -1 +0,0 @@ -0 204800 writeboost 251:0 251:3 diff --git a/wbtools/data/sys_dev.0 b/wbtools/data/sys_dev.0 deleted file mode 100644 index cb196e4..0000000 --- a/wbtools/data/sys_dev.0 +++ /dev/null @@ -1,5 +0,0 @@ -MAJOR=252 -MINOR=1 -DEVNAME=vda1 -DEVTYPE=partition -PARTN=1 diff --git a/wbtools/src/lib.rs b/wbtools/src/lib.rs deleted file mode 100644 index de67f83..0000000 --- a/wbtools/src/lib.rs +++ /dev/null @@ -1,239 +0,0 @@ -extern crate byteorder; - -use byteorder::{LittleEndian, ReadBytesExt}; -use std::collections::HashMap; -use std::fs::File; -use std::io::Cursor; -use std::io::Read; -use std::io::Seek; -use std::io::SeekFrom; -use std::process::Command; - -pub static VERSION: &'static str = "1.0.1"; -pub static AUTHOR: &'static str = "Akira Hayakawa "; - -pub struct BlockDevice { - name: String, -} - -impl BlockDevice { - pub fn new(name_: String) -> Self { - BlockDevice { name: name_ } - } - pub fn name(&self) -> String { - self.name.to_owned() - } - pub fn size(&self) -> i64 { - use std::str::FromStr; - let output: Vec = Command::new("blockdev") - .arg("--getsz") - .arg(&self.name()) - .output() - .expect(&format!("Failed to get the size of {}", self.name())) - .stdout; - let output = String::from_utf8(output) - .expect("Invalid utf8 output") - .to_string(); - let output = output.trim_end(); - i64::from_str(output).expect("Couldn't parse as i64") - } -} - -pub struct CacheDevice { - pub dev: BlockDevice, -} - -impl CacheDevice { - pub fn new(name: String) -> Self { - CacheDevice { - dev: BlockDevice::new(name), - } - } - fn nr_segments(&self) -> i32 { - ((self.dev.size() - (1 << 11)) / (1 << 10)) as i32 - } - pub fn calc_segment_start(&self, id: i32) -> i32 { - let idx = (id - 1) % self.nr_segments(); - (1 << 11) + (idx * (1 << 10)) - } -} - -pub struct SegmentHeader { - pub id: u64, - pub checksum: u32, - pub length: u8, -} - -impl SegmentHeader { - pub fn from_buf(data: &[u8]) -> SegmentHeader { - let mut rdr = Cursor::new(data); - let id_ = rdr.read_u64::().unwrap(); - let checksum_ = rdr.read_u32::().unwrap(); - let length_ = rdr.read_u8().unwrap(); - SegmentHeader { - id: id_, - checksum: checksum_, - length: length_, - } - } - pub fn uninitialized(&self) -> bool { - self.id == 0 - } -} - -pub struct Metablock { - pub sector: u64, - pub dirty_bits: u8, -} - -pub struct Segment {} - -impl Segment { - pub fn from_buf(buf: &[u8]) -> (SegmentHeader, Vec) { - let seg = SegmentHeader::from_buf(buf); - - let mut metablocks = Vec::new(); - let mut rdr = Cursor::new(buf); - rdr.seek(SeekFrom::Start(512)).unwrap(); - for _ in 0..seg.length { - let sector_ = rdr.read_u64::().unwrap(); - let dirty_bits_ = rdr.read_u8().unwrap(); - let metablock = Metablock { - sector: sector_, - dirty_bits: dirty_bits_, - }; - metablocks.push(metablock); - let padding = 16 - (8 + 1); - rdr.seek(SeekFrom::Current(padding)).unwrap(); - } - (seg, metablocks) - } -} - -pub struct SuperBlockHeader { - pub magic: u32, -} - -impl SuperBlockHeader { - pub fn from_buf(data: &[u8]) -> SuperBlockHeader { - let mut rdr = Cursor::new(data); - let magic_ = rdr.read_u32::().unwrap(); - SuperBlockHeader { magic: magic_ } - } -} - -pub struct SuperBlockRecord { - pub last_writeback_segment_id: u64, -} - -impl SuperBlockRecord { - pub fn from_buf(data: &[u8]) -> SuperBlockRecord { - let mut rdr = Cursor::new(data); - let last_writeback_segment_id_ = rdr.read_u64::().unwrap(); - SuperBlockRecord { - last_writeback_segment_id: last_writeback_segment_id_, - } - } -} - -#[derive(Debug)] -pub struct SysDevTable { - map: HashMap, -} - -impl SysDevTable { - pub fn from_file(path: &str) -> SysDevTable { - let mut f = File::open(path).unwrap(); - let mut s = String::new(); - f.read_to_string(&mut s).unwrap(); - let it = s.lines().map(|line| { - let v: Vec<&str> = line.split("=").collect(); - (v[0].to_string(), v[1].to_string()) - }); - let mut m = HashMap::new(); - for (k, v) in it { - m.insert(k, v); - } - SysDevTable { map: m } - } - pub fn get(&self, name: &str) -> String { - self.map[name].to_string() - } -} - -#[test] -fn test_read_sys_dev_file() { - let t = SysDevTable::from_file("data/sys_dev.0"); - assert_eq!(t.get("DEVNAME"), "vda1"); -} - -pub struct BlockNumber { - value: String, -} - -impl BlockNumber { - pub fn unwrap(&self) -> String { - self.value.to_owned() - } - pub fn sys_dev_table(&self) -> SysDevTable { - let path = format!("/sys/dev/block/{}/uevent", self.value); - SysDevTable::from_file(&path) - } -} - -pub struct DMTable { - pub backing_dev: BlockNumber, - pub cache_dev: BlockNumber, -} - -impl DMTable { - pub fn parse(line: String) -> DMTable { - let line: Vec = line - .split(" ") - .filter(|x| x != &"") - .map(|x| x.to_string()) - .collect(); - DMTable { - backing_dev: BlockNumber { - value: line[3].clone(), - }, - cache_dev: BlockNumber { - value: line[4].clone(), - }, - } - } -} - -#[test] -fn test_dmtable_parse() { - let mut s = String::new(); - let mut f = File::open("data/sample.table.226").unwrap(); - f.read_to_string(&mut s).unwrap(); - let t = DMTable::parse(s.trim().to_string()); - println!("{}", s.clone()); - assert_eq!(t.backing_dev.unwrap(), "251:0"); - assert_eq!(t.cache_dev.unwrap(), "251:3"); -} - -pub struct WBDev { - name: String, -} - -impl WBDev { - pub fn new(name_: String) -> WBDev { - WBDev { name: name_ } - } - pub fn table(&self) -> DMTable { - let output = Command::new("dmsetup") - .arg("table") - .arg(&self.name) - .output() - .expect("Fail to dmsetup table") - .stdout; - let output = String::from_utf8(output) - .expect("Invalid utf8 output") - .to_string(); - let output = output.trim().to_string(); - DMTable::parse(output) - } -} diff --git a/wbtools/src/wbcheck.rs b/wbtools/src/wbcheck.rs deleted file mode 100644 index 6adc41b..0000000 --- a/wbtools/src/wbcheck.rs +++ /dev/null @@ -1,68 +0,0 @@ -extern crate clap; -extern crate crc; - -use std::fs::File; -use std::io::Read; -use std::io::Seek; -use std::io::SeekFrom; - -use crc::{Crc, CRC_32_ISCSI}; -pub const CASTAGNOLI: Crc = Crc::::new(&CRC_32_ISCSI); -fn checksum(data: &[u8]) -> u32 { - CASTAGNOLI.checksum(data) -} - -use clap::Parser; -#[derive(Parser)] -#[command(name = "wbcheck")] -#[command(about = "Check if the segment is broken")] -#[command(author, version)] -struct Args { - #[arg(help = "Path to the cache device")] - cachedev: String, - #[arg(help = "Segment id")] - segid: i32, -} - -#[test] -fn test_checksum() { - let buf = vec![0; 4096 - 512]; - assert_eq!(checksum(&buf), 143703573); -} - -fn main() { - let args = Args::parse(); - - let devname: String = args.cachedev; - let id = args.segid; - let cache_dev = lib::CacheDevice::new(devname.to_owned()); - - let mut f = File::open(&devname).expect(&format!("Device {} not found", &devname)); - - let start_byte: u64 = (cache_dev.calc_segment_start(id) as u64) << 9; - f.seek(SeekFrom::Start(start_byte)).unwrap(); - - let header = { - let mut buf = vec![0; 512]; - f.read(&mut buf).unwrap(); - lib::SegmentHeader::from_buf(&buf) - }; - - if header.uninitialized() { - std::process::exit(0); - } - - let computed = { - let size: usize = (4096 - 512) + ((header.length as usize) << 12); - let mut buf = vec![0; size]; - f.read(&mut buf).unwrap(); - checksum(&buf) - }; - - if computed != header.checksum { - panic!( - "Checksum is broken. computed={}, expected={}", - computed, header.checksum - ); - } -} diff --git a/wbtools/src/wbcreate.rs b/wbtools/src/wbcreate.rs deleted file mode 100644 index 9450071..0000000 --- a/wbtools/src/wbcreate.rs +++ /dev/null @@ -1,108 +0,0 @@ -extern crate clap; - -use std::process::Command; - -use clap::Parser; -#[derive(Parser)] -#[command(name = "wbcreate")] -#[command(about = "Create a writeboost device")] -#[command(author, version)] -#[clap(rename_all = "snake_case")] -struct Args { - #[arg(help = "Name of the writeboost device")] - lvname: String, - #[arg(help = "Path to the backing device")] - backingdev: String, - #[arg(help = "Path to the cache device")] - cachedev: String, - #[arg( - help = "Reformat the cache device. This cleans up all existing cache blocks", - long - )] - reformat: bool, - #[arg(long)] - write_around_mode: bool, - #[arg(long, value_name = "INT")] - writeback_threshold: Option, - #[arg(long, value_name = "INT")] - nr_max_batched_writeback: Option, - #[arg(long, value_name = "INT")] - update_sb_record_interval: Option, - #[arg(long, value_name = "INT")] - sync_data_interval: Option, - #[arg(long, value_name = "INT")] - read_cache_threshold: Option, -} - -fn main() { - let args = Args::parse(); - - let wbname = args.lvname; - let backing_dev = { - let name = args.backingdev; - lib::BlockDevice::new(name) - }; - let cache_dev_name = args.cachedev; - - if args.reformat { - let status = Command::new("dd") - .arg("if=/dev/zero") - .arg(format!("of={}", cache_dev_name)) - .arg("bs=512") - .arg("count=1") - .status() - .expect("Failed to zero out the cache device"); - assert!(status.success()); - } - - let mut optionals: Vec = Vec::new(); - if args.write_around_mode { - optionals.push("write_around_mode".to_string()); - optionals.push("1".to_string()); - } - if let Some(v) = args.writeback_threshold { - optionals.push("writeback_threshold".to_string()); - optionals.push(v.to_string()); - } - if let Some(v) = args.nr_max_batched_writeback { - optionals.push("nr_max_batched_writeback".to_string()); - optionals.push(v.to_string()); - } - if let Some(v) = args.update_sb_record_interval { - optionals.push("update_sb_record_interval".to_string()); - optionals.push(v.to_string()); - } - if let Some(v) = args.sync_data_interval { - optionals.push("sync_data_interval".to_string()); - optionals.push(v.to_string()); - } - if let Some(v) = args.read_cache_threshold { - optionals.push("read_cache_threshold".to_string()); - optionals.push(v.to_string()); - } - - let n = optionals.len(); - - let optionals_table = if n == 0 { - "".to_string() - } else { - format!(" {} {}", n, optionals.join(" ")) - }; - - let table = format!( - "0 {} writeboost {} {}{}", - backing_dev.size(), - backing_dev.name(), - cache_dev_name, - optionals_table - ); - - let status = Command::new("dmsetup") - .arg("create") - .arg(wbname) - .arg("--table") - .arg(table) - .status() - .expect("Failed to execute dmsetup create"); - assert!(status.success()); -} diff --git a/wbtools/src/wbdump.rs b/wbtools/src/wbdump.rs deleted file mode 100644 index ad339ed..0000000 --- a/wbtools/src/wbdump.rs +++ /dev/null @@ -1,49 +0,0 @@ -extern crate clap; - -use std::str::FromStr; - -use clap::Parser; -#[derive(Parser)] -#[command(name = "wbdump")] -#[command(about = "Dump a cache block")] -#[command(author, version)] -struct Args { - #[arg(help = "Path to the cache device")] - cachedev: String, - #[arg(help = "Metablock index")] - mbidx: i32, - #[arg(long, help = "MBIDX is relative to this SEGID (default is 1)")] - #[arg(default_value_t = 1)] - segid: i32, -} - -fn main() { - let args = Args::parse(); - - let mb_idx: i32 = args.mbidx; - let cache_dev = { - let devname = args.cachedev; - lib::CacheDevice::new(devname.to_owned()) - }; - - let mut base_id = args.segid; - - base_id += mb_idx / 127; - let idx_inseg = mb_idx % 127; - let start_byte = (cache_dev.calc_segment_start(base_id) << 9) + ((1 + idx_inseg) << 12); - - use std::process::Command; - let output = Command::new("od") - .arg(format!("-j{}", start_byte)) - .arg("-N4096") - .arg("-Ax") - .arg(&cache_dev.dev.name()) - .output() - .expect("failed to execute od") - .stdout; - let output = String::from_utf8(output) - .expect("invalid utf8 output") - .to_string(); - let output = output.trim(); - println!("{}", output); -} diff --git a/wbtools/src/wbmeta.rs b/wbtools/src/wbmeta.rs deleted file mode 100644 index b3709b9..0000000 --- a/wbtools/src/wbmeta.rs +++ /dev/null @@ -1,74 +0,0 @@ -extern crate clap; - -use std::fs::File; -use std::io::Read; -use std::io::Seek; -use std::io::SeekFrom; - -use clap::Parser; -#[derive(Parser)] -#[command(name = "wbmeta")] -#[command(about = "Dump a segment header")] -#[command(author, version)] -struct Args { - #[arg(help = "Path to the cache device")] - cachedev: String, - #[arg(help = "Segment id. 0 means the superblock")] - segid: i32, -} - -fn main() { - let args = Args::parse(); - - let devname: String = args.cachedev; - let id: i32 = args.segid; - - let cache_dev = lib::CacheDevice::new(devname.to_owned()); - - if id == 0 { - // superblock - let mut buf = [0; 512]; - let mut f = File::open(&devname).unwrap(); - f.read(&mut buf).unwrap(); - let sup_header = lib::SuperBlockHeader::from_buf(&buf); - - let s = if sup_header.magic == 0x57427374 { - "formatted" - } else { - "unformatted" - }; - - println!("[superblock header]"); - println!("magic = {} ({})", sup_header.magic, s); - - f.seek(SeekFrom::Start((1u64 << 20) - 512)).unwrap(); - f.read(&mut buf).unwrap(); - let sup_record = lib::SuperBlockRecord::from_buf(&buf); - - println!("[superblock record]"); - println!( - "last writeback id = {}", - sup_record.last_writeback_segment_id - ); - } else { - // header - let mut buf = [0; 4096]; - let mut f = File::open(&devname).unwrap(); - let start_byte: u64 = (cache_dev.calc_segment_start(id) as u64) << 9; - f.seek(SeekFrom::Start(start_byte)).unwrap(); - f.read(&mut buf).unwrap(); - let (header, metablocks) = lib::Segment::from_buf(&buf); - - println!("[segment header]"); - println!("id = {}", header.id); - println!("checksumx = {}", header.checksum); - println!("length = {}", header.length); - - for (i, metablock) in metablocks.iter().enumerate() { - println!( - "[{}] sector={}, dirty_bits={}", - i, metablock.sector, metablock.dirty_bits - ); - } - } -} diff --git a/wbtools/src/wbremove.rs b/wbtools/src/wbremove.rs deleted file mode 100644 index 07c7eb3..0000000 --- a/wbtools/src/wbremove.rs +++ /dev/null @@ -1,79 +0,0 @@ -extern crate clap; - -use clap::Parser; -use std::process::Command; - -#[derive(Parser)] -#[command(name = "wbremove")] -#[command(about = "Remove a writeboost device")] -#[command(author, version)] -struct Args { - #[arg(help = "Name of the writeboost device")] - lvname: String, - #[arg(long, help = "Don't flush RAM buffer to cache device before removing")] - noflush: bool, - #[arg( - long, - help = "Don't write back dirty caches to the backing device before removing" - )] - nowriteback: bool, -} - -fn main() { - let args = Args::parse(); - - let wbname = args.lvname; - - let do_flush = !args.noflush; - if do_flush { - let status = Command::new("dmsetup") - .arg("suspend") - .arg(&wbname) - .status() - .expect("Failed to flush transient data"); - assert!(status.success()); - - let status = Command::new("dmsetup") - .arg("resume") - .arg(&wbname) - .status() - .expect("Failed to flush transient data"); - assert!(status.success()); - } - - let do_writeback = !args.nowriteback; - if do_writeback { - let status = Command::new("dmsetup") - .arg("message") - .arg(&wbname) - .arg("0") - .arg("drop_caches") - .status() - .expect("Failed to drop caches"); - assert!(status.success()); - } - - let cache_dev_name = lib::WBDev::new(wbname.to_string()) - .table() - .cache_dev - .sys_dev_table() - .get("DEVNAME"); - - let status = Command::new("dmsetup") - .arg("remove") - .arg(&wbname) - .status() - .expect("Failed to execute dmsetup remove"); - assert!(status.success()); - - if do_writeback { - let status = Command::new("dd") - .arg("if=/dev/zero") - .arg(format!("of=/dev/{}", cache_dev_name)) - .arg("bs=512") - .arg("count=1") - .status() - .expect("Failed to zero out the cache device"); - assert!(status.success()); - } -} diff --git a/wbtools/src/wbstatus.rs b/wbtools/src/wbstatus.rs deleted file mode 100644 index 822b1a6..0000000 --- a/wbtools/src/wbstatus.rs +++ /dev/null @@ -1,63 +0,0 @@ -extern crate clap; - -use std::io::{self, Read}; - -fn print_status(s: &str) { - let toks: Vec<&str> = { s.split_whitespace().collect() }; - - println!("cursor pos = {}", toks[3]); - println!("# of cache blocks = {}", toks[4]); - println!("# of segments = {}", toks[5]); - println!("current id = {}", toks[6]); - println!("last flushed id = {}", toks[7]); - println!("last writeback id = {}", toks[8]); - println!("# of dirty cache blocks = {}", toks[9]); - println!("# of partial flushes = {}", toks[26]); - - println!("write? hit? on_buffer? fullsize?"); - for i in 0..16 { - let b = |bit: usize| { - if (i & (1 << bit)) > 0 { - 1 - } else { - 0 - } - }; - let v = toks[10 + i]; - println!( - "{} {} {} {} {}", - b(3), - b(2), - b(1), - b(0), - v - ); - } -} - -#[test] -fn test_print_status() { - let mut buf = String::new(); - let mut f = std::fs::File::open("data/sample.status.226").unwrap(); - f.read_to_string(&mut buf).unwrap(); - print_status(&buf); -} - -use clap::Parser; -#[derive(Parser)] -#[command(name = "wbstatus")] -#[command(about = "Pretty-print the dmsetup status output")] -#[command(override_usage = "dmsetup status wbdev | wbstatus")] -#[command(author, version)] -struct Args; - -fn main() -> anyhow::Result<()> { - let _ = Args::parse(); - - let mut buf = String::new(); - io::stdin().read_to_string(&mut buf)?; - - print_status(&buf); - - Ok(()) -}