265{
266 summary = {};
267
268 try
269 {
270 fs::create_directories(output_dir);
271 fs::create_directories(fs::path(output_dir) / "images");
272 fs::create_directories(fs::path(output_dir) / "carved");
273 }
274 catch (const std::exception& ex)
275 {
276 error = ex.what();
277 return false;
278 }
279
280 if (cancel_cb && cancel_cb())
281 {
282 logs.push_back(
trs(
"Recovery cancelled by user."));
283 return false;
284 }
285
286 DiskReader reader;
287
288 std::string open_error;
289
291 {
292 error =
trs(
"Unable to open source");
293
294 if (!open_error.empty())
295 error += std::string(": ") + open_error;
296
297 logs.push_back(
trs(
"Recovery open error:") + std::string(
" ") + error);
298
299 return false;
300 }
301
302 std::unordered_set<size_t> selected_indices(
303 selected_result_indices.begin(),
304 selected_result_indices.end());
305
306 const bool filter_results = !selected_indices.empty();
307
308 size_t index = 0;
309
310 for (size_t result_index = 0;
311 result_index < report.
results.size();
312 ++result_index)
313 {
314 if (cancel_cb && cancel_cb())
315 {
316 logs.push_back(
trs(
"Recovery cancelled during image export."));
317 return false;
318 }
319
320 const auto&
r = report.
results[result_index];
321
322 if (filter_results && !selected_indices.count(result_index))
323 continue;
324
326 continue;
327
328 if (
r.offset_bytes +
r.size_bytes > reader.
size())
329 continue;
330
331 std::ostringstream name;
332
333 name << std::setw(3)
334 << std::setfill('0')
335 << index++
336 << '_'
338 << '_'
340 << ".img";
341
342 fs::path out_path =
343 fs::path(output_dir) / "images" / name.str();
344
346 reader,
349 out_path,
350 error,
351 cancel_cb))
352 {
353 return false;
354 }
355
357
358 logs.push_back(
359 trs(
"Exported volume image:") +
360 std::string(" ") +
361 out_path.string());
362 }
363
364 if (cancel_cb && cancel_cb())
365 {
366 logs.push_back(
trs(
"Recovery cancelled before filesystem recovery."));
367 return false;
368 }
369
370 ScanReport filtered_report = report;
371
372 if (filter_results)
373 {
374 filtered_report.
results.clear();
375
377 {
378 if (selected_indices.count(idx))
380 }
381 }
382
383 std::vector<RecoveredFileRecord> fs_recovered;
384
386 reader,
387 filtered_report,
388 output_dir,
389 fs_recovered,
390 logs,
391 error))
392 {
393 return false;
394 }
395
396 for (const auto& rf : fs_recovered)
398
399 if (cancel_cb && cancel_cb())
400 {
401 logs.push_back(
trs(
"Recovery cancelled during filesystem recovery."));
402 return false;
403 }
404
405 const auto carve_ranges =
407 report,
408 selected_indices,
409 filter_results,
411
412 std::vector<CarvedFile> carved;
413
414 if (!carve_ranges.empty())
415 {
417 reader,
418 carve_ranges,
419 (fs::path(output_dir) / "carved").string(),
420 carved,
421 logs,
422 error))
423 {
424 return false;
425 }
426 }
427
428 for (const auto& cf : carved)
430
431 if (cancel_cb && cancel_cb())
432 {
433 logs.push_back(
trs(
"Recovery cancelled during carving."));
434 return false;
435 }
436
438 fs::path(output_dir),
439 report,
440 selected_indices,
441 filter_results,
442 summary,
443 fs_recovered,
444 carved,
445 error);
446
447 if (!error.empty())
448 return false;
449
450 logs.push_back(
trs(
"Recovery export completed."));
451
452 return true;
453}
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)
std::string trs(const char *msgid)
std::string sanitize(const std::string &in)
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::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::vector< std::string > recovered_files
std::vector< ScanResult > results