Skip to main content

linuxutils_disk/
isosize.rs

1use linuxutils_common::man::ManContent;
2
3pub const MAN: ManContent = ManContent::empty();
4
5use clap::Parser;
6use std::{
7    fs::File,
8    io::{Read, Seek, SeekFrom},
9    process::ExitCode,
10};
11
12const PVD_OFFSET: u64 = 0x8000;
13const PVD_TYPE_PRIMARY: u8 = 1;
14const CD001_MAGIC: &[u8; 5] = b"CD001";
15
16#[derive(Parser)]
17#[command(
18    name = "isosize",
19    about = "Output the length of an iso9660 filesystem",
20    override_usage = "isosize [options] <iso9660_image_file> ..."
21)]
22pub struct Args {
23    /// Show sector count and sector size
24    #[arg(short = 'x', long = "sectors")]
25    sectors: bool,
26
27    /// Divisor for the output size (when not using --sectors)
28    #[arg(
29        short = 'd',
30        long = "divisor",
31        value_name = "number",
32        default_value_t = 1
33    )]
34    divisor: u64,
35
36    /// ISO 9660 image files
37    #[arg(required = true)]
38    files: Vec<String>,
39}
40
41fn isosize(path: &str) -> Result<(u32, u16), String> {
42    let mut file = File::open(path)
43        .map_err(|e| format!("isosize: failed to open '{}': {}", path, e))?;
44
45    let mut pvd = [0u8; 132];
46    file.seek(SeekFrom::Start(PVD_OFFSET))
47        .map_err(|e| format!("isosize: failed to seek in '{}': {}", path, e))?;
48    file.read_exact(&mut pvd).map_err(|e| {
49        format!("isosize: failed to read from '{}': {}", path, e)
50    })?;
51
52    if pvd[0] != PVD_TYPE_PRIMARY || &pvd[1..6] != CD001_MAGIC {
53        return Err(format!(
54            "isosize: '{}': might not be an iso9660 filesystem",
55            path
56        ));
57    }
58
59    let block_count = u32::from_le_bytes([pvd[80], pvd[81], pvd[82], pvd[83]]);
60    let block_size = u16::from_le_bytes([pvd[128], pvd[129]]);
61
62    Ok((block_count, block_size))
63}
64
65pub fn run(args: Args) -> ExitCode {
66    if args.divisor == 0 {
67        eprintln!("isosize: invalid divisor: 0");
68        return ExitCode::from(1);
69    }
70
71    let multiple = args.files.len() > 1;
72    let mut failures = 0usize;
73
74    for path in &args.files {
75        match isosize(path) {
76            Ok((block_count, block_size)) => {
77                let prefix = if multiple {
78                    format!("{}\t", path)
79                } else {
80                    String::new()
81                };
82
83                if args.sectors {
84                    println!(
85                        "{}sector count: {}, sector size: {}",
86                        prefix, block_count, block_size
87                    );
88                } else {
89                    let size = block_size as u64 * block_count as u64;
90                    println!("{}{}", prefix, size / args.divisor);
91                }
92            }
93            Err(e) => {
94                eprintln!("{}", e);
95                failures += 1;
96            }
97        }
98    }
99
100    if failures == args.files.len() {
101        ExitCode::from(32)
102    } else if failures > 0 {
103        ExitCode::from(64)
104    } else {
105        ExitCode::SUCCESS
106    }
107}