DF-3223 / patch_inode.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | /* * DF-3223 patcher: offline-patch a HAMMER1 image so a *live* PFS#0 inode * record (rec_type=HAMMER_RECTYPE_INODE, delete_tid==0, obj_id>1) carries * data_offset == 0 in its B-Tree leaf element. * * hammer_btree_extract() (sys/vfs/hammer/hammer_btree.c:728-731) returns * SUCCESS with cursor->data == NULL when data_offset == 0 (the early * return happens BEFORE the CRC test), so prune_check_nlinks() * (sys/vfs/hammer/hammer_prune.c:330-332) dereferences * cursor->data->inode.nlinks == NULL+72 -> kernel-mode page fault. * * Scans the image at 512-byte granularity for valid B-Tree leaf nodes * (self-identified by their stored node CRC), rewrites leaf->data_offset * and recomputes the node CRC (auto-detects vol_version<=6 crc32 vs >=7 * iscsi_crc32 by checking which algorithm reproduces the stored CRC). * * usage: ./patch_inode <image> (sets data_offset = 0) */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #include <fcntl.h> #include <unistd.h> #define BUFSIZE 4096 /* hammer btree node size */ #define BTREE_TYPE_L (('L') & 0xff) #define RECTYPE_INODE 0x0001 #define LOCALIZE_PSEUDOFS_MASK 0xFFFF0000U /* leaf element offsets (see hammer_btree.h) */ #define E_OFF_OBJID 0 #define E_OFF_CRTID 16 #define E_OFF_DELTID 24 #define E_OFF_RTYPES 32 /* 2 bytes */ #define E_OFF_LOCAL 36 /* 4 bytes */ #define E_OFF_DATAOFF 48 /* 8 bytes */ #define E_OFF_DATALEN 56 /* 4 bytes */ /* ------------------------------------------------------------------ */ /* CRC-32 (poly 0xEDB88320, init/xorout 0xFFFFFFFF) - vol version <=6 */ static uint32_t crc32_tab[256]; static void crc32_init(void) { for (uint32_t i = 0; i < 256; i++) { uint32_t c = i; for (int k = 0; k < 8; k++) c = (c & 1) ? 0xEDB88320U ^ (c >> 1) : c >> 1; crc32_tab[i] = c; } } static uint32_t crc32_buf(const void *p, size_t n) { const unsigned char *b = p; uint32_t c = 0xFFFFFFFFU; for (size_t i = 0; i < n; i++) c = crc32_tab[(c ^ b[i]) & 0xff] ^ (c >> 8); return (c ^ 0xFFFFFFFFU); } /* CRC-32C Castagnoli (poly 0x82F63B78) - vol version >=7 */ static uint32_t crc32c_tab[256]; static void crc32c_init(void) { for (uint32_t i = 0; i < 256; i++) { uint32_t c = i; for (int k = 0; k < 8; k++) c = (c & 1) ? 0x82F63B78U ^ (c >> 1) : c >> 1; crc32c_tab[i] = c; } } static uint32_t crc32c_buf(const void *p, size_t n) { const unsigned char *b = p; uint32_t c = 0xFFFFFFFFU; for (size_t i = 0; i < n; i++) c = crc32c_tab[(c ^ b[i]) & 0xff] ^ (c >> 8); return (c ^ 0xFFFFFFFFU); } int main(int argc, char **argv) { unsigned char node[BUFSIZE]; uint32_t stored, calc_a, calc_b, newcrc; off_t off; int fd, count, patched = 0, nodes = 0; if (argc < 2) { fprintf(stderr, "usage: %s <image>\n", argv[0]); exit(2); } crc32_init(); crc32c_init(); fd = open(argv[1], O_RDWR); if (fd < 0) { perror("open"); exit(2); } for (off = 0; ; off += 512) { if (lseek(fd, off, SEEK_SET) < 0) break; if (read(fd, node, BUFSIZE) != BUFSIZE) break; /* node header: crc(0) r01(4) parent(8) count(16) type(20) */ memcpy(&stored, node, 4); memcpy(&count, node + 16, 4); if (node[20] != BTREE_TYPE_L) continue; if (count < 1 || count > 63) continue; /* CRC over node minus first 4 bytes */ calc_a = crc32_buf(node + 4, BUFSIZE - 4); calc_b = crc32c_buf(node + 4, BUFSIZE - 4); if (stored != calc_a && stored != calc_b) continue; /* not a valid node at this offset */ nodes++; { int is_c = (stored == calc_b); int hit = 0; for (int i = 0; i < count; i++) { unsigned char *elm = node + 64 + 64 * i; uint16_t rt; uint32_t loc; uint64_t objid, deltid, doff; int32_t dl; memcpy(&rt, elm + E_OFF_RTYPES, 2); memcpy(&loc, elm + E_OFF_LOCAL, 4); memcpy(&objid, elm + E_OFF_OBJID, 8); memcpy(&deltid, elm + E_OFF_DELTID, 8); memcpy(&doff, elm + E_OFF_DATAOFF, 8); memcpy(&dl, elm + E_OFF_DATALEN, 4); if (rt != RECTYPE_INODE) continue; if (loc & LOCALIZE_PSEUDOFS_MASK) continue; /* not PFS#0 */ if (deltid != 0) continue; /* not a live inode */ if ((int64_t)objid <= 1) continue; /* skip root inode */ printf("found live inode leaf @off=%jd: " "objid=%016jx loc=%08x " "data_off=%016jx data_len=%d -> 0\n", (intmax_t)off, (uintmax_t)objid, loc, (uintmax_t)doff, dl); doff = 0; memcpy(elm + E_OFF_DATAOFF, &doff, 8); hit = 1; patched++; } if (hit) { newcrc = is_c ? crc32c_buf(node + 4, BUFSIZE - 4) : crc32_buf(node + 4, BUFSIZE - 4); memcpy(node, &newcrc, 4); if (lseek(fd, off, SEEK_SET) < 0 || write(fd, node, BUFSIZE) != BUFSIZE) { perror("write"); exit(1); } printf("patched node @off=%jd (crc algo %s)\n", (intmax_t)off, is_c ? "iscsi/v7" : "crc32/v6"); } } } printf("done: %d valid leaf nodes scanned, %d live inode records " "patched to data_offset=0\n", nodes, patched); close(fd); return (patched == 0); } |