Skip to content
API Documentation
BastionGuard™
xfs_dir.hpp
Go to the documentation of this file.
1// xfs_dir.hpp -- XFS shortform directory parsing.
2//
3// Layout (reference: fs/xfs/libxfs/xfs_da_format.h in the Linux kernel):
4//
5// struct xfs_dir2_sf_hdr {
6// __u8 count; // entries excluding "." and ".."
7// __u8 i8count; // entries that use 8-byte inode numbers
8// __u32_or_64 parent; // parent inode (4 bytes if i8count==0, else 8)
9// };
10//
11// struct xfs_dir2_sf_entry {
12// __u8 namelen;
13// __u8 offset[2];
14// __u8 name[namelen];
15// __u8 ftype; // V5 only when SB has XFS_SB_FEAT_INCOMPAT_FTYPE
16// __u32_or_64 inumber;
17// };
18//
19// The "all 4 or all 8 bytes" rule is critical. If `i8count > 0`, every
20// inode number in the directory (parent included) is 8 bytes. Otherwise,
21// every inode number is 4 bytes. We honour this strictly.
22//
23// The pseudo-dirent offset (2 bytes after namelen) is for the older
24// dir2 indexing scheme; we don't use it for recovery, but we have to
25// skip those bytes correctly.
26//
27// FTYPE byte: present only when the volume's SB has the FTYPE feature
28// flag. We get that flag from `Superblock::has_ftype()`.
29
30#pragma once
31
32#include "xfs_format.hpp"
33
34#include <cstdint>
35#include <string>
36#include <vector>
37
38namespace xfs {
39
40constexpr uint8_t XFS_DIR3_FT_UNKNOWN = 0;
41constexpr uint8_t XFS_DIR3_FT_REG_FILE = 1;
42constexpr uint8_t XFS_DIR3_FT_DIR = 2;
43constexpr uint8_t XFS_DIR3_FT_CHRDEV = 3;
44constexpr uint8_t XFS_DIR3_FT_BLKDEV = 4;
45constexpr uint8_t XFS_DIR3_FT_FIFO = 5;
46constexpr uint8_t XFS_DIR3_FT_SOCK = 6;
47constexpr uint8_t XFS_DIR3_FT_SYMLINK = 7;
48
49// Block-format directory (single-block). The block contains:
50// header + data entries + (leaf tail at end -- we ignore it).
51constexpr uint32_t XFS_DIR2_BLOCK_MAGIC = 0x58443242u; // "XD2B" (V4)
52constexpr uint32_t XFS_DIR3_BLOCK_MAGIC = 0x58444233u; // "XDB3" (V5)
53
54// Leaf-format directory: data blocks. The block contains:
55// header + data entries (no leaf tail; leaf is in a separate block range).
56constexpr uint32_t XFS_DIR2_DATA_MAGIC = 0x58443244u; // "XD2D" (V4)
57constexpr uint32_t XFS_DIR3_DATA_MAGIC = 0x58444433u; // "XDD3" (V5)
58
59// Free-record sentinel inside a directory data block
60constexpr uint16_t XFS_DIR2_DATA_FREE_TAG = 0xFFFFu;
61
62struct DirEntry {
63 std::string name; // raw (NOT yet sanitized for host FS)
64 uint64_t ino {0}; // global inode number
65 uint8_t ftype {XFS_DIR3_FT_UNKNOWN}; // valid only if SB has FTYPE
66 bool ftype_valid {false};
67};
68
70 uint64_t parent_ino {0};
71 std::vector<DirEntry> entries; // does NOT include "." and ".."
72};
73
74// Parse a shortform directory. `data` points to the data fork content
75// of the directory inode, `data_len` is its valid byte length, and
76// `has_ftype` says whether each entry has a 1-byte ftype before the
77// inumber (XFS_SB_FEAT_INCOMPAT_FTYPE).
78//
79// Returns false on malformed/truncated input. On success, `out` holds
80// the parent inode and the list of entries.
81bool parse_shortform_dir(const unsigned char* data,
82 uint32_t data_len,
83 bool has_ftype,
84 ShortformDir& out);
85
86// =============================================================================
87// Block / leaf directory data-block parsing
88// =============================================================================
89//
90// A "data block" of a block- or leaf-format directory has this layout:
91//
92// [ header (V4: 16 bytes / V5: 64 bytes) ]
93// [ data entries OR unused (free) records, alternating ]
94// [ optional leaf-tail-table (only for BLOCK format -- we ignore it) ]
95//
96// Each data entry:
97// __be64 inumber
98// __u8 namelen
99// __u8 name[namelen]
100// __u8 ftype (V5 + FTYPE feature only)
101// __be16 tag (offset of entry start, used for indexing)
102// padding to 8-byte alignment of the NEXT record
103//
104// Each unused (free) record:
105// __be16 freetag = 0xFFFF
106// __be16 length (length of this free hole, IN BYTES)
107// __be16 tag (lookup pointer)
108//
109// Length of unused records is at least 16 bytes; the iterator skips
110// them entirely.
111//
112// For block-format directories the trailing leaf table sits at the END
113// of the block; we stop the iteration when we'd read into it. The leaf
114// tail starts at an offset stored in the (V4) data_hdr.bestfree or in
115// the V5 dir3_blk_tail; parsing it precisely is unnecessary for
116// recovery -- a clean check `(rec_len == 0) || (offset + rec_len > tail_off)`
117// is enough to stop safely.
118//
119// `block_buf` points to the start of the directory data block (block_size bytes).
120// `is_v5` says which header layout to expect (40-byte dir3_blk_hdr+).
121// `accept_block_magic` says whether to accept BLOCK magic (single-block dir);
122// `accept_data_magic` whether to accept DATA magic (leaf-format data block).
123//
124// Returns false on bad magic / corrupted layout. Appends entries to `out`.
125// Skips "." and ".." (recovery does not export them).
126
127bool parse_dir_data_block(const unsigned char* block_buf,
128 uint32_t block_size,
129 bool is_v5,
130 bool has_ftype,
131 bool accept_block_magic,
132 bool accept_data_magic,
133 std::vector<DirEntry>& out);
134
135} // namespace xfs
constexpr uint16_t XFS_DIR2_DATA_FREE_TAG
Definition xfs_dir.hpp:60
constexpr uint8_t XFS_DIR3_FT_UNKNOWN
Definition xfs_dir.hpp:40
constexpr uint8_t XFS_DIR3_FT_FIFO
Definition xfs_dir.hpp:45
constexpr uint8_t XFS_DIR3_FT_SOCK
Definition xfs_dir.hpp:46
constexpr uint32_t XFS_DIR2_BLOCK_MAGIC
Definition xfs_dir.hpp:51
constexpr uint8_t XFS_DIR3_FT_REG_FILE
Definition xfs_dir.hpp:41
constexpr uint32_t XFS_DIR3_BLOCK_MAGIC
Definition xfs_dir.hpp:52
constexpr uint8_t XFS_DIR3_FT_CHRDEV
Definition xfs_dir.hpp:43
bool parse_dir_data_block(const unsigned char *block_buf, uint32_t block_size, bool is_v5, bool has_ftype, bool accept_block_magic, bool accept_data_magic, std::vector< DirEntry > &out)
Definition xfs_dir.cpp:122
constexpr uint8_t XFS_DIR3_FT_DIR
Definition xfs_dir.hpp:42
constexpr uint32_t XFS_DIR3_DATA_MAGIC
Definition xfs_dir.hpp:57
constexpr uint8_t XFS_DIR3_FT_SYMLINK
Definition xfs_dir.hpp:47
bool parse_shortform_dir(const unsigned char *data, uint32_t data_len, bool has_ftype, ShortformDir &out)
Definition xfs_dir.cpp:30
constexpr uint8_t XFS_DIR3_FT_BLKDEV
Definition xfs_dir.hpp:44
constexpr uint32_t XFS_DIR2_DATA_MAGIC
Definition xfs_dir.hpp:56
std::string name
Definition xfs_dir.hpp:63
bool ftype_valid
Definition xfs_dir.hpp:66
uint8_t ftype
Definition xfs_dir.hpp:65
uint64_t ino
Definition xfs_dir.hpp:64
uint64_t parent_ino
Definition xfs_dir.hpp:70
std::vector< DirEntry > entries
Definition xfs_dir.hpp:71