/* dump-cachefs.c: CacheFS dumper * * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ #include #include #include #include #include #include #include #include #include #include #include #define PAGE_SHIFT 12 #define PAGE_SIZE (1 << PAGE_SHIFT) #include "cachefs-layout.h" #define RED "\e[31m" #define B_RED "\e[31;1m" #define GREEN "\e[32m" #define B_GREEN "\e[32;1m" #define YELLOW "\e[33m" #define B_YELLOW "\e[33;1m" #define BLUE "\e[34m" #define B_BLUE "\e[34;1m" #define MAGENTA "\e[35m" #define CYAN "\e[36m" #define BRIGHT "\e[1m" #define NORMAL "\e[m" #define ERROR(x, y, ...) do { if (x) { fprintf(stderr, y, ##__VA_ARGS__); exit(1); } } while(0) static int fd = -1; static int exitcode = 0; static char ignore_unready = 0; static char dumpjnl; static char hideattr; static char hideempty; static char dumptree; static unsigned prune = 0xffffffff; static struct cachefs_ondisc_superblock *super; static cachefs_block_t tree_root, unready, nullptr; static unsigned long long n_empty_leaves; static unsigned long long n_index_leaves; static unsigned long long n_file_leaves; static unsigned long long n_other_leaves; static unsigned long long n_shortcut_leaves; static unsigned long long n_ptr_leaves; static unsigned long long n_ptr_ptrs; static unsigned long n_data_files; static unsigned long n_data_blocks; static unsigned long n_nodes; static unsigned int maxdepth; static unsigned int n_root_ptr_leaves; static unsigned int n_root_ptr_ptrs; static void cachefs_dump_scan_journal(void); static void cachefs_dump_tree(cachefs_block_t bix, int depth); static void cachefs_dump_pointer_leaf(const struct cachefs_ondisc_leaf *leaf, int depth, int slot); static void cachefs_dump_inode_leaf(const struct cachefs_ondisc_leaf *leaf, int depth, int slot); static void cachefs_dump_shortcut_leaf(const struct cachefs_ondisc_leaf *leaf, int depth, int slot); static char cachefs_dump_indent[65536]; enum cachefs_replay_empty { CACHEFS_REPLAY_NOTHING_YET, CACHEFS_REPLAY_FOUND_MARKS, CACHEFS_REPLAY_GAP_TO_END, }; struct cachefs_key_extract { uint16_t koffset; /* offset of key in leaf */ uint16_t kfixlen; /* length of fixed part of key (bits) */ uint16_t kvarlen; /* offset of length of variable part of key */ uint16_t kvarlenshift; /* amount to shift kvarlen by to get bit count */ uint16_t digestoff; /* offset of digest in leaf */ }; static const struct cachefs_key_extract cachefs_key_extract_tbl[] = { [CACHEFS_ONDISC_OBJTYPE_SHORTCUT] = { /* key { data[0..klen] } */ .koffset = offsetof(struct cachefs_ondisc_leaf, u.shortcut.key), .kfixlen = 0, .kvarlen = offsetof(struct cachefs_ondisc_leaf, u.shortcut.klen), .kvarlenshift = 0, .digestoff = 0, }, [CACHEFS_ONDISC_OBJTYPE_INDEX_OBJECT ... CACHEFS_ONDISC_OBJTYPE_OTHER_OBJECT ] = { /* key { digest, parent, netfs_data[0..netfs_klen] } */ .koffset = offsetof(struct cachefs_ondisc_leaf, u.object.key), .kfixlen = (sizeof(cachefs_digest_t) + sizeof(uint64_t) + sizeof(uint16_t)) * 8, .kvarlen = offsetof(struct cachefs_ondisc_leaf, u.object.netfs_klen), .kvarlenshift = 3, .digestoff = offsetof(struct cachefs_ondisc_leaf, u.object.key), }, }; /*****************************************************************************/ /* * extract the level'th subkey of a key * - we assume we extract from bit 0 upwards as LSB data * - each level is CACHEFS_ONDISC_LEVEL_BITS of the key */ unsigned cachefs_extract_subkey(const struct cachefs_ondisc_leaf *leaf, int level) { static const struct cachefs_key_extract *ex; unsigned subkey, klen; uint8_t *key; ex = &cachefs_key_extract_tbl[leaf->type]; /* find the key data */ key = (uint8_t *) leaf + ex->koffset; /* work out the number of bits in the key */ klen = 0; if (ex->kvarlen) { klen = *(uint16_t *)((unsigned long) leaf + ex->kvarlen); klen <<= ex->kvarlenshift; } klen += ex->kfixlen; /* skip the first initial whole bytes of the key */ level *= CACHEFS_ONDISC_LEVEL_BITS; key += level >> 3; klen -= level & ~7; level &= 7; /* the next three bytes contain all the bits we could possibly want; * but which bits are which may somewhat depend on byte order */ subkey = *key++; subkey |= *key++ << 8; subkey |= *key << 16; /* limit the key to the correct number of bits * * klen mask 1<<-2nd--><-1st--> * 20 10 0 * 321*987654321*987654321* * <--------> width=10, level=0 * <-------->| * <--------> | * <--------> | * <--------> | * <--------> | * <--------> | * <--------> | width=10, level=7 * <------------> width=14, level=0 * <------------>| * <------------> | * <------------> | * <------------> | * <------------> | * <------------> | * <------------> | width=14, level=7 */ subkey >>= level; subkey &= (1 << CACHEFS_ONDISC_LEVEL_BITS) - 1; subkey <<= CACHEFS_BLOCK_SHIFT; return subkey; } /* end cachefs_extract_subkey() */ /*****************************************************************************/ /* * */ int main(int argc, char *argv[]) { struct stat st; int opt, root = 0; while (opt = getopt(argc, argv, "ehjp:r:t"), opt != EOF ) { switch (opt) { case 'e': hideempty = 1; break; case 'h': hideattr = 1; break; case 'j': dumpjnl++; break; case 'p': prune = strtoul(optarg, NULL, 0) * 4; break; case 'r': root = strtoul(optarg, NULL, 0); ignore_unready = 1; break; case 't': dumptree = 1; break; default: fprintf(stderr, "Unknown option\n"); exit(2); } } argc -= optind; argv += optind; if (!*argv) { fprintf(stderr, "Missing blockdev name\n"); exit(2); } /* open the block device and check it */ fd = open(argv[0], O_RDONLY); ERROR(fd < 0, "open"); ERROR(fstat(fd, &st) < 0, "stat"); if (!S_ISBLK(st.st_mode)) { fprintf(stderr, "Not a blockdev\n"); exit(2); } /* read the superblock and check that */ super = mmap(NULL, PAGE_SIZE, PROT_READ, MAP_PRIVATE, fd, 0); ERROR(!super, "mmap super"); if (memcmp(super->magic, CACHEFS_SUPER_MAGIC, CACHEFS_SUPER_MAGIC_SIZE) == 0 ) { } else if (memcmp(super->magic, CACHEFS_SUPER_MAGIC_NEEDS_INIT, CACHEFS_SUPER_MAGIC_SIZE) == 0 ) { fprintf(stderr, "Found uninitialised cache\n"); exit(1); } else { fprintf(stderr, "Wrong magic number on cache\n"); exit(1); } if (super->endian != CACHEFS_SUPER_ENDIAN) { fprintf(stderr, "Cache is wrong endianness\n"); exit(1); } if (super->version != CACHEFS_SUPER_VERSION) { fprintf(stderr, "Cache is wrong version\n"); exit(1); } nullptr = super->bix_null; /* scan the journal to find the root of the indexing tree */ cachefs_dump_scan_journal(); /* recursively dump the tree */ if (root <= 0) root = tree_root; if (dumptree) printf(BRIGHT "Root %x" NORMAL "\n", root); cachefs_dump_tree(root, 0); if (exitcode == 0) { unsigned long long n_leaves, n_ptrs, n_nullptrs; unsigned int n_root_ptrs, n_root_nullptrs; if (dumptree) printf("\n"); n_leaves = n_nodes; n_leaves *= CACHEFS_ONDISC_LEAF_PER_BLOCK; n_ptrs = n_ptr_leaves << CACHEFS_ONDISC_PTRPERLEAF_SHIFT; n_nullptrs = n_ptrs - n_ptr_ptrs; n_root_ptrs = n_root_ptr_leaves << CACHEFS_ONDISC_PTRPERLEAF_SHIFT; n_root_nullptrs = n_root_ptrs - n_root_ptr_ptrs; printf("Number of Nodes : %20lu\n", n_nodes); printf("Number of Leaves : %20llu\n", n_leaves); printf("Number of Empty Leaves : %20llu [%llu%%]\n", n_empty_leaves, n_empty_leaves * 100 / n_leaves); printf("Number of Index Leaves : %20llu [%llu%%]\n", n_index_leaves, n_index_leaves * 100 / n_leaves); printf("Number of File Leaves : %20llu [%llu%%]\n", n_file_leaves, n_file_leaves * 100 / n_leaves); printf("Number of Other Leaves : %20llu [%llu%%]\n", n_other_leaves, n_other_leaves * 100 / n_leaves); printf("Number of Shortcut Leaves : %20llu [%llu%%]\n", n_shortcut_leaves, n_shortcut_leaves * 100 / n_leaves); printf("Number of Pointer Leaves : %20llu [%llu%%]\n", n_ptr_leaves, n_ptr_leaves * 100 / n_leaves); if (n_ptrs > 0) { printf("Number of Pointer Slots : %20llu\n", n_ptrs); printf("Number of Null Pointers : %20llu [%llu%%]\n", n_nullptrs, n_nullptrs * 100 / n_ptrs); printf("Number of Filled Pointers : %20llu [%llu%%]\n", n_ptr_ptrs, n_ptr_ptrs * 100 / n_ptrs); } printf("Number of Root Ptr Leaves : %20u [%lu%%]\n", n_root_ptr_leaves, n_root_ptr_leaves * 100 / CACHEFS_ONDISC_LEAF_PER_BLOCK); if (n_root_ptrs > 0) { printf("Number of Root Ptr Slots : %20u\n", n_root_ptrs); printf("Number of Null Root Ptrs : %20u [%u%%]\n", n_root_nullptrs, n_root_nullptrs * 100 / n_root_ptrs); printf("Number of Filled Root Ptrs: %20u [%u%%]\n", n_root_ptr_ptrs, n_root_ptr_ptrs * 100 / n_root_ptrs); } printf("Number of Files with Data : %20lu\n", n_data_files); printf("Number of Data Blocks : %20lu\n", n_data_blocks); printf("Max Depth : %u\n", maxdepth + 1); } exit(exitcode); } /* end main() */ /*****************************************************************************/ /* * scan the journal to find the root of the indexing tree */ static void cachefs_dump_scan_journal(void) { const struct cachefs_ondisc_journal *jentry; const void *journal; int32_t tmp; size_t jsize, jents; int error, slot; uint32_t serial_next = 0; uint32_t serial_hi = 0; uint32_t serial_wrap = 0; unsigned slot_hi = 0; unsigned slot_wrap = 0; char wrapped = 0; enum cachefs_replay_empty empty = CACHEFS_REPLAY_NOTHING_YET; /* map the journal into memory */ jsize = (super->bix_cache - super->bix_journal) * PAGE_SIZE; jents = jsize / super->jnl_rsize; journal = mmap(NULL, jsize, PROT_READ, MAP_PRIVATE, fd, super->bix_journal * PAGE_SIZE); ERROR(journal == MAP_FAILED, "mmap journal"); if (dumpjnl > 1) { printf("SERIAL " " | UNREADY ALLOC RCM RCY REAP ALRCM " " META D_USED D_PINNED D_R_USED D_R_PIN D_RESVED BALANCE" "\n"); printf("========" " | ======== ======== ======== ======== ========" " ======== ======== ======== ======== ======== ========" " ======== ========" "\n"); } error = 0; for (slot = 0; slot < jents; slot++) { jentry = journal + slot * super->jnl_rsize; if (dumpjnl) { if (jentry->mark == 0) { const uint8_t *stuff; int loop; stuff = (const uint8_t *) jentry; for (loop = 0; loop < super->jnl_rsize; loop++) { if (*stuff++) { fprintf(stderr, "Entry in journal slot %u" " is not all zeros\n", slot); exit(1); } } goto no_print; } if (slot != 0) { tmp = (int32_t) serial_next - (int32_t) jentry->serial; if (tmp != 0) printf("----------\n"); } if (dumpjnl == 1) { printf("%x %08x %08x" " | %08x %08x[%3x]+%-4x %08x+%-4x" " | %08x+%-4x %08x[%04x]" " | %08x[%04hx] %08x[%04hx]\n", jentry->mark, jentry->serial, jentry->tree_root, jentry->alloc_unready, jentry->alloc_pfree, jentry->alloc_pfree_pt, jentry->alloc_pfree_n, jentry->alloc_sfree, jentry->alloc_sfree_n, jentry->rcm_ready, jentry->rcm_ready_n, jentry->rcm_collector, jentry->rcm_coll_pt, jentry->rcy_processor, // jentry->rcy_collector, jentry->rcy_procsp, // jentry->rcy_collsp, jentry->reap_collector, jentry->reap_collsp); } else { unsigned long balance; balance = jentry->alloc_unready - super->bix_cache; balance -= jentry->space_alloc; balance -= jentry->space_rcm; balance -= jentry->space_rcy; balance -= jentry->space_reap; balance -= jentry->space_alrcm_nodes; balance -= jentry->space_meta; balance -= jentry->space_data_used; balance -= jentry->space_data_pinned; balance -= jentry->space_data_rsv_data; balance -= jentry->space_data_rsv_pin; balance -= jentry->space_data_reserved; printf("%08x" " | %08x %08x %08x %08x %08x %08x %08x" " %08x %08x %08x %08x %08x %08x" "\n", jentry->serial, jentry->alloc_unready, jentry->space_alloc, jentry->space_rcm, jentry->space_rcy, jentry->space_reap, jentry->space_alrcm_nodes, jentry->space_meta, jentry->space_data_used, jentry->space_data_pinned, jentry->space_data_rsv_data, jentry->space_data_rsv_pin, jentry->space_data_reserved, balance); } no_print: ; } /* validate the journal marks */ if (error) { serial_next = jentry->serial + 1; continue; } if (jentry->mark >= CACHEFS_ONDISC_JNL__LAST) { fprintf(stderr, "Unknown mark in journal (%x)\n", jentry->mark); error = 1; continue; } if (jentry->mark == CACHEFS_ONDISC_JNL_EMPTY) { switch (empty) { case CACHEFS_REPLAY_FOUND_MARKS: if (wrapped) { fprintf(stderr, "End of journal missing" " (slot %x, %x)\n", slot_wrap, slot); error = 1; continue; } /* found an empty slot after a valid slot */ empty = CACHEFS_REPLAY_GAP_TO_END; continue; case CACHEFS_REPLAY_NOTHING_YET: fprintf(stderr, "CacheFS:" " Initial journal record missing" " (slot %d)\n", slot); error = 1; continue; case CACHEFS_REPLAY_GAP_TO_END: continue; default: abort(); } } if (empty == CACHEFS_REPLAY_GAP_TO_END) { /* found empty slots between two valid marks */ fprintf(stderr, "Unexpected holes in journal (slot %x)\n", slot); error = 1; break; } empty = CACHEFS_REPLAY_FOUND_MARKS; /* can't relate the first slot to anything else */ if (slot == 0) { tree_root = jentry->tree_root; unready = jentry->alloc_unready; serial_next = jentry->serial + 1; serial_hi = jentry->serial; slot_hi = 0; continue; } /* validate the next serial number */ tmp = (int32_t) serial_next - (int32_t) jentry->serial; if (tmp != 0) { /* non-contiguous serial numbers - check that the * journal wrapped */ if (wrapped) { fprintf(stderr, "Journal has multiple wrap points" " (slot %u{%x} ... %u{%x})\n", slot_wrap, serial_wrap, slot, jentry->serial); error = 1; serial_next = jentry->serial + 1; continue; } if (tmp != CACHEFS_ONDISC_JNL_NUMENTS) { fprintf(stderr, "CacheFS:" " Journal wrap displacement %d not %d" " (slot %u{%x}, %u{%x})\n", tmp, CACHEFS_ONDISC_JNL_NUMENTS, slot - 1, serial_next - 1, slot, jentry->serial); error = 1; serial_next = jentry->serial + 1; continue; } serial_next = jentry->serial + 1; serial_wrap = jentry->serial; slot_wrap = slot; wrapped = 1; } else { /* this entry followed directly on from the last; if it * has the new current highest serial then copy the * data back */ if (!wrapped) { tree_root = jentry->tree_root; unready = jentry->alloc_unready; serial_hi = jentry->serial; slot_hi = slot; } serial_next = jentry->serial + 1; } } if (error) { fprintf(stderr, "Journal errors\n"); exit(1); } ERROR(munmap((void *) journal, jsize) < 0, "munmap journal"); } /* end cachefs_dump_scan_journal() */ /*****************************************************************************/ /* * recursively dump the indexing tree */ static void cachefs_dump_tree(cachefs_block_t bix, int depth) { void (*dump_leaf)(const struct cachefs_ondisc_leaf *leaf, int depth, int slot); const struct cachefs_ondisc_leaf *leaf; const void *node; const char *type; int loop; if (bix < super->bix_cache) { printf(B_RED "Block %x beyond start of cache" NORMAL "\n", bix); fprintf(stderr, "Block %x beyond start of cache\n", bix); exitcode = 1; return; } if (bix > super->bix_end) { printf(B_RED "Block %x beyond end of cache" NORMAL "\n", bix); fprintf(stderr, "Block %x beyond end of cache\n", bix); exitcode = 1; return; } if (bix > unready && !ignore_unready) { printf(B_RED "Block %x not yet ready" NORMAL "\n", bix); fprintf(stderr, "Block %x not yet ready\n", bix); exitcode = 1; return; } if (depth > prune) return; node = mmap(NULL, PAGE_SIZE, PROT_READ, MAP_PRIVATE, fd, bix * PAGE_SIZE); ERROR(node == MAP_FAILED, "mmap block 0x%x", bix); n_nodes++; if (maxdepth < depth / 8) maxdepth = depth / 8; for (loop = 0; loop < CACHEFS_ONDISC_LEAF_PER_BLOCK; loop++) { leaf = node + (loop << super->leaf_shift); type = "???"; dump_leaf = NULL; switch (leaf->type) { case CACHEFS_ONDISC_OBJTYPE_EMPTY_SLOT: n_empty_leaves++; type = BLUE "Empty Leaf" NORMAL; if (hideempty) continue; break; case CACHEFS_ONDISC_OBJTYPE_INDEX_OBJECT: n_index_leaves++; type = YELLOW "Index Leaf" NORMAL; dump_leaf = cachefs_dump_inode_leaf; break; case CACHEFS_ONDISC_OBJTYPE_DATA_OBJECT: n_file_leaves++; type = GREEN "Data Leaf" NORMAL; dump_leaf = cachefs_dump_inode_leaf; break; case CACHEFS_ONDISC_OBJTYPE_OTHER_OBJECT: n_other_leaves++; type = B_GREEN "Other Leaf" NORMAL; dump_leaf = cachefs_dump_inode_leaf; break; case CACHEFS_ONDISC_OBJTYPE_SHORTCUT: n_shortcut_leaves++; type = CYAN "Shortcut Leaf" NORMAL; dump_leaf = cachefs_dump_shortcut_leaf; break; default: printf(B_RED " Unrecognised object type %x" NORMAL "\n", leaf->type); fprintf(stderr, " Unrecognised object type %x\n", leaf->type); exitcode = 1; continue; /* pointer block leaf */ #if CACHEFS_ONDISC_OBJTYPE_NULL_POINTER < CACHEFS_ONDISC_OBJTYPE_FIRST_POINTER case CACHEFS_ONDISC_OBJTYPE_NULL_POINTER: #endif case CACHEFS_ONDISC_OBJTYPE_FIRST_POINTER ... CACHEFS_ONDISC_OBJTYPE_LAST_POINTER: n_ptr_leaves++; if (depth == 0) n_root_ptr_leaves++; type = B_BLUE "Pointer Leaf" NORMAL; dump_leaf = cachefs_dump_pointer_leaf; break; } if (dumptree) { printf("%s", cachefs_dump_indent); printf("\\_ %s %d", type, loop); } if (dump_leaf) { cachefs_dump_indent[depth + 0] = ' '; cachefs_dump_indent[depth + 1] = ' '; cachefs_dump_indent[depth + 2] = ' '; cachefs_dump_indent[depth + 3] = ' '; cachefs_dump_indent[depth + 4] = 0; if (loop < CACHEFS_ONDISC_LEAF_PER_BLOCK - 1) cachefs_dump_indent[depth + 0] = '|'; (*dump_leaf)(leaf, depth + 4, loop); cachefs_dump_indent[depth + 0] = 0; } else { if (dumptree) printf("\n"); } } ERROR(munmap((void *) node, PAGE_SIZE) < 0, "munmap node"); } /* end cachefs_dump_tree() */ /*****************************************************************************/ /* * */ static void cachefs_dump_pointer_leaf(const struct cachefs_ondisc_leaf *leaf, int depth, int slot) { const cachefs_block_t *ptrs; int loop, next, n; if (dumptree) printf("\n"); /* dump the key data */ ptrs = (const cachefs_block_t *) leaf; n = 1 << CACHEFS_ONDISC_PTRPERLEAF_SHIFT; for (loop = 0; loop < n; loop++) if (ptrs[loop] != nullptr) goto not_empty; return; not_empty: for (; loop < n; loop = next) { for (next = loop + 1; next < n; next++) if (ptrs[next] != nullptr) break; n_ptr_ptrs++; if (depth == 4) n_root_ptr_ptrs++; /* dump the dependent block */ if (dumptree) { uint16_t offset; offset = loop; offset += slot << CACHEFS_ONDISC_PTRPERLEAF_SHIFT; offset <<= CACHEFS_BLOCK_SHIFT; printf("%s\\_ " BRIGHT "Node %x" NORMAL " (subkey %04x)\n", cachefs_dump_indent, ptrs[loop], offset); } cachefs_dump_indent[depth + 0] = ':'; cachefs_dump_indent[depth + 1] = ' '; cachefs_dump_indent[depth + 2] = ' '; cachefs_dump_indent[depth + 3] = ' '; cachefs_dump_indent[depth + 4] = 0; if (next >= n) cachefs_dump_indent[depth + 0] = ' '; cachefs_dump_tree(ptrs[loop], depth + 4); cachefs_dump_indent[depth + 0] = 0; } } /* end cachefs_dump_pointer_leaf() */ /*****************************************************************************/ /* * */ static void cachefs_dump_inode_leaf(const struct cachefs_ondisc_leaf *leaf, int depth, int slot) { const uint8_t *data; uint64_t objid, pobjid, size; uint8_t digest[sizeof(leaf->u.object.key)], x; char stuff[17]; int loop, loop2; n_data_blocks += leaf->u.object.nblocks; if (leaf->u.object.nblocks > 0) n_data_files++; if (dumptree) { objid = leaf->u.object.objid; size = leaf->u.object.size; memcpy(&pobjid, leaf->u.object.parent, sizeof(objid)); memcpy(digest, &leaf->u.object.key, sizeof(digest)); printf(" ino:" B_YELLOW "%llx" NORMAL " type:%u:" BRIGHT "%s" NORMAL "\n", objid, leaf->u.object.object_type, leaf->u.object.object_name); if (!hideattr) { /* describe the key */ printf("%s", cachefs_dump_indent); printf("\\_ " MAGENTA "Key" NORMAL ":" " Digest %02x%02x%02x%02x Parent %llx Keylen %u\n", digest[0], digest[1], digest[2], digest[3], pobjid, leaf->u.object.netfs_klen); /* dump the key data */ cachefs_dump_indent[depth + 0] = '|'; cachefs_dump_indent[depth + 1] = ' '; cachefs_dump_indent[depth + 2] = ' '; cachefs_dump_indent[depth + 3] = ' '; cachefs_dump_indent[depth + 4] = 0; for (loop = 0; loop < leaf->u.object.netfs_klen; ) { printf("%s", cachefs_dump_indent); memset(stuff, 0, sizeof(stuff)); for (loop2 = 0; loop2 < 16 && loop < leaf->u.object.netfs_klen; loop2++, loop++ ) { x = leaf->u.object.netfs_data[loop]; stuff[loop2] = isprint(x) ? x : '.'; printf("%02x", x); } for (; loop2 < 16; loop2++) printf(" "); printf(" %s\n", stuff); } cachefs_dump_indent[depth + 0] = 0; /* dump the flags */ if (leaf->u.object.flags) { printf("%s", cachefs_dump_indent); printf("\\_ " MAGENTA "Flags" NORMAL ": %02x", leaf->u.object.flags); if (leaf->u.object.flags & CACHEFS_ONDISC_OBJECT_HAS_CHILDREN) printf(" HasChildren"); if (leaf->u.object.flags & CACHEFS_ONDISC_OBJECT_IS_PINNED) printf(" IsPinned"); if (leaf->u.object.flags & CACHEFS_ONDISC_DONT_CULL_DIRECTLY) printf(" DontCull"); printf("\n"); } /* dump a subkey list */ printf("%s", cachefs_dump_indent); printf("\\_ " MAGENTA "Subkeys" NORMAL ":"); for (loop = 0; loop < depth / 8; loop++) printf(" %04x", cachefs_extract_subkey(leaf, loop)); printf("\n"); /* describe the aux data */ printf("%s", cachefs_dump_indent); printf("\\_ " MAGENTA "Aux" NORMAL ":" " size %llx auxlen %u\n", size, leaf->u.object.netfs_dlen); /* dump the aux data */ cachefs_dump_indent[depth + 0] = ' '; cachefs_dump_indent[depth + 1] = ' '; cachefs_dump_indent[depth + 2] = ' '; cachefs_dump_indent[depth + 3] = ' '; cachefs_dump_indent[depth + 4] = 0; if (leaf->ptr) cachefs_dump_indent[depth + 0] = '|'; data = leaf->u.object.netfs_data + leaf->u.object.netfs_klen; for (loop = 0; loop < leaf->u.object.netfs_dlen; ) { printf("%s", cachefs_dump_indent); memset(stuff, 0, sizeof(stuff)); for (loop2 = 0; loop2 < 16 && loop < leaf->u.object.netfs_dlen; loop2++, loop++ ) { x = data[loop]; stuff[loop2] = isprint(x) ? x : '.'; printf("%02x", x); } for (; loop2 < 16; loop2++) printf(" "); printf(" %s\n", stuff); } cachefs_dump_indent[depth + 0] = 0; /* dump information about the data block tree */ if (leaf->ptr != nullptr || leaf->u.object.nblocks > 0) { printf("%s", cachefs_dump_indent); printf("\\_ " MAGENTA "Data" NORMAL ":" " %d levels at %x [%u blocks]\n", leaf->u.object.data_levels, leaf->ptr, leaf->u.object.nblocks); } } } } /* end cachefs_dump_inode_leaf() */ /*****************************************************************************/ /* * */ static void cachefs_dump_shortcut_leaf(const struct cachefs_ondisc_leaf *leaf, int depth, int slot) { uint8_t x; char stuff[17]; int loop, loop2, klen; if (dumptree) { printf("\n"); /* describe the key */ printf("%s", cachefs_dump_indent); printf("\\_ " MAGENTA "Key" NORMAL ":" " klen:%u level:%u soff:%04x\n", leaf->u.shortcut.klen, leaf->u.shortcut.level, leaf->u.shortcut.s_offset); /* dump the key data */ cachefs_dump_indent[depth + 0] = '|'; cachefs_dump_indent[depth + 1] = ' '; cachefs_dump_indent[depth + 2] = ' '; cachefs_dump_indent[depth + 3] = ' '; cachefs_dump_indent[depth + 4] = 0; klen = leaf->u.shortcut.klen / CACHEFS_ONDISC_LEVEL_BITS; for (loop = 0; loop < klen; ) { printf("%s", cachefs_dump_indent); memset(stuff, 0, sizeof(stuff)); for (loop2 = 0; loop2 < 16 && loop < klen; loop2++, loop++ ) { x = leaf->u.shortcut.key[loop]; stuff[loop2] = isprint(x) ? x : '.'; printf("%02x", x); } if (klen > 16) for (; loop2 < 16; loop2++) printf(" "); printf(" %s\n", stuff); } cachefs_dump_indent[depth + 0] = 0; /* dump the dependent block */ printf("%s\\_ " BRIGHT "Node %x" NORMAL "\n", cachefs_dump_indent, leaf->ptr); } cachefs_dump_indent[depth + 0] = ' '; cachefs_dump_indent[depth + 1] = ' '; cachefs_dump_indent[depth + 2] = ' '; cachefs_dump_indent[depth + 3] = ' '; cachefs_dump_indent[depth + 4] = 0; cachefs_dump_tree(leaf->ptr, depth + 4); cachefs_dump_indent[depth + 0] = 0; } /* end cachefs_dump_shortcut_leaf() */