├── .gitignore ├── Basics ├── Basics.h ├── BlockGroupItem.cpp ├── BlockGroupItem.h ├── BtrfsHeader.cpp ├── BtrfsHeader.h ├── BtrfsItem.h ├── BtrfsKey.cpp ├── BtrfsKey.h ├── CMakeLists.txt ├── ChunkData.cpp ├── ChunkData.h ├── ChunkItem.cpp ├── ChunkItem.h ├── DevData.cpp ├── DevData.h ├── DevItem.cpp ├── DevItem.h ├── DirItem.cpp ├── DirItem.h ├── Enums.h ├── Exceptions.h ├── ExtentData.cpp ├── ExtentData.h ├── ExtentItem.cpp ├── ExtentItem.h ├── InodeData.cpp ├── InodeData.h ├── InodeItem.cpp ├── InodeItem.h ├── InodeRef.cpp ├── InodeRef.h ├── ItemHead.cpp ├── ItemHead.h ├── KeyPtr.cpp ├── KeyPtr.h ├── RootItem.cpp ├── RootItem.h ├── RootRef.cpp ├── RootRef.h ├── Stripe.cpp ├── Stripe.h └── UnknownItem.h ├── CMakeLists.txt ├── Doxyfile ├── LICENSE ├── Pool ├── BtrfsPool.cpp ├── BtrfsPool.h ├── CMakeLists.txt ├── DeviceRecord.cpp ├── DeviceRecord.h ├── Functions.cpp ├── Functions.h └── Pool.h ├── README.md ├── Tools ├── CMakeLists.txt ├── DEVLS_README.md ├── FLS_README.md ├── FSSTAT_README.md ├── ICAT_README.md ├── ISTAT_README.md ├── SUBLS_README.md ├── Tools.h ├── devls.cpp ├── fls.cpp ├── fsstat.cpp ├── icat.cpp ├── istat.cpp └── subls.cpp ├── Trees ├── BtrfsNode.cpp ├── BtrfsNode.h ├── CMakeLists.txt ├── ChunkTree.cpp ├── ChunkTree.h ├── DirContent.cpp ├── DirContent.h ├── FilesystemTree.cpp ├── FilesystemTree.h ├── InternalNode.cpp ├── InternalNode.h ├── LeafNode.cpp ├── LeafNode.h ├── SuperBlock.cpp ├── SuperBlock.h └── Trees.h ├── Utility ├── CMakeLists.txt ├── ReadInt.cpp ├── ReadInt.h ├── StringProcess.cpp ├── StringProcess.h ├── Utility.h ├── Uuid.cpp └── Uuid.h └── btrfrsc.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Executable 2 | btrfrsc 3 | fsstat 4 | fls 5 | istat 6 | icat 7 | subls 8 | devls 9 | 10 | # Object files 11 | *.o 12 | *.ko 13 | *.obj 14 | *.elf 15 | 16 | # Precompiled Headers 17 | *.gch 18 | *.pch 19 | 20 | # Libraries 21 | *.lib 22 | *.a 23 | *.la 24 | *.lo 25 | 26 | # Shared objects (inc. Windows DLLs) 27 | *.dll 28 | *.so 29 | *.so.* 30 | *.dylib 31 | 32 | # Executables 33 | *.exe 34 | *.out 35 | *.app 36 | *.i*86 37 | *.x86_64 38 | *.hex 39 | 40 | # Debug files 41 | *.dSYM/ 42 | 43 | # Auto generated documents 44 | docs/* 45 | *.swp 46 | 47 | # Input files 48 | files/* 49 | test 50 | 51 | # CMake build files 52 | build/* 53 | 54 | # VSCode files 55 | .vscode/* 56 | 57 | # Bash scripts 58 | *.sh 59 | -------------------------------------------------------------------------------- /Basics/Basics.h: -------------------------------------------------------------------------------- 1 | #ifndef BASICS_H 2 | #define BASICS_H 3 | 4 | #include "Exceptions.h" 5 | #include "Enums.h" 6 | #include "BtrfsKey.h" 7 | #include "BtrfsHeader.h" 8 | #include "ItemHead.h" 9 | #include "BtrfsItem.h" 10 | #include "KeyPtr.h" 11 | 12 | #include "InodeData.h" 13 | #include "InodeItem.h" 14 | #include "RootItem.h" 15 | #include "RootRef.h" 16 | 17 | #include "InodeRef.h" 18 | #include "DevItem.h" 19 | #include "DirItem.h" 20 | 21 | #include "ExtentData.h" 22 | #include "ChunkItem.h" 23 | #include "Stripe.h" 24 | #include "ExtentItem.h" 25 | #include "BlockGroupItem.h" 26 | 27 | #include "UnknownItem.h" 28 | 29 | #endif 30 | 31 | -------------------------------------------------------------------------------- /Basics/BlockGroupItem.cpp: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Implementation of class BlockGroupItem 5 | 6 | #include 7 | #include "BlockGroupItem.h" 8 | #include "Utility/ReadInt.h" 9 | 10 | namespace btrForensics{ 11 | 12 | //! Constructor of block group item. 13 | //! 14 | //! \param head Item head points to this data. 15 | //! \param endian The endianess of the array. 16 | //! \param arr Byte array storing block group item data. 17 | //! 18 | BlockGroupItem::BlockGroupItem(ItemHead* head, TSK_ENDIAN_ENUM endian, uint8_t arr[]) 19 | :BtrfsItem(head) 20 | { 21 | int arIndex(0); //Key initialized already. 22 | usedAmount = read64Bit(endian, arr + arIndex); 23 | arIndex += 0x08; 24 | 25 | chunkTreeId = read64Bit(endian, arr + arIndex); 26 | arIndex += 0x08; 27 | 28 | flags = read64Bit(endian, arr + arIndex); 29 | arIndex += 0x08; 30 | } 31 | 32 | 33 | //! Return infomation about the item data as string. 34 | std::string BlockGroupItem::dataInfo() const 35 | { 36 | std::ostringstream oss; 37 | oss << std::dec; 38 | oss << "Used amount: " << usedAmount << '\n'; 39 | oss << "Flags: " << flags << '\n'; 40 | return oss.str(); 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /Basics/BlockGroupItem.h: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Header file of class BlockGroupItem 5 | 6 | #ifndef BLK_GRP_ITEM_H 7 | #define BLK_GRP_ITEM_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include "Basics.h" 13 | 14 | namespace btrForensics { 15 | //! Block group item. 16 | class BlockGroupItem : public BtrfsItem { 17 | private: 18 | uint64_t usedAmount; 19 | uint64_t chunkTreeId; 20 | uint64_t flags; 21 | 22 | public: 23 | BlockGroupItem(ItemHead* head, TSK_ENDIAN_ENUM endian, uint8_t arr[]); 24 | ~BlockGroupItem() = default; 25 | 26 | std::string dataInfo() const override; 27 | }; 28 | } 29 | 30 | #endif 31 | 32 | -------------------------------------------------------------------------------- /Basics/BtrfsHeader.cpp: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Implementation of class BtrfsHeader 5 | 6 | #include "BtrfsHeader.h" 7 | #include "Utility/ReadInt.h" 8 | 9 | namespace btrForensics{ 10 | 11 | //! Constructor of Btrfs header. 12 | //! 13 | //! \param endian The endianess of the array. 14 | //! \param arr Byte array storing header data. 15 | //! 16 | BtrfsHeader::BtrfsHeader(TSK_ENDIAN_ENUM endian, uint8_t arr[]) 17 | :fsUUID(endian, arr + 0x20), chunkTrUUID(endian, arr + 0x40) 18 | { 19 | int arIndex(0); 20 | for(int i=0; i<0x20; i++){ 21 | checksum[i] = arr[arIndex++]; 22 | } 23 | 24 | arIndex += 0x10; //fsUUID, initialized ahead. 25 | 26 | address = read64Bit(endian, arr + arIndex); 27 | arIndex += 0x08; 28 | 29 | for(int i=0; i<0x08; i++){ 30 | flags[i] = arr[arIndex++]; 31 | } 32 | 33 | arIndex += 0x10; //chunkTrUUID, initialized ahead. 34 | 35 | generation = read64Bit(endian, arr + arIndex); 36 | arIndex += 0x08; 37 | 38 | treeId = read64Bit(endian, arr + arIndex); 39 | arIndex += 0x08; 40 | 41 | numOfItems = read32Bit(endian, arr + arIndex); 42 | arIndex += 0x04; 43 | 44 | level = arr[arIndex++]; 45 | } 46 | 47 | 48 | //! Get number of items stored in this node. 49 | const uint32_t BtrfsHeader::getNumOfItems() const 50 | { 51 | return numOfItems; 52 | } 53 | 54 | 55 | //! Overloaded stream operator. 56 | std::ostream &operator<<(std::ostream &os, const BtrfsHeader &header) 57 | { 58 | os << "FS UUID: " << header.fsUUID.encode() << '\n'; 59 | 60 | os << std::uppercase << std::hex 61 | << "Node address: " << header.address << '\n'; 62 | 63 | os << "Chunk tree UUID: " << header.chunkTrUUID.encode() << '\n'; 64 | 65 | os << "Generation: " << header.generation << '\n'; 66 | os << "Tree id: " << header.treeId << '\n'; 67 | os << "Number of items: " << header.numOfItems << '\n'; 68 | os << "Level: " << (int)header.level; 69 | 70 | return os; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /Basics/BtrfsHeader.h: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Header file of class BtrfsHeader 5 | 6 | #ifndef BTRFS_HEADER_H 7 | #define BTRFS_HEADER_H 8 | 9 | #include 10 | #include 11 | #include "Utility/Uuid.h" 12 | 13 | namespace btrForensics{ 14 | //! Header of a node in btrfs. 15 | class BtrfsHeader { 16 | private: 17 | uint8_t checksum[0x20]; //0x0 18 | 19 | const UUID fsUUID; //0x20 20 | 21 | uint64_t address; //0x30 22 | uint8_t flags[0x08]; 23 | 24 | const UUID chunkTrUUID; //0x40 25 | 26 | uint64_t generation; //0x50 27 | uint64_t treeId; 28 | 29 | uint32_t numOfItems; //0x60 30 | uint8_t level; 31 | 32 | //Total bytes: 0x65 33 | 34 | public: 35 | BtrfsHeader(TSK_ENDIAN_ENUM endian, uint8_t arr[]); 36 | ~BtrfsHeader() = default; //!< Destructor 37 | 38 | const uint32_t getNumOfItems() const; 39 | 40 | //! Return true if the header indicates it is a leaf node. 41 | const bool isLeafNode() const { return level == 0; } 42 | 43 | friend std::ostream &operator<<( 44 | std::ostream &os, const BtrfsHeader &header); 45 | 46 | static const int SIZE_OF_HEADER = 0x65; //!< Size of node header in bytes. 47 | }; 48 | 49 | } 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /Basics/BtrfsItem.h: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Header file of class BtrfsItem 5 | 6 | #ifndef BTR_ITEM_H 7 | #define BTR_ITEM_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include "ItemHead.h" 13 | 14 | namespace btrForensics{ 15 | //! Btrfs item in a leaf node, this is an abstract class. 16 | class BtrfsItem { 17 | public: 18 | const ItemHead* itemHead ; //!< Item stored in leaf node. 19 | 20 | //! Constructor of item 21 | BtrfsItem(ItemHead* head):itemHead(head) {} 22 | virtual ~BtrfsItem() { delete itemHead; } 23 | 24 | //! Get id of the item. 25 | const uint64_t getId() const { return itemHead->key.objId; } 26 | //! Get type code of the item. 27 | const ItemType getItemType() const { return itemHead->key.itemType; } 28 | 29 | //! Overloaded stream operator. 30 | friend std::ostream &operator<<(std::ostream &os, const BtrfsItem &item) 31 | { 32 | os << "[Item]\n"; 33 | os << *item.itemHead << "\n[Data]\n" << item.dataInfo(); 34 | os << "\n" << std::endl; 35 | return os; 36 | } 37 | 38 | 39 | //! Return infomation about the item data. 40 | //! Virtual function to be overridden by derived classes. 41 | virtual std::string dataInfo() const = 0; 42 | }; 43 | } 44 | 45 | #endif 46 | 47 | -------------------------------------------------------------------------------- /Basics/BtrfsKey.cpp: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Implementation of class BtrfsKey 5 | 6 | #include "BtrfsKey.h" 7 | #include "Utility/ReadInt.h" 8 | 9 | namespace btrForensics{ 10 | 11 | ///! Constructor of key. 12 | //! 13 | //! \param endian The endianess of byte array. 14 | //! \param arr Byte array storing key data. 15 | //! 16 | BtrfsKey::BtrfsKey(TSK_ENDIAN_ENUM endian, uint8_t arr[]) 17 | { 18 | int arIndex = 0; 19 | 20 | objId = read64Bit(endian, arr + arIndex); 21 | arIndex += 8; 22 | 23 | uint8_t type = arr[arIndex++]; 24 | 25 | offset = read64Bit(endian, arr + arIndex); 26 | 27 | switch(type){ 28 | case 0x01: 29 | itemType = ItemType::INODE_ITEM; 30 | break; 31 | case 0x0c: 32 | itemType = ItemType::INODE_REF; 33 | break; 34 | case 0x0d: 35 | itemType = ItemType::INODE_EXTREF; 36 | break; 37 | case 0x18: 38 | itemType = ItemType::XATTR_ITEM; 39 | break; 40 | case 0x30: 41 | itemType = ItemType::ORPHAN_ITEM; 42 | break; 43 | case 0x3c: 44 | itemType = ItemType::DIR_LOG_ITEM; 45 | break; 46 | case 0x48: 47 | itemType = ItemType::DIR_LOG_INDEX; 48 | break; 49 | case 0x54: 50 | itemType = ItemType::DIR_ITEM; 51 | break; 52 | case 0x60: 53 | itemType = ItemType::DIR_INDEX; 54 | break; 55 | case 0x6c: 56 | itemType = ItemType::EXTENT_DATA; 57 | break; 58 | case 0x80: 59 | itemType = ItemType::EXTENT_CSUM; 60 | break; 61 | case 0x84: 62 | itemType = ItemType::ROOT_ITEM; 63 | break; 64 | case 0x90: 65 | itemType = ItemType::ROOT_BACKREF; 66 | break; 67 | case 0x9c: 68 | itemType = ItemType::ROOT_REF; 69 | break; 70 | case 0xa8: 71 | itemType = ItemType::EXTENT_ITEM; 72 | break; 73 | case 0xb0: 74 | itemType = ItemType::TREE_BLOCK_REF; 75 | break; 76 | case 0xb2: 77 | itemType = ItemType::EXTENT_DATA_REF; 78 | break; 79 | case 0xb4: 80 | itemType = ItemType::EXTENT_REF_V0; 81 | break; 82 | case 0xb6: 83 | itemType = ItemType::SHARED_BLOCK_REF; 84 | break; 85 | case 0xb8: 86 | itemType = ItemType::SHARED_DATA_REF; 87 | break; 88 | case 0xc0: 89 | itemType = ItemType::BLOCK_GROUP_ITEM; 90 | break; 91 | case 0xcc: 92 | itemType = ItemType::DEV_EXTENT; 93 | break; 94 | case 0xd8: 95 | itemType = ItemType::DEV_ITEM; 96 | break; 97 | case 0xe4: 98 | itemType = ItemType::CHUNK_ITEM; 99 | break; 100 | case 0xfd: 101 | itemType = ItemType::STRING_ITEM; 102 | break; 103 | default: 104 | itemType = ItemType::UNKNOWN; 105 | } 106 | } 107 | 108 | 109 | 110 | //! Overloaded stream operator. 111 | std::ostream &operator<<(std::ostream &os, const BtrfsKey &key) 112 | { 113 | os << "Key - Object id: " << std::dec << key.objId; 114 | os << std::uppercase << std::hex; 115 | os << " (0x" << key.objId << ")\n"; 116 | os << "Key - Item type: " << key.getItemTypeStr() << '\n'; 117 | os << "Key - Offset: 0x" << key.offset << '\n'; 118 | } 119 | 120 | 121 | //! Get type description of the item. 122 | //! 123 | //! \return Item type in string. 124 | //! 125 | const std::string BtrfsKey::getItemTypeStr() const 126 | { 127 | std::string type; 128 | switch(itemType){ 129 | case ItemType::INODE_ITEM: 130 | type = "inode item"; 131 | break; 132 | case ItemType::INODE_REF: 133 | type = "inode ref"; 134 | break; 135 | case ItemType::INODE_EXTREF: 136 | type = "inode extref"; 137 | break; 138 | case ItemType::XATTR_ITEM: 139 | type = "xattr item"; 140 | break; 141 | case ItemType::ORPHAN_ITEM: 142 | type = "orphan item"; 143 | break; 144 | case ItemType::DIR_LOG_ITEM: 145 | type = "dir log item"; 146 | break; 147 | case ItemType::DIR_LOG_INDEX: 148 | type = "dir log index"; 149 | break; 150 | case ItemType::DIR_ITEM: 151 | type = "dir item"; 152 | break; 153 | case ItemType::DIR_INDEX: 154 | type = "dir index"; 155 | break; 156 | case ItemType::EXTENT_DATA: 157 | type = "extent data"; 158 | break; 159 | case ItemType::EXTENT_CSUM: 160 | type = "extent csum"; 161 | break; 162 | case ItemType::ROOT_ITEM: 163 | type = "root item"; 164 | break; 165 | case ItemType::ROOT_BACKREF: 166 | type = "root backref"; 167 | break; 168 | case ItemType::ROOT_REF: 169 | type = "root ref"; 170 | break; 171 | case ItemType::EXTENT_ITEM: 172 | type = "extent item"; 173 | break; 174 | case ItemType::TREE_BLOCK_REF: 175 | type = "tree block ref"; 176 | break; 177 | case ItemType::EXTENT_DATA_REF: 178 | type = "extent data ref"; 179 | break; 180 | case ItemType::EXTENT_REF_V0: 181 | type = "extent ref v0"; 182 | break; 183 | case ItemType::SHARED_BLOCK_REF: 184 | type = "shared block ref"; 185 | break; 186 | case ItemType::SHARED_DATA_REF: 187 | type = "shared data ref"; 188 | break; 189 | case ItemType::BLOCK_GROUP_ITEM: 190 | type = "block group item"; 191 | break; 192 | case ItemType::DEV_EXTENT: 193 | type = "dev extent"; 194 | break; 195 | case ItemType::DEV_ITEM: 196 | type = "dev item"; 197 | break; 198 | case ItemType::CHUNK_ITEM: 199 | type = "chunk item"; 200 | break; 201 | case ItemType::STRING_ITEM: 202 | type = "string item"; 203 | break; 204 | default: 205 | type = "unknown"; 206 | break; 207 | } 208 | 209 | return type; 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /Basics/BtrfsKey.h: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Header file of class BtrfsKey 5 | 6 | #ifndef BTR_KEY_H 7 | #define BTR_KEY_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include "Enums.h" 13 | 14 | namespace btrForensics{ 15 | //! Key of an item 16 | class BtrfsKey { 17 | public: 18 | uint64_t objId; //!< Object id. 19 | ItemType itemType; //!< For detailed list, please read Btrfs Wiki. 20 | uint64_t offset; //!< The meaning depends on the item type. 21 | 22 | //Total bytes: 0x11 23 | public: 24 | BtrfsKey(TSK_ENDIAN_ENUM endian, uint8_t arr[]); 25 | ~BtrfsKey() = default; //!< Destructor 26 | 27 | friend std::ostream &operator<<( 28 | std::ostream &os, const BtrfsKey &key); 29 | 30 | //const uint8_t getItemType() const; 31 | const std::string getItemTypeStr() const; 32 | 33 | static const int SIZE_OF_KEY = 0x11; //!< Size of a key in bytes. 34 | }; 35 | } 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /Basics/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${PROJECT_SOURCE_DIR}) 2 | 3 | aux_source_directory(. BASICS_SRCS) 4 | 5 | add_library(Basics ${BASICS_SRCS}) 6 | 7 | -------------------------------------------------------------------------------- /Basics/ChunkData.cpp: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Implementation of class ChunkData 5 | 6 | #include 7 | #include "ChunkData.h" 8 | #include "Utility/ReadInt.h" 9 | 10 | namespace btrForensics{ 11 | 12 | //! Constructor of ChunkData. 13 | //! 14 | //! \param endian The endianess of the array. 15 | //! \param arr Byte array storing inode data. 16 | //! 17 | ChunkData::ChunkData(TSK_ENDIAN_ENUM endian, uint8_t arr[]) 18 | { 19 | int arIndex(0); 20 | 21 | chunkSize = read64Bit(endian, arr + arIndex); 22 | arIndex += 0x08; 23 | objId = read64Bit(endian, arr + arIndex); 24 | arIndex += 0x08; 25 | 26 | stripeLength = read64Bit(endian, arr + arIndex); 27 | arIndex += 0x08; 28 | type = read64Bit(endian, arr + arIndex); 29 | arIndex += 0x08; 30 | 31 | optIoAlignment = read32Bit(endian, arr + arIndex); 32 | arIndex += 0x04; 33 | optIoWidth = read32Bit(endian, arr + arIndex); 34 | arIndex += 0x04; 35 | minIoSize = read32Bit(endian, arr + arIndex); 36 | arIndex += 0x04; 37 | 38 | numStripe = read16Bit(endian, arr + arIndex); 39 | arIndex += 0x02; 40 | subStripe = read16Bit(endian, arr + arIndex); 41 | arIndex += 0x02; 42 | 43 | for(uint16_t i=0; ideviceId << '\n'; 69 | oss << "Offset: 0x" << std::uppercase << std::hex << stripe->offset << '\n'; 70 | oss << "Device UUID: " << stripe->devUUID.encode() << '\n'; 71 | } 72 | oss << "----------------------------------------------" << '\n'; 73 | return oss.str(); 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /Basics/ChunkData.h: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Header file of class ChunkData 5 | 6 | #ifndef CHUNK_DATA_H 7 | #define CHUNK_DATA_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "Utility/Utility.h" 14 | #include "Stripe.h" 15 | 16 | using std::vector; 17 | 18 | namespace btrForensics{ 19 | //! Chunk item data. 20 | class ChunkData { 21 | public: 22 | uint64_t chunkSize; 23 | uint64_t objId; 24 | 25 | uint64_t stripeLength; //0x10 26 | uint64_t type; 27 | 28 | uint32_t optIoAlignment; //0x20 29 | uint32_t optIoWidth; 30 | uint32_t minIoSize; 31 | 32 | uint16_t numStripe; 33 | uint16_t subStripe; 34 | 35 | vector btrStripes; //0x30 36 | 37 | public: 38 | ChunkData(TSK_ENDIAN_ENUM endian, uint8_t arr[]); 39 | ~ChunkData(); 40 | 41 | std::string dataInfo() const ; 42 | 43 | //! Get offset 44 | //uint64_t getOffset() const { return offset; } 45 | 46 | //static const int SIZE_OF_CHUNK_DATA= 0x30; //!< Size of chunk item data in bytes. 47 | }; 48 | } 49 | 50 | #endif 51 | 52 | 53 | -------------------------------------------------------------------------------- /Basics/ChunkItem.cpp: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Implementation of class ChunkItem 5 | 6 | #include 7 | #include "ChunkItem.h" 8 | #include "Utility/ReadInt.h" 9 | 10 | namespace btrForensics{ 11 | 12 | //! Constructor of ChunkItem. 13 | //! 14 | //! \param head Item head points to this data. 15 | //! \param endian The endianess of the array. 16 | //! \param arr Byte array storing chunk item data. 17 | //! 18 | ChunkItem::ChunkItem(ItemHead* head, TSK_ENDIAN_ENUM endian, uint8_t arr[]) 19 | :BtrfsItem(head), data(endian, arr) 20 | { 21 | } 22 | 23 | //! Return infomation about the item data as string. 24 | std::string ChunkItem::dataInfo() const 25 | { 26 | return data.dataInfo(); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /Basics/ChunkItem.h: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Header file of class ChunkItem 5 | 6 | #ifndef CHUNK_ITEM_H 7 | #define CHUNK_ITEM_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include "BtrfsItem.h" 13 | #include "ChunkData.h" 14 | 15 | namespace btrForensics{ 16 | //! Chunk item data. 17 | class ChunkItem : public BtrfsItem { 18 | public: 19 | ChunkData data; //!< Data part of chunk item. 20 | 21 | public: 22 | ChunkItem(ItemHead* head, TSK_ENDIAN_ENUM endian, uint8_t arr[]); 23 | ~ChunkItem() = default; //!< Destructor 24 | 25 | std::string dataInfo() const override; 26 | }; 27 | } 28 | 29 | #endif 30 | 31 | -------------------------------------------------------------------------------- /Basics/DevData.cpp: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Dev item infomation. 5 | 6 | #include 7 | #include "DevData.h" 8 | #include "Utility/ReadInt.h" 9 | 10 | namespace btrForensics{ 11 | 12 | //! Constructor of device item. 13 | //! 14 | //! \param endian The endianess of the array. 15 | //! \param arr Byte array storing device item data. 16 | //! 17 | DevData::DevData(TSK_ENDIAN_ENUM endian, uint8_t arr[]) 18 | :devUUID(endian, arr + 0x42), fsUUID(endian, arr + 0x52) 19 | { 20 | int arIndex(0); 21 | 22 | deviceId = read64Bit(endian, arr + arIndex); 23 | arIndex += 0x08; 24 | 25 | bytes = read64Bit(endian, arr + arIndex); 26 | arIndex += 0x08; 27 | 28 | bytesUsed = read64Bit(endian, arr + arIndex); 29 | arIndex += 0x08; 30 | 31 | optIOAlign = read32Bit(endian, arr + arIndex); 32 | arIndex += 0x04; 33 | 34 | optIOWidth = read32Bit(endian, arr + arIndex); 35 | arIndex += 0x04; 36 | 37 | minimalIOSize = read32Bit(endian, arr + arIndex); 38 | arIndex += 0x04; 39 | 40 | type = read64Bit(endian, arr + arIndex); 41 | arIndex += 0x08; 42 | 43 | generation = read64Bit(endian, arr + arIndex); 44 | arIndex += 0x08; 45 | 46 | offset = read64Bit(endian, arr + arIndex); 47 | arIndex += 0x08; 48 | 49 | devGroup = read32Bit(endian, arr + arIndex); 50 | arIndex += 0x04; 51 | 52 | seekSpeed = arr[arIndex++]; 53 | bandWidth = arr[arIndex++]; 54 | } 55 | 56 | //! Return infomation about the item data as string 57 | std::string DevData::dataInfo() const 58 | { 59 | std::ostringstream oss; 60 | oss << "Device ID: " << deviceId << '\n'; 61 | oss << "Device UUID: " << devUUID.encode() << '\n'; 62 | oss << std::dec; 63 | oss << "Total bytes: " << humanSize(bytes) << '\n'; 64 | oss << "Bytes used: " << humanSize(bytesUsed) << '\n'; 65 | oss << "File System UUID: " << fsUUID.encode() << '\n'; 66 | return oss.str(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Basics/DevData.h: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Header file of DevData. 5 | 6 | #ifndef DEV_DATA 7 | #define DEV_DATA 8 | 9 | #include "Utility/Uuid.h" 10 | #include "BtrfsItem.h" 11 | 12 | namespace btrForensics{ 13 | //! Device item data. 14 | class DevData { 15 | public: 16 | uint64_t deviceId; //0x0 17 | uint64_t bytes; 18 | uint64_t bytesUsed; 19 | 20 | uint32_t optIOAlign; 21 | uint32_t optIOWidth; 22 | uint32_t minimalIOSize; 23 | 24 | uint64_t type; 25 | uint64_t generation; 26 | uint64_t offset; 27 | uint32_t devGroup; 28 | 29 | uint8_t seekSpeed; //0x40 30 | uint8_t bandWidth; 31 | 32 | UUID devUUID; 33 | UUID fsUUID; 34 | 35 | public: 36 | DevData(TSK_ENDIAN_ENUM endian, uint8_t arr[]); 37 | ~DevData() = default; //!< Destructor 38 | 39 | std::string dataInfo() const; 40 | }; 41 | } 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /Basics/DevItem.cpp: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Implementation of class DevItem 5 | 6 | #include 7 | #include "DevItem.h" 8 | #include "Utility/ReadInt.h" 9 | 10 | namespace btrForensics{ 11 | 12 | //! Constructor of DevItem. 13 | //! 14 | //! \param head Item head points to this data. 15 | //! \param endian The endianess of the array. 16 | //! \param arr Byte array storing inode data. 17 | //! 18 | DevItem::DevItem(ItemHead* head, TSK_ENDIAN_ENUM endian, uint8_t arr[]) 19 | :BtrfsItem(head), data(endian, arr) 20 | { 21 | } 22 | 23 | //! Return infomation about the item data as string. 24 | std::string DevItem::dataInfo() const 25 | { 26 | return data.dataInfo(); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /Basics/DevItem.h: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Header file of class DevItem 5 | 6 | #ifndef DEV_ITEM_H 7 | #define DEV_ITEM_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include "BtrfsItem.h" 13 | #include "DevData.h" 14 | 15 | namespace btrForensics{ 16 | //! Dev item data. 17 | class DevItem : public BtrfsItem { 18 | private: 19 | DevData data; 20 | 21 | public: 22 | DevItem(ItemHead* head, TSK_ENDIAN_ENUM endian, uint8_t arr[]); 23 | ~DevItem() = default; //!< Destructor 24 | 25 | std::string dataInfo() const override; 26 | }; 27 | } 28 | 29 | #endif 30 | 31 | -------------------------------------------------------------------------------- /Basics/DirItem.cpp: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Implementation of class DirItem 5 | 6 | #include 7 | #include "DirItem.h" 8 | #include "Utility/ReadInt.h" 9 | 10 | namespace btrForensics{ 11 | 12 | //! Constructor of dir item. 13 | //! 14 | //! \param head Item head points to this data. 15 | //! \param endian The endianess of the array. 16 | //! \param arr Byte array storing dir item data. 17 | //! 18 | DirItem::DirItem(ItemHead* head, TSK_ENDIAN_ENUM endian, uint8_t arr[]) 19 | :BtrfsItem(head), targetKey(endian, arr) 20 | { 21 | int arIndex(BtrfsKey::SIZE_OF_KEY); //Key initialized already. 22 | transId = read64Bit(endian, arr + arIndex); 23 | arIndex += 0x08; 24 | 25 | dataSize = read16Bit(endian, arr + arIndex); 26 | arIndex += 0x02; 27 | nameSize = read16Bit(endian, arr + arIndex); 28 | arIndex += 0x02; 29 | 30 | childType = arr[arIndex++]; 31 | 32 | dirName = new char[nameSize](); 33 | for(int i=0; i < nameSize; ++i){ 34 | dirName[i] = arr[arIndex++]; 35 | } 36 | 37 | switch(childType) { 38 | case 0: 39 | type = DirItemType::UNKNOWN; 40 | break; 41 | case 1: 42 | type = DirItemType::REGULAR_FILE; 43 | break; 44 | case 2: 45 | type = DirItemType::DIRECTORY; 46 | break; 47 | case 3: 48 | type = DirItemType::CHAR_DEVICE; 49 | break; 50 | case 4: 51 | type = DirItemType::BLK_DEVICE; 52 | break; 53 | case 5: 54 | type = DirItemType::FIFO; 55 | break; 56 | case 6: 57 | type = DirItemType::SOCKET; 58 | break; 59 | case 7: 60 | type = DirItemType::SYMB_LINK; 61 | break; 62 | case 8: 63 | type = DirItemType::EXT_ATTR; 64 | break; 65 | } 66 | 67 | //Directory data not processed yet. 68 | } 69 | 70 | 71 | //!< Destructor 72 | DirItem::~DirItem() 73 | { 74 | if(dirName != nullptr) 75 | delete [] dirName; 76 | } 77 | 78 | 79 | //! Return name of the directory. 80 | std::string DirItem::getDirName() const 81 | { 82 | return std::string(dirName, nameSize); 83 | } 84 | 85 | 86 | //! Return infomation about the item data as string. 87 | std::string DirItem::dataInfo() const 88 | { 89 | std::ostringstream oss; 90 | oss << targetKey; 91 | oss << "Type: " << (int)childType << '\n'; 92 | oss << "Name: " << getDirName(); 93 | return oss.str(); 94 | } 95 | } 96 | 97 | -------------------------------------------------------------------------------- /Basics/DirItem.h: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Header file of class DirItem 5 | 6 | #ifndef DIR_ITEM_H 7 | #define DIR_ITEM_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include "Basics.h" 13 | 14 | namespace btrForensics { 15 | //! Directory item data. 16 | class DirItem : public BtrfsItem { 17 | public: 18 | const BtrfsKey targetKey; //!< Key of the item. 19 | DirItemType type; //!< Type of directory item. 20 | private: 21 | uint64_t transId; 22 | uint16_t dataSize; 23 | uint16_t nameSize; 24 | uint8_t childType; 25 | char *dirName; 26 | char *dirData; 27 | 28 | public: 29 | DirItem(ItemHead* head, TSK_ENDIAN_ENUM endian, uint8_t arr[]); 30 | ~DirItem(); 31 | 32 | //! Get inode number of target this item points to. 33 | uint64_t getTargetInode() { return targetKey.objId; } 34 | 35 | //! Get item type of target this item points to. 36 | ItemType getTargetType() { return targetKey.itemType; } 37 | 38 | std::string getDirName() const; 39 | 40 | std::string dataInfo() const override; 41 | }; 42 | } 43 | 44 | #endif 45 | 46 | -------------------------------------------------------------------------------- /Basics/Enums.h: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Define useful enumerations 5 | 6 | #ifndef ENUMS_H 7 | #define ENUMS_H 8 | 9 | #include 10 | 11 | namespace btrForensics { 12 | //! Type of an item. 13 | enum class ItemType : uint8_t { 14 | INODE_ITEM = 0x01, 15 | INODE_REF= 0x0c, 16 | INODE_EXTREF= 0x0d, 17 | XATTR_ITEM = 0x18, 18 | ORPHAN_ITEM = 0x30, 19 | DIR_LOG_ITEM = 0x3c, 20 | DIR_LOG_INDEX = 0x48, 21 | DIR_ITEM = 0x54, 22 | DIR_INDEX = 0x60, 23 | EXTENT_DATA = 0x6c, 24 | EXTENT_CSUM = 0x80, 25 | ROOT_ITEM = 0x84, 26 | ROOT_BACKREF = 0x90, 27 | ROOT_REF = 0x9c, 28 | EXTENT_ITEM = 0xa8, 29 | TREE_BLOCK_REF = 0xb0, 30 | EXTENT_DATA_REF = 0xb2, 31 | EXTENT_REF_V0 = 0xb4, 32 | SHARED_BLOCK_REF = 0xb6, 33 | SHARED_DATA_REF = 0xb8, 34 | BLOCK_GROUP_ITEM = 0xc0, 35 | DEV_EXTENT = 0xcc, 36 | DEV_ITEM = 0xd8, 37 | CHUNK_ITEM = 0xe4, 38 | STRING_ITEM = 0xfd, 39 | UNKNOWN = 0xff 40 | }; 41 | 42 | //! Type of a dir item. 43 | enum class DirItemType : uint8_t { 44 | UNKNOWN, //!< Unknown 45 | REGULAR_FILE, //!< Regular file 46 | DIRECTORY, //!< Directory 47 | CHAR_DEVICE, //!< Character device 48 | BLK_DEVICE, //!< Block device 49 | FIFO, //!< FIFO 50 | SOCKET, //!< Socket 51 | SYMB_LINK, //!< Symbolic link 52 | EXT_ATTR //!< Extended attribute 53 | }; 54 | } 55 | 56 | #endif -------------------------------------------------------------------------------- /Basics/Exceptions.h: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Define exceptions. 5 | 6 | #ifndef EXCPT_H 7 | #define EXCPT_H 8 | 9 | #include 10 | #include 11 | 12 | namespace btrForensics { 13 | 14 | //! Exception thrown when read data does no match Btrfs format. 15 | class FsDamagedException : public std::runtime_error { 16 | public: 17 | explicit FsDamagedException(const std::string& str) 18 | :std::runtime_error(str) {} 19 | }; 20 | 21 | 22 | //! Exception thrown when device information not matched. 23 | class FsDeviceException : public std::runtime_error { 24 | public: 25 | explicit FsDeviceException(const std::string& str) 26 | :std::runtime_error(str) {} 27 | }; 28 | } 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /Basics/ExtentData.cpp: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Header file of class ExtentData 5 | 6 | #include 7 | #include "ExtentData.h" 8 | 9 | namespace btrForensics { 10 | //! Constructor of extent data. 11 | //! 12 | //! \param head Item head points to this data. 13 | //! \param endian The endianess of the array. 14 | //! \param arr Byte array storing extent data. 15 | //! \param address Physical address of current Btrfs item data part. 16 | //! 17 | ExtentData::ExtentData(ItemHead* head, TSK_ENDIAN_ENUM endian, uint8_t arr[], uint64_t address) 18 | :BtrfsItem(head) 19 | { 20 | int arIndex(0); 21 | 22 | generation = read64Bit(endian, arr + arIndex); 23 | arIndex += 0x08; 24 | 25 | decodedSize = read64Bit(endian, arr + arIndex); 26 | arIndex += 0x08; 27 | 28 | compression = arr[arIndex++]; 29 | encryption = arr[arIndex++]; 30 | 31 | otherEncoding = read16Bit(endian, arr + arIndex); 32 | arIndex += 0x02; 33 | type = arr[arIndex++]; 34 | 35 | if(type == 0) { 36 | dataAddress = address + PART_ONE_SIZE; 37 | } 38 | else { 39 | logicalAddress = read64Bit(endian, arr + arIndex); 40 | arIndex += 0x08; 41 | extentSize = read64Bit(endian, arr + arIndex); 42 | arIndex += 0x08; 43 | extentOffset = read64Bit(endian, arr + arIndex); 44 | arIndex += 0x08; 45 | numOfBytes = read64Bit(endian, arr + arIndex); 46 | arIndex += 0x08; 47 | } 48 | } 49 | 50 | 51 | //! Return infomation about the item data as string. 52 | std::string ExtentData::dataInfo() const 53 | { 54 | std::ostringstream oss; 55 | oss << "Size: " << std::dec << decodedSize << " bytes\n"; 56 | oss << "Compression: " << (int)compression << "\n"; 57 | oss << "Type: " << (type==0 ? "inline" : "regular") << "\n"; 58 | 59 | if(type != 0) { 60 | oss << std::uppercase << std::hex; 61 | oss << "Logical address of extent: " << logicalAddress << "\n"; 62 | oss << "Size of extent: " << extentSize << "\n"; 63 | oss << "Offset within extent: " << extentOffset << "\n"; 64 | oss << "Logical number of bytes: " << numOfBytes << "\n"; 65 | } 66 | 67 | return oss.str(); 68 | } 69 | } -------------------------------------------------------------------------------- /Basics/ExtentData.h: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Header file of class ExtentData 5 | 6 | #ifndef EXTENT_DATA_H 7 | #define EXTENT_DATA_H 8 | 9 | #include 10 | #include 11 | #include "Basics.h" 12 | 13 | namespace btrForensics { 14 | //! Contents of a file 15 | class ExtentData : public BtrfsItem { 16 | public: 17 | uint64_t dataAddress; //!< Physical address of inline data, only works for inline file. 18 | 19 | uint64_t generation; //!< Generaion. 20 | uint64_t decodedSize; //!< Decoded size of extent. 21 | uint8_t compression; //!< Compression algorithm, 0 for none. 22 | uint8_t encryption; //!< Encryption algorithm, 0 for none. 23 | uint16_t otherEncoding; //!< 0 for none. 24 | uint8_t type; //!< 0 if inline. 25 | 26 | uint64_t logicalAddress; //!< Logical address of extent for non-inline file. 27 | uint64_t extentSize; //!< Size of extent for non-inline file. 28 | uint64_t extentOffset; //!< Offset within extent for non-inline file. 29 | uint64_t numOfBytes; //!< Logical number of bytes in extent for non-inline file. 30 | 31 | public: 32 | ExtentData(ItemHead* head, TSK_ENDIAN_ENUM endian, uint8_t arr[], uint64_t address); 33 | ~ExtentData() = default; //!< Destructor 34 | 35 | std::string dataInfo() const override; 36 | 37 | static const uint64_t PART_ONE_SIZE = 0x15; //!< Size of first part of extent data. 38 | }; 39 | } 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /Basics/ExtentItem.cpp: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Implementation of class ExtentItem 5 | 6 | #include 7 | #include "ExtentItem.h" 8 | #include "Utility/ReadInt.h" 9 | 10 | namespace btrForensics{ 11 | 12 | //! Constructor of extent item. 13 | //! 14 | //! \param head Item head points to this data. 15 | //! \param endian The endianess of the array. 16 | //! \param arr Byte array storing extent item data. 17 | //! 18 | ExtentItem::ExtentItem(ItemHead* head, TSK_ENDIAN_ENUM endian, uint8_t arr[]) 19 | :BtrfsItem(head), key(endian, arr+0x18) 20 | { 21 | int arIndex(0); //Key initialized already. 22 | refCount = read64Bit(endian, arr + arIndex); 23 | arIndex += 0x08; 24 | 25 | generation = read64Bit(endian, arr + arIndex); 26 | arIndex += 0x08; 27 | 28 | flags = read64Bit(endian, arr + arIndex); 29 | arIndex += 0x08; 30 | 31 | arIndex += BtrfsKey::SIZE_OF_KEY; 32 | 33 | level = arr[arIndex++]; 34 | } 35 | 36 | 37 | //! Return infomation about the item data as string. 38 | std::string ExtentItem::dataInfo() const 39 | { 40 | std::ostringstream oss; 41 | oss << std::dec; 42 | oss << "Reference count: " << refCount << '\n'; 43 | oss << "Generation: " << generation << '\n'; 44 | oss << "Flags: " << flags << '\n'; 45 | oss << key; 46 | oss << (int)level << "\n"; 47 | return oss.str(); 48 | } 49 | } 50 | 51 | -------------------------------------------------------------------------------- /Basics/ExtentItem.h: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Header file of class ExtentItem 5 | 6 | #ifndef EXTENT_ITEM_H 7 | #define EXTENT_ITEM_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include "Basics.h" 13 | 14 | namespace btrForensics { 15 | //! Extent item. 16 | class ExtentItem : public BtrfsItem { 17 | private: 18 | uint64_t refCount; 19 | uint64_t generation; 20 | uint64_t flags; 21 | BtrfsKey key; 22 | uint8_t level; 23 | 24 | public: 25 | ExtentItem(ItemHead* head, TSK_ENDIAN_ENUM endian, uint8_t arr[]); 26 | ~ExtentItem() = default; 27 | 28 | std::string dataInfo() const override; 29 | }; 30 | } 31 | 32 | #endif 33 | 34 | -------------------------------------------------------------------------------- /Basics/InodeData.cpp: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Implementation of class InodeData 5 | 6 | #include 7 | #include "InodeData.h" 8 | #include "Utility/ReadInt.h" 9 | 10 | namespace btrForensics{ 11 | 12 | //! Constructor of InodeData. 13 | //! 14 | //! \param endian The endianess of the array. 15 | //! \param arr Byte array storing inode data. 16 | //! 17 | InodeData::InodeData(TSK_ENDIAN_ENUM endian, uint8_t arr[]) 18 | { 19 | int arIndex(0); 20 | 21 | generation = read64Bit(endian, arr + arIndex); 22 | arIndex += 0x08; 23 | 24 | transId = read64Bit(endian, arr + arIndex); 25 | arIndex += 0x08; 26 | 27 | stSize = read64Bit(endian, arr + arIndex); 28 | arIndex += 0x08; 29 | 30 | stBlocks = read64Bit(endian, arr + arIndex); 31 | arIndex += 0x08; 32 | 33 | blockGroup = read64Bit(endian, arr + arIndex); 34 | arIndex += 0x08; 35 | 36 | stNlink = read32Bit(endian, arr + arIndex); 37 | arIndex += 0x04; 38 | 39 | stUid = read32Bit(endian, arr + arIndex); 40 | arIndex += 0x04; 41 | 42 | stGid = read32Bit(endian, arr + arIndex); 43 | arIndex += 0x04; 44 | 45 | stMode = read32Bit(endian, arr + arIndex); 46 | arIndex += 0x04; 47 | 48 | stRdev = read64Bit(endian, arr + arIndex); 49 | arIndex += 0x08; 50 | 51 | for(int i=0; i<0x08; i++){ 52 | flags[i] = arr[arIndex++]; 53 | } 54 | 55 | sequence = read64Bit(endian, arr + arIndex); 56 | arIndex += 0x08; 57 | 58 | arIndex += 0x20; //Reserved 59 | 60 | accessTime = read64Bit(endian, arr + arIndex); 61 | arIndex += 0x0c; //4 extra bytes of nano seconds omitted. 62 | 63 | createdTime = read64Bit(endian, arr + arIndex); 64 | arIndex += 0x0c; //4 extra bytes of nano seconds omitted. 65 | 66 | modifiedTime = read64Bit(endian, arr + arIndex); 67 | arIndex += 0x0c; //4 extra bytes of nano seconds omitted. 68 | 69 | } 70 | 71 | //! Return infomation about the item data as string. 72 | std::string InodeData::dataInfo() const 73 | { 74 | std::ostringstream oss; 75 | oss << "Size: " << std::dec << humanSize(stSize) << "\n"; 76 | oss << "Created time: " << std::asctime(std::localtime(&createdTime)); 77 | oss << "Access time: " << std::asctime(std::localtime(&accessTime)); 78 | oss << "Modified time: " << std::asctime(std::localtime(&modifiedTime)); 79 | return oss.str(); 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /Basics/InodeData.h: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Header file of class InodeData 5 | 6 | #ifndef INODE_DATA_H 7 | #define INODE_DATA_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace btrForensics{ 15 | //! Inode item data. 16 | class InodeData { 17 | private: 18 | uint64_t generation; 19 | uint64_t transId; 20 | 21 | uint64_t stSize; //0x10 22 | uint64_t stBlocks; 23 | 24 | uint64_t blockGroup; //0x20 25 | uint32_t stNlink; 26 | uint32_t stUid; 27 | uint32_t stGid; 28 | uint32_t stMode; 29 | uint64_t stRdev; 30 | 31 | uint8_t flags[8]; //0x40 32 | uint64_t sequence; 33 | 34 | public: 35 | time_t accessTime; //!< Acess time. 36 | time_t createdTime; //!< Inode info change time. 37 | time_t modifiedTime; //!< Modification time. 38 | 39 | public: 40 | InodeData(TSK_ENDIAN_ENUM endian, uint8_t arr[]); 41 | ~InodeData() = default; //!< Destructor 42 | 43 | //! Get size of the file. 44 | uint64_t getSize() const { return stSize; } 45 | 46 | std::string dataInfo() const ; 47 | 48 | static const int SIZE_OF_INODE_DATA= 0xa0; //!< Size of an inode in bytes. 49 | }; 50 | } 51 | 52 | #endif 53 | 54 | -------------------------------------------------------------------------------- /Basics/InodeItem.cpp: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Implementation of class InodeItem 5 | 6 | #include 7 | #include "InodeItem.h" 8 | #include "Utility/ReadInt.h" 9 | 10 | namespace btrForensics{ 11 | 12 | //! Constructor of InodeItem. 13 | //! 14 | //! \param head Item head points to this data. 15 | //! \param endian The endianess of the array. 16 | //! \param arr Byte array storing inode data. 17 | //! 18 | InodeItem::InodeItem(ItemHead* head, TSK_ENDIAN_ENUM endian, uint8_t arr[]) 19 | :BtrfsItem(head), data(endian, arr) 20 | { 21 | } 22 | 23 | 24 | //! Print time info. 25 | std::string InodeItem::printTime() const 26 | { 27 | std::stringstream oss; 28 | oss << "Created time: " << std::asctime(std::localtime(&data.createdTime)); 29 | oss << "Access time: " << std::asctime(std::localtime(&data.accessTime)); 30 | oss << "Modified time: " << std::asctime(std::localtime(&data.modifiedTime)); 31 | return oss.str(); 32 | } 33 | 34 | 35 | //! Return infomation about the item data as string. 36 | std::string InodeItem::dataInfo() const 37 | { 38 | return data.dataInfo(); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /Basics/InodeItem.h: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Header file of class InodeItem 5 | 6 | #ifndef INODE_ITEM_H 7 | #define INODE_ITEM_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include "BtrfsItem.h" 13 | #include "InodeData.h" 14 | 15 | namespace btrForensics{ 16 | //! Inode item data. 17 | class InodeItem : public BtrfsItem { 18 | private: 19 | InodeData data; 20 | 21 | public: 22 | InodeItem(ItemHead* head, TSK_ENDIAN_ENUM endian, uint8_t arr[]); 23 | ~InodeItem() = default; //!< Destructor 24 | 25 | std::string printTime() const; 26 | 27 | //! Get size of the file. 28 | uint64_t getSize() const { return data.getSize(); } 29 | 30 | std::string dataInfo() const override; 31 | }; 32 | } 33 | 34 | #endif 35 | 36 | -------------------------------------------------------------------------------- /Basics/InodeRef.cpp: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Implementation of class InodeRef 5 | 6 | #include 7 | #include "InodeRef.h" 8 | #include "Utility/ReadInt.h" 9 | 10 | namespace btrForensics{ 11 | 12 | //! Constructor of inode ref. 13 | //! 14 | //! \param head Item head points to this data. 15 | //! \param endian The endianess of the array. 16 | //! \param arr Byte array storing inode ref data. 17 | //! 18 | InodeRef::InodeRef(ItemHead* head, TSK_ENDIAN_ENUM endian, uint8_t arr[]) 19 | :BtrfsItem(head) 20 | { 21 | int arIndex(0); 22 | indexInDir = read64Bit(endian, arr + arIndex); 23 | arIndex += 0x08; 24 | 25 | nameSize = read16Bit(endian, arr + arIndex); 26 | arIndex += 0x02; 27 | 28 | nameInDir = new char[nameSize](); 29 | for(int i=0; i < nameSize; ++i){ 30 | nameInDir[i] = arr[arIndex++]; 31 | } 32 | } 33 | 34 | 35 | //!< Destructor 36 | InodeRef::~InodeRef() 37 | { 38 | if(nameInDir != nullptr) 39 | delete [] nameInDir; 40 | } 41 | 42 | 43 | //! Return name of the inode reference. 44 | std::string InodeRef::getDirName() const 45 | { 46 | return std::string(nameInDir, nameSize); 47 | } 48 | 49 | 50 | //! Return infomation about the item data as string. 51 | std::string InodeRef::dataInfo() const 52 | { 53 | std::ostringstream oss; 54 | oss << "Index in directory: " << indexInDir << '\n'; 55 | oss << "Name in directory: " << getDirName(); 56 | return oss.str(); 57 | } 58 | } 59 | 60 | -------------------------------------------------------------------------------- /Basics/InodeRef.h: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Header file of class InodeRef 5 | 6 | #ifndef INODE_REF_H 7 | #define INODE_REF_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include "BtrfsItem.h" 13 | 14 | namespace btrForensics{ 15 | //! Inode reference data. 16 | class InodeRef : public BtrfsItem { 17 | private: 18 | uint64_t indexInDir; 19 | uint16_t nameSize; 20 | char *nameInDir; 21 | 22 | public: 23 | InodeRef(ItemHead* head, TSK_ENDIAN_ENUM endian, uint8_t arr[]); 24 | ~InodeRef(); 25 | 26 | std::string getDirName() const; 27 | 28 | std::string dataInfo() const override; 29 | }; 30 | } 31 | 32 | #endif 33 | 34 | -------------------------------------------------------------------------------- /Basics/ItemHead.cpp: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Implementation of class ItemHead 5 | 6 | #include "ItemHead.h" 7 | #include "Utility/ReadInt.h" 8 | 9 | namespace btrForensics{ 10 | 11 | //! Constructor of Btrfs header. 12 | //! 13 | //! \param endian The endianess of the array. 14 | //! \param arr Byte array storing item head data. 15 | //! \param headerEnd Physical address of leaf header ending. 16 | //! \param offset Offset from leaf header endint to this item. 17 | //! 18 | ItemHead::ItemHead(TSK_ENDIAN_ENUM endian, uint8_t arr[], uint64_t headerEnd, uint64_t offset) 19 | :key(endian, arr), itemPhyAddr(headerEnd + offset) 20 | { 21 | int arIndex(BtrfsKey::SIZE_OF_KEY); //Key initialized already. 22 | dataOffset = read32Bit(endian, arr + arIndex); 23 | arIndex += 0x04; 24 | 25 | dataSize = read32Bit(endian, arr + arIndex); 26 | arIndex += 0x04; 27 | 28 | dataPhyAddr = headerEnd + dataOffset; 29 | } 30 | 31 | 32 | //! Overloaded stream operator. 33 | std::ostream &operator<<(std::ostream &os, const ItemHead &itemHead) 34 | { 35 | os << "Item physical address: 0x" << itemHead.itemPhyAddr << '\n'; 36 | os << "Data physical address: 0x" << itemHead.dataPhyAddr << "\n"; 37 | os << "Data size: 0x" << itemHead.dataSize << "\n\n"; 38 | os << itemHead.key << '\n'; 39 | 40 | return os; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /Basics/ItemHead.h: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Header file of class ItemHead 5 | 6 | #ifndef ITEM_HAED_H 7 | #define ITEM_HAED_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include "BtrfsKey.h" 13 | 14 | namespace btrForensics{ 15 | //! Node item's head, stored right after node header. 16 | class ItemHead { 17 | public: 18 | const BtrfsKey key; //!< Key of the item. 19 | private: 20 | uint32_t dataOffset; //!< Relative to end of header. 21 | uint32_t dataSize; 22 | 23 | //Total bytes: 0x19 24 | 25 | uint64_t itemPhyAddr; //!< Physical address of item head. 26 | uint64_t dataPhyAddr; //!< Physical address of item data. 27 | public: 28 | ItemHead(TSK_ENDIAN_ENUM endian, uint8_t arr[], uint64_t headerEnd, uint64_t offset); 29 | ~ItemHead() = default; //!< Destructor 30 | 31 | //! Return offset of data linked to this item, 32 | //! relative to end of header. 33 | const uint32_t getDataOffset() const { return dataOffset; } 34 | 35 | //! Return size of data linked to this item. 36 | const uint32_t getDataSize() const { return dataSize; } 37 | 38 | friend std::ostream &operator<<( 39 | std::ostream &os, const ItemHead &itemHead); 40 | 41 | static const int SIZE_OF_ITEM_HEAD = 0x19; //!< Size of an item head in bytes. 42 | }; 43 | } 44 | 45 | #endif 46 | 47 | -------------------------------------------------------------------------------- /Basics/KeyPtr.cpp: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Implementation of class KeyPtr 5 | 6 | #include "KeyPtr.h" 7 | #include "Utility/ReadInt.h" 8 | #include "Trees/BtrfsNode.h" 9 | 10 | namespace btrForensics{ 11 | 12 | //! Constructor of Btrfs key pointer. 13 | //! 14 | //! \param endian The endianess of the array. 15 | //! \param arr Byte array storing key pointer data. 16 | //! 17 | KeyPtr::KeyPtr(TSK_ENDIAN_ENUM endian, uint8_t arr[]) 18 | :key(endian, arr), childNode(nullptr) 19 | { 20 | int arIndex(BtrfsKey::SIZE_OF_KEY); //Key initialized already. 21 | blkNum = read64Bit(endian, arr + arIndex); 22 | arIndex += 0x08; 23 | 24 | generation = read64Bit(endian, arr + arIndex); 25 | arIndex += 0x08; 26 | } 27 | 28 | 29 | //!< Destructor 30 | KeyPtr::~KeyPtr() 31 | { 32 | if(childNode != nullptr) 33 | delete childNode; 34 | } 35 | 36 | 37 | //! Overloaded stream operator. 38 | std::ostream &operator<<(std::ostream &os, const KeyPtr &keyPtr) 39 | { 40 | os << keyPtr.key; 41 | os << "Block number: " << keyPtr.blkNum << '\n'; 42 | os << "Generation: " << keyPtr.generation << '\n'; 43 | 44 | return os; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /Basics/KeyPtr.h: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Header file of class KeyPtr 5 | 6 | #ifndef KEY_PTR_H 7 | #define KEY_PTR_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include "BtrfsKey.h" 13 | //#include "Trees/BtrfsNode.h" 14 | 15 | namespace btrForensics{ 16 | class BtrfsNode; 17 | 18 | //! Key pointers stored in internal nodes, stored right after node header. 19 | class KeyPtr{ 20 | public: 21 | const BtrfsKey key; //!< Key of the key pointer. 22 | const BtrfsNode* childNode; //!< Pointer to the child node pointed by this key pointer. 23 | private: 24 | uint64_t blkNum; 25 | uint64_t generation; 26 | 27 | //Total bytes: 0x21 28 | 29 | public: 30 | KeyPtr(TSK_ENDIAN_ENUM endian, uint8_t arr[]); 31 | ~KeyPtr(); 32 | 33 | const uint64_t getBlkNum() const { return blkNum; } //!< Return block number. 34 | const uint32_t getGeneration() const { return generation; } //!< Return generation. 35 | 36 | friend std::ostream &operator<<( 37 | std::ostream &os, const KeyPtr &keyPtr); 38 | 39 | static const int SIZE_OF_KEY_PTR = 0x21; //!< Size of a key pointer in bytes. 40 | }; 41 | } 42 | 43 | #endif 44 | 45 | -------------------------------------------------------------------------------- /Basics/RootItem.cpp: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Implementation of class RootItem 5 | 6 | #include 7 | #include "RootItem.h" 8 | #include "Utility/ReadInt.h" 9 | 10 | namespace btrForensics{ 11 | 12 | //! Constructor of root item. 13 | //! 14 | //! \param head Item head points to this data. 15 | //! \param endian The endianess of the array. 16 | //! \param arr Byte array storing root item data. 17 | //! 18 | RootItem::RootItem(ItemHead* head, TSK_ENDIAN_ENUM endian, uint8_t arr[]) 19 | :BtrfsItem(head), inode(endian, arr), dropProgress(endian, arr + 0xdc) 20 | { 21 | int arIndex(InodeData::SIZE_OF_INODE_DATA); 22 | 23 | exptGen = read64Bit(endian, arr + arIndex); 24 | arIndex += 0x08; 25 | 26 | rootObjId = read64Bit(endian, arr + arIndex); 27 | arIndex += 0x08; 28 | 29 | blkNum = read64Bit(endian, arr + arIndex); 30 | arIndex += 0x08; 31 | 32 | byteLimit = read64Bit(endian, arr + arIndex); 33 | arIndex += 0x08; 34 | 35 | byteUsed = read64Bit(endian, arr + arIndex); 36 | arIndex += 0x08; 37 | 38 | lastGenOfSnapshot = read64Bit(endian, arr + arIndex); 39 | arIndex += 0x08; 40 | 41 | for(int i=0; i<0x08; i++){ 42 | flags[i] = arr[arIndex++]; 43 | } 44 | 45 | numOfRefs = read32Bit(endian, arr + arIndex); 46 | arIndex += 0x04; 47 | 48 | arIndex += BtrfsKey::SIZE_OF_KEY; 49 | 50 | dropLevel = arr[arIndex++]; 51 | rootLevel = arr[arIndex++]; 52 | } 53 | 54 | 55 | //! Get block number of the root node. 56 | const uint64_t RootItem::getBlockNumber() const 57 | { 58 | return blkNum; 59 | } 60 | 61 | 62 | //! Return infomation about the item data as string. 63 | std::string RootItem::dataInfo() const 64 | { 65 | std::ostringstream oss; 66 | oss << "Object id of root directory: " << rootObjId << '\n'; 67 | 68 | oss << std::uppercase << std::hex 69 | << "Block number: " << blkNum << '\n'; 70 | 71 | oss << "Bytes used: " << byteUsed << '\n'; 72 | return oss.str(); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /Basics/RootItem.h: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Header file of class RootItem 5 | 6 | #ifndef ROOT_ITEM_H 7 | #define ROOT_ITEM_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include "BtrfsItem.h" 13 | #include "InodeData.h" 14 | 15 | namespace btrForensics{ 16 | //! Root item data. 17 | class RootItem : public BtrfsItem { 18 | private: 19 | const InodeData inode; //!< Inode 20 | 21 | uint64_t exptGen; //0xa0 22 | uint64_t rootObjId; //!< Id of root directory, always 0x100. 23 | 24 | uint64_t blkNum; //0xb0 25 | uint64_t byteLimit; 26 | 27 | uint64_t byteUsed; //0xc0 28 | uint64_t lastGenOfSnapshot; 29 | 30 | uint8_t flags[8]; //0xd0 31 | uint32_t numOfRefs; 32 | 33 | const BtrfsKey dropProgress; //!< Always 0(?) 34 | 35 | uint8_t dropLevel; 36 | uint8_t rootLevel; 37 | 38 | public: 39 | RootItem(ItemHead* head, TSK_ENDIAN_ENUM endian, uint8_t arr[]); 40 | ~RootItem() = default; //!< Destructor 41 | 42 | const uint64_t getRootObjId() const { return rootObjId; } //!< Get id of root directory 43 | const uint64_t getBlockNumber() const; 44 | std::string dataInfo() const override; 45 | }; 46 | } 47 | 48 | #endif 49 | 50 | -------------------------------------------------------------------------------- /Basics/RootRef.cpp: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Implementation of class RootRef 5 | 6 | #include 7 | #include "RootRef.h" 8 | #include "Utility/ReadInt.h" 9 | 10 | namespace btrForensics{ 11 | 12 | //! Constructor of root ref item. 13 | //! 14 | //! \param head Item head points to this data. 15 | //! \param endian The endianess of the array. 16 | //! \param arr Byte array storing root ref item data. 17 | //! 18 | RootRef::RootRef(ItemHead* head, TSK_ENDIAN_ENUM endian, uint8_t arr[]) 19 | :BtrfsItem(head) 20 | { 21 | int arIndex(0); //Key initialized already. 22 | dirId = read64Bit(endian, arr + arIndex); 23 | arIndex += 0x08; 24 | 25 | index = read64Bit(endian, arr + arIndex); 26 | arIndex += 0x08; 27 | 28 | nameSize = read16Bit(endian, arr + arIndex); 29 | arIndex += 0x02; 30 | 31 | dirName = new char[nameSize](); 32 | for(int i=0; i < nameSize; ++i){ 33 | dirName[i] = arr[arIndex++]; 34 | } 35 | } 36 | 37 | 38 | //!< Destructor 39 | RootRef::~RootRef() 40 | { 41 | if(dirName != nullptr) 42 | delete [] dirName; 43 | } 44 | 45 | 46 | //! Return name of the directory. 47 | std::string RootRef::getDirName() const 48 | { 49 | return std::string(dirName, nameSize); 50 | } 51 | 52 | 53 | //! Return infomation about the item data as string. 54 | std::string RootRef::dataInfo() const 55 | { 56 | std::ostringstream oss; 57 | oss << std::dec; 58 | oss << "Directory id: " << dirId << '\n'; 59 | oss << "Directory index: " << index << '\n'; 60 | oss << "Name: " << getDirName(); 61 | return oss.str(); 62 | } 63 | } 64 | 65 | -------------------------------------------------------------------------------- /Basics/RootRef.h: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Header file of class RootRef 5 | 6 | #ifndef DIR_REF_ITEM_H 7 | #define DIR_REF_ITEM_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include "Basics.h" 13 | 14 | namespace btrForensics { 15 | //! Can represent both root_ref and root_backref items. 16 | class RootRef : public BtrfsItem { 17 | public: 18 | private: 19 | uint64_t dirId; 20 | uint64_t index; 21 | uint16_t nameSize; 22 | char *dirName; 23 | 24 | public: 25 | RootRef(ItemHead* head, TSK_ENDIAN_ENUM endian, uint8_t arr[]); 26 | ~RootRef(); 27 | 28 | //! Get directory id that contains the subtree. 29 | uint64_t getDirId() { return dirId; } 30 | 31 | //! Get index number. 32 | uint64_t getIndex() { return index; } 33 | 34 | std::string getDirName() const; 35 | 36 | std::string dataInfo() const override; 37 | }; 38 | } 39 | 40 | #endif 41 | 42 | -------------------------------------------------------------------------------- /Basics/Stripe.cpp: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Stripe infomation. 5 | 6 | #include 7 | #include "Stripe.h" 8 | #include "Utility/ReadInt.h" 9 | 10 | namespace btrForensics{ 11 | //! Constructor of device item. 12 | //! 13 | //! \param endian The endianess of the array. 14 | //! \param arr Byte array storing device item data. 15 | //! 16 | Stripe::Stripe(TSK_ENDIAN_ENUM endian, uint8_t arr[]) 17 | :devUUID(endian, arr + 0x10) 18 | { 19 | int arIndex(0); 20 | 21 | deviceId = read64Bit(endian, arr + arIndex); 22 | arIndex += 0x08; 23 | 24 | offset = read64Bit(endian, arr + arIndex); 25 | arIndex += 0x08; 26 | } 27 | 28 | //! Return infomation about the stripe data as string 29 | std::string Stripe::dataInfo() const 30 | { 31 | std::ostringstream oss; 32 | oss << std::dec << "Device ID: " << deviceId << '\n'; 33 | oss << std::uppercase << std::hex; 34 | oss << "Stripe offset: " << offset << '\n'; 35 | oss << "Device UUID: " << devUUID.encode() << '\n'; 36 | return oss.str(); 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /Basics/Stripe.h: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Header file of Stripe. 5 | 6 | #ifndef STRIPE 7 | #define STRIPE 8 | 9 | #include "Utility/Uuid.h" 10 | 11 | namespace btrForensics { 12 | //! Stripe information stored in chunk item. 13 | class Stripe { 14 | public: 15 | uint64_t deviceId; //0x0 16 | uint64_t offset; 17 | 18 | UUID devUUID; //0x10 19 | 20 | Stripe(TSK_ENDIAN_ENUM endian, uint8_t arr[]); 21 | ~Stripe() = default; //!< Destructor 22 | 23 | std::string dataInfo() const; 24 | 25 | static const int SIZE_OF_STRIPE = 0X20; //!< Size of stripe in bytes. 26 | }; 27 | } 28 | 29 | #endif 30 | 31 | -------------------------------------------------------------------------------- /Basics/UnknownItem.h: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Header file of class UnknownItem 5 | 6 | #ifndef UNKNOWN_ITEM_H 7 | #define UNKNOWN_ITEM_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include "Basics.h" 13 | 14 | namespace btrForensics{ 15 | //! Unknown item data. 16 | class UnknownItem : public BtrfsItem { 17 | public: 18 | //! Constructor of unknown item. 19 | UnknownItem(ItemHead* head):BtrfsItem(head) {} 20 | ~UnknownItem() = default; //!< Destructor 21 | 22 | //! Infomation unavailable yet. 23 | std::string dataInfo() const override { return "Unknown item"; } 24 | }; 25 | } 26 | 27 | #endif 28 | 29 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | project(btrForensics) 3 | 4 | set(CMAKE_CXX_COMPILER "g++") 5 | set(CMAKE_CXX_STANDARD 11) 6 | 7 | set(CMAKE_CXX_FLAGS "-g -Wall -ltsk") 8 | 9 | include_directories(${PROJECT_SOURCE_DIR}) 10 | 11 | add_subdirectory(Basics) 12 | add_subdirectory(Utility) 13 | add_subdirectory(Trees) 14 | add_subdirectory(Pool) 15 | add_subdirectory(Tools) 16 | 17 | set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}) 18 | 19 | add_executable(btrfrsc btrfrsc.cpp) 20 | 21 | target_link_libraries(btrfrsc Pool Trees) 22 | target_link_libraries(btrfrsc Trees Pool Basics Utility tsk) 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2016 Shujian Yang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Pool/BtrfsPool.cpp: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Implementation of class BtrfsPool. 5 | 6 | #include 7 | #include 8 | #include 9 | #include "BtrfsPool.h" 10 | #include "Functions.h" 11 | 12 | using namespace std; 13 | 14 | namespace btrForensics { 15 | 16 | //! Constructor of Btrfs pool 17 | //! 18 | //! \param img Image file. 19 | //! \param end The endianess of the array. 20 | //! \param devOffsets Offsets of different devices 21 | //! \param fsRootId Id of root of Filesystem Tree. 22 | //! 23 | BtrfsPool::BtrfsPool(TSK_IMG_INFO *img, TSK_ENDIAN_ENUM end, 24 | vector devOffsets, uint64_t fsRootId) 25 | :image(img), endian(end) 26 | { 27 | uint64_t devCount(0); 28 | for(auto dev_off : devOffsets) { 29 | char *diskArr = new char[SuperBlock::SUPBLK_SIZE](); 30 | tsk_img_read(img, dev_off + SuperBlock::SUPBLK_ADDR, diskArr, SuperBlock::SUPBLK_SIZE); 31 | SuperBlock *supblk = new SuperBlock(TSK_LIT_ENDIAN, (uint8_t*)diskArr); 32 | 33 | if(fsUUID.isUnused()) { 34 | fsUUID = supblk->fsUUID; 35 | } 36 | else if(fsUUID != supblk->fsUUID) { 37 | ostringstream oss; 38 | oss << "Found superblocks do not belong to the same pool.\n"; 39 | oss << "The following different filesystem UUIDs were found:\n"; 40 | oss << supblk->fsUUID.encode() << '\n'; 41 | oss << fsUUID.encode() << '\n'; 42 | throw FsDeviceException(oss.str()); 43 | } 44 | 45 | const DevData *dev = &(supblk->devItemData); 46 | deviceTable[dev->deviceId] = new DeviceRecord(dev->deviceId, 47 | dev_off, dev->devUUID, supblk); 48 | 49 | devCount = supblk->numDevices; 50 | } 51 | 52 | if(devOffsets.size() != devCount) { 53 | ostringstream oss; 54 | oss << "Input incomplete: device(s) missing.\n"; 55 | oss << std::dec; 56 | oss << "Expected device number: " << devCount << '\n'; 57 | oss << "Input device number: " << devOffsets.size() << '\n'; 58 | throw FsDeviceException(oss.str()); 59 | } 60 | 61 | primarySupblk = deviceTable[1]->superBlk; 62 | 63 | initializeChunkTree(); 64 | initializeRootTree(); 65 | initializeFileTree(fsRootId); 66 | } 67 | 68 | 69 | //! Destructor 70 | BtrfsPool::~BtrfsPool() 71 | { 72 | if(primarySupblk != nullptr) 73 | delete primarySupblk; 74 | for(auto &record : deviceTable) { 75 | delete record.second; 76 | record.second = nullptr; 77 | } 78 | } 79 | 80 | 81 | //! List devices of the pool 82 | //! 83 | //! \return String of device description 84 | //! 85 | string BtrfsPool::devInfo() const 86 | { 87 | ostringstream oss; 88 | oss << "All devices accepted. Device number: " << deviceTable.size() << "\n"; 89 | oss << "Pool UUID: " << fsUUID.encode() << "\n"; 90 | oss << "-----------------------------------" << "\n"; 91 | 92 | for(const auto dev : deviceTable) { 93 | oss << (dev.second)->devInfo() << "\n"; 94 | } 95 | 96 | return oss.str(); 97 | } 98 | 99 | 100 | //! Find device offset within the image in bytes 101 | //! 102 | //! \param devId 103 | //! 104 | //! \return Device offset in bytes. 105 | //! 106 | uint64_t BtrfsPool::getDevOffset(uint64_t devId) const 107 | { 108 | return deviceTable.at(devId)->deviceOffset; 109 | } 110 | 111 | 112 | //! Get the physical addresses from a chunk item. 113 | //! 114 | //! \param logicalAddr Logical address to convert. 115 | //! \param key Key storing chunk logical address. 116 | //! \param chunkData Chunk item data storing corresponding physical address. 117 | //! 118 | //! \return Mapped physical address. 0 if not valid. 119 | //! 120 | vector BtrfsPool::getAddrFromChunk(uint64_t logicalAddr, 121 | const BtrfsKey* key, const ChunkData* chunkData) const 122 | { 123 | vector physicalAddrs; 124 | uint64_t chunkLogical = key->offset; //Key offset stores logical address. 125 | 126 | //Input logical address should be larger than chunk logial address. 127 | if(logicalAddr < chunkLogical) 128 | throw FsDamagedException("Superblock chunk item error." 129 | "Unable to map logical address to physical address."); 130 | 131 | //There should be at least one stripe within the chunk data. 132 | if(chunkData->numStripe == 0) 133 | throw FsDamagedException("Superblock chunk item error. No stripe found."); 134 | else if(chunkData->numStripe > 1) 135 | throw FsDamagedException("Test only: This chunk has more than 1 stripes!"); 136 | 137 | for(const auto &stripe : chunkData->btrStripes) { 138 | //Get device offset from stripe device ID 139 | uint64_t deviceOffset = getDevOffset(stripe->deviceId); 140 | //Data offset stores relative physical address. 141 | uint64_t chunkPhysical = deviceOffset + stripe->offset; 142 | 143 | physicalAddrs.push_back(logicalAddr - chunkLogical + chunkPhysical); 144 | } 145 | 146 | return physicalAddrs; 147 | } 148 | 149 | 150 | //! Read data from image based on given logical address and chunk. 151 | //! 152 | //! \param[out] data Array with data read from image 153 | //! \param logicalAddr Logical address of data. 154 | //! \param key Btrfs chunk item key. 155 | //! \param chunkData Btrfs chunk item data. 156 | //! \param size Size of data. 157 | //! 158 | //! \return Starting physcial address of data in the image. 159 | //! 160 | uint64_t BtrfsPool::readChunkData(char *data, uint64_t logicalAddr, 161 | const BtrfsKey* key, const ChunkData* chunkData, const uint64_t size) const 162 | { 163 | vector physicalAddrs = getAddrFromChunk(logicalAddr, key, chunkData); 164 | 165 | uint64_t readSize(0); 166 | uint64_t length = chunkData->stripeLength; 167 | for(const auto &addr: physicalAddrs) { 168 | while(size - readSize > length) { 169 | tsk_img_read(image, addr + readSize, data + readSize, length); 170 | readSize += length; 171 | } 172 | tsk_img_read(image, addr + readSize, data + readSize, size - readSize); 173 | } 174 | 175 | return physicalAddrs[0]; 176 | } 177 | 178 | 179 | //! Read data from image based on given logical address. 180 | //! 181 | //! \param[out] data Array with data read from image 182 | //! \param logicalAddr Logical address of data. 183 | //! \param size Size of data. 184 | //! 185 | //! \return Starting physcial address of data in the image. 186 | //! 187 | uint64_t BtrfsPool::readData(char *data, uint64_t logicalAddr, 188 | const uint64_t size) const 189 | { 190 | const ChunkItem* chunk = chunkTree->getChunkItem(logicalAddr); 191 | return readChunkData(data, logicalAddr, &(chunk->itemHead->key), &(chunk->data), size); 192 | } 193 | 194 | 195 | //! Initialize the chunk tree of the pool 196 | void BtrfsPool::initializeChunkTree() 197 | { 198 | chunkTree = new ChunkTree(this); 199 | } 200 | 201 | 202 | //! Initialize root of Root Tree. 203 | void BtrfsPool::initializeRootTree() 204 | { 205 | uint64_t rootTreeLogAddr = primarySupblk->getRootLogAddr(); 206 | 207 | char* diskArr = new char[BtrfsHeader::SIZE_OF_HEADER](); 208 | uint64_t rootTreePhyAddr = readData(diskArr, rootTreeLogAddr, BtrfsHeader::SIZE_OF_HEADER); 209 | 210 | BtrfsHeader* rootHeader = new BtrfsHeader(endian, (uint8_t*)diskArr); 211 | delete [] diskArr; 212 | 213 | uint64_t itemListStart = rootTreePhyAddr + BtrfsHeader::SIZE_OF_HEADER; 214 | if(rootHeader->isLeafNode()) 215 | rootTree = new LeafNode(image, rootHeader, endian, itemListStart); 216 | else 217 | rootTree = new InternalNode(image, rootHeader, endian, itemListStart); 218 | } 219 | 220 | 221 | //! Initialize file system tree. 222 | //! 223 | //! \param fsRootId Subvolume id 224 | //! 225 | void BtrfsPool::initializeFileTree(uint64_t fsRootId) 226 | { 227 | if(fsRootId == 0){ 228 | uint64_t defaultId = getDefaultFsId(); 229 | 230 | fsTree = new FilesystemTree(rootTree, defaultId, this); 231 | fsTreeDefault = fsTree; 232 | } 233 | else{ 234 | const BtrfsItem* foundItem; 235 | if(treeSearchById(rootTree, fsRootId, 236 | [&foundItem](const LeafNode* leaf, uint64_t targetId) 237 | { return searchForItem(leaf, targetId, ItemType::ROOT_BACKREF, foundItem); })) { 238 | fsTree = new FilesystemTree(rootTree, fsRootId, this); 239 | fsTreeDefault = fsTree; 240 | } 241 | else 242 | throw runtime_error("The argument of -s option is not a valid subvolume id."); 243 | } 244 | } 245 | 246 | 247 | //! Obatin root item id of default volume (filesystem tree). 248 | uint64_t BtrfsPool::getDefaultFsId() 249 | { 250 | //Technically, the default volume id should be found in offset 0x80 in superblock. 251 | //Currently, it is set as 6 by default. 252 | uint64_t defaultDirId(6); 253 | 254 | uint64_t defaultId(0); 255 | const BtrfsItem* foundItem; 256 | if(treeSearchById(rootTree, defaultDirId, 257 | [&foundItem](const LeafNode* leaf, uint64_t targetId) 258 | { return searchForItem(leaf, targetId, ItemType::DIR_ITEM, foundItem); })) { 259 | const DirItem* dir = static_cast(foundItem); 260 | defaultId = dir->targetKey.objId; //This is id of root item to filesystem tree. 261 | } 262 | else 263 | throw FsDamagedException("Default directory dir item not found."); 264 | 265 | return defaultId; 266 | } 267 | 268 | 269 | //! Navigate to selected node and print information. 270 | //! 271 | //! \param root Starting root node. 272 | //! \param os Output stream where the infomation is printed. 273 | //! \param is Input stream telling which node is the one to be read. 274 | //! 275 | void BtrfsPool::navigateNodes(const BtrfsNode* root, 276 | std::ostream& os, std::istream& is) const 277 | { 278 | const BtrfsNode *node = root; 279 | const BtrfsHeader *header; 280 | while(true) { 281 | header = node->nodeHeader; 282 | os << node->info() << endl; 283 | 284 | uint64_t offset(0); 285 | map nodeAddrs; 286 | if(header->isLeafNode()){ 287 | const LeafNode *leaf = static_cast(node); 288 | 289 | for(auto item : leaf->itemList){ 290 | if(item->getItemType() == ItemType::ROOT_ITEM){ 291 | const RootItem *rootItm = static_cast(item); 292 | nodeAddrs[item->getId()] 293 | = rootItm->getBlockNumber(); 294 | } 295 | } 296 | } 297 | else { 298 | const InternalNode* internal = static_cast(node); 299 | 300 | for(auto ptr : internal->keyPointers) { 301 | nodeAddrs[ptr->key.objId] = ptr->getBlkNum(); 302 | } 303 | } 304 | 305 | if(nodeAddrs.size() == 0) { 306 | os << "This is a leaf node with no root items." << endl; 307 | break; 308 | } 309 | 310 | bool quit(false); 311 | string input; 312 | uint64_t inputId; 313 | while(true) { 314 | os << "Child nodes or tree roots with following object ids are found." << endl; 315 | for(auto addr : nodeAddrs) { 316 | if(header->isLeafNode()) 317 | os << dec << addr.first << " "; 318 | else 319 | os << dec << "[" << addr.first << "] "; 320 | } 321 | 322 | os << endl; 323 | os << "To read a child node or a tree root, please enter its object id in the list: "; 324 | os << "(Enter 'q' to quit.)" << endl; 325 | is >> input; 326 | 327 | if(input == "q") return; 328 | 329 | stringstream(input) >> inputId; 330 | if(nodeAddrs.find(inputId) != nodeAddrs.end()) break; 331 | os << "Wrong object id, please enter a correct one.\n" << endl; 332 | } 333 | os << endl; 334 | 335 | bool nodeExisted(false); 336 | using ConstNodePtr = const BtrfsNode*; 337 | ConstNodePtr* childPtr(nullptr); 338 | if(!header->isLeafNode()) { 339 | const InternalNode* internal = static_cast(node); 340 | 341 | for(auto ptr : internal->keyPointers) { 342 | if(ptr->key.objId == inputId && ptr->childNode != nullptr) { 343 | node = ptr->childNode; 344 | nodeExisted = true; 345 | break; 346 | } 347 | else if(ptr->key.objId == inputId) { 348 | childPtr = &ptr->childNode; 349 | break; 350 | } 351 | } 352 | } 353 | if(nodeExisted) continue; //The child node has already been built. 354 | 355 | offset = nodeAddrs[inputId]; 356 | char *headerArr = new char[BtrfsHeader::SIZE_OF_HEADER](); 357 | uint64_t physicalAddr = readData(headerArr, offset, BtrfsHeader::SIZE_OF_HEADER); 358 | header = new BtrfsHeader(TSK_LIT_ENDIAN, (uint8_t*)headerArr); 359 | delete [] headerArr; 360 | 361 | uint64_t itemOffset = physicalAddr + BtrfsHeader::SIZE_OF_HEADER; 362 | 363 | if(header->isLeafNode()){ 364 | node = new LeafNode(image, header, endian, itemOffset); 365 | } 366 | else { 367 | node = new InternalNode(image, header, endian, itemOffset); 368 | } 369 | 370 | if(childPtr!=nullptr) 371 | *childPtr = node; 372 | } 373 | } 374 | 375 | 376 | //! Switch to a subvolume or snapshot and exploere files within. 377 | //! 378 | //! \param os Output stream where the infomation is printed. 379 | //! \param is Input stream telling which node is the one to be read. 380 | //! \return True if switched. 381 | //! 382 | bool BtrfsPool::switchFsTrees(ostream& os, istream& is) 383 | { 384 | vector foundRootRefs; 385 | treeTraverse(rootTree, [&foundRootRefs](const LeafNode* leaf) 386 | { return filterItems(leaf, ItemType::ROOT_BACKREF, foundRootRefs); }); 387 | 388 | if(foundRootRefs.size() == 0) { 389 | os << "\nNo subvolumes or snapshots are found.\n" << endl; 390 | return false; 391 | } 392 | 393 | uint64_t selectedId(0); 394 | while(true) { 395 | os << "The following subvolumes or snapshots are found:" << endl; 396 | int index(0); 397 | for(auto item : foundRootRefs) { 398 | const RootRef* ref = static_cast(item); 399 | os << "[" << dec << setfill(' ') << setw(2) << ++index << "] " 400 | << setw(7) << ref->getId() << " " << ref->getDirName() << '\n'; 401 | } 402 | os << endl; 403 | 404 | string input; 405 | os << "To visit a subvolume or snapshot, please enter its index in the list:\n"; 406 | os << "(Enter ''q' to quit.)" << endl; 407 | is >> input; 408 | 409 | if(input == "q") return false; 410 | int inputIndex; 411 | stringstream(input) >> inputIndex; 412 | if(inputIndex > 0 && inputIndex <= foundRootRefs.size()) { 413 | selectedId = foundRootRefs[inputIndex-1]->getId(); 414 | break; 415 | } 416 | os << "Wrong index, please enter a correct one.\n\n\n" << endl; 417 | } 418 | 419 | fsTree = new FilesystemTree(rootTree, selectedId, this); 420 | os << "\n" << std::string(60, '=') << "\n"; 421 | os << endl; 422 | return true; 423 | } 424 | 425 | 426 | //! Recursively traverse child nodes and process it if it is a leaf node. 427 | //! 428 | //! \param node Node being processed. 429 | //! \param readOnlyFunc A function type which accepts a LeafNode* 430 | //! and a vector& parameters and returns void. 431 | //! 432 | void BtrfsPool::treeTraverse(const BtrfsNode *node, 433 | function readOnlyFunc) const 434 | { 435 | if(node->nodeHeader->isLeafNode()){ 436 | const LeafNode* leaf = static_cast(node); 437 | readOnlyFunc(leaf); 438 | } 439 | else { 440 | const InternalNode* internal = static_cast(node); 441 | const BtrfsNode *newNode; 442 | 443 | for(auto ptr : internal->keyPointers) { 444 | if(ptr->childNode != nullptr) { 445 | newNode = ptr->childNode; 446 | } 447 | else { 448 | char *headerArr = new char[BtrfsHeader::SIZE_OF_HEADER](); 449 | uint64_t physicalAddr = readData(headerArr, ptr->getBlkNum(), 450 | BtrfsHeader::SIZE_OF_HEADER); 451 | BtrfsHeader *header = new BtrfsHeader(TSK_LIT_ENDIAN, (uint8_t*)headerArr); 452 | delete [] headerArr; 453 | 454 | uint64_t itemOffset = physicalAddr + BtrfsHeader::SIZE_OF_HEADER; 455 | 456 | if(header->isLeafNode()){ 457 | newNode = new LeafNode(image, header, endian, itemOffset); 458 | } 459 | else { 460 | newNode = new InternalNode(image, header, endian, itemOffset); 461 | } 462 | 463 | ptr->childNode = newNode; 464 | } 465 | 466 | if(newNode != nullptr) 467 | treeTraverse(newNode, readOnlyFunc); 468 | } 469 | } 470 | } 471 | 472 | 473 | //! Recursively traverse nodes in chunk tree and search if it is a leaf node. 474 | //! 475 | //! \param node Node being processed. 476 | //! \param searchFunc A function type which accepts a LeafNode* 477 | //! parameter and returns true if certain object is found. 478 | //! \return True if target is found in leaf node. 479 | //! 480 | bool BtrfsPool::chunkTreeSearch(const BtrfsNode *node, 481 | function searchFunc) const 482 | { 483 | if(node->nodeHeader->isLeafNode()){ 484 | const LeafNode *leaf = static_cast(node); 485 | return searchFunc(leaf); 486 | } 487 | else { 488 | const InternalNode *internal = static_cast(node); 489 | const BtrfsNode *newNode; 490 | 491 | for(auto ptr : internal->keyPointers) { 492 | if(ptr->childNode != nullptr) { 493 | newNode = ptr->childNode; 494 | } 495 | else { 496 | const SuperBlock *supBlk = primarySupblk; 497 | char *headerArr = new char[BtrfsHeader::SIZE_OF_HEADER](); 498 | uint64_t physicalAddr = readChunkData(headerArr, ptr->getBlkNum(), 499 | &(supBlk->chunkKey), &(supBlk->chunkData), 500 | BtrfsHeader::SIZE_OF_HEADER); 501 | 502 | BtrfsHeader *header = new BtrfsHeader(TSK_LIT_ENDIAN, (uint8_t*)headerArr); 503 | delete [] headerArr; 504 | 505 | uint64_t itemOffset = physicalAddr + BtrfsHeader::SIZE_OF_HEADER; 506 | 507 | if(header->isLeafNode()){ 508 | newNode = new LeafNode(image, header, endian, itemOffset); 509 | } 510 | else { 511 | newNode = new InternalNode(image, header, endian, itemOffset); 512 | } 513 | 514 | ptr->childNode = newNode; 515 | } 516 | 517 | if(newNode != nullptr && treeSearch(newNode, searchFunc)) 518 | return true; 519 | } 520 | return false; 521 | } 522 | } 523 | 524 | 525 | //! Recursively traverse child nodes and search if it is a leaf node. 526 | //! 527 | //! \param node Node being processed. 528 | //! \param searchFunc A function type which accepts a LeafNode* 529 | //! parameter and returns true if certain object is found. 530 | //! \return True if target is found in leaf node. 531 | //! 532 | bool BtrfsPool::treeSearch(const BtrfsNode *node, 533 | function searchFunc) const 534 | { 535 | if(node->nodeHeader->isLeafNode()){ 536 | const LeafNode *leaf = static_cast(node); 537 | return searchFunc(leaf); 538 | } 539 | else { 540 | const InternalNode *internal = static_cast(node); 541 | const BtrfsNode *newNode; 542 | 543 | for(auto ptr : internal->keyPointers) { 544 | if(ptr->childNode != nullptr) { 545 | newNode = ptr->childNode; 546 | } 547 | else { 548 | char *headerArr = new char[BtrfsHeader::SIZE_OF_HEADER](); 549 | uint64_t physicalAddr = readData(headerArr, ptr->getBlkNum(), 550 | BtrfsHeader::SIZE_OF_HEADER); 551 | 552 | BtrfsHeader *header = new BtrfsHeader(TSK_LIT_ENDIAN, (uint8_t*)headerArr); 553 | delete [] headerArr; 554 | 555 | uint64_t itemOffset = physicalAddr + BtrfsHeader::SIZE_OF_HEADER; 556 | 557 | if(header->isLeafNode()){ 558 | newNode = new LeafNode(image, header, endian, itemOffset); 559 | } 560 | else { 561 | newNode = new InternalNode(image, header, endian, itemOffset); 562 | } 563 | 564 | ptr->childNode = newNode; 565 | } 566 | 567 | if(newNode != nullptr && treeSearch(newNode, searchFunc)) 568 | return true; 569 | } 570 | return false; 571 | } 572 | } 573 | 574 | 575 | //! Recursively traverse child nodes and search if it is a leaf node. 576 | //! 577 | //! \param node Node being processed. 578 | //! \param targetId Object id of target to search for. 579 | //! \param searchFunc A function type which accepts a LeafNode* 580 | //! parameter and returns true if certain object is found. 581 | //! \return True if target is found in leaf node. 582 | //! 583 | bool BtrfsPool::treeSearchById(const BtrfsNode *node, uint64_t targetId, 584 | function searchFunc) const 585 | { 586 | if(node->nodeHeader->isLeafNode()){ 587 | const LeafNode *leaf = static_cast(node); 588 | return searchFunc(leaf, targetId); 589 | } 590 | else { 591 | const InternalNode *internal = static_cast(node); 592 | const BtrfsNode *newNode; 593 | 594 | const auto &vecPtr = internal->keyPointers; 595 | for(int i=0; ikey.objId > targetId) 598 | return false; 599 | if(ptr->key.objIdkey.objIdchildNode != nullptr) { 604 | newNode = ptr->childNode; 605 | } 606 | else { 607 | char *headerArr = new char[BtrfsHeader::SIZE_OF_HEADER](); 608 | uint64_t physicalAddr = readData(headerArr, ptr->getBlkNum(), 609 | BtrfsHeader::SIZE_OF_HEADER); 610 | BtrfsHeader *header = new BtrfsHeader(TSK_LIT_ENDIAN, (uint8_t*)headerArr); 611 | delete [] headerArr; 612 | 613 | uint64_t itemOffset = physicalAddr + BtrfsHeader::SIZE_OF_HEADER; 614 | 615 | if(header->isLeafNode()){ 616 | newNode = new LeafNode(image, header, endian, itemOffset); 617 | } 618 | else { 619 | newNode = new InternalNode(image, header, endian, itemOffset); 620 | } 621 | 622 | ptr->childNode = newNode; 623 | } 624 | 625 | if(newNode != nullptr && treeSearchById(newNode, targetId, searchFunc)) 626 | return true; 627 | } 628 | return false; 629 | } 630 | } 631 | 632 | } 633 | 634 | -------------------------------------------------------------------------------- /Pool/BtrfsPool.h: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Header file of class BtrfsPool. 5 | 6 | #ifndef BTRFS_POOL_H 7 | #define BTRFS_POOL_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "DeviceRecord.h" 14 | #include "Basics/Basics.h" 15 | #include "Trees/Trees.h" 16 | 17 | namespace btrForensics { 18 | 19 | //! Manage devices registered for a btrfs filesystem. 20 | class BtrfsPool { 21 | public: 22 | TSK_IMG_INFO *image; //!< Image file 23 | TSK_ENDIAN_ENUM endian; //!< Endianness. 24 | 25 | UUID fsUUID; //!< Filesystem UUID 26 | SuperBlock* primarySupblk; //!< Primary SuperBlock, currently chosen from the device with ID 1. 27 | std::map deviceTable; //!< Stores device information 28 | 29 | 30 | ChunkTree* chunkTree; //!< The chunk tree. 31 | FilesystemTree* fsTree; //!< The file system tree. 32 | FilesystemTree* fsTreeDefault; //!< Default file system tree. 33 | const BtrfsNode* rootTree; //!< Root node of the root tree. 34 | 35 | public: 36 | BtrfsPool(TSK_IMG_INFO*, TSK_ENDIAN_ENUM, vector, uint64_t = 0); 37 | ~BtrfsPool(); 38 | 39 | std::string devInfo() const; 40 | uint64_t getDevOffset(uint64_t devId) const; 41 | 42 | std::vector getAddrFromChunk(uint64_t logicalAddr, 43 | const BtrfsKey* key, const ChunkData* chunkData) const; 44 | uint64_t readChunkData(char *data, uint64_t logicalAddr, 45 | const BtrfsKey* key, const ChunkData* chunkData, uint64_t size) const; 46 | uint64_t readData(char *data, uint64_t logicalAddr, uint64_t size) const; 47 | 48 | 49 | void initializeChunkTree(); 50 | void initializeRootTree(); 51 | void initializeFileTree(uint64_t); 52 | uint64_t getDefaultFsId(); 53 | 54 | void navigateNodes(const BtrfsNode* root, std::ostream& os, std::istream& is) const; 55 | bool switchFsTrees(std::ostream& os, std::istream& is); 56 | 57 | void treeTraverse(const BtrfsNode* node, 58 | std::function readOnlyFunc) const; 59 | 60 | bool chunkTreeSearch(const BtrfsNode* node, 61 | std::function searchFunc) const; 62 | 63 | bool treeSearch(const BtrfsNode* node, 64 | std::function searchFunc) const; 65 | 66 | bool treeSearchById(const BtrfsNode* node, uint64_t targetId, 67 | std::function searchFunc) const; 68 | }; 69 | } 70 | 71 | #endif 72 | 73 | -------------------------------------------------------------------------------- /Pool/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${PROJECT_SOURCE_DIR}) 2 | 3 | aux_source_directory(. POOL_SRCS) 4 | 5 | add_library(Pool ${POOL_SRCS}) 6 | 7 | -------------------------------------------------------------------------------- /Pool/DeviceRecord.cpp: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Implementation of class DeviceRecord. 5 | 6 | #include 7 | #include "DeviceRecord.h" 8 | 9 | namespace btrForensics { 10 | 11 | //! Constructor of DeviceRecord 12 | //! \param id Id of the device 13 | //! \param offset Offset of the device 14 | //! \param uuid UUID of the device 15 | //! \param supblk Super Block stored in that device 16 | //! 17 | DeviceRecord::DeviceRecord(uint64_t id, uint64_t offset, UUID uuid, SuperBlock *supblk) 18 | :deviceId(id), deviceOffset(offset), devUUID(uuid), superBlk(supblk) 19 | { 20 | } 21 | 22 | //! Print device info. 23 | std::string DeviceRecord::devInfo() const 24 | { 25 | std::ostringstream oss; 26 | oss << "Device ID: " << deviceId << '\n'; 27 | oss << "Device UUID: " << devUUID.encode() << '\n'; 28 | 29 | return oss.str(); 30 | } 31 | 32 | } 33 | 34 | -------------------------------------------------------------------------------- /Pool/DeviceRecord.h: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Header file of class DeviceRecord. 5 | 6 | #ifndef DEVICE_RECORD_H 7 | #define DEVICE_RECORD_H 8 | 9 | #include 10 | #include "Utility/Uuid.h" 11 | #include "Trees/SuperBlock.h" 12 | 13 | namespace btrForensics { 14 | 15 | //! Device registered for a btrfs filesystem. 16 | class DeviceRecord { 17 | public: 18 | uint64_t deviceId; //!< ID of the device. 19 | uint64_t deviceOffset; //!< Offset of the device from the beginning of the image in bytes. 20 | UUID devUUID; //!< Device UUID 21 | SuperBlock *superBlk; //!< SuperBlock stored in thi device. 22 | 23 | public: 24 | std::string devInfo() const; 25 | 26 | DeviceRecord(uint64_t, uint64_t, UUID, SuperBlock*); 27 | ~DeviceRecord() = default; //!< Destructor 28 | }; 29 | } 30 | 31 | #endif 32 | 33 | -------------------------------------------------------------------------------- /Pool/Functions.cpp: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Implementations of functions declared in Functions.h. 5 | 6 | #include 7 | #include 8 | #include "Functions.h" 9 | 10 | namespace btrForensics{ 11 | 12 | //! Prints names of files stored in a leaf node. 13 | //! 14 | //! \param leaf Pointer to the leaf node. 15 | //! \param os Output stream where the infomation is printed. 16 | //! 17 | void printLeafDir(const LeafNode* leaf, std::ostream &os) 18 | { 19 | for(auto item : leaf->itemList){ 20 | if(item->getItemType() == ItemType::DIR_INDEX){ 21 | const DirItem *dir = static_cast(item); 22 | if(dir->type == DirItemType::REGULAR_FILE) 23 | os << dir->getDirName() << '\n'; 24 | } 25 | } 26 | } 27 | 28 | 29 | //! Search for first item with given id and type in a leaf node. 30 | //! 31 | //! \param leaf Pointer to the leaf node. 32 | //! \param inodeNum The inode number to search for. 33 | //! \param type The type of the item to search for. 34 | //! \param[out] item Found ItemHead pointer. Only the first found will be returned. 35 | //! 36 | //! \return True if the item is found. 37 | //! 38 | bool searchForItem(const LeafNode* leaf, uint64_t inodeNum, 39 | ItemType type, const BtrfsItem* &foundItem) 40 | { 41 | for(auto item : leaf->itemList) { 42 | if(item->getId() > inodeNum) //Items are sorted in leaf nodes by ids. 43 | return false; 44 | if(item->getId() == inodeNum && 45 | item->getItemType() == type) { 46 | foundItem = item; 47 | return true; 48 | } 49 | } 50 | return false; 51 | } 52 | 53 | 54 | //! Find all items with given id and type in a leaf node. 55 | //! 56 | //! \param leaf Pointer to the leaf node. 57 | //! \param inodeNum The inode number to search for. 58 | //! \param type The type of the item to search for. 59 | //! \param[out] vec Vector storing all found items. 60 | //! 61 | //! \return True if all items with the inodeNum has been found. 62 | //! 63 | bool filterItems(const LeafNode* leaf, uint64_t inodeNum, ItemType type, 64 | vector &vec) 65 | { 66 | for(auto item : leaf->itemList) { 67 | if(item->getId() > inodeNum) //Items are sorted in leaf nodes by ids. 68 | return true; 69 | if(item->getId() == inodeNum && 70 | item->getItemType() == type) { 71 | // Is it possible to find duplicate items? 72 | //auto result = find(vec.cbegin(), vec.cend(), item); 73 | //if(result == vec.cend()) 74 | vec.push_back(item); 75 | } 76 | } 77 | return false; 78 | } 79 | 80 | 81 | //! Find all items with given type in a leaf node. 82 | //! 83 | //! \param leaf Pointer to the leaf node. 84 | //! \param type The type of the item to search for. 85 | //! \param vec Vector storing all found items. 86 | //! 87 | void filterItems(const LeafNode* leaf, ItemType type, vector &vec) 88 | { 89 | for(auto item : leaf->itemList) { 90 | if(item->getItemType() == type) 91 | vec.push_back(item); 92 | } 93 | } 94 | 95 | 96 | //! Overloaded stream operator. 97 | std::ostream &operator<<(std::ostream& os, const DirItemType& type) 98 | { 99 | switch(type) { 100 | case DirItemType::UNKNOWN: 101 | os << "~"; 102 | break; 103 | case DirItemType::REGULAR_FILE: 104 | os << "r"; 105 | break; 106 | case DirItemType::DIRECTORY: 107 | os << "d"; 108 | break; 109 | case DirItemType::CHAR_DEVICE: 110 | os << "c"; 111 | break; 112 | case DirItemType::BLK_DEVICE: 113 | os << "b"; 114 | break; 115 | case DirItemType::FIFO: 116 | os << "p"; 117 | break; 118 | case DirItemType::SOCKET: 119 | os << "h"; 120 | break; 121 | case DirItemType::SYMB_LINK: 122 | os << "l"; 123 | break; 124 | case DirItemType::EXT_ATTR: 125 | os << "e"; 126 | break; 127 | }; 128 | 129 | return os; 130 | } 131 | 132 | } 133 | 134 | -------------------------------------------------------------------------------- /Pool/Functions.h: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Assistant functions defined here. 5 | 6 | #ifndef FUNCTIONS_H 7 | #define FUNCTIONS_H 8 | 9 | #include 10 | #include 11 | #include "Pool.h" 12 | 13 | namespace btrForensics{ 14 | 15 | void printLeafDir(const LeafNode*, std::ostream&); 16 | 17 | bool searchForItem(const LeafNode*, uint64_t, ItemType, const BtrfsItem*&); 18 | 19 | bool filterItems(const LeafNode*, uint64_t, ItemType, vector&); 20 | 21 | void filterItems(const LeafNode*, ItemType, vector&); 22 | 23 | std::ostream &operator<<(std::ostream& os, const DirItemType& type); 24 | } 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /Pool/Pool.h: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | 4 | #ifndef EXAMINERS_H 5 | #define EXAMINERS_H 6 | 7 | #include "Trees/Trees.h" 8 | 9 | #include "DeviceRecord.h" 10 | //#include "TreeExaminer.h" 11 | #include "Functions.h" 12 | #include "BtrfsPool.h" 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # btrForensics 2 | Forensic Analysis Tool for Btrfs File System. 3 | 4 | ### Platform: 5 | Linux 6 | 7 | ### Prerequisite: 8 | Install the Sleuth Kit library --> [Link](https://github.com/sleuthkit/sleuthkit.git) 9 | 10 | ### Build: 11 | ``` 12 | mkdir build 13 | 14 | cd build 15 | 16 | cmake .. 17 | 18 | make 19 | ``` 20 | 21 | ### Input File: 22 | Raw image which contains a btrfs partition, or a partition device file with btrfs. 23 | 24 | ### Usage: 25 | ``` 26 | btrfrsc [-o offset1,offset2,offset3...] image 27 | ``` 28 | 29 | -o offset: Offset to the beginning of the partition (in sectors). 30 | May have multiple values if the pool is made up by multiple partitions(devices). 31 | 32 | ### Current Capabilities: 33 | 1. Browse nodes derived from root tree and print information. 34 | 2. Browse nodes in filesystem tree and print information. 35 | 3. List all files in default filesystem tree. 36 | 4. Explor files and subdirectories in default root directory. 37 | 5. Switch to a subvolume or snapshot and exploere files within. 38 | 6. Read a file from image and save to current directory. 39 | 40 | ### Tools 41 | There will be some stand alone programs built in Tools/ folder. 42 | 43 | Most of them simulates functions of tools in The Sleuth's Kit. 44 | 45 | Current list: 46 | 47 | **Tools/fsstat:** Print information about the file system. 48 | **Tools/fls:** List files and/or directories in a Btrfs partition image. 49 | **Tools/istat:** Print information about an inode. 50 | **Tools/icat:** Output the contents of file with provided inode number in Btrfs. 51 | **Tools/subls:** List subvolumes and snapshots in a Btrfs image. 52 | 53 | ### Note: 54 | Reference of Btrfs structure can be found in [btrfs Wiki](https://btrfs.wiki.kernel.org/index.php/Main_Page). 55 | 56 | Btrfs on-disk format: [Link](https://btrfs.wiki.kernel.org/index.php/On-disk_Format) 57 | 58 | ### License: 59 | This software uses MIT License. 60 | 61 | The Sleuth Kit library is employed. 62 | 63 | You can find the Sleuth Kit from [sleuthkit/sleuthkit](https://github.com/sleuthkit/sleuthkit.git) 64 | 65 | -------------------------------------------------------------------------------- /Tools/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${PROJECT_SOURCE_DIR}) 2 | 3 | set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/Tools) 4 | 5 | add_executable(fsstat fsstat.cpp) 6 | target_link_libraries(fsstat Pool Trees) 7 | target_link_libraries(fsstat Trees Pool Basics Utility tsk) 8 | 9 | add_executable(fls fls.cpp) 10 | target_link_libraries(fls Pool Trees) 11 | target_link_libraries(fls Trees Pool Basics Utility tsk) 12 | 13 | add_executable(icat icat.cpp) 14 | target_link_libraries(icat Pool Trees) 15 | target_link_libraries(icat Trees Pool Basics Utility tsk) 16 | 17 | add_executable(istat istat.cpp) 18 | target_link_libraries(istat Pool Trees) 19 | target_link_libraries(istat Trees Pool Basics Utility tsk) 20 | 21 | add_executable(subls subls.cpp) 22 | target_link_libraries(subls Pool Trees) 23 | target_link_libraries(subls Trees Pool Basics Utility tsk) 24 | 25 | add_executable(devls devls.cpp) 26 | target_link_libraries(devls Pool Trees) 27 | target_link_libraries(devls Trees Pool Basics Utility tsk) 28 | 29 | -------------------------------------------------------------------------------- /Tools/DEVLS_README.md: -------------------------------------------------------------------------------- 1 | # devls 2 | List devices that make up a complete btrfs pool 3 | 4 | ### Input File: 5 | Raw image of a btrfs partition, or a partition device file using btrfs. 6 | 7 | ### Usage: 8 | ``` 9 | devls [-o offset1,offset2,offset3...] image 10 | ``` 11 | 12 | -o offset: Offset to the beginning of the partition (in sectors). 13 | May have multiple values if the pool is made up by multiple partitions(devices). 14 | 15 | ### License: 16 | This software uses MIT License. 17 | -------------------------------------------------------------------------------- /Tools/FLS_README.md: -------------------------------------------------------------------------------- 1 | # fls 2 | List files and/or directories in a Btrfs partition image. 3 | 4 | This is a simulation to The Sleuth Kit's fls program. 5 | 6 | ### Input File: 7 | Raw image of a btrfs partition, or a partition device file using btrfs. 8 | 9 | ### Usage: 10 | ``` 11 | fls [-rDF] [-o offset1,offset2,offset3...] [-s subvolumeid] image [inode] 12 | ``` 13 | 14 | If [inode] is not given, the root directory is used. 15 | 16 | -o offset: Offset to the beginning of the partition (in sectors). 17 | May have multiple values if the pool is made up by multiple partitions(devices). 18 | 19 | -s subvolumeid: The id of subvolume or snapshot. List can be found by using subls tool. 20 | 21 | -r: Recurse on directory entries 22 | 23 | -D: Display only directories 24 | 25 | -F: Display only files 26 | 27 | ### Note: 28 | Unable to list deleted files yet. 29 | 30 | ### License: 31 | This software uses MIT License. 32 | -------------------------------------------------------------------------------- /Tools/FSSTAT_README.md: -------------------------------------------------------------------------------- 1 | # fsstat 2 | Print information about the file system. 3 | 4 | This is a simulation to The Sleuth Kit's fsstat program. 5 | 6 | ### Input File: 7 | Raw image of a btrfs partition, or a partition device file using btrfs. 8 | 9 | ### Usage: 10 | ``` 11 | fsstat [-o offset1,offset2,offset3...] image 12 | ``` 13 | 14 | -o offset: Offset to the beginning of the partition (in sectors). 15 | May have multiple values if the pool is made up by multiple partitions(devices). 16 | 17 | ### License: 18 | This software uses MIT License. 19 | -------------------------------------------------------------------------------- /Tools/ICAT_README.md: -------------------------------------------------------------------------------- 1 | # icat 2 | Output the contents of file with provided inode number in Btrfs. 3 | 4 | This is a simulation to The Sleuth Kit's icat program. 5 | 6 | ### Input File: 7 | Raw image of a btrfs partition, or a partition device file using btrfs. 8 | 9 | ### Usage: 10 | ``` 11 | icat [-o offset1,offset2,offset3...] [-s subvolumeid] image inode 12 | ``` 13 | 14 | -o offset: Offset to the beginning of the partition (in sectors). 15 | May have multiple values if the pool is made up by multiple partitions(devices). 16 | 17 | -s subvolumeid: The id of subvolume or snapshot. List can be found by using subls tool. 18 | 19 | ### Note: 20 | Unlike icat in The Sleuth Kit, this program write file with original file name to directory. 21 | 22 | ### License: 23 | This software uses MIT License. 24 | 25 | -------------------------------------------------------------------------------- /Tools/ISTAT_README.md: -------------------------------------------------------------------------------- 1 | # istat 2 | Print information about an inode. 3 | 4 | This is a simulation to The Sleuth Kit's istat program. 5 | 6 | ### Input File: 7 | Raw image of a btrfs partition, or a partition device file using btrfs. 8 | 9 | ### Usage: 10 | ``` 11 | istat [-o offset1,offset2,offset3...] [-s subvolumeid] image inode 12 | ``` 13 | 14 | -o offset: Offset to the beginning of the partition (in sectors). 15 | May have multiple values if the pool is made up by multiple partitions(devices). 16 | 17 | -s subvolumeid: The id of subvolume or snapshot. List can be found by using subls tool. 18 | 19 | ### Note: 20 | Unable to distinguish files and directories yet. 21 | 22 | ### License: 23 | This software uses MIT License. 24 | -------------------------------------------------------------------------------- /Tools/SUBLS_README.md: -------------------------------------------------------------------------------- 1 | # subls 2 | List subvolumes and snapshots in a Btrfs image with corresponding id. 3 | 4 | ### Input File: 5 | Raw image of a btrfs partition, or a partition device file using btrfs. 6 | 7 | ### Usage: 8 | ``` 9 | subls [-o offset1,offset2,offset3...] image 10 | ``` 11 | 12 | -o offset: Offset to the beginning of the partition (in sectors). 13 | May have multiple values if the pool is made up by multiple partitions(devices). 14 | 15 | ### License: 16 | This software uses MIT License. 17 | -------------------------------------------------------------------------------- /Tools/Tools.h: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | 4 | #ifndef TOOLS_H 5 | #define TOOLS_H 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "Basics/Basics.h" 18 | #include "Trees/Trees.h" 19 | #include "Examiners/Examiners.h" 20 | #include "Utility/Utility.h" 21 | 22 | #endif 23 | 24 | -------------------------------------------------------------------------------- /Tools/devls.cpp: -------------------------------------------------------------------------------- 1 | //! \file subls.cpp 2 | //! \author Shujian Yang 3 | //! 4 | //! Main function of devls. 5 | //! 6 | //! List devices registered in a Btrfs pool. 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "Basics/Basics.h" 19 | #include "Trees/Trees.h" 20 | #include "Pool/Pool.h" 21 | #include "Utility/Utility.h" 22 | 23 | using namespace std; 24 | 25 | using namespace btrForensics; 26 | 27 | int main(int argc, char *argv[]) 28 | { 29 | TSK_OFF_T offsetSector(0); 30 | int option; 31 | vector offsetStr; 32 | vector devOffsets; 33 | 34 | while((option = getopt(argc, argv, "o:")) != -1){ 35 | switch(option){ 36 | case 'o': 37 | offsetStr = strSplit(optarg, ","); 38 | for(auto str : offsetStr){ 39 | if( (offsetSector = tsk_parse_offset(str.c_str())) == -1){ 40 | tsk_error_print(stderr); 41 | exit(1); 42 | } 43 | devOffsets.push_back(offsetSector); 44 | } 45 | break; 46 | case '?': 47 | default: 48 | cerr << "Unkown arguments." << endl; 49 | } 50 | } 51 | 52 | if(optind >= argc) { 53 | cerr << "Please provide the image name" << endl; 54 | exit(1); 55 | } 56 | 57 | if(devOffsets.size() == 0) 58 | devOffsets.push_back(0); 59 | 60 | string img_name(argv[optind]); 61 | 62 | TSK_IMG_INFO *img = tsk_img_open(1, &argv[optind], TSK_IMG_TYPE_DETECT, 0); 63 | if(img == NULL){ 64 | tsk_error_print(stderr); 65 | cerr << "Cannot open image " << img_name << "." << endl; 66 | exit(1); 67 | } 68 | 69 | for(auto &offSec : devOffsets) { 70 | TSK_OFF_T offsetByte(offSec * img->sector_size); 71 | if( offsetByte >= img->size){ 72 | cerr << "Offset is too large." << endl; 73 | exit(1); 74 | } 75 | offSec = offsetByte; 76 | } 77 | 78 | try { 79 | /*char *diskArr = new char[SuperBlock::SIZE_OF_SPR_BLK](); 80 | tsk_img_read(img, offsetByte + SuperBlock::ADDR_OF_SPR_BLK, diskArr, SuperBlock::SIZE_OF_SPR_BLK); 81 | SuperBlock supblk(TSK_LIT_ENDIAN, (uint8_t*)diskArr); 82 | delete [] diskArr;*/ 83 | 84 | BtrfsPool btr(img, TSK_LIT_ENDIAN, devOffsets); 85 | cout << btr.devInfo() << endl; 86 | 87 | } catch(std::bad_alloc& ba) { 88 | cerr << "Error when allocating objects.\n" << ba.what() << endl; 89 | } catch(FsDamagedException& fsEx) { 90 | cerr << "Error: Btrfs filesystem damaged.\n" << fsEx.what() << endl; 91 | } catch(exception& e) { 92 | cerr << e.what() << endl; 93 | } 94 | 95 | return 0; 96 | } 97 | 98 | -------------------------------------------------------------------------------- /Tools/fls.cpp: -------------------------------------------------------------------------------- 1 | //! \file fls.cpp 2 | //! \author Shujian Yang 3 | //! 4 | //! Main function of fls. 5 | //! 6 | //! Used to list files in a Btrfs image. 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "Basics/Basics.h" 19 | #include "Trees/Trees.h" 20 | #include "Pool/Pool.h" 21 | #include "Utility/Utility.h" 22 | 23 | using namespace std; 24 | 25 | using namespace btrForensics; 26 | 27 | int main(int argc, char *argv[]) 28 | { 29 | TSK_OFF_T offsetSector(0); 30 | bool dirFlag(true); 31 | bool fileFlag(true); 32 | bool recursive(false); 33 | uint64_t rootFsId(0); 34 | int option; 35 | vector offsetStr; 36 | vector devOffsets; 37 | 38 | while((option = getopt(argc, argv, "DFro:s:")) != -1){ 39 | stringstream ss; 40 | switch(option){ 41 | case 'o': 42 | offsetStr = strSplit(optarg, ","); 43 | for(auto str : offsetStr){ 44 | if( (offsetSector = tsk_parse_offset(str.c_str())) == -1){ 45 | tsk_error_print(stderr); 46 | exit(1); 47 | } 48 | devOffsets.push_back(offsetSector); 49 | } 50 | break; 51 | case 's': 52 | ss << optarg; 53 | ss >> rootFsId; 54 | break; 55 | case 'D': 56 | dirFlag = true; 57 | fileFlag = false; 58 | break; 59 | case 'F': 60 | fileFlag = true; 61 | dirFlag = false; 62 | break; 63 | case 'r': 64 | recursive = true; 65 | break; 66 | case '?': 67 | default: 68 | cerr << "Unkown arguments." << endl; 69 | } 70 | } 71 | 72 | if(optind >= argc) { 73 | cerr << "Please provide the image name" << endl; 74 | exit(1); 75 | } 76 | 77 | if(devOffsets.size() == 0) 78 | devOffsets.push_back(0); 79 | 80 | string img_name(argv[optind]); 81 | 82 | TSK_IMG_INFO *img = tsk_img_open(1, &argv[optind], TSK_IMG_TYPE_DETECT, 0); 83 | if(img == NULL){ 84 | tsk_error_print(stderr); 85 | cerr << "Cannot open image " << img_name << "." << endl; 86 | exit(1); 87 | } 88 | 89 | for(auto &offSec : devOffsets) { 90 | TSK_OFF_T offsetByte(offSec * img->sector_size); 91 | if( offsetByte >= img->size){ 92 | cerr << "Offset is too large." << endl; 93 | exit(1); 94 | } 95 | offSec = offsetByte; 96 | } 97 | 98 | try { 99 | BtrfsPool btr(img, TSK_LIT_ENDIAN, devOffsets, rootFsId); 100 | 101 | uint64_t targetId(btr.fsTree->rootDirId); 102 | if(argc -1 > optind) { 103 | stringstream ss; 104 | ss << argv[optind+1]; 105 | ss >> targetId; 106 | } 107 | 108 | btr.fsTree->listDirItemsById(targetId, dirFlag, fileFlag, recursive, 0, cout); 109 | } catch(std::bad_alloc& ba) { 110 | cerr << "Error when allocating objects.\n" << ba.what() << endl; 111 | } catch(FsDamagedException& fsEx) { 112 | cerr << "Error: Btrfs filesystem damaged.\n" << fsEx.what() << endl; 113 | } catch(exception& e) { 114 | cerr << e.what() << endl; 115 | } 116 | 117 | return 0; 118 | } 119 | 120 | -------------------------------------------------------------------------------- /Tools/fsstat.cpp: -------------------------------------------------------------------------------- 1 | //! \file fsstat.cpp 2 | //! \author Shujian Yang 3 | //! 4 | //! Main function of fsstat. 5 | //! 6 | //! Print information about the file system. 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "Basics/Basics.h" 19 | #include "Trees/Trees.h" 20 | #include "Pool/Pool.h" 21 | #include "Utility/Utility.h" 22 | 23 | using namespace std; 24 | 25 | using namespace btrForensics; 26 | 27 | int main(int argc, char *argv[]) 28 | { 29 | TSK_OFF_T offsetSector(0); 30 | int option; 31 | vector offsetStr; 32 | vector devOffsets; 33 | 34 | while((option = getopt(argc, argv, "o:")) != -1){ 35 | switch(option){ 36 | case 'o': 37 | offsetStr = strSplit(optarg, ","); 38 | for(auto str : offsetStr){ 39 | if( (offsetSector = tsk_parse_offset(str.c_str())) == -1){ 40 | tsk_error_print(stderr); 41 | exit(1); 42 | } 43 | devOffsets.push_back(offsetSector); 44 | } 45 | break; 46 | case '?': 47 | default: 48 | cerr << "Unkown arguments." << endl; 49 | } 50 | } 51 | 52 | if(optind >= argc) { 53 | cerr << "Please provide the image name." << endl; 54 | exit(1); 55 | } 56 | 57 | if(devOffsets.size() == 0) 58 | devOffsets.push_back(0); 59 | 60 | string img_name(argv[optind]); 61 | 62 | TSK_IMG_INFO *img = tsk_img_open(1, &argv[optind], TSK_IMG_TYPE_DETECT, 0); 63 | if(img == NULL){ 64 | tsk_error_print(stderr); 65 | cerr << "Cannot open image " << img_name << "." << endl; 66 | exit(1); 67 | } 68 | 69 | for(auto &offSec : devOffsets) { 70 | TSK_OFF_T offsetByte(offSec * img->sector_size); 71 | if( offsetByte >= img->size){ 72 | cerr << "Offset is too large." << endl; 73 | exit(1); 74 | } 75 | offSec = offsetByte; 76 | } 77 | 78 | try { 79 | BtrfsPool btr(img, TSK_LIT_ENDIAN, devOffsets); 80 | 81 | SuperBlock *supblk = btr.primarySupblk; 82 | 83 | cout << *supblk << endl; 84 | cout << "Magic: " << supblk->printMagic() << endl; 85 | 86 | cout << supblk->printSpace() << endl; 87 | cout << endl; 88 | 89 | cout << "Label: " << supblk->printLabel() << endl; 90 | cout << "\n" << endl; 91 | } catch(std::bad_alloc& ba) { 92 | cerr << "Error when allocating objects.\n" << ba.what() << endl; 93 | } catch(FsDamagedException& fsEx) { 94 | cerr << "Error: Btrfs filesystem damaged.\n" << fsEx.what() << endl; 95 | } catch(exception& e) { 96 | cerr << e.what() << endl; 97 | } 98 | 99 | return 0; 100 | } 101 | 102 | -------------------------------------------------------------------------------- /Tools/icat.cpp: -------------------------------------------------------------------------------- 1 | //! \file icat.cpp 2 | //! \author Shujian Yang 3 | //! 4 | //! Main function of icat. 5 | //! 6 | //! Output the contents of file with provided inode number in Btrfs. 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "Basics/Basics.h" 19 | #include "Trees/Trees.h" 20 | #include "Pool/Pool.h" 21 | #include "Utility/Utility.h" 22 | 23 | using namespace std; 24 | 25 | using namespace btrForensics; 26 | 27 | int main(int argc, char *argv[]) 28 | { 29 | TSK_OFF_T offsetSector(0); 30 | uint64_t rootFsId(0); 31 | int option; 32 | vector offsetStr; 33 | vector devOffsets; 34 | 35 | while((option = getopt(argc, argv, "o:s:")) != -1){ 36 | stringstream ss; 37 | switch(option){ 38 | case 'o': 39 | offsetStr = strSplit(optarg, ","); 40 | for(auto str : offsetStr){ 41 | if( (offsetSector = tsk_parse_offset(str.c_str())) == -1){ 42 | tsk_error_print(stderr); 43 | exit(1); 44 | } 45 | devOffsets.push_back(offsetSector); 46 | } 47 | break; 48 | case 's': 49 | ss << optarg; 50 | ss >> rootFsId; 51 | break; 52 | case '?': 53 | default: 54 | cerr << "Unkown arguments." << endl; 55 | } 56 | } 57 | 58 | if(optind >= argc) { 59 | cerr << "Please provide the image name." << endl; 60 | exit(1); 61 | } 62 | if(argc < optind + 2) { 63 | cerr << "Please provde the file inode number." << endl; 64 | exit(1); 65 | } 66 | 67 | if(devOffsets.size() == 0) 68 | devOffsets.push_back(0); 69 | 70 | string img_name(argv[optind]); 71 | 72 | TSK_IMG_INFO *img = tsk_img_open(1, &argv[optind], TSK_IMG_TYPE_DETECT, 0); 73 | if(img == NULL){ 74 | tsk_error_print(stderr); 75 | cerr << "Cannot open image " << img_name << "." << endl; 76 | exit(1); 77 | } 78 | 79 | for(auto &offSec : devOffsets) { 80 | TSK_OFF_T offsetByte(offSec * img->sector_size); 81 | if( offsetByte >= img->size){ 82 | cerr << "Offset is too large." << endl; 83 | exit(1); 84 | } 85 | offSec = offsetByte; 86 | } 87 | 88 | try { 89 | BtrfsPool btr(img, TSK_LIT_ENDIAN, devOffsets, rootFsId); 90 | 91 | uint64_t targetId(btr.fsTree->rootDirId); 92 | stringstream ss; 93 | ss << argv[optind+1]; 94 | ss >> targetId; 95 | 96 | bool success; 97 | success = btr.fsTree->readFile(targetId); 98 | 99 | if(success) 100 | cout << "Success: File written to current directory." << endl; 101 | else 102 | cout << "Error: File not found or has no content." << endl; 103 | } catch(std::bad_alloc& ba) { 104 | cerr << "Error when allocating objects.\n" << ba.what() << endl; 105 | } catch(FsDamagedException& fsEx) { 106 | cerr << "Error: Btrfs filesystem damaged.\n" << fsEx.what() << endl; 107 | } catch(exception& e) { 108 | cerr << e.what() << endl; 109 | } 110 | 111 | return 0; 112 | } 113 | 114 | -------------------------------------------------------------------------------- /Tools/istat.cpp: -------------------------------------------------------------------------------- 1 | //! \file istat.cpp 2 | //! \author Shujian Yang 3 | //! 4 | //! Main function of istat. 5 | //! 6 | //! Print information about an inode. 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "Basics/Basics.h" 19 | #include "Trees/Trees.h" 20 | #include "Pool/Pool.h" 21 | #include "Utility/Utility.h" 22 | 23 | using namespace std; 24 | 25 | using namespace btrForensics; 26 | 27 | int main(int argc, char *argv[]) 28 | { 29 | TSK_OFF_T offsetSector(0); 30 | uint64_t rootFsId(0); 31 | int option; 32 | vector offsetStr; 33 | vector devOffsets; 34 | 35 | while((option = getopt(argc, argv, "o:s:")) != -1){ 36 | stringstream ss; 37 | switch(option){ 38 | case 'o': 39 | offsetStr = strSplit(optarg, ","); 40 | for(auto str : offsetStr){ 41 | if( (offsetSector = tsk_parse_offset(str.c_str())) == -1){ 42 | tsk_error_print(stderr); 43 | exit(1); 44 | } 45 | devOffsets.push_back(offsetSector); 46 | } 47 | break; 48 | case 's': 49 | ss << optarg; 50 | ss >> rootFsId; 51 | break; 52 | case '?': 53 | default: 54 | cerr << "Unkown arguments." << endl; 55 | } 56 | } 57 | 58 | if(optind >= argc) { 59 | cerr << "Please provide the image name." << endl; 60 | exit(1); 61 | } 62 | if(argc < optind + 2) { 63 | cerr << "Please provde the file inode number." << endl; 64 | exit(1); 65 | } 66 | 67 | if(devOffsets.size() == 0) 68 | devOffsets.push_back(0); 69 | 70 | string img_name(argv[optind]); 71 | 72 | TSK_IMG_INFO *img = tsk_img_open(1, &argv[optind], TSK_IMG_TYPE_DETECT, 0); 73 | if(img == NULL){ 74 | tsk_error_print(stderr); 75 | cerr << "Cannot open image " << img_name << "." << endl; 76 | exit(1); 77 | } 78 | 79 | for(auto &offSec : devOffsets) { 80 | TSK_OFF_T offsetByte(offSec * img->sector_size); 81 | if( offsetByte >= img->size){ 82 | cerr << "Offset is too large." << endl; 83 | exit(1); 84 | } 85 | offSec = offsetByte; 86 | } 87 | 88 | try { 89 | BtrfsPool btr(img, TSK_LIT_ENDIAN, devOffsets, rootFsId); 90 | 91 | uint64_t targetId(btr.fsTree->rootDirId); 92 | stringstream ss; 93 | ss << argv[optind+1]; 94 | ss >> targetId; 95 | 96 | bool success; 97 | success = btr.fsTree->showInodeInfo(targetId, cout); 98 | 99 | if(!success) 100 | cout << "Error: Inode not found." << endl; 101 | } catch(std::bad_alloc& ba) { 102 | cerr << "Error when allocating objects.\n" << ba.what() << endl; 103 | } catch(FsDamagedException& fsEx) { 104 | cerr << "Error: Btrfs filesystem damaged.\n" << fsEx.what() << endl; 105 | } catch(exception& e) { 106 | cerr << e.what() << endl; 107 | } 108 | 109 | return 0; 110 | } 111 | 112 | -------------------------------------------------------------------------------- /Tools/subls.cpp: -------------------------------------------------------------------------------- 1 | //! \file subls.cpp 2 | //! \author Shujian Yang 3 | //! 4 | //! Main function of subls. 5 | //! 6 | //! List subvolumes and snapshots in a Btrfs image. 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "Basics/Basics.h" 19 | #include "Trees/Trees.h" 20 | #include "Pool/Pool.h" 21 | #include "Utility/Utility.h" 22 | 23 | using namespace std; 24 | 25 | using namespace btrForensics; 26 | 27 | int main(int argc, char *argv[]) 28 | { 29 | TSK_OFF_T offsetSector(0); 30 | int option; 31 | vector offsetStr; 32 | vector devOffsets; 33 | 34 | while((option = getopt(argc, argv, "o:")) != -1){ 35 | switch(option){ 36 | case 'o': 37 | offsetStr = strSplit(optarg, ","); 38 | for(auto str : offsetStr){ 39 | if( (offsetSector = tsk_parse_offset(str.c_str())) == -1){ 40 | tsk_error_print(stderr); 41 | exit(1); 42 | } 43 | devOffsets.push_back(offsetSector); 44 | } 45 | break; 46 | case '?': 47 | default: 48 | cerr << "Unkown arguments." << endl; 49 | } 50 | } 51 | 52 | if(optind >= argc) { 53 | cerr << "Please provide the image name" << endl; 54 | exit(1); 55 | } 56 | 57 | if(devOffsets.size() == 0) 58 | devOffsets.push_back(0); 59 | 60 | string img_name(argv[optind]); 61 | 62 | TSK_IMG_INFO *img = tsk_img_open(1, &argv[optind], TSK_IMG_TYPE_DETECT, 0); 63 | if(img == NULL){ 64 | tsk_error_print(stderr); 65 | cerr << "Cannot open image " << img_name << "." << endl; 66 | exit(1); 67 | } 68 | 69 | TSK_OFF_T offsetByte(offsetSector * img->sector_size); 70 | if( offsetByte >= img->size){ 71 | cerr << "Offset is too large." << endl; 72 | exit(1); 73 | } 74 | 75 | try { 76 | BtrfsPool btr(img, TSK_LIT_ENDIAN, devOffsets); 77 | 78 | vector foundRootRefs; 79 | btr.treeTraverse(btr.rootTree, [&foundRootRefs](const LeafNode* leaf) 80 | { return filterItems(leaf, ItemType::ROOT_BACKREF, foundRootRefs); }); 81 | 82 | if(foundRootRefs.size() == 0) { 83 | cout << "\nNo subvolumes or snapshots are found.\n" << endl; 84 | return 0; 85 | } 86 | 87 | cout << "The following subvolumes or snapshots are found:" << endl; 88 | for(auto item : foundRootRefs) { 89 | const RootRef* ref = static_cast(item); 90 | cout << dec << setfill(' ') << setw(7); 91 | cout << ref->getId() << " " << ref->getDirName() << '\n'; 92 | } 93 | } catch(std::bad_alloc& ba) { 94 | cerr << "Error when allocating objects.\n" << ba.what() << endl; 95 | } catch(FsDamagedException& fsEx) { 96 | cerr << "Error: Btrfs filesystem damaged.\n" << fsEx.what() << endl; 97 | } catch(exception& e) { 98 | cerr << e.what() << endl; 99 | } 100 | 101 | return 0; 102 | } 103 | 104 | -------------------------------------------------------------------------------- /Trees/BtrfsNode.cpp: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Implementation of class BtrfsNode. 5 | 6 | #include "BtrfsNode.h" 7 | 8 | namespace btrForensics{ 9 | //! Constructor of btrfs node. 10 | //! 11 | //! \param header Pointer to header of a node. 12 | //! 13 | BtrfsNode::BtrfsNode(const BtrfsHeader *header) 14 | :nodeHeader(header) {} 15 | } 16 | -------------------------------------------------------------------------------- /Trees/BtrfsNode.h: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Header file of class BtrfsNode. 5 | 6 | #ifndef NODE_H 7 | #define NODE_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include "Utility/Utility.h" 13 | #include "Basics/Basics.h" 14 | 15 | namespace btrForensics{ 16 | //! Represent a node in B-tree structure. 17 | class BtrfsNode { 18 | public: 19 | const BtrfsHeader *nodeHeader; //!< Header of a node. 20 | 21 | BtrfsNode(const BtrfsHeader *header); 22 | ~BtrfsNode() { if(nodeHeader!=nullptr) delete nodeHeader; } //! Destructor 23 | 24 | //! Return infomation about the node. 25 | //! Virtual function to be overridden by derived classes. 26 | virtual const std::string info() const = 0; 27 | }; 28 | 29 | } 30 | 31 | #endif 32 | 33 | -------------------------------------------------------------------------------- /Trees/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${PROJECT_SOURCE_DIR}) 2 | 3 | aux_source_directory(. STRUCT_SRCS) 4 | 5 | add_library(Trees ${STRUCT_SRCS}) 6 | 7 | -------------------------------------------------------------------------------- /Trees/ChunkTree.cpp: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Header file of class ChunkTree 5 | 6 | #include 7 | #include "ChunkTree.h" 8 | #include "Pool/Functions.h" 9 | 10 | namespace btrForensics { 11 | 12 | //! Constructor of tree analyzer. 13 | //! 14 | //! \param pool Pointer to a Btrfs pool. 15 | //! 16 | ChunkTree::ChunkTree(BtrfsPool *pool) 17 | :btrPool(pool) 18 | { 19 | const SuperBlock *supBlk = btrPool->primarySupblk; 20 | 21 | char *diskArr = new char[BtrfsHeader::SIZE_OF_HEADER](); 22 | //std::cout << std::hex << supBlk->chunkTrRootAddr << std::endl; 23 | uint64_t chunkTreePhyAddr = btrPool->readChunkData(diskArr, supBlk->chunkTrRootAddr, 24 | &(supBlk->chunkKey), &(supBlk->chunkData), BtrfsHeader::SIZE_OF_HEADER); 25 | 26 | BtrfsHeader *chunkHeader = new BtrfsHeader(pool->endian, (uint8_t*)diskArr); 27 | delete [] diskArr; 28 | 29 | //std::cout << *chunkHeader << std::endl; 30 | uint64_t itemListAddr = chunkTreePhyAddr + BtrfsHeader::SIZE_OF_HEADER; 31 | 32 | const BtrfsNode* chunkTree; 33 | if(chunkHeader->isLeafNode()) 34 | chunkRoot = new LeafNode(pool->image, chunkHeader, 35 | pool->endian, itemListAddr); 36 | else 37 | chunkRoot = new InternalNode(pool->image, chunkHeader, 38 | TSK_LIT_ENDIAN, itemListAddr); 39 | } 40 | 41 | 42 | //!< Destructor 43 | ChunkTree::~ChunkTree() 44 | { 45 | if(chunkRoot != nullptr) 46 | delete chunkRoot; 47 | } 48 | 49 | 50 | //! Find chunk item where the logical address is matched. 51 | //! 52 | //! \param leaf Pointer to the leaf node. 53 | //! \param targetLogAddr Logical address to convert. 54 | //! \param foundChunk Found chunk item 55 | //! 56 | //! \return Return true when the chunk is found. 57 | //! 58 | bool ChunkTree::findChunkItem(const LeafNode* leaf, uint64_t targetLogAddr, 59 | const ChunkItem* &foundChunk) const 60 | { 61 | const BtrfsItem* target(nullptr); 62 | 63 | for(auto item : leaf->itemList) { 64 | //The item must be a chunk item. 65 | if(item->getItemType() != ItemType::CHUNK_ITEM) 66 | continue; 67 | //Chunk logical address should be just smaller than or equal to 68 | //target logical address. 69 | //In other words, find the chunk with logcial address that is the 70 | //largest one but smaller or equal to target logical address. 71 | if(item->itemHead->key.offset <= targetLogAddr) 72 | target = item; 73 | else 74 | break; 75 | } 76 | 77 | if(target == nullptr) 78 | return false; 79 | 80 | foundChunk = static_cast(target); 81 | 82 | return true; 83 | } 84 | 85 | 86 | //! Convert logical address to physical address. 87 | //! 88 | //! \param logicalAddr 64-bit logial address. 89 | //! \return 64-bit physical address. 90 | //! 91 | const ChunkItem* ChunkTree::getChunkItem(uint64_t logicalAddr) const 92 | { 93 | const ChunkItem* chunk(nullptr); 94 | btrPool->chunkTreeSearch(chunkRoot, 95 | [this, logicalAddr, &chunk](const LeafNode* leaf) 96 | { return this->findChunkItem(leaf, logicalAddr, chunk); }); 97 | 98 | return chunk; 99 | } 100 | 101 | } 102 | 103 | -------------------------------------------------------------------------------- /Trees/ChunkTree.h: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Header file of class ChunkTree 5 | 6 | #ifndef CHUNK_TREE_H 7 | #define CHUNK_TREE_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include "Basics/Basics.h" 13 | 14 | namespace btrForensics { 15 | class BtrfsPool; 16 | class LeafNode; 17 | 18 | //! Process chunk tree of Btrfs. 19 | class ChunkTree { 20 | public: 21 | const BtrfsNode* chunkRoot; //!< Root of chunk tree. 22 | private: 23 | BtrfsPool* btrPool; 24 | 25 | public: 26 | ChunkTree(BtrfsPool *pool); 27 | ~ChunkTree(); 28 | 29 | bool findChunkItem(const LeafNode* leaf, uint64_t targetLogAddr, 30 | const ChunkItem* &foundChunk) const; 31 | const ChunkItem* getChunkItem(uint64_t logicalAddr) const; 32 | }; 33 | 34 | } 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /Trees/DirContent.cpp: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Implementation of class DirContent. 5 | 6 | #include 7 | #include 8 | #include "DirContent.h" 9 | 10 | using namespace std; 11 | 12 | namespace btrForensics { 13 | 14 | //! Constructor of DirContent. 15 | DirContent::DirContent(const InodeItem* inodeItem, 16 | const InodeRef* inodeRef, std::vector &dirItems) 17 | :name(inodeRef->getDirName()), inode(inodeItem), ref(inodeRef) 18 | { 19 | for(auto item : dirItems){ 20 | DirItem* dirEntry = (DirItem*)item; 21 | children.push_back(dirEntry); 22 | } 23 | 24 | if(name == "..") // This is the root directory. 25 | name = "/"; 26 | } 27 | 28 | 29 | //! Overloaded stream operator. 30 | std::ostream &operator<<(std::ostream& os, const DirContent& dirc) 31 | { 32 | os << "[" << dirc.name << "]" << endl; 33 | for(auto child : dirc.children) { 34 | if(child->getTargetType() == ItemType::ROOT_ITEM) 35 | continue; 36 | 37 | //os << " \e(0\x74\x71\e(B" << dec; 38 | os << " |" << dec; 39 | os << setfill('-') << setw(8) << child->getTargetInode(); 40 | 41 | if(child->type == DirItemType::DIRECTORY) 42 | os << " [" << child->getDirName() << "]\n"; 43 | else 44 | os << " " << child->getDirName() << "\n"; 45 | } 46 | os << endl; 47 | } 48 | 49 | } 50 | 51 | -------------------------------------------------------------------------------- /Trees/DirContent.h: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Header file of class DirContent. 5 | 6 | #ifndef DIR_CONTENT_H 7 | #define DIR_CONTENT_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "Basics/Basics.h" 14 | 15 | namespace btrForensics{ 16 | class FileTreeAnalyzer; 17 | 18 | //! Record of a directory. 19 | class DirContent { 20 | public: 21 | const InodeItem *inode; //!< Inode of this directory. 22 | const InodeRef *ref; //!< Inode points to this directory. 23 | std::string name; //!< Name of this directory. 24 | std::vector children; //!< Entries of this directory. 25 | 26 | DirContent(const InodeItem* inodeItem, const InodeRef* inodeRef, 27 | std::vector &dirItems); 28 | ~DirContent() = default; //!< Destructor. 29 | 30 | friend std::ostream &operator<<(std::ostream&, const DirContent&); 31 | }; 32 | 33 | } 34 | 35 | #endif 36 | 37 | -------------------------------------------------------------------------------- /Trees/FilesystemTree.cpp: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Implementation of class FilesystemTree. 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "FilesystemTree.h" 13 | #include "Pool/Functions.h" 14 | 15 | using namespace std; 16 | 17 | namespace btrForensics { 18 | 19 | //! Constructor of tree analyzer. 20 | //! 21 | //! \param rootNode Root node of the root tree to be analyzed. 22 | //! \param rootItemId Root item id of the file system tree. 23 | //! \param pool BtrfsPoll used to analyze file system tree. 24 | //! 25 | FilesystemTree::FilesystemTree(const BtrfsNode* rootNode, 26 | uint64_t rootItemId, BtrfsPool* pool) 27 | :btrPool(pool) 28 | { 29 | const BtrfsItem* foundItem; 30 | const RootItem* rootItm; 31 | if(btrPool->treeSearchById(rootNode, rootItemId, 32 | [&foundItem](const LeafNode* leaf, uint64_t targetId) 33 | { return searchForItem(leaf, targetId, ItemType::ROOT_ITEM, foundItem); })) { 34 | rootItm = static_cast(foundItem); 35 | } 36 | else { 37 | ostringstream oss; 38 | oss << "Filesystem tree pointed by root item " << rootItemId << " is not found."; 39 | throw FsDamagedException(oss.str()); 40 | } 41 | 42 | uint64_t offset = rootItm->getBlockNumber(); 43 | const SuperBlock *supBlk = btrPool->primarySupblk; 44 | 45 | char *headerArr = new char[BtrfsHeader::SIZE_OF_HEADER](); 46 | uint64_t physicalAddr = btrPool->readData(headerArr, offset, BtrfsHeader::SIZE_OF_HEADER); 47 | rootDirId = rootItm->getRootObjId(); 48 | 49 | const BtrfsHeader *fileTreeHeader = 50 | new BtrfsHeader(TSK_LIT_ENDIAN, (uint8_t*)headerArr); 51 | delete [] headerArr; 52 | 53 | uint64_t itemOffset = physicalAddr + BtrfsHeader::SIZE_OF_HEADER; 54 | 55 | if(fileTreeHeader->isLeafNode()){ 56 | fileTreeRoot = new LeafNode(btrPool->image, 57 | fileTreeHeader, btrPool->endian, itemOffset); 58 | } 59 | else { 60 | fileTreeRoot = new InternalNode(btrPool->image, 61 | fileTreeHeader, btrPool->endian, itemOffset); 62 | } 63 | } 64 | 65 | 66 | //!< Destructor 67 | FilesystemTree::~FilesystemTree() 68 | { 69 | if(fileTreeRoot != nullptr) 70 | delete fileTreeRoot; 71 | } 72 | 73 | 74 | //! List all dir items in this tree. 75 | //! 76 | //! \param os Output stream where the infomation is printed. 77 | //! 78 | void FilesystemTree::listDirItems(ostream &os) 79 | { 80 | //treeTraverse(fileTreeRoot, bind(printLeafDir, _1, _2, ref(os))); 81 | 82 | //Choose Lamba over std::bind. 83 | //See "Effective Modern C++" Item 34. 84 | btrPool->treeTraverse(fileTreeRoot, 85 | [&os](const LeafNode *leaf) { printLeafDir(leaf, os); }); 86 | } 87 | 88 | 89 | //! List all directory items in a directory with given id. 90 | //! 91 | //! \param id Id of the target directory. 92 | //! \param dirFlag Whether to list directories. 93 | //! \param fileFlag Whether to list regular files. 94 | //! \param recursive Whether list items recursively in subdirectories. 95 | //! \param level Level used to determine number of "+"s, usually set as 0. 96 | //! \param os Output stream where the infomation is printed. 97 | //! 98 | void FilesystemTree::listDirItemsById(uint64_t id, bool dirFlag, bool fileFlag, 99 | bool recursive, int level, std::ostream& os) 100 | { 101 | DirContent* dir = getDirContent(id); 102 | if(dir == nullptr) { 103 | return; 104 | } 105 | 106 | for(auto child : dir->children) { 107 | if(child->getTargetType() != ItemType::INODE_ITEM) 108 | continue; 109 | if((fileFlag && child->type == DirItemType::REGULAR_FILE) 110 | || (dirFlag && child->type == DirItemType::DIRECTORY)) { 111 | if(level!=0) os << string(level, '+') << " "; 112 | os << child->type << '/' << child->type << " "; 113 | ostringstream oss; 114 | oss << dec << child->getTargetInode() << ':'; 115 | os << setfill(' ') << setw(9) << left << oss.str(); 116 | os << " " << child->getDirName() << endl; 117 | } 118 | if(recursive && child->type == DirItemType::DIRECTORY) { 119 | uint64_t newId = child->targetKey.objId; 120 | listDirItemsById(newId, dirFlag, fileFlag, recursive, level+1, os); 121 | } 122 | } 123 | } 124 | 125 | 126 | //! Locate the directory with give inode number. 127 | //! 128 | //! \param id Inode number of directory. 129 | //! 130 | DirContent* FilesystemTree::getDirContent(uint64_t id) 131 | { 132 | const BtrfsItem* foundItem; 133 | if(btrPool->treeSearchById(fileTreeRoot, id, 134 | [&foundItem](const LeafNode* leaf, uint64_t targetId) 135 | { return searchForItem(leaf, targetId, ItemType::INODE_ITEM, foundItem); })) { 136 | const InodeItem* rootInode = static_cast(foundItem); 137 | 138 | btrPool->treeSearchById(fileTreeRoot, id, 139 | [&foundItem](const LeafNode* leaf, uint64_t targetId) 140 | { return searchForItem(leaf, targetId, ItemType::INODE_REF, foundItem); }); 141 | const InodeRef* rootRef = static_cast(foundItem); 142 | 143 | vector foundItems; 144 | btrPool->treeSearchById(fileTreeRoot, id, 145 | [&foundItems](const LeafNode* leaf, uint64_t targetId) 146 | { return filterItems(leaf, targetId, ItemType::DIR_INDEX, foundItems); }); 147 | 148 | return new DirContent(rootInode, rootRef, foundItems); 149 | } 150 | return nullptr; 151 | } 152 | 153 | 154 | //! List files in a directory and navigate to subdirectory. 155 | //! 156 | //! \param os Output stream where the infomation is printed. 157 | //! \param is Input stream telling which directory is the one to be read. 158 | //! 159 | const void FilesystemTree::explorFiles(std::ostream& os, istream& is) 160 | { 161 | DirContent* dir = getDirContent(rootDirId); 162 | if(dir == nullptr) { 163 | os << "Root directory not found." << endl; 164 | return; 165 | } 166 | 167 | os << "Root directory content:\n" <children) { 172 | if(dirItem->getTargetType() == ItemType::ROOT_ITEM) { 173 | ostringstream oss; 174 | os << " \e(0\x74\x71\e(B" << dec; 175 | oss << "[" << dirItem->getTargetInode() << "]"; 176 | os << setfill(' ') << setw(9) << oss.str(); 177 | os << " " << dirItem->getDirName() << '\n'; 178 | } 179 | } 180 | os << endl; 181 | 182 | while(true) { 183 | uint64_t targetInode; 184 | map dirList; 185 | string input; 186 | int inputId; 187 | 188 | int count(0); 189 | for(int i = 0; i < dir->children.size(); ++i) { 190 | if(dir->children[i]->type == DirItemType::DIRECTORY 191 | && dir->children[i]->getTargetType() == ItemType::INODE_ITEM) 192 | dirList[++count] = i; 193 | } 194 | if(count == 0) { 195 | while(true) { 196 | os << "No child directory is found.\n"; 197 | os << "(Enter 'r' to return to previous directory or 'q' to quit.)" << endl; 198 | is >> input; 199 | if(input == "q") return; 200 | if(input == "r") break; 201 | } 202 | targetInode = dir->ref->itemHead->key.offset; 203 | } 204 | else{ 205 | while(true) { 206 | os << "Child directory with following inode numbers are found." << endl; 207 | for(auto entry : dirList) { 208 | DirItem* item = dir->children[entry.second]; 209 | os << "[" << dec << setfill(' ') << setw(2) << entry.first << "] " 210 | << setw(7) << item->getTargetInode() << " " << item->getDirName() << '\n'; 211 | } 212 | os << endl; 213 | os << "To visit a child directory, please enter its index in the list:\n"; 214 | os << "(Enter 'r' to return to previous directory or 'q' to quit.)" << endl; 215 | is >> input; 216 | 217 | if(input == "q") return; 218 | if(input == "r") { 219 | targetInode = dir->ref->itemHead->key.offset; 220 | break; 221 | } 222 | stringstream(input) >> inputId; 223 | if(dirList.find(inputId) != dirList.end()) { 224 | int target = dirList[inputId]; 225 | DirItem* targetItem = dir->children[target]; 226 | targetInode = targetItem->getTargetInode(); 227 | break; 228 | } 229 | os << "Wrong index, please enter a correct one.\n" << endl; 230 | } 231 | os << endl; 232 | } 233 | 234 | DirContent* newDir = getDirContent(targetInode); 235 | delete dir; 236 | dir = newDir; 237 | 238 | if(newDir == nullptr) { 239 | os << "Error, Directory not found." << endl; 240 | return; 241 | } 242 | os << std::string(60, '=') << "\n\n"; 243 | os << "Directory content:\n" <treeSearchById(fileTreeRoot, id, 259 | [&foundItem](const LeafNode* leaf, uint64_t targetId) 260 | { return searchForItem(leaf, targetId, ItemType::INODE_ITEM, foundItem); })) 261 | return false; 262 | const InodeItem* inode = static_cast(foundItem); 263 | uint64_t fileSize = inode->getSize(); 264 | 265 | if(!btrPool->treeSearchById(fileTreeRoot, id, 266 | [&foundItem](const LeafNode* leaf, uint64_t targetId) 267 | { return searchForItem(leaf, targetId, ItemType::INODE_REF, foundItem); })) 268 | return false; 269 | const InodeRef* inodeRef = static_cast(foundItem); 270 | string fileName = inodeRef->getDirName(); 271 | 272 | 273 | vector foundExtents; 274 | btrPool->treeSearchById(fileTreeRoot, id, 275 | [&foundExtents](const LeafNode* leaf, uint64_t targetId) 276 | { return filterItems(leaf, targetId, ItemType::EXTENT_DATA, foundExtents); }); 277 | if(foundExtents.size() < 1) 278 | return false; 279 | 280 | uint64_t unreadSize = fileSize; 281 | for(auto extent : foundExtents) { 282 | const ExtentData* data = static_cast(extent); 283 | if(data->compression + data->encryption + data->otherEncoding != 0) 284 | return false; 285 | 286 | ofstream ofs(fileName, ofstream::app | ofstream::binary); 287 | if(!ofs) return false; 288 | if(data->type == 0) { //Is inline file. 289 | char* dataArr = new char[fileSize]; 290 | tsk_img_read(btrPool->image, data->dataAddress, dataArr, fileSize); 291 | ofs.write(dataArr, fileSize); 292 | delete [] dataArr; 293 | ofs.close(); 294 | return true; 295 | } 296 | else { 297 | char* dataArr; 298 | if(unreadSize > data->numOfBytes) { 299 | dataArr = new char[data->numOfBytes](); 300 | btrPool->readData(dataArr, data->logicalAddress, data->numOfBytes); 301 | ofs.write(dataArr, data->numOfBytes); 302 | unreadSize -= data->numOfBytes; 303 | delete [] dataArr; 304 | } 305 | else { 306 | dataArr = new char[unreadSize](); 307 | btrPool->readData(dataArr, data->logicalAddress, unreadSize); 308 | ofs.write(dataArr, unreadSize); 309 | unreadSize = 0; 310 | delete [] dataArr; 311 | } 312 | ofs.close(); 313 | } 314 | } 315 | if(unreadSize != 0) { 316 | throw FsDamagedException("File extents missing. Written file is incomplete."); 317 | } 318 | return true; 319 | } 320 | 321 | 322 | //! Print infomation about target inode. 323 | //! 324 | //! \param id Id of the target inode. 325 | //! \param os Output stream where the infomation is printed. 326 | //! 327 | //! \return True if inode is found. 328 | //! 329 | const bool FilesystemTree::showInodeInfo(uint64_t id, std::ostream& os) 330 | { 331 | const BtrfsItem* foundItem; 332 | if(!btrPool->treeSearchById(fileTreeRoot, id, 333 | [&foundItem](const LeafNode* leaf, uint64_t targetId) 334 | { return searchForItem(leaf, targetId, ItemType::INODE_ITEM, foundItem); })) 335 | return false; 336 | const InodeItem* inode = static_cast(foundItem); 337 | uint64_t size = inode->getSize(); 338 | 339 | if(!btrPool->treeSearchById(fileTreeRoot, id, 340 | [&foundItem](const LeafNode* leaf, uint64_t targetId) 341 | { return searchForItem(leaf, targetId, ItemType::INODE_REF, foundItem); })) 342 | return false; 343 | const InodeRef* inodeRef = static_cast(foundItem); 344 | string name = inodeRef->getDirName(); 345 | 346 | os << dec; 347 | os << "Inode number: " << id << endl; 348 | os << "Size: " << humanSize(size) << endl; 349 | os << "Name: " << name << endl; 350 | 351 | os << "\nDirectory Entry Times(local);" << endl; 352 | os << inode->printTime() << endl; 353 | 354 | return true; 355 | } 356 | 357 | } 358 | 359 | -------------------------------------------------------------------------------- /Trees/FilesystemTree.h: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Header file of class FilesystemTree. 5 | 6 | #ifndef FILE_TREE_H 7 | #define FILE_TREE_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "Basics/Basics.h" 16 | #include "DirContent.h" 17 | 18 | namespace btrForensics { 19 | class BtrfsPool; 20 | 21 | //! Analyze the file system tree in btrfs. 22 | class FilesystemTree { 23 | public: 24 | const BtrfsNode *fileTreeRoot; //!< Root node of the filesystem tree. 25 | uint64_t rootDirId; //!< Inode number of root directory. 26 | 27 | private: 28 | BtrfsPool* btrPool; 29 | 30 | public: 31 | FilesystemTree(const BtrfsNode*, uint64_t rootItemId, BtrfsPool*); 32 | ~FilesystemTree(); 33 | 34 | void listDirItems(std::ostream& os); 35 | void listDirItemsById(uint64_t id, bool dirFlag, bool fileFlag, 36 | bool recursive, int level, std::ostream& os); 37 | 38 | DirContent* getDirContent(uint64_t id); 39 | 40 | const void explorFiles(std::ostream& os, std::istream& is); 41 | 42 | const bool readFile(uint64_t id); 43 | const bool showInodeInfo(uint64_t id, std::ostream& os); 44 | }; 45 | } 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /Trees/InternalNode.cpp: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Implementation of class InternalNode. 5 | 6 | #include 7 | #include "InternalNode.h" 8 | 9 | namespace btrForensics{ 10 | 11 | //! Constructor of btrfs leaf node. 12 | //! 13 | //! \param img Image file. 14 | //! \param imgOffset Offset to the partition. 15 | //! \param header Pointer to header of a node. 16 | //! \param endian The endianess of the array. 17 | //! \param startOffset Offset of the node, right after header. 18 | //! 19 | InternalNode::InternalNode(TSK_IMG_INFO *img, const BtrfsHeader *header, 20 | TSK_ENDIAN_ENUM endian, uint64_t startOffset) 21 | :BtrfsNode(header) 22 | { 23 | char *diskArr; 24 | uint64_t itemOffset(0); 25 | uint32_t itemNum = header -> getNumOfItems(); 26 | 27 | for(uint32_t i=0; i 10 | #include 11 | #include "BtrfsNode.h" 12 | 13 | using std::vector; 14 | 15 | namespace btrForensics{ 16 | class KeyPtr; 17 | //! Internal node in B-tree structure. 18 | class InternalNode : public BtrfsNode { 19 | public: 20 | vector keyPointers; //!< Key pointers to other nodes. 21 | 22 | public: 23 | InternalNode(TSK_IMG_INFO*, const BtrfsHeader*, TSK_ENDIAN_ENUM, uint64_t); 24 | ~InternalNode(); 25 | 26 | const std::string info() const override; 27 | }; 28 | 29 | } 30 | 31 | #endif 32 | 33 | -------------------------------------------------------------------------------- /Trees/LeafNode.cpp: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Implementation of class LeafNode. 5 | 6 | #include 7 | #include "LeafNode.h" 8 | 9 | namespace btrForensics{ 10 | 11 | //! Constructor of btrfs leaf node. 12 | //! 13 | //! \param img Image file. 14 | //! \param header Pointer to header of a node. 15 | //! \param endian The endianess of the array. 16 | //! \param startOffset Offset of the node, right after header. 17 | //! 18 | LeafNode::LeafNode(TSK_IMG_INFO *img, const BtrfsHeader *header, 19 | TSK_ENDIAN_ENUM endian, uint64_t startOffset) 20 | :BtrfsNode(header) 21 | { 22 | char *diskArr; 23 | uint64_t itemOffset(0); 24 | uint32_t itemNum = header -> getNumOfItems(); 25 | 26 | for(uint32_t i=0; igetDataSize()](); 36 | uint64_t dataOffset = startOffset + itemHead->getDataOffset(); 37 | tsk_img_read(img, dataOffset, itmArr, itemHead->getDataSize()); 38 | 39 | switch(itemHead->key.itemType){ 40 | case ItemType::INODE_ITEM: 41 | newItem = new InodeItem(itemHead, TSK_LIT_ENDIAN, (uint8_t*)itmArr); 42 | break; 43 | case ItemType::INODE_REF: 44 | newItem = new InodeRef(itemHead, TSK_LIT_ENDIAN, (uint8_t*)itmArr); 45 | break; 46 | case ItemType::DIR_ITEM: //Both types use the same structure. 47 | case ItemType::DIR_INDEX: 48 | newItem = new DirItem(itemHead, TSK_LIT_ENDIAN, (uint8_t*)itmArr); 49 | break; 50 | case ItemType::ROOT_ITEM: 51 | newItem = new RootItem(itemHead, TSK_LIT_ENDIAN, (uint8_t*)itmArr); 52 | break; 53 | case ItemType::ROOT_REF: //Both types use the same structure. 54 | case ItemType::ROOT_BACKREF: 55 | newItem = new RootRef(itemHead, TSK_LIT_ENDIAN, (uint8_t*)itmArr); 56 | break; 57 | case ItemType::CHUNK_ITEM: 58 | newItem = new ChunkItem(itemHead, TSK_LIT_ENDIAN, (uint8_t*)itmArr); 59 | break; 60 | case ItemType::EXTENT_DATA: 61 | newItem = new ExtentData(itemHead, TSK_LIT_ENDIAN, (uint8_t*)itmArr, dataOffset); 62 | break; 63 | case ItemType::BLOCK_GROUP_ITEM: 64 | newItem = new BlockGroupItem(itemHead, TSK_LIT_ENDIAN, (uint8_t*)itmArr); 65 | break; 66 | case ItemType::EXTENT_ITEM: 67 | newItem = new ExtentItem(itemHead, TSK_LIT_ENDIAN, (uint8_t*)itmArr); 68 | break; 69 | case ItemType::DEV_ITEM: 70 | newItem = new DevItem(itemHead, TSK_LIT_ENDIAN, (uint8_t*)itmArr); 71 | break; 72 | default: 73 | newItem = new UnknownItem(itemHead); 74 | } 75 | 76 | if(newItem != nullptr){ 77 | itemList.push_back(newItem); 78 | } 79 | 80 | delete [] diskArr; 81 | delete [] itmArr; 82 | 83 | itemOffset += ItemHead::SIZE_OF_ITEM_HEAD; 84 | } 85 | } 86 | 87 | 88 | //! Destructor 89 | LeafNode::~LeafNode() 90 | { 91 | for(auto item : itemList) 92 | delete item; 93 | } 94 | 95 | 96 | //! Print info about this node. 97 | const std::string LeafNode::info() const 98 | { 99 | std::ostringstream oss; 100 | 101 | oss << *nodeHeader << "\n\n"; 102 | oss << "Item list:" << '\n'; 103 | oss << std::string(30, '=') << "\n\n"; 104 | 105 | for(auto &item : itemList){ 106 | oss << *item; 107 | oss << std::string(30, '=') << "\n\n"; 108 | } 109 | 110 | return oss.str(); 111 | } 112 | 113 | } 114 | 115 | -------------------------------------------------------------------------------- /Trees/LeafNode.h: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! Header file of class LeafNode. 5 | 6 | #ifndef LEAF_NODE_H 7 | #define LEAF_NODE_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include "Trees.h" 13 | 14 | using std::vector; 15 | 16 | namespace btrForensics{ 17 | //! Leaf node in B-tree structure. 18 | class LeafNode : public BtrfsNode { 19 | public: 20 | vector itemList; //!< Stores items and their data. 21 | 22 | public: 23 | LeafNode(TSK_IMG_INFO*, const BtrfsHeader*, TSK_ENDIAN_ENUM, uint64_t); 24 | ~LeafNode(); 25 | 26 | const std::string info() const override; 27 | }; 28 | } 29 | 30 | #endif 31 | 32 | -------------------------------------------------------------------------------- /Trees/SuperBlock.cpp: -------------------------------------------------------------------------------- 1 | //! 2 | //! \file 3 | //! \author Shujian Yang 4 | //! 5 | //! SuperBlock implementation. 6 | 7 | #include 8 | #include "SuperBlock.h" 9 | #include "Utility/ReadInt.h" 10 | #include "Pool/Functions.h" 11 | 12 | namespace btrForensics{ 13 | 14 | //! Constructor of super block. 15 | //! 16 | //! \param endian The endianess of the array. 17 | //! \param arr Byte array storing super block data. 18 | //! 19 | SuperBlock::SuperBlock(TSK_ENDIAN_ENUM endian, uint8_t arr[]) 20 | :fsUUID(endian, arr + 0x20), devItemData(endian, arr + 0xc9), 21 | chunkKey(endian, arr + 0x32b), chunkData(endian, arr + 0x33c) 22 | { 23 | int arIndex(0); 24 | for(int i=0; i<0x20; i++){ 25 | checksum[i] = arr[arIndex++]; 26 | } 27 | 28 | arIndex += 0x10; //fsUUID, initialized ahead. 29 | 30 | address = read64Bit(endian, arr + arIndex); 31 | arIndex += 0x08; 32 | 33 | for(int i=0; i<0x08; i++){ 34 | flags[i] = arr[arIndex++]; 35 | } 36 | 37 | for(int i=0; i<0x08; i++, arIndex++){ 38 | magic[i] = arr[arIndex]; 39 | } 40 | if(std::string(magic, 8) != "_BHRfS_M") 41 | throw FsDamagedException("Superblock not found. Possibly not a Btrfs partition."); 42 | 43 | generation = read64Bit(endian, arr + arIndex); 44 | arIndex += 0x08; 45 | 46 | rootTrRootAddr = read64Bit(endian, arr + arIndex); 47 | arIndex += 0x08; 48 | 49 | chunkTrRootAddr = read64Bit(endian, arr + arIndex); 50 | arIndex += 0x08; 51 | 52 | logTrRootAddr = read64Bit(endian, arr + arIndex); 53 | arIndex += 0x08; 54 | 55 | logRootTransid = read64Bit(endian, arr + arIndex); 56 | arIndex += 0x08; 57 | 58 | totalBytes = read64Bit(endian, arr + arIndex); 59 | arIndex += 0x08; 60 | 61 | bytesUsed = read64Bit(endian, arr + arIndex); 62 | arIndex += 0x08; 63 | 64 | rootDirObjectid = read64Bit(endian, arr + arIndex); 65 | arIndex += 0x08; 66 | 67 | numDevices = read64Bit(endian, arr + arIndex); 68 | arIndex += 0x08; 69 | 70 | sectorSize = read32Bit(endian, arr + arIndex); 71 | arIndex += 0x04; 72 | 73 | nodeSize = read32Bit(endian, arr + arIndex); 74 | arIndex += 0x04; 75 | 76 | leafSize = read32Bit(endian, arr + arIndex); 77 | arIndex += 0x04; 78 | 79 | stripeSize = read32Bit(endian, arr + arIndex); 80 | arIndex += 0x04; 81 | 82 | sysChunkArrSize = read32Bit(endian, arr + arIndex); 83 | arIndex += 0x04; 84 | 85 | chunkRootGeneration = read64Bit(endian, arr + arIndex); 86 | arIndex += 0x08; 87 | 88 | compatFlags = read64Bit(endian, arr + arIndex); 89 | arIndex += 0x08; 90 | 91 | compatRoFlags = read64Bit(endian, arr + arIndex); 92 | arIndex += 0x08; 93 | 94 | imcompatFlags = read64Bit(endian, arr + arIndex); 95 | arIndex += 0x08; 96 | 97 | csumType = read16Bit(endian, arr + arIndex); 98 | arIndex += 0x2; 99 | 100 | rootLevel = arr[arIndex++]; 101 | chunkRootLevel = arr[arIndex++]; 102 | logRootLevel = arr[arIndex++]; 103 | 104 | arIndex += DEV_ITEM_SIZE; 105 | 106 | for(int i=0; i 11 | #include 12 | #include "Utility/Utility.h" 13 | #include "Basics/Basics.h" 14 | 15 | namespace btrForensics{ 16 | //! Store super block info. 17 | class SuperBlock{ 18 | public: 19 | static constexpr int DEV_ITEM_SIZE = 0x62; 20 | static constexpr int LABEL_SIZE = 0x100; 21 | 22 | uint8_t checksum[0x20]; //0x0 23 | 24 | const UUID fsUUID; //0x20 25 | 26 | uint64_t address; //0x30 27 | uint8_t flags[0x8]; 28 | 29 | char magic[0x8]; //0x40 30 | uint64_t generation; 31 | 32 | uint64_t rootTrRootAddr; //0x50 33 | uint64_t chunkTrRootAddr; 34 | 35 | uint64_t logTrRootAddr; //0x60 36 | uint64_t logRootTransid; 37 | 38 | uint64_t totalBytes; //0x70 39 | uint64_t bytesUsed; 40 | 41 | uint64_t rootDirObjectid; //0x80 42 | uint64_t numDevices; 43 | 44 | uint32_t sectorSize; //0x90 45 | uint32_t nodeSize; 46 | uint32_t leafSize; 47 | uint32_t stripeSize; 48 | 49 | uint32_t sysChunkArrSize; //0xa0 50 | uint64_t chunkRootGeneration; 51 | uint64_t compatFlags; 52 | uint64_t compatRoFlags; 53 | uint64_t imcompatFlags; 54 | 55 | uint16_t csumType; 56 | uint8_t rootLevel; 57 | uint8_t chunkRootLevel; 58 | uint8_t logRootLevel; 59 | 60 | const DevData devItemData; //0xc9 61 | uint8_t label[LABEL_SIZE]; 62 | 63 | const BtrfsKey chunkKey; 64 | const ChunkData chunkData; 65 | 66 | public: 67 | SuperBlock(TSK_ENDIAN_ENUM endian, uint8_t arr[]); 68 | ~SuperBlock() = default; //!< Destructor 69 | 70 | const uint64_t getChunkPhyAddr() const; 71 | const uint64_t getRootLogAddr() const; 72 | 73 | const std::string printMagic() const; 74 | const std::string printSpace() const; 75 | const std::string printLabel() const; 76 | 77 | static const int SUPBLK_ADDR = 0x10000; //!< Address of superblock on disk. 78 | static const int SUPBLK_SIZE = 0xb2b; //!< Size of superblock on disk. 79 | 80 | friend std::ostream &operator<<(std::ostream &os, SuperBlock &supb); 81 | }; 82 | } 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /Trees/Trees.h: -------------------------------------------------------------------------------- 1 | #ifndef TREES_H_H 2 | #define TREES_H_H 3 | 4 | #include "Basics/Basics.h" 5 | #include "SuperBlock.h" 6 | 7 | #include "BtrfsNode.h" 8 | #include "InternalNode.h" 9 | #include "LeafNode.h" 10 | 11 | #include "DirContent.h" 12 | 13 | #include "ChunkTree.h" 14 | #include "FilesystemTree.h" 15 | 16 | #endif 17 | 18 | -------------------------------------------------------------------------------- /Utility/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${PROJECT_SOURCE_DIR}) 2 | 3 | aux_source_directory(. UTIL_SRCS) 4 | 5 | add_library(Utility ${UTIL_SRCS}) 6 | -------------------------------------------------------------------------------- /Utility/ReadInt.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | * \author Shujian Yang 4 | * 5 | * File containing integer reading functions. 6 | */ 7 | 8 | #include 9 | #include "ReadInt.h" 10 | 11 | namespace btrForensics { 12 | 13 | /** 14 | * Read a 16-bit long number from byte array. 15 | * 16 | * \param endian The endianess of the array. 17 | * \param arr The array containg the bytes. 18 | * 19 | */ 20 | const uint16_t read16Bit(TSK_ENDIAN_ENUM endian, const uint8_t *arr) 21 | { 22 | uint16_t num(0); 23 | 24 | if(endian == TSK_LIT_ENDIAN) { 25 | num += (uint16_t)*(arr + 1); 26 | num = num << 8; 27 | num += (uint16_t)*arr; 28 | } 29 | else if(endian == TSK_BIG_ENDIAN) { 30 | num += (uint16_t)*arr; 31 | num += num << 8; 32 | num += (uint16_t)*(arr + 1); 33 | } 34 | 35 | return num; 36 | } 37 | 38 | /** 39 | * Read a 32-bit long number from byte array. 40 | * 41 | * \param endian The endianess of the array. 42 | * \param arr The array containg the bytes. 43 | * 44 | */ 45 | const uint32_t read32Bit(TSK_ENDIAN_ENUM endian, const uint8_t *arr) 46 | { 47 | uint32_t num(0); 48 | 49 | if(endian == TSK_LIT_ENDIAN) { 50 | for(int i=3; i>=0; i--){ 51 | num <<= 8; 52 | num += (uint32_t)*(arr + i); 53 | } 54 | } 55 | else if(endian == TSK_BIG_ENDIAN) { 56 | for(int i=0; i<=3; i--){ 57 | num <<= 8; 58 | num += (uint32_t)*(arr + i); 59 | } 60 | } 61 | 62 | return num; 63 | } 64 | 65 | /** 66 | * Read a 64-bit long number from byte array. 67 | * 68 | * \param endian The endianess of the array. 69 | * \param arr The array containg the bytes. 70 | * 71 | */ 72 | const uint64_t read64Bit(TSK_ENDIAN_ENUM endian, const uint8_t *arr) 73 | { 74 | uint64_t num(0); 75 | 76 | if(endian == TSK_LIT_ENDIAN) { 77 | for(int i=7; i>=0; i--){ 78 | num <<= 8; 79 | num += (uint64_t)*(arr + i); 80 | } 81 | } 82 | else if(endian == TSK_BIG_ENDIAN) { 83 | for(int i=0; i<=7; i--){ 84 | num <<= 8; 85 | num += (uint64_t)*(arr + i); 86 | } 87 | } 88 | 89 | return num; 90 | } 91 | 92 | 93 | //! Convert bytes into human-friendly string format. 94 | //! 95 | //! \param bytes Byte number to convert. 96 | //! 97 | //! \return String of size in B, KB, MB... 98 | //! 99 | std::string humanSize(uint64_t bytes) 100 | { 101 | std::string totalSfx; 102 | 103 | if(bytes >> 10 == 0){ 104 | totalSfx = "B"; 105 | } 106 | else if((bytes >>= 10) >> 10 == 0){ 107 | totalSfx = "KB"; 108 | } 109 | else if((bytes >>= 10) >> 10 == 0){ 110 | totalSfx = "MB"; 111 | } 112 | else { 113 | bytes >>= 10; 114 | totalSfx = "GB"; 115 | } 116 | 117 | std::ostringstream oss; 118 | oss << bytes << totalSfx; 119 | return oss.str(); 120 | } 121 | 122 | } 123 | 124 | -------------------------------------------------------------------------------- /Utility/ReadInt.h: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | * \author Shujian Yang 4 | * 5 | * Header file of read integer functions. 6 | */ 7 | 8 | #ifndef READINT_H 9 | #define READINT_H 10 | 11 | #include 12 | #include 13 | 14 | namespace btrForensics{ 15 | 16 | const uint16_t read16Bit(TSK_ENDIAN_ENUM endian, const uint8_t *arr); 17 | 18 | const uint32_t read32Bit(TSK_ENDIAN_ENUM endian, const uint8_t *arr); 19 | 20 | const uint64_t read64Bit(TSK_ENDIAN_ENUM endian, const uint8_t *arr); 21 | 22 | std::string humanSize(uint64_t bytes); 23 | } 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /Utility/StringProcess.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | * \author Shujian Yang 4 | * 5 | * File containing string processing functions. 6 | */ 7 | 8 | #include "StringProcess.h" 9 | #include 10 | 11 | using namespace std; 12 | 13 | namespace btrForensics { 14 | 15 | //! Split a string into multiple sub strings by separator 16 | //! 17 | //! \param input The string to split. 18 | //! \param separator The separator 19 | //! 20 | //! \return Vector conataining splitted strings. 21 | //! 22 | vector strSplit(string input, string separator) 23 | { 24 | vector subs; 25 | typedef string::size_type st; 26 | st begin = 0; 27 | st end = input.size(); 28 | while(begin < end) { 29 | st index = input.find(separator, begin); 30 | if(index == string::npos){ 31 | subs.push_back(input.substr(begin)); 32 | break; 33 | } 34 | string s(input, begin, index - begin); 35 | if(!s.empty()) 36 | subs.push_back(s); 37 | begin = index + 1; 38 | } 39 | 40 | return subs; 41 | } 42 | 43 | //! Strip spaces at the two ends of a string. 44 | //! 45 | //! \param input The string to split. 46 | //! 47 | //! \return Vector conataining splitted strings. 48 | //! 49 | string strStrip(string input) 50 | { 51 | auto begin = input.find_first_not_of(' '); 52 | auto end = input.find_last_not_of(' '); 53 | if(begin != string::npos && end != string::npos && begin <= end) 54 | return input.substr(begin, end - begin + 1); 55 | else 56 | return ""; 57 | } 58 | 59 | } 60 | 61 | -------------------------------------------------------------------------------- /Utility/StringProcess.h: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | * \author Shujian Yang 4 | * 5 | * Header file of string process. 6 | */ 7 | 8 | #ifndef STR_PRO_H 9 | #define STR_PRO_H 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | namespace btrForensics { 16 | std::vector strSplit(std::string, std::string); 17 | 18 | std::string strStrip(std::string); 19 | } 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /Utility/Utility.h: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | * \author Shujian Yang 4 | * 5 | * Header file of utility. 6 | */ 7 | 8 | #ifndef UTILITY_H 9 | #define UTILITY_H 10 | 11 | #include "ReadInt.h" 12 | #include "StringProcess.h" 13 | #include "Uuid.h" 14 | 15 | #endif 16 | 17 | -------------------------------------------------------------------------------- /Utility/Uuid.cpp: -------------------------------------------------------------------------------- 1 | //! \file 2 | //! \author Shujian Yang 3 | //! 4 | //! UUID processing. 5 | 6 | #include 7 | #include 8 | #include "Uuid.h" 9 | #include "Utility/ReadInt.h" 10 | 11 | namespace btrForensics { 12 | 13 | //! Default constructor of UUID 14 | UUID::UUID() 15 | { 16 | data_1 = 0; 17 | data_2 = 0; 18 | data_3 = 0; 19 | 20 | for(int i=0; i d4(data_4[0]); 58 | if(!d4.test(7)) 59 | uuidVar = NETWORK_COMPUTING_SYSTEM; 60 | if(d4.test(7) && !d4.test(6)) 61 | uuidVar = STANDARD; 62 | if(d4.test(7) && d4.test(6) && !d4.test(5)) 63 | uuidVar = MS_COM; 64 | } 65 | 66 | 67 | //! Copy constructor of UUID 68 | UUID::UUID(const UUID &origin) 69 | { 70 | data_1 = origin.data_1; 71 | data_2 = origin.data_2; 72 | data_3 = origin.data_3; 73 | 74 | for(int i=0; i> 12; 398 | 399 | return ver; 400 | } 401 | 402 | 403 | //! Get UUID variant string. 404 | //! 405 | //! \return String of variant infomation. 406 | //! 407 | const std::string UUID::variantInfo() const 408 | { 409 | std::string variant(""); 410 | switch(uuidVar){ 411 | case NETWORK_COMPUTING_SYSTEM: 412 | variant = "Network Computing System"; 413 | break; 414 | case STANDARD: 415 | variant = "RFC 4122 Standard"; 416 | break; 417 | case MS_COM: 418 | variant = "Microsoft COM"; 419 | break; 420 | case RESERVED: 421 | variant = "Reserved"; 422 | break; 423 | default: 424 | variant = "Unknown"; 425 | } 426 | 427 | return variant; 428 | } 429 | 430 | 431 | //! Get UUID version string. 432 | //! 433 | //! \return String of version infomation. 434 | //! 435 | const std::string UUID::versionInfo() const 436 | { 437 | std::string version(""); 438 | if(uuidVar != STANDARD) 439 | return "Invalid"; 440 | 441 | switch(getVersion()){ 442 | case 1: 443 | version = "Ver 1: MAC address & date-time"; 444 | break; 445 | case 2: 446 | version = "Ver 2: DCE security"; 447 | break; 448 | case 3: 449 | version = "Ver 3: MD5 hash & namespace"; 450 | break; 451 | case 4: 452 | version = "Ver 4: Random number"; 453 | break; 454 | case 5: 455 | version = "Ver 5: SHA-1 hash & namespace"; 456 | break; 457 | default: 458 | version = "Unknown"; 459 | } 460 | 461 | return version; 462 | } 463 | 464 | 465 | //! Overloaded equality operator 466 | bool operator==(const UUID &lhs, const UUID &rhs) 467 | { 468 | if(lhs.data_1 != rhs.data_1 || lhs.data_2 != rhs.data_2 469 | || lhs.data_3 != rhs.data_3) 470 | return false; 471 | 472 | for(int i=0; i 10 | #include 11 | #include 12 | 13 | namespace btrForensics { 14 | 15 | //! Universally Unique IDentifier (UUID). 16 | class UUID{ 17 | public: 18 | //! Types of UUID 19 | enum Variant { 20 | NETWORK_COMPUTING_SYSTEM, //!< Network Computing System 21 | STANDARD, //!< RFC 4122 Standard 22 | MS_COM, //!< Microsoft COM 23 | RESERVED //!< Reserved 24 | }; 25 | 26 | //! Different versions of UUID. 27 | enum Version { 28 | MAC_DATE = 1, //!< Time-based version 29 | DCE, //!< DEC Security version 30 | MD5_NAMESPACE, //!< Name-based version using MD5 31 | RANDOM, //!< Randomly or pseudo-randomly generated version 32 | SHA_NAMESPACE, //!< Name-based version using SHA-1 33 | UNKNOWN 34 | }; 35 | static constexpr int DATA_4_SIZE = 8; //!< Size of data 4 array. 36 | 37 | private: 38 | uint32_t data_1; 39 | uint16_t data_2; 40 | uint16_t data_3; 41 | uint8_t data_4[DATA_4_SIZE]; 42 | Variant uuidVar; 43 | Version uuidVer; 44 | 45 | private: 46 | const int getVersion() const; 47 | 48 | public: 49 | UUID(); 50 | UUID(TSK_ENDIAN_ENUM endian, uint8_t arr[]); 51 | UUID(TSK_ENDIAN_ENUM endian, gpt_entry &entry); 52 | UUID(const UUID &origin); 53 | ~UUID() = default; //!< Destructor 54 | 55 | const bool isUnused() const; 56 | const bool match(uint32_t, uint16_t, uint16_t, uint64_t) const; 57 | 58 | const std::string encode() const; 59 | const std::string guidType() const; 60 | const std::string variantInfo() const; 61 | const std::string versionInfo() const; 62 | 63 | friend bool operator==(const UUID &lhs, const UUID &rhs); 64 | friend bool operator!=(const UUID &lhs, const UUID &rhs); 65 | 66 | UUID& operator=(const UUID& rhs); 67 | 68 | static const int BYTES_OF_UUID = 16; //!< UUID byte length when stored in machine, 69 | static const int LENGTH_OF_UUID_STRING = 36; //!< String length used to represent a UUID. 70 | }; 71 | 72 | } 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /btrfrsc.cpp: -------------------------------------------------------------------------------- 1 | //! \file btrfrsc.cpp 2 | //! \author Shujian Yang 3 | //! 4 | //! Main function. 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "Basics/Basics.h" 19 | #include "Trees/Trees.h" 20 | #include "Pool/Pool.h" 21 | #include "Utility/Utility.h" 22 | 23 | using namespace std; 24 | 25 | using namespace btrForensics; 26 | 27 | int main(int argc, char *argv[]) 28 | { 29 | TSK_OFF_T offsetSector(0); 30 | int option; 31 | vector offsetStr; 32 | vector devOffsets; 33 | 34 | while((option = getopt(argc, argv, "o:")) != -1){ 35 | switch(option){ 36 | case 'o': 37 | offsetStr = strSplit(optarg, ","); 38 | for(auto str : offsetStr){ 39 | if( (offsetSector = tsk_parse_offset(str.c_str())) == -1){ 40 | tsk_error_print(stderr); 41 | exit(1); 42 | } 43 | devOffsets.push_back(offsetSector); 44 | } 45 | break; 46 | case '?': 47 | default: 48 | cerr << "Unkown arguments." << endl; 49 | } 50 | } 51 | 52 | if(optind >= argc) { 53 | cerr << "Please provide the image name" << endl; 54 | exit(1); 55 | } 56 | 57 | if(devOffsets.size() == 0) 58 | devOffsets.push_back(0); 59 | 60 | string img_name(argv[optind]); 61 | 62 | TSK_IMG_INFO *img = tsk_img_open(1, &argv[optind], TSK_IMG_TYPE_DETECT, 0); 63 | if(img == NULL){ 64 | tsk_error_print(stderr); 65 | cerr << "Cannot open image " << img_name << "." << endl; 66 | exit(1); 67 | } 68 | 69 | for(auto &offSec : devOffsets) { 70 | TSK_OFF_T offsetByte(offSec * img->sector_size); 71 | if( offsetByte >= img->size){ 72 | cerr << "Offset is too large." << endl; 73 | exit(1); 74 | } 75 | offSec = offsetByte; 76 | } 77 | 78 | try { 79 | BtrfsPool btr(img, TSK_LIT_ENDIAN, devOffsets); 80 | 81 | SuperBlock *supblk = btr.primarySupblk; 82 | 83 | cout << *supblk << endl; 84 | cout << "Magic: " << supblk->printMagic() << endl; 85 | 86 | cout << supblk->printSpace() << endl; 87 | cout << endl; 88 | 89 | cout << "Label: " << supblk->printLabel() << endl; 90 | cout << "\n" << endl; 91 | 92 | string answer; 93 | 94 | while(true) { 95 | cout << "MAIN MENU -- What do you want to do?" << endl; 96 | cout << "[1] Browse nodes derived from root tree and print information." << endl; 97 | cout << "[2] Browse nodes in chunk tree and print information." << endl; 98 | cout << "[3] Browse nodes in filesystem tree and print information." << endl; 99 | cout << "[4] List all files in default filesystem tree." << endl; 100 | cout << "[5] Explor files and subdirectories in default root directory." << endl; 101 | cout << "[6] Switch to a subvolume or snapshot and explore files within." << endl; 102 | cout << "[7] Read a file from image and save to current directory." << endl; 103 | cout << "[q] Quit." << endl; 104 | cout << "Enter your choice > "; 105 | cin >> answer; 106 | cout << endl; 107 | 108 | if(answer == "q") break; 109 | cout << std::string(60, '=') << "\n"; 110 | cout << endl; 111 | if(answer == "1"){ 112 | btr.navigateNodes(btr.rootTree, cout, cin); 113 | } 114 | else if(answer == "2"){ 115 | btr.navigateNodes(btr.chunkTree->chunkRoot, cout, cin); 116 | } 117 | else if(answer == "3"){ 118 | btr.navigateNodes(btr.fsTreeDefault->fileTreeRoot, cout, cin); 119 | } 120 | else if(answer == "4") { 121 | cout << "Listing directory items...\n" << endl; 122 | uint64_t targetId(btr.fsTree->rootDirId); 123 | btr.fsTree->listDirItemsById(targetId, true, true, true, 0, cout); 124 | cout << endl; 125 | } 126 | else if(answer == "5") { 127 | btr.fsTree->explorFiles(cout, cin); 128 | } 129 | else if(answer == "6") { 130 | if(btr.switchFsTrees(cout, cin)) { 131 | btr.fsTree->explorFiles(cout, cin); 132 | delete btr.fsTree; 133 | btr.fsTree = btr.fsTreeDefault; 134 | } 135 | } 136 | else if(answer == "7") { 137 | cout << "Please enter the inode number of file." << endl; 138 | cout << "(Enter 'q' to quit.)" << endl; 139 | string input; 140 | uint64_t targetId; 141 | bool success(false); 142 | while(true) { 143 | cin >> input; 144 | if(input == "q") break; 145 | if(stringstream(input) >> targetId) { 146 | success = btr.fsTree->readFile(targetId); 147 | break; 148 | } 149 | else { 150 | cin.clear(); 151 | cout << "Invalid input. Please try again.\n" << endl; 152 | } 153 | } 154 | if(success) 155 | cout << "Success: File written to current directory." << endl; 156 | else 157 | cout << "Error: File not found or has no content." << endl; 158 | } 159 | else 160 | cout << "Invalid option. Please choose again." << endl; 161 | 162 | cout << endl; 163 | } 164 | } catch(std::bad_alloc& ba) { 165 | cerr << "Error when allocating objects.\n" << ba.what() << endl; 166 | } catch(FsDamagedException& fsEx) { 167 | cerr << "Error: Btrfs filesystem damaged.\n" << fsEx.what() << endl; 168 | } catch(exception& e) { 169 | cerr << e.what() << endl; 170 | } 171 | 172 | return 0; 173 | } 174 | 175 | --------------------------------------------------------------------------------