Skip to content

Instantly share code, notes, and snippets.

@CamJN

CamJN/main.rs Secret

Created June 12, 2018 14:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CamJN/febccca8003822c4003a9cae8fb22300 to your computer and use it in GitHub Desktop.
Save CamJN/febccca8003822c4003a9cae8fb22300 to your computer and use it in GitHub Desktop.
scan macOS for readable inodes
extern crate users;
extern crate num_cpus;
use std::thread;
use std::fs;
use std::io;
use std::cmp;
use std::str::Lines;
use std::process::Command;
use std::io::{Write, Error, ErrorKind};
use std::os::unix::fs::{MetadataExt,PermissionsExt};
use users::{get_current_gid,get_current_uid};
const S_IRUSR:u32 = 0o00400;
const S_IRGRP:u32 = 0o00040;
const S_IROTH:u32 = 0o00004;
fn get_from_command<C,O>(command: &'static str, arg: &'static str, callback: C) -> io::Result<O>
where C: FnOnce(&str, Lines) -> io::Result<O> {
let output = Command::new(command)
.args(&[arg])
.output()?;
if output.status.success() {
if let Ok(out) = String::from_utf8(output.stdout) {
let mut iter = out.lines();
if let Some(line1) = iter.next() {
callback(line1, iter)
} else {
Err(Error::new(ErrorKind::InvalidData, format!("{} output malformed.",command)))
}
} else {
Err(Error::new(ErrorKind::InvalidData, format!("{} output malformed.",command)))
}
} else {
Err(Error::new(ErrorKind::Other, format!("Calling `{} {}` failed.", command, arg)))
}
}
fn inodes() -> io::Result<usize> {
get_from_command("df","/",|labels,mut iter|{
if let Some(values) = iter.next() {
let pair = labels.split_whitespace()
.zip(values.split_whitespace())
.find(|(e,_)|e==&"iused").unwrap();
let res = pair.1.parse::<usize>().unwrap();
Ok(res)
} else {
Err(Error::new(ErrorKind::InvalidData, "df output malformed."))
}
})
}
fn vol() -> io::Result<String> {
get_from_command("stat", "/.file", |out,_|{
let mut iter = out.split_whitespace();
if let Some(vol) = iter.next() {
Ok(vol.to_string())
} else {
Err(Error::new(ErrorKind::InvalidData, "stat output malformed."))
}
})
}
fn main() -> io::Result<()> {
let vol = vol()?;
// my 4 core i7 can check 76,934 inodes per second, so I don't feel like exploring the whole available space, especially since it's unlikely to be highly populated at the higher end
let max = inodes()? * 10; // takes ~10 minutes
//let max = std::u32::MAX as usize; // true max for hfs+, takes about 12-16 hours
//let max = std::u64::MAX as usize; // true max for apfs, would take about 8 million years
let my_gid = get_current_gid();
let my_uid = get_current_uid();
let cpus = num_cpus::get();
let threads: Vec<thread::JoinHandle<Vec<usize>>> = (1..=cpus).map(|core| {
let my_max = core * (max / cpus);
let my_min = cmp::max(1, (core - 1) * (max / cpus));
let my_vol = vol.clone();
thread::spawn(move || {
(my_min..my_max).filter(|i| {
if let Ok(meta) = fs::metadata(format!("/.vol/{}/{}",my_vol,i)) {
(meta.permissions().mode() & S_IROTH > 0) ||
(meta.gid() == my_gid && (meta.permissions().mode() & S_IRGRP) > 0) ||
(meta.uid() == my_uid && (meta.permissions().mode() & S_IRUSR) > 0)
} else {
false
}
}).collect()
})
}).collect();
let mut res = threads.into_iter()
.filter_map(|child| child.join().ok() )
.flat_map(|x|x.into_iter()).peekable();
if res.peek().is_none() {
Err(Error::new(ErrorKind::NotFound, "Could not find file."))
} else {
let stdout = io::stdout();
let mut lock = stdout.lock();
res.for_each(|i| if let Err(e) = writeln!(lock,"inode {} has matching gid", i) {
eprintln!("{}",e);
});
Ok(())
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment