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