From 610eedfd15785eba2a24e37f2c42303b02a3fa08 Mon Sep 17 00:00:00 2001 From: Asjid Kalam Date: Mon, 18 Sep 2023 00:30:54 -0700 Subject: [PATCH] added src --- Cargo.toml | 12 ++++++ README.md | 19 +++++++++- config.json | 8 ++++ src/main.rs | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 Cargo.toml create mode 100644 config.json create mode 100644 src/main.rs diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..9cd5cdf --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "debouncer" +version = "0.1.0" +description = "cache and restore files from memory" +edition = "2021" +author = "Asjid Kalam <@odinshell>" + +[dependencies] +notify = "6.1.1" +serde = { version = "1", features = ["derive"] } +serde_json = "1.0" +notify-debouncer-full = "0.3.1" diff --git a/README.md b/README.md index 3ea8c54..57cfd5c 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,19 @@ # debouncer -rust filewatcher, cache and restore files from memory +rust filewatcher, cache and restore files from memory. +prevents privileged users in the container to remove/modify restricted files. + + +## usage +```bash +./debouncer config.json +``` + +`config.json` will contain the a structure of **watched_paths** which will be cached and restored, if any modify/remove events are triggered. + + +## build from source +```bash +git clone https://github.com/asjidkalam/debouncer.git +cd debouncer/ +cargo build --release +``` diff --git a/config.json b/config.json new file mode 100644 index 0000000..6cdb4c4 --- /dev/null +++ b/config.json @@ -0,0 +1,8 @@ +// include all the filepaths that you want to be restricted +{ + "watched_paths": [ + "/app/index.js", + "/app/package.json", + "/flag" + ] +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..8fe5a40 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,107 @@ +use std::{ + env, + fs, + path::Path, + collections::HashMap, + time::Duration, +}; +use notify::{ + RecursiveMode, + Watcher, + Result, +}; +use notify_debouncer_full::{ + new_debouncer, + Debouncer, + FileIdMap, +}; +use serde::Deserialize; + +/// debouncer + +//// OBJECTIVES +/// 1. cache the files on memory in startup [DONE] +/// 2. check for events on restricted file [DONE] +/// 3. restore backup files for modify/delete events [DONE] +/// 5. cache files again if changed, from INotifyWatcher [DONE] +/// 4. setup systemd [TODO] + +#[derive(Debug, Deserialize)] +struct Config { + watched_paths: Vec, +} + +fn load_config(filename: &str) -> Result { + let config_file = fs::read_to_string(filename)?; + let config = serde_json::from_str(&config_file).map_err(|e| { + notify::Error::io(std::io::Error::new(std::io::ErrorKind::Other, e.to_string())) + })?; + Ok(config) +} + +fn cache_files(path: &str, deb: &mut Debouncer) { + if let Err(e) = deb.watcher().watch(Path::new(path), RecursiveMode::Recursive) { + eprintln!("Failed to watch path: {}. Error: {:?}", path, e); + } + deb.cache().add_root(Path::new(path), RecursiveMode::Recursive); +} + +fn initialize_cache(path: &str, store: &mut HashMap, debouncer: &mut Debouncer) { + println!("[CACHE]: {}", path); + + if let Ok(org_file) = fs::read_to_string(path) { + store.insert(path.to_string(), org_file); + } else { + eprintln!("Unable to read files to restrict: {}", path); + } + + cache_files(path, debouncer); +} + +fn main() -> Result<()> { + let args: Vec = env::args().collect(); + if args.len() < 2 { + eprintln!("Usage: {} ", args[0]); + return Ok(()); + } + + let config = load_config(&args[1])?; + let mut store: HashMap = HashMap::new(); + let (tx, rx) = std::sync::mpsc::channel(); + let mut debouncer = new_debouncer(Duration::from_secs(1), None, tx)?; + + for path in &config.watched_paths { + initialize_cache(path, &mut store, &mut debouncer); + } + + for result in rx { + match result { + Ok(events) => { + for event in events { + let event_path_result = event.paths.get(0).and_then(|path| path.clone().into_os_string().into_string().ok()); + + if let Some(event_path) = event_path_result { + if let Some(cached_content) = store.get(&event_path) { + if let Err(err) = fs::write(&event_path, cached_content) { + eprintln!("Unable to write file: {:?}", err); + } + } + + println!("[CACHE] File restored! - {}", event_path); + // after the file is restored, cache again. + cache_files(&event_path, &mut debouncer); + } else { + eprintln!("Event path not found or not valid UTF-8."); + } + } + } + Err(errors) => { + for error in errors { + eprintln!("{:?}", error); + } + } + } + } + + Ok(()) +}