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}
24
25pub fn execute(opts: &DumpOptions, writer: &mut dyn Write) -> Result<(), IdbError> {
43 if let Some(abs_offset) = opts.offset {
44 return dump_at_offset(
46 &opts.file,
47 abs_offset,
48 opts.length.unwrap_or(256),
49 opts.raw,
50 writer,
51 );
52 }
53
54 let mut ts = match opts.page_size {
56 Some(ps) => Tablespace::open_with_page_size(&opts.file, ps)?,
57 None => Tablespace::open(&opts.file)?,
58 };
59
60 let page_size = ts.page_size();
61 let page_num = opts.page.unwrap_or(0);
62 let page_data = ts.read_page(page_num)?;
63
64 let length = opts.length.unwrap_or(page_size as usize);
65 let dump_len = length.min(page_data.len());
66 let base_offset = page_num * page_size as u64;
67
68 if opts.raw {
69 writer
70 .write_all(&page_data[..dump_len])
71 .map_err(|e| IdbError::Io(format!("Cannot write to stdout: {}", e)))?;
72 } else {
73 wprintln!(
74 writer,
75 "Hex dump of {} page {} ({} bytes):",
76 opts.file,
77 page_num,
78 dump_len
79 )?;
80 wprintln!(writer)?;
81 wprintln!(writer, "{}", hex_dump(&page_data[..dump_len], base_offset))?;
82 }
83
84 Ok(())
85}
86
87fn dump_at_offset(
88 file: &str,
89 offset: u64,
90 length: usize,
91 raw: bool,
92 writer: &mut dyn Write,
93) -> Result<(), IdbError> {
94 let mut f =
95 File::open(file).map_err(|e| IdbError::Io(format!("Cannot open {}: {}", file, e)))?;
96
97 let file_size = f
98 .metadata()
99 .map_err(|e| IdbError::Io(format!("Cannot stat {}: {}", file, e)))?
100 .len();
101
102 if offset >= file_size {
103 return Err(IdbError::Argument(format!(
104 "Offset {} is beyond file size {}",
105 offset, file_size
106 )));
107 }
108
109 let available = (file_size - offset) as usize;
110 let read_len = length.min(available);
111
112 f.seek(SeekFrom::Start(offset))
113 .map_err(|e| IdbError::Io(format!("Cannot seek to offset {}: {}", offset, e)))?;
114
115 let mut buf = vec![0u8; read_len];
116 f.read_exact(&mut buf).map_err(|e| {
117 IdbError::Io(format!(
118 "Cannot read {} bytes at offset {}: {}",
119 read_len, offset, e
120 ))
121 })?;
122
123 if raw {
124 writer
125 .write_all(&buf)
126 .map_err(|e| IdbError::Io(format!("Cannot write to stdout: {}", e)))?;
127 } else {
128 wprintln!(
129 writer,
130 "Hex dump of {} at offset {} ({} bytes):",
131 file,
132 offset,
133 read_len
134 )?;
135 wprintln!(writer)?;
136 wprintln!(writer, "{}", hex_dump(&buf, offset))?;
137 }
138
139 Ok(())
140}