Skip to content
API Documentation
BastionGuard™
XfsRecovery Class Reference

#include <xfs_recovery.hpp>

Public Member Functions

 XfsRecovery (DiskReader &reader)
bool recover (uint64_t volume_offset, const std::string &output_dir, std::vector< RecoveredFileRecord > &recovered, std::vector< std::string > &logs, std::string &error)

Private Attributes

DiskReader & reader_

Detailed Description

Definition at line 29 of file xfs_recovery.hpp.

Constructor & Destructor Documentation

◆ XfsRecovery()

XfsRecovery::XfsRecovery ( DiskReader & reader)
explicit

Definition at line 415 of file xfs_recovery.cpp.

415: reader_(reader) {}
DiskReader & reader_

References reader_.

Member Function Documentation

◆ recover()

bool XfsRecovery::recover ( uint64_t volume_offset,
const std::string & output_dir,
std::vector< RecoveredFileRecord > & recovered,
std::vector< std::string > & logs,
std::string & error )

Definition at line 417 of file xfs_recovery.cpp.

421 {
422 logs.push_back(trs("XFS recovery (session 4: full dir/file extraction) starting at offset") +
423 std::string(" ") + std::to_string(volume_offset));
424
425 // ---- Step 1: read and parse the volume superblock ----
426 std::array<unsigned char, 512> sb_sector{};
427 if (!read_exact(reader_, volume_offset, sb_sector.data(), sb_sector.size())) {
428 error = trs("XFS: cannot read superblock");
429 return false;
430 }
431
432 xfs::Superblock sb;
433 if (!xfs::parse_superblock(sb_sector.data(), sb)) {
434 error = trs("XFS: not a valid superblock at this offset");
435 return false;
436 }
437
438 // Geometry sanity vs disk
439 {
440 std::string ge;
441 if (!validate_geometry(reader_, volume_offset, sb, ge)) {
442 error = trs("XFS: geometry inconsistent:") + std::string(" ") + ge;
443 return false;
444 }
445 }
446
447 // ---- Step 2: log the volume layout (helps the user / debugging) ----
448 {
449 std::ostringstream m;
450 m << "XFS volume identified: V" << sb.format_version()
451 << " blocksize=" << sb.blocksize
452 << " sectsize=" << sb.sectsize
453 << " inodesize=" << sb.inodesize
454 << " agcount=" << sb.agcount
455 << " agblocks=" << sb.agblocks
456 << " dblocks=" << sb.dblocks
457 << " size=" << (sb.volume_size_bytes() / (1024 * 1024)) << " MiB";
458 const std::string lbl = sb.label();
459 if (!lbl.empty()) m << " label=\"" << lbl << "\"";
460 logs.push_back(m.str());
461 }
462
463 if (sb.is_v5()) {
464 std::ostringstream m;
465 m << "XFS V5 features: ";
466 bool any = false;
467 if (sb.features_incompat & xfs::XFS_SB_FEAT_INCOMPAT_FTYPE) { m << "FTYPE "; any = true; }
468 if (sb.features_incompat & xfs::XFS_SB_FEAT_INCOMPAT_SPINODES){ m << "SPARSE "; any = true; }
469 if (sb.features_incompat & xfs::XFS_SB_FEAT_INCOMPAT_BIGTIME) { m << "BIGTIME "; any = true; }
470 if (sb.features_incompat & xfs::XFS_SB_FEAT_INCOMPAT_NREXT64) { m << "NREXT64 "; any = true; }
471 if (sb.features_ro_compat & xfs::XFS_SB_FEAT_RO_COMPAT_FINOBT){ m << "FINOBT "; any = true; }
472 if (sb.features_ro_compat & xfs::XFS_SB_FEAT_RO_COMPAT_REFLINK){m << "REFLINK "; any = true; }
473 if (!any) m << "(none)";
474 logs.push_back(m.str());
475 }
476
477 // ---- Step 3: build the volume context, load AG headers ----
478 xfs::VolumeContext vol;
479 vol.sb = sb;
480 vol.volume_offset = volume_offset;
481 vol.ag.assign(sb.agcount, xfs::AGContext{});
482
483 // Cap iteration to a reasonable maximum to defend against pathological
484 // SB values (we already validated agcount above, this is belt-and-braces).
485 constexpr uint32_t MAX_AG_TO_PROBE = 4096;
486 const uint32_t agcount = std::min<uint32_t>(sb.agcount, MAX_AG_TO_PROBE);
487
488 uint32_t agf_ok = 0, agi_ok = 0;
489 for (uint32_t i = 0; i < agcount; ++i) {
490 load_ag_headers(reader_, volume_offset, sb, i, vol.ag[i], logs);
491 if (vol.ag[i].agf_loaded) ++agf_ok;
492 if (vol.ag[i].agi_loaded) ++agi_ok;
493 }
494
495 {
496 std::ostringstream m;
497 m << "XFS AG headers loaded: AGF " << agf_ok << "/" << agcount
498 << ", AGI " << agi_ok << "/" << agcount;
499 logs.push_back(m.str());
500 }
501
502 if (agf_ok == 0 && agi_ok == 0) {
503 error = trs("XFS: no AG headers could be parsed; volume looks corrupt or "
504 "this is a partition without an XFS volume at offset 0");
505 return false;
506 }
507
508 // Per AG with a loaded AGI, emit one line of diagnostics that will be
509 // useful when we add inode iteration in session 2.
510 for (uint32_t i = 0; i < agcount; ++i) {
511 if (!vol.ag[i].agi_loaded) continue;
512 std::ostringstream m;
513 m << " AG " << i
514 << ": ialloc=" << vol.ag[i].agi.count
515 << " ifree=" << vol.ag[i].agi.freecount
516 << " ibtree_root=blk" << vol.ag[i].agi.root
517 << " ibtree_depth=" << vol.ag[i].agi.level;
518 logs.push_back(m.str());
519 }
520
521 // ---- Step 4: tree-walk from the root inode and export files ----
522 //
523 // We start at sb.rootino and recursively descend through shortform
524 // directories. For every regular file in extents format we read the
525 // file content via the extent list and write it to the output dir.
526 // For symlinks (shortform) we write the link target as a small file.
527 // For shortform regular files (di_format=1, rare) we dump the inline
528 // bytes.
529 //
530 // We do NOT yet handle directory block/leaf/node formats or extent
531 // B+tree (di_format=3). Those land in session 4.
532
533 constexpr int MAX_DEPTH = 64; // defensive against pathological dirs
534 constexpr size_t MAX_FILES = 200'000; // safety cap on exported files
535
536 // Output root: <output_dir>/filesystem_recovered/xfs/offset_<N>/
537 fs::path xfs_out_root =
538 fs::path(output_dir) / "filesystem_recovered" / "xfs" /
539 ("offset_" + std::to_string(volume_offset));
540 {
541 std::error_code ec;
542 fs::create_directories(xfs_out_root, ec);
543 if (ec) {
544 error = "Cannot create output directory: " + xfs_out_root.string();
545 return false;
546 }
547 }
548
549 std::set<uint64_t> visited_inodes;
550 size_t files_exported = 0;
551 size_t dirs_walked = 0;
552 size_t skipped_unsupported = 0;
553 size_t errors_logged = 0;
554
555 // Recursive walker. Returns false ONLY on fatal user-level cap.
556 // For per-inode errors we just log and continue.
557 std::function<bool(uint64_t, const fs::path&, int)> walk_dir =
558 [&](uint64_t ino, const fs::path& out_dir, int depth) -> bool
559 {
560 if (depth >= MAX_DEPTH) {
561 logs.push_back("XFS: max recursion depth reached, stopping branch.");
562 return true;
563 }
564 if (!visited_inodes.insert(ino).second) {
565 return true; // cycle, skip
566 }
567 if (files_exported >= MAX_FILES) {
568 logs.push_back("XFS: file export safety cap reached ("
569 + std::to_string(MAX_FILES) + ").");
570 return false;
571 }
572
573 std::vector<unsigned char> ibuf;
574 if (!read_dinode(reader_, vol, ino, ibuf)) {
575 ++errors_logged;
576 logs.push_back("XFS: failed to read dinode for ino "
577 + std::to_string(ino));
578 return true;
579 }
580 xfs::DinodeCore core;
581 if (!xfs::parse_dinode_core(ibuf.data(), core)) {
582 ++errors_logged;
583 logs.push_back("XFS: bad dinode magic at ino "
584 + std::to_string(ino));
585 return true;
586 }
587 if (!core.is_dir()) {
588 // Not a directory -- this should only happen on the very
589 // first call if rootino isn't actually a dir (corrupt SB).
590 return true;
591 }
592 ++dirs_walked;
593
594 const uint32_t fork_off = xfs::dinode_data_fork_offset(core);
595 const uint32_t fork_len = xfs::dinode_data_fork_length(core, vol.sb.inodesize);
596 const unsigned char* fork = ibuf.data() + fork_off;
597
598 // Build a uniform "list of entries" regardless of which directory
599 // format the dinode uses (shortform / block / leaf / node / btree).
600 // Each branch fills `dir_entries`.
601 std::vector<xfs::DirEntry> dir_entries;
602
603 if (core.format == xfs::XFS_DINODE_FMT_LOCAL) {
604 xfs::ShortformDir sf;
605 if (!xfs::parse_shortform_dir(fork, fork_len,
606 vol.sb.has_ftype(), sf)) {
607 ++errors_logged;
608 logs.push_back("XFS: cannot parse shortform dir at ino "
609 + std::to_string(ino));
610 return true;
611 }
612 dir_entries = std::move(sf.entries);
613 }
614 else if (core.format == xfs::XFS_DINODE_FMT_EXTENTS ||
616 // Block-format (single data block) and leaf/node-format
617 // (multiple data blocks) directories are both ultimately a
618 // sequence of "data blocks" inside the directory's logical
619 // address space below 32 GiB. We collect every extent and
620 // parse each block uniformly.
621 std::vector<xfs::Extent> extents;
622 std::string cerr;
623 if (!collect_extents(reader_, vol, core, fork, fork_len,
624 /*max_extents=*/100'000,
625 extents, logs, cerr)) {
626 ++errors_logged;
627 logs.push_back("XFS: dir ino " + std::to_string(ino) +
628 " extent list error: " + cerr);
629 return true;
630 }
632 vol.sb.has_ftype(),
633 dir_entries, logs)) {
634 ++errors_logged;
635 logs.push_back("XFS: dir ino " + std::to_string(ino) +
636 " could not be parsed (block/leaf format)");
637 return true;
638 }
639 }
640 else {
641 ++skipped_unsupported;
642 std::ostringstream m;
643 m << "XFS: directory ino " << ino
644 << " uses unsupported di_format="
645 << static_cast<int>(core.format);
646 logs.push_back(m.str());
647 return true;
648 }
649
650 // For each entry, dispatch on type
651 for (const auto& e : dir_entries) {
652 const std::string safe_name = text_utils::sanitize_component(e.name);
653 const fs::path child_path = out_dir / safe_name;
654
655 std::vector<unsigned char> child_buf;
656 if (!read_dinode(reader_, vol, e.ino, child_buf)) {
657 ++errors_logged;
658 logs.push_back("XFS: cannot read child dinode for '"
659 + e.name + "' (ino " + std::to_string(e.ino) + ")");
660 continue;
661 }
662 xfs::DinodeCore child_core;
663 if (!xfs::parse_dinode_core(child_buf.data(), child_core)) {
664 ++errors_logged;
665 logs.push_back("XFS: bad child dinode magic for '"
666 + e.name + "'");
667 continue;
668 }
669
670 const uint32_t cfork_off = xfs::dinode_data_fork_offset(child_core);
671 const uint32_t cfork_len = xfs::dinode_data_fork_length(child_core, vol.sb.inodesize);
672 const unsigned char* cfork = child_buf.data() + cfork_off;
673
674 if (child_core.is_dir()) {
675 std::error_code ec;
676 fs::create_directories(child_path, ec);
677 if (ec) {
678 ++errors_logged;
679 logs.push_back("XFS: cannot mkdir " + child_path.string());
680 continue;
681 }
682 if (!walk_dir(e.ino, child_path, depth + 1)) return false;
683 }
684 else if (child_core.is_reg()) {
685 // Two cases: file in extents/btree format, or rare inline.
686 if (child_core.format == xfs::XFS_DINODE_FMT_EXTENTS ||
687 child_core.format == xfs::XFS_DINODE_FMT_BTREE) {
688 std::vector<xfs::Extent> extents;
689 std::string cerr;
690 if (!collect_extents(reader_, vol, child_core,
691 cfork, cfork_len, /*max_extents=*/1'000'000,
692 extents, logs, cerr)) {
693 ++errors_logged;
694 logs.push_back("XFS: cannot read extents for '" + e.name +
695 "': " + cerr);
696 continue;
697 }
698 std::string ferr;
699 if (write_extents_to_path(reader_, vol, std::move(extents),
700 static_cast<uint64_t>(child_core.size),
701 child_path, ferr)) {
702 const char* note = (child_core.format == xfs::XFS_DINODE_FMT_BTREE)
703 ? "btree-extents" : "extents";
704 recovered.push_back({
705 child_path.string(), "XFS",
706 (out_dir / safe_name).lexically_relative(xfs_out_root).string(),
707 static_cast<uint64_t>(child_core.size),
708 "full", note});
709 ++files_exported;
710 } else {
711 ++errors_logged;
712 logs.push_back("XFS: failed to export '" + e.name +
713 "': " + ferr);
714 }
715 }
716 else if (child_core.format == xfs::XFS_DINODE_FMT_LOCAL) {
717 // Inline file (rare on XFS but exists for tiny files
718 // when forkoff allows it).
719 std::ofstream out(child_path, std::ios::binary | std::ios::trunc);
720 if (out) {
721 const uint64_t bytes = std::min<uint64_t>(
722 static_cast<uint64_t>(child_core.size), cfork_len);
723 out.write(reinterpret_cast<const char*>(cfork),
724 static_cast<std::streamsize>(bytes));
725 recovered.push_back({
726 child_path.string(), "XFS",
727 (out_dir / safe_name).lexically_relative(xfs_out_root).string(),
728 bytes, "full", "shortform-inline"});
729 ++files_exported;
730 } else {
731 ++errors_logged;
732 logs.push_back("XFS: cannot create file " + child_path.string());
733 }
734 }
735 else {
736 // dev / uuid / ... -- not file content
737 ++skipped_unsupported;
738 }
739 }
740 else if (child_core.is_symlink()) {
741 std::string target;
742 if (child_core.format == xfs::XFS_DINODE_FMT_LOCAL) {
743 // Inline symlink target.
744 const uint64_t link_bytes = std::min<uint64_t>(
745 static_cast<uint64_t>(child_core.size), cfork_len);
746 target.assign(reinterpret_cast<const char*>(cfork),
747 static_cast<size_t>(link_bytes));
748 }
749 else if (child_core.format == xfs::XFS_DINODE_FMT_EXTENTS ||
750 child_core.format == xfs::XFS_DINODE_FMT_BTREE) {
751 // Long symlink: target stored as file data in extents
752 // or BMBT. Read it the same way as a regular file body.
753 std::vector<xfs::Extent> extents;
754 std::string cerr;
755 if (!collect_extents(reader_, vol, child_core,
756 cfork, cfork_len, /*max_extents=*/256,
757 extents, logs, cerr)) {
758 ++errors_logged;
759 logs.push_back("XFS: cannot read symlink extents for '"
760 + e.name + "': " + cerr);
761 continue;
762 }
763 std::vector<unsigned char> body;
764 std::string rerr;
765 if (!read_extents_data(reader_, vol, extents,
766 static_cast<uint64_t>(child_core.size),
767 body, rerr)) {
768 ++errors_logged;
769 logs.push_back("XFS: cannot read symlink body for '"
770 + e.name + "': " + rerr);
771 continue;
772 }
773 target.assign(reinterpret_cast<const char*>(body.data()),
774 body.size());
775 }
776 else {
777 ++skipped_unsupported;
778 continue;
779 }
780
781 // Try to make a real symlink first; if that fails (e.g.
782 // target outside the export dir, perms), write a sidecar.
783 std::error_code ec;
784 fs::create_symlink(target, child_path, ec);
785 if (ec) {
786 std::ofstream out(child_path.string() + ".symlink",
787 std::ios::binary | std::ios::trunc);
788 if (out) out << target;
789 }
790 recovered.push_back({
791 child_path.string(), "XFS",
792 (out_dir / safe_name).lexically_relative(xfs_out_root).string(),
793 target.size(), "full",
794 (child_core.format == xfs::XFS_DINODE_FMT_LOCAL)
795 ? "symlink-shortform" : "symlink-extents"});
796 ++files_exported;
797 }
798 else if (child_core.is_special()) {
799 // Char/block/fifo/socket -- nothing meaningful to export.
800 ++skipped_unsupported;
801 }
802 else {
803 ++skipped_unsupported;
804 }
805 }
806 return true;
807 };
808
809 // Kick off the walk from the root inode.
810 if (sb.rootino == 0) {
811 error = "XFS: superblock has no root inode (rootino=0).";
812 return false;
813 }
814 walk_dir(sb.rootino, xfs_out_root, 0);
815
816 // ---- Step 5 (session 5): FINOBT pass for recently-deleted file recovery
817 //
818 // Only on V5 volumes with FINOBT feature. For each free inode slot,
819 // try to read the dinode -- if the inode wasn't yet reused by XFS,
820 // it still holds the deleted file's data. We export to a flat
821 // "deleted/" subdirectory because we have no way to reconstruct the
822 // original directory path or filename: the dirent that named it has
823 // already been removed from the parent.
824
825 size_t deleted_recovered = 0;
826 size_t deleted_attempted = 0;
827 size_t deleted_stale = 0; // valid magic but format/mode implausible
828
829 if (sb.is_v5() &&
831
832 fs::path deleted_dir = xfs_out_root / "deleted";
833 std::error_code dec;
834 fs::create_directories(deleted_dir, dec);
835
836 // Collect candidates from FINOBT
837 std::vector<xfs::AllocatedInode> free_slots;
838 auto fcb = [&](const xfs::InobtRec& rec, uint32_t agno) -> bool {
839 xfs::expand_chunk_free(rec, agno, vol, free_slots);
840 // Safety cap: don't enumerate more than 200k stale slots
841 if (free_slots.size() >= 200'000) {
842 logs.push_back("XFS FINOBT: candidate cap reached (200k slots)");
843 return false;
844 }
845 return true;
846 };
847 std::vector<std::string> finobt_logs;
848 xfs::walk_all_free_inode_records(reader_, vol, fcb, finobt_logs);
849 for (auto& l : finobt_logs) logs.push_back(std::move(l));
850
851 logs.push_back("XFS FINOBT: " + std::to_string(free_slots.size())
852 + " free-inode candidates collected.");
853
854 // For each free slot, try to read the stale dinode
855 for (const auto& fi : free_slots) {
856 // Skip slots we already saw as live inodes (defensive --
857 // shouldn't happen in a healthy FS, but FINOBT and IBT can
858 // disagree on damaged volumes).
859 if (visited_inodes.count(fi.ino) != 0) continue;
860 ++deleted_attempted;
861
862 std::vector<unsigned char> ibuf(vol.sb.inodesize, 0);
863 if (!reader_.read_at(fi.disk_offset, ibuf.data(), ibuf.size())) {
864 continue;
865 }
866 xfs::DinodeCore core;
867 if (!xfs::parse_dinode_core(ibuf.data(), core)) {
868 continue; // dinode already overwritten or never written
869 }
870
871 // Heuristic checks: a "still recoverable" stale dinode
872 // typically still has its original mode/format/size. If
873 // mode == 0 or size == 0 or format is exotic, skip.
874 if (core.mode == 0) { ++deleted_stale; continue; }
875 if (core.size <= 0) { ++deleted_stale; continue; }
876
877 // We only recover regular files here. Recovering a deleted
878 // directory's content is complicated (the data blocks may
879 // be partially overwritten, the dirent records would need
880 // to be cross-referenced) and out of scope for this session.
881 if (!core.is_reg()) { ++deleted_stale; continue; }
882
883 const uint32_t cfork_off = xfs::dinode_data_fork_offset(core);
884 const uint32_t cfork_len = xfs::dinode_data_fork_length(core,
885 vol.sb.inodesize);
886 const unsigned char* cfork = ibuf.data() + cfork_off;
887
888 // Build a synthetic filename: inode_<NUM>.bin
889 std::ostringstream nm;
890 nm << "inode_" << fi.ino << ".bin";
891 const fs::path out_path = deleted_dir / nm.str();
892
893 std::vector<xfs::Extent> extents;
894 std::vector<std::string> ex_logs;
895 std::string ex_err;
896 if (!collect_extents(reader_, vol, core, cfork, cfork_len,
897 1'000'000, extents, ex_logs, ex_err)) {
898 ++deleted_stale;
899 continue;
900 }
901 // Skip files with no extents (typical for never-written or
902 // already-overwritten slots)
903 if (extents.empty() && core.format != xfs::XFS_DINODE_FMT_LOCAL) {
904 ++deleted_stale;
905 continue;
906 }
907
908 std::vector<unsigned char> file_data;
909 if (core.format == xfs::XFS_DINODE_FMT_LOCAL) {
910 // Inline file: just copy the inline bytes
911 const uint64_t bytes = std::min<uint64_t>(
912 static_cast<uint64_t>(core.size), cfork_len);
913 file_data.assign(cfork, cfork + bytes);
914 } else {
915 if (!read_extents_data(reader_, vol, extents,
916 static_cast<uint64_t>(core.size),
917 file_data, ex_err)) {
918 ++deleted_stale;
919 continue;
920 }
921 }
922
923 std::ofstream out(out_path, std::ios::binary | std::ios::trunc);
924 if (!out) { ++deleted_stale; continue; }
925 out.write(reinterpret_cast<const char*>(file_data.data()),
926 static_cast<std::streamsize>(file_data.size()));
927 recovered.push_back({
928 out_path.string(), "XFS",
929 ("deleted/" + nm.str()),
930 file_data.size(), "deleted", "finobt"});
931 ++deleted_recovered;
932 }
933
934 std::ostringstream m;
935 m << "XFS FINOBT: deleted-file recovery -- attempted "
936 << deleted_attempted << " slots, recovered "
937 << deleted_recovered << " files, "
938 << deleted_stale << " stale/unsupported.";
939 logs.push_back(m.str());
940
941 if (deleted_recovered == 0 && deleted_attempted > 0) {
942 logs.push_back("XFS FINOBT: no deleted files recovered. "
943 "This is normal on a long-running filesystem "
944 "where free inode slots have been overwritten "
945 "by new allocations. Recovery only works on "
946 "deletions that occurred since the last reuse.");
947 }
948 }
949
950 {
951 std::ostringstream m;
952 m << "XFS recovery completed. "
953 << "Files exported: " << files_exported
954 << ", directories walked: " << dirs_walked
955 << ", deleted files recovered: " << deleted_recovered
956 << ", skipped (non-shortform/unsupported): " << skipped_unsupported
957 << ", per-inode errors: " << errors_logged << ".";
958 logs.push_back(m.str());
959 }
960
961 // Note for the user: residual unsupported items
962 if (skipped_unsupported > 0) {
963 logs.push_back("XFS: some entries were skipped (special device files, "
964 "non-standard inode formats, or unrecognised data blocks). "
965 "Regular shortform/block/leaf directories and "
966 "extents/btree files were processed.");
967 }
968
969 return files_exported > 0 || dirs_walked > 0 || deleted_recovered > 0;
970}
bool read_at(uint64_t offset, void *buffer, size_t len)
uint64_t size() const
std::string trs(const char *msgid)
Definition i18n.cpp:22
bool read_exact(std::ifstream &f, char *buf, std::size_t len)
bool validate_geometry(DiskReader &reader, uint64_t volume_offset, const hfs::VolumeHeader &vh, std::string &err)
void load_ag_headers(DiskReader &reader, uint64_t volume_offset, const xfs::Superblock &sb, uint32_t agno, xfs::AGContext &ag, std::vector< std::string > &logs)
bool read_dinode(DiskReader &reader, const xfs::VolumeContext &vol, uint64_t ino, std::vector< unsigned char > &buf)
bool read_extents_data(DiskReader &reader, const xfs::VolumeContext &vol, const std::vector< xfs::Extent > &extents, uint64_t file_size, std::vector< unsigned char > &out, std::string &err)
bool collect_extents(DiskReader &reader, const xfs::VolumeContext &vol, const xfs::DinodeCore &core, const unsigned char *fork_data, uint32_t fork_len, uint32_t max_extents, std::vector< xfs::Extent > &out, std::vector< std::string > &logs, std::string &err)
bool write_extents_to_path(DiskReader &reader, const xfs::VolumeContext &vol, std::vector< xfs::Extent > extents, uint64_t file_size, const fs::path &out_path, std::string &err)
bool collect_dir_entries_block_or_leaf(DiskReader &reader, const xfs::VolumeContext &vol, const std::vector< xfs::Extent > &extents, bool has_ftype, std::vector< xfs::DirEntry > &out, std::vector< std::string > &logs)
std::string sanitize_component(const std::string &in)
constexpr uint32_t XFS_SB_FEAT_INCOMPAT_BIGTIME
constexpr uint8_t XFS_DINODE_FMT_EXTENTS
Definition xfs_inode.hpp:99
bool walk_all_free_inode_records(DiskReader &reader, const VolumeContext &vol, const InobtRecordCallback &on_record, std::vector< std::string > &logs)
constexpr uint8_t XFS_DINODE_FMT_LOCAL
Definition xfs_inode.hpp:98
bool parse_dinode_core(const unsigned char *buf, DinodeCore &out)
Definition xfs_inode.cpp:15
bool parse_superblock(const unsigned char *buf, Superblock &out)
uint32_t dinode_data_fork_offset(const DinodeCore &core)
constexpr uint8_t XFS_DINODE_FMT_BTREE
constexpr uint32_t XFS_SB_FEAT_INCOMPAT_SPINODES
constexpr uint32_t XFS_SB_FEAT_RO_COMPAT_FINOBT
uint32_t dinode_data_fork_length(const DinodeCore &core, uint32_t inode_size)
Definition xfs_inode.cpp:58
constexpr uint32_t XFS_SB_FEAT_INCOMPAT_FTYPE
constexpr uint32_t XFS_SB_FEAT_RO_COMPAT_REFLINK
bool parse_shortform_dir(const unsigned char *data, uint32_t data_len, bool has_ftype, ShortformDir &out)
Definition xfs_dir.cpp:30
void expand_chunk_free(const InobtRec &rec, uint32_t agno, const VolumeContext &vol, std::vector< AllocatedInode > &out)
constexpr uint32_t XFS_SB_FEAT_INCOMPAT_NREXT64
bool is_symlink() const
bool is_dir() const
bool is_special() const
bool is_reg() const
std::vector< DirEntry > entries
Definition xfs_dir.hpp:71
uint64_t volume_size_bytes() const
uint32_t features_incompat
std::string label() const
unsigned format_version() const
uint32_t features_ro_compat
bool is_v5() const
bool has_ftype() const
std::vector< AGContext > ag

