959 {
960 const uint64_t disk_size = reader.
size();
961 if (disk_size == 0) {
962 error = "Carving: disk size is zero";
963 return false;
964 }
966 ? disk_size
967 : std::min<uint64_t>(disk_size,
969
970 fs::path out_root = fs::path(output_dir) / "carved";
971 {
972 std::error_code ec;
973 fs::create_directories(out_root, ec);
974 }
975
977
978 size_t longest_magic = 0;
979 for (const auto& f : formats) {
980 const size_t needed = f.magic.size() + f.magic_offset_in_file;
981 if (needed > longest_magic) longest_magic = needed;
982 }
983
984 constexpr size_t MAIN_WINDOW = 16 * 1024 * 1024;
985 const size_t overlap = longest_magic;
986 std::vector<unsigned char> window(MAIN_WINDOW + overlap);
987
988 size_t carved_count = 0;
989 uint64_t pos = 0;
990 int ident_counter = 0;
991
992 auto peek_more = [&](uint64_t abs_off, unsigned char* dst,
993 size_t want) -> size_t {
994 if (abs_off >= disk_size) return 0;
995 const size_t can = static_cast<size_t>(
996 std::min<uint64_t>(want, disk_size - abs_off));
997 return reader.
read_at(abs_off, dst, can) ? can : 0;
998 };
999
1000 logs.push_back(
trs(
"Carving: starting scan up to ") +
1001 std::to_string(scan_limit / (1024 * 1024)) + " MiB");
1002
1003 while (pos < scan_limit) {
1004 const size_t want = static_cast<size_t>(
1005 std::min<uint64_t>(window.size(), scan_limit - pos));
1006 if (!reader.
read_at(pos, window.data(), want)) {
1007 logs.push_back(
trs(
"Carving: read failure at offset ") +
1008 std::to_string(pos) +
trs(
", skipping ahead"));
1009 pos += MAIN_WINDOW;
1010 continue;
1011 }
1012
1013
1014 const size_t scan_end = (want > overlap) ? (want - overlap) : want;
1015
1016 const bool last = (pos + want >= scan_limit);
1017 const size_t actual_end = last ? want : scan_end;
1018
1019 for (size_t i = 0; i < actual_end; ++i) {
1020 for (const auto& f : formats) {
1021 const size_t mlen = f.magic.size();
1022 if (i + mlen > want) continue;
1023 if (std::memcmp(window.data() + i, f.magic.data(), mlen) != 0)
1024 continue;
1025
1026 if (i < f.magic_offset_in_file) continue;
1027 const size_t file_local_start = i - f.magic_offset_in_file;
1028 const uint64_t file_disk_off = pos + file_local_start;
1029
1030
1031 const size_t window_remaining = want - file_local_start;
1032
1033 DetectResult dr = f.detect(
1034 window.data() + file_local_start,
1035 window_remaining, file_disk_off,
1038 );
1039
1040 if (!dr.ok) continue;
1041 if (dr.size == 0 || dr.size > opts.
max_file_size + 1)
continue;
1042
1043
1044 fs::path out_dir = out_root / f.name;
1045 {
1046 std::error_code ec;
1047 fs::create_directories(out_dir, ec);
1048 }
1049 std::ostringstream nm;
1050 nm << std::hex << file_disk_off << "_" << ident_counter
1051 << "." << f.extension;
1052 ++ident_counter;
1053 fs::path out_path = out_dir / nm.str();
1054
1055 std::ofstream out(out_path, std::ios::binary | std::ios::trunc);
1056 if (!out) continue;
1057
1058 constexpr size_t WBUF = 1024 * 1024;
1059 std::vector<unsigned char> wbuf(WBUF);
1060 uint64_t written = 0;
1061 while (written < dr.size) {
1062 const size_t step = static_cast<size_t>(
1063 std::min<uint64_t>(WBUF, dr.size - written));
1064 if (!reader.
read_at(file_disk_off + written,
1065 wbuf.data(), step)) break;
1066 out.write(reinterpret_cast<const char*>(wbuf.data()),
1067 static_cast<std::streamsize>(step));
1068 written += step;
1069 }
1070 out.close();
1071
1072 if (written > 0) {
1073 std::string note = "carved-";
1074 note += f.name;
1075 if (dr.size_is_capped) note += "-capped";
1076 recovered.push_back({
1077 out_path.string(), "carving",
1078 ("carved/" + std::string(f.name) + "/" + nm.str()),
1079 written,
1080 dr.size_is_capped ? "partial" : "full",
1081 note});
1082 ++carved_count;
1083 }
1084
1085
1086
1087
1088 const uint64_t skip_until = file_disk_off + dr.size;
1089 if (skip_until > pos + i) {
1090 const uint64_t skip = skip_until - (pos + i);
1091 if (skip > 0 && skip < want - i) {
1092 i += static_cast<size_t>(skip - 1);
1093 }
1094 }
1095 break;
1096 }
1097 }
1098 pos += MAIN_WINDOW;
1099 }
1100
1101 {
1102 std::ostringstream m;
1103 m <<
trs(
"Carving: completed. Files recovered: ") << carved_count;
1104 logs.push_back(m.str());
1105 }
1106 return carved_count > 0;
1107}
bool read_at(uint64_t offset, void *buffer, size_t len)
std::string trs(const char *msgid)
std::function< size_t(uint64_t abs_off, unsigned char *dst, size_t want)> PeekMore
std::vector< CarverFormat > build_formats()
uint64_t max_bytes_to_scan