DF-3187 / patch_dlen.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 | /* * DF-3187 patcher: offline-patch a HAMMER1 image so a PFS record leaf * carries an out-of-range data_len (default -1). * * Scans the image at 512-byte granularity for valid B-Tree leaf nodes * (self-identified by their stored node CRC), locates elements with * rec_type == HAMMER_RECTYPE_PFS (0x15), rewrites leaf->data_len 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_dlen <image> [value] (default value = -1) */ #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_PFS 0x0015 #define LOCALIZE_MISC 0x00000002 /* ------------------------------------------------------------------ */ /* 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; int32_t value = -1; off_t off; int fd, count, patched = 0, nodes = 0; if (argc < 2) { fprintf(stderr, "usage: %s <image> [value]\n", argv[0]); exit(2); } if (argc >= 3) value = (int32_t)strtol(argv[2], NULL, 0); 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; int32_t dl; memcpy(&rt, elm + 32, 2); /* base.rec_type */ memcpy(&loc, elm + 36, 4); /* base.localization */ memcpy(&dl, elm + 56, 4); /* leaf.data_len */ if (rt == RECTYPE_PFS && loc == LOCALIZE_MISC) { int64_t key; memcpy(&key, elm + 8, 8); if (key == 0) continue; /* PFS#0 read at mount: leave intact */ printf("found PFS leaf @off=%jd: " "key(pfs lo)=%08jx " "data_len=%d -> %d\n", (intmax_t)off, (uintmax_t)key, dl, value); dl = value; memcpy(elm + 48, &dl, 4); 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 PFS records patched " "to data_len=%d\n", nodes, patched, value); close(fd); return (patched == 0); } |