References xfs::VolumeContext::ag, xfs::Superblock::agblocks, xfs::Superblock::agcount, xfs::Superblock::blocksize, xfs::Superblock::dblocks, xfs::dinode_data_fork_length(), xfs::dinode_data_fork_offset(), xfs::ShortformDir::entries, xfs::expand_chunk_free(), xfs::Superblock::features_incompat, xfs::Superblock::features_ro_compat, xfs::DinodeCore::format, xfs::Superblock::format_version(), xfs::Superblock::has_ftype(), xfs::Superblock::inodesize, xfs::DinodeCore::is_dir(), xfs::DinodeCore::is_reg(), xfs::DinodeCore::is_special(), xfs::DinodeCore::is_symlink(), xfs::Superblock::is_v5(), xfs::Superblock::label(), xfs::DinodeCore::mode, xfs::parse_dinode_core(), xfs::parse_shortform_dir(), xfs::parse_superblock(), DiskReader::read_at(), reader_, xfs::Superblock::rootino, text_utils::sanitize_component(), xfs::VolumeContext::sb, xfs::Superblock::sectsize, DiskReader::size(), xfs::DinodeCore::size, trs(), xfs::VolumeContext::volume_offset, xfs::Superblock::volume_size_bytes(), xfs::walk_all_free_inode_records(), xfs::XFS_DINODE_FMT_BTREE, xfs::XFS_DINODE_FMT_EXTENTS, xfs::XFS_DINODE_FMT_LOCAL, xfs::XFS_SB_FEAT_INCOMPAT_BIGTIME, xfs::XFS_SB_FEAT_INCOMPAT_FTYPE, xfs::XFS_SB_FEAT_INCOMPAT_NREXT64, xfs::XFS_SB_FEAT_INCOMPAT_SPINODES, xfs::XFS_SB_FEAT_RO_COMPAT_FINOBT, and xfs::XFS_SB_FEAT_RO_COMPAT_REFLINK.

Member Data Documentation

◆ reader_

DiskReader& XfsRecovery::reader_
private

Definition at line 41 of file xfs_recovery.hpp.

Referenced by recover(), and XfsRecovery().


The documentation for this class was generated from the following files: