14#include <unordered_set>
16namespace fs = std::filesystem;
23 const fs::path& out_path,
25 std::function<
bool()> cancel_cb)
27 std::ofstream out(out_path, std::ios::binary);
29 error =
trs(
"Unable to create output file") + std::string(
": ") + out_path.string();
33 std::vector<unsigned char> buffer(1024 * 1024);
35 uint64_t remaining = size;
36 uint64_t cursor = offset;
40 if (cancel_cb && cancel_cb())
42 error =
trs(
"Recovery cancelled by user");
46 const size_t chunk =
static_cast<size_t>(
47 std::min<uint64_t>(buffer.size(), remaining));
49 if (!reader.
read_at(cursor, buffer.data(), chunk))
51 error =
trs(
"Read error while exporting partition image");
55 out.write(
reinterpret_cast<const char*
>(buffer.data()),
56 static_cast<std::streamsize
>(chunk));
60 error =
trs(
"Write error while exporting partition image");
85 if (isalnum(
static_cast<unsigned char>(c)))
87 else if (c ==
' ' || c ==
'-' || c ==
'_')
98 const fs::path& output_dir,
100 const std::unordered_set<size_t>& selected_indices,
103 const std::vector<RecoveredFileRecord>& fs_recovered,
104 const std::vector<CarvedFile>& carved,
107 std::ofstream out(output_dir /
"recovery_manifest.txt", std::ios::binary);
111 error =
trs(
"Unable to create output file") + std::string(
": ") +
112 (output_dir /
"recovery_manifest.txt").
string();
117 out <<
"selected_filter=" << (filter_results ? 1 : 0) <<
"\n";
118 out <<
"selected_results=" << selected_indices.size() <<
"\n";
122 out <<
"[filesystem_recovery]\n";
124 for (
const auto& rf : fs_recovered)
126 out << rf.filesystem <<
'\t'
127 << rf.integrity <<
'\t'
128 << rf.size_bytes <<
'\t'
129 << rf.original_path <<
'\t'
132 if (!rf.note.empty())
133 out <<
'\t' << rf.note;
138 out <<
"\n[carved]\n";
140 for (
const auto& cf : carved)
142 out << cf.type <<
'\t'
144 << cf.output_path <<
'\n';
150 const std::unordered_set<size_t>& selected_indices,
152 uint64_t source_size)
154 std::vector<std::pair<uint64_t, uint64_t>> ranges;
156 auto add_range = [&](uint64_t offset, uint64_t size)
158 if (size == 0 || offset >= source_size)
161 size = std::min<uint64_t>(size, source_size - offset);
165 ranges.emplace_back(offset, size);
169 auto& last = ranges.back();
171 const uint64_t last_end = last.first + last.second;
173 if (offset <= last_end)
175 const uint64_t new_end = std::max<uint64_t>(last_end, offset + size);
176 last.second = new_end - last.first;
180 ranges.emplace_back(offset, size);
186 ranges.emplace_back(0, source_size);
190 std::vector<std::pair<uint64_t, uint64_t>> temp;
192 for (
size_t idx = 0; idx < report.
results.size(); ++idx)
194 if (!selected_indices.count(idx))
197 const auto& r = report.
results[idx];
199 if (r.offset_bytes == 0 && r.size_bytes == 0)
210 temp.emplace_back(r.offset_bytes, r.size_bytes);
220 std::sort(temp.begin(), temp.end(),
221 [](
const auto& a,
const auto& b)
223 return a.first < b.first;
226 for (
const auto& r : temp)
227 add_range(r.first, r.second);
237 const std::string& input_path,
238 const std::string& output_image_path,
240 std::vector<std::string>& logs,
242 std::function<
bool()> cancel_cb)
252 logs.insert(logs.end(), report.
logs.begin(), report.
logs.end());
259 const std::string& output_dir,
260 const std::vector<size_t>& selected_result_indices,
262 std::vector<std::string>& logs,
264 std::function<
bool()> cancel_cb)
270 fs::create_directories(output_dir);
271 fs::create_directories(fs::path(output_dir) /
"images");
272 fs::create_directories(fs::path(output_dir) /
"carved");
274 catch (
const std::exception& ex)
280 if (cancel_cb && cancel_cb())
282 logs.push_back(
trs(
"Recovery cancelled by user."));
288 std::string open_error;
292 error =
trs(
"Unable to open source");
294 if (!open_error.empty())
295 error += std::string(
": ") + open_error;
297 logs.push_back(
trs(
"Recovery open error:") + std::string(
" ") + error);
302 std::unordered_set<size_t> selected_indices(
303 selected_result_indices.begin(),
304 selected_result_indices.end());
306 const bool filter_results = !selected_indices.empty();
310 for (
size_t result_index = 0;
311 result_index < report.
results.size();
314 if (cancel_cb && cancel_cb())
316 logs.push_back(
trs(
"Recovery cancelled during image export."));
320 const auto& r = report.
results[result_index];
322 if (filter_results && !selected_indices.count(result_index))
325 if (!should_export(r) || r.size_bytes == 0)
328 if (r.offset_bytes + r.size_bytes > reader.
size())
331 std::ostringstream name;
343 fs::path(output_dir) /
"images" / name.str();
359 trs(
"Exported volume image:") +
364 if (cancel_cb && cancel_cb())
366 logs.push_back(
trs(
"Recovery cancelled before filesystem recovery."));
374 filtered_report.
results.clear();
376 for (
size_t idx = 0; idx < report.
results.size(); ++idx)
378 if (selected_indices.count(idx))
383 std::vector<RecoveredFileRecord> fs_recovered;
396 for (
const auto& rf : fs_recovered)
399 if (cancel_cb && cancel_cb())
401 logs.push_back(
trs(
"Recovery cancelled during filesystem recovery."));
405 const auto carve_ranges =
406 selected_carve_ranges(
412 std::vector<CarvedFile> carved;
414 if (!carve_ranges.empty())
419 (fs::path(output_dir) /
"carved").
string(),
428 for (
const auto& cf : carved)
431 if (cancel_cb && cancel_cb())
433 logs.push_back(
trs(
"Recovery cancelled during carving."));
437 write_recovery_manifest(
438 fs::path(output_dir),
450 logs.push_back(
trs(
"Recovery export completed."));
bool read_at(uint64_t offset, void *buffer, size_t len)
bool open_readonly(const std::string &path, std::string &error)
static bool carve_ranges_to_directory(DiskReader &reader, const std::vector< std::pair< uint64_t, uint64_t > > &ranges, const std::string &output_dir, std::vector< CarvedFile > &carved, std::vector< std::string > &logs, std::string &error)
static bool recover_supported_filesystems(DiskReader &reader, const ScanReport &report, const std::string &output_dir, std::vector< RecoveredFileRecord > &recovered, std::vector< std::string > &logs, std::string &error)
static bool create_rescue_image(const std::string &input_path, const std::string &output_image_path, RecoverySummary &summary, std::vector< std::string > &logs, std::string &error, CancelCallback cancel_cb)
static bool export_results_and_carve(const ScanReport &report, const std::string &output_dir, const std::vector< size_t > &selected_result_indices, RecoverySummary &summary, std::vector< std::string > &logs, std::string &error, CancelCallback cancel_cb)
static bool create_image(const std::string &device_path, const std::string &image_path, RescueImageReport &report, std::string &error, ProgressCallback progress_cb={}, CancelCallback cancel_cb={})
std::string trs(const char *msgid)
bool export_range(DiskReader &reader, uint64_t offset, uint64_t size, const fs::path &out_path, std::string &error, std::function< bool()> cancel_cb)
void write_recovery_manifest(const fs::path &output_dir, const ScanReport &report, const std::unordered_set< size_t > &selected_indices, bool filter_results, const RecoverySummary &summary, const std::vector< RecoveredFileRecord > &fs_recovered, const std::vector< CarvedFile > &carved, std::string &error)
std::string sanitize(const std::string &s)
std::vector< std::pair< uint64_t, uint64_t > > selected_carve_ranges(const ScanReport &report, const std::unordered_set< size_t > &selected_indices, bool filter_results, uint64_t source_size)
bool should_export(const ScanResult &r)
std::vector< std::string > exported_images
std::string rescue_map_path
std::string rescue_image_path
std::vector< std::string > recovered_files
std::vector< std::string > logs
std::vector< ScanResult > results