1use std::fs::File;
2use std::io::{Read, Seek, SeekFrom, Write};
3
4use crate::cli::wprintln;
5use crate::util::hex::hex_dump;
6use crate::IdbError;
7
8pub struct DumpOptions {
10 pub file: String,
12 pub page: Option<u64>,
14 pub offset: Option<u64>,
16 pub length: Option<usize>,
18 pub raw: bool,
20 pub page_size: Option<u32>,
22 pub keyring: Option<String>,
24 pub decrypt: bool,
26 pub mmap: bool,
28}
29
30pub fn execute(opts: &DumpOptions, writer: &mut dyn Write) -> Result<(), IdbError> {
48 if let Some(abs_offset) = opts.offset {
49 return dump_at_offset(
51 &opts.file,
52 abs_offset,
53 opts.length.unwrap_or(256),
54 opts.raw,
55 writer,
56 );
57 }
58
59 let mut ts = crate::cli::open_tablespace(&opts.file, opts.page_size, opts.mmap)?;
61
62 if opts.decrypt {
63 if let Some(ref keyring_path) = opts.keyring {
64 crate::cli::setup_decryption(&mut ts, keyring_path)?;
65 } else {
66 return Err(IdbError::Argument(
67 "--decrypt requires --keyring <path>".to_string(),
68 ));
69 }
70 }
71
72 let page_size = ts.page_size();
73 let page_num = opts.page.unwrap_or(0);
74 let page_data = ts.read_page(page_num)?;
75
76 let length = opts.length.unwrap_or(page_size as usize);
77 let dump_len = length.min(page_data.len());
78 let base_offset = page_num * page_size as u64;
79
80 if opts.raw {
81 writer
82 .write_all(&page_data[..dump_len])
83 .map_err(|e| IdbError::Io(format!("Cannot write to stdout: {}", e)))?;
84 } else {
85 wprintln!(
86 writer,
87 "Hex dump of {} page {} ({} bytes):",
88 opts.file,
89 page_num,
90 dump_len
91 )?;
92 wprintln!(writer)?;
93 wprintln!(writer, "{}", hex_dump(&page_data[..dump_len], base_offset))?;
94 }
95
96 Ok(())
97}
98
99fn dump_at_offset(
100 file: &str,
101 offset: u64,
102 length: usize,
103 raw: bool,
104 writer: &mut dyn Write,
105) -> Result<(), IdbError> {
106 let mut f =
107 File::open(file).map_err(|e| IdbError::Io(format!("Cannot open {}: {}", file, e)))?;
108
109 let file_size = f
110 .metadata()
111 .map_err(|e| IdbError::Io(format!("Cannot stat {}: {}", file, e)))?
112 .len();
113
114 if offset >= file_size {
115 return Err(IdbError::Argument(format!(
116 "Offset {} is beyond file size {}",
117 offset, file_size
118 )));
119 }
120
121 let available = (file_size - offset) as usize;
122 let read_len = length.min(available);
123
124 f.seek(SeekFrom::Start(offset))
125 .map_err(|e| IdbError::Io(format!("Cannot seek to offset {}: {}", offset, e)))?;
126
127 let mut buf = vec![0u8; read_len];
128 f.read_exact(&mut buf).map_err(|e| {
129 IdbError::Io(format!(
130 "Cannot read {} bytes at offset {}: {}",
131 read_len, offset, e
132 ))
133 })?;
134
135 if raw {
136 writer
137 .write_all(&buf)
138 .map_err(|e| IdbError::Io(format!("Cannot write to stdout: {}", e)))?;
139 } else {
140 wprintln!(
141 writer,
142 "Hex dump of {} at offset {} ({} bytes):",
143 file,
144 offset,
145 read_len
146 )?;
147 wprintln!(writer)?;
148 wprintln!(writer, "{}", hex_dump(&buf, offset))?;
149 }
150
151 Ok(())
152}