DF-3033 / craft_3033.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 172 173 | /* * craft_3033.c - DF-3033 image patcher: finds the btree leaf node holding a * HAMMER_RECTYPE_DATA record for obj_id <oid> (or the FIRST DATA record with * data_len==16384), rewrites leaf->data_len = 0x20000 (128K > MAXBSIZE), * recomputes leaf->data_crc (crc32/crc32c over 128K at the record's data * offset) and the btree node crc. * * Steps around it: newfs_hammer; mount; dd 16K file; sync; umount; * craft_3033 <img> <oid>; mount -u rw; read file (cache); hammer reblock. * * usage: craft_3033 <img> [oid] */ #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <string.h> #include <inttypes.h> #include <unistd.h> #include <fcntl.h> #include <sys/types.h> #include <stddef.h> #include <sys/stat.h> #include <hammer_disk.h> #include <hammer_btree.h> /* ---- crc32 (IEEE) and crc32c (Castagnoli) ---- */ static uint32_t crc_tab[16][256], tabs_ready; static void init_tabs(void) { static const uint32_t poly_n[] = {0xEDB88320u,0x82F63B78u}; if (tabs_ready) return; for (int k = 0; k < 2; k++) { for (uint32_t i = 0; i < 256; i++) { uint32_t c = i; for (int j = 0; j < 8; j++) c = (c & 1) ? (poly_n[k] ^ (c >> 1)) : (c >> 1); crc_tab[k*8+0][i] = c; } } for (int k = 0; k < 2; k++) for (int s = 1; s < 8; s++) for (uint32_t i = 0; i < 256; i++) crc_tab[k*8+s][i] = (crc_tab[k*8+s-1][i] >> 8) ^ crc_tab[k*8+0][crc_tab[k*8+s-1][i] & 0xff]; tabs_ready = 1; } static uint32_t crc_gen(int c, const void *buf, size_t len, uint32_t seed) { const uint8_t *p = buf; uint32_t crc = seed; while (len >= 8) { crc ^= (uint32_t)p[0] | ((uint32_t)p[1]<<8) | ((uint32_t)p[2]<<16) | ((uint32_t)p[3]<<24); crc = crc_tab[c*8+7][crc & 0xff] ^ crc_tab[c*8+6][(crc>>8)&0xff] ^ crc_tab[c*8+5][(crc>>16)&0xff] ^ crc_tab[c*8+4][crc>>24] ^ crc_tab[c*8+3][p[4]] ^ crc_tab[c*8+2][p[5]] ^ crc_tab[c*8+1][p[6]] ^ crc_tab[c*8+0][p[7]]; p += 8; len -= 8; } while (len--) crc = crc_tab[c*8+0][ (crc ^ *p++) & 0xff ] ^ (crc >> 8); return crc; } static uint32_t crc32_ieee(const void *b, size_t n){ return crc_gen(0,b,n,0xFFFFFFFFu)^0xFFFFFFFFu; } static uint32_t crc32_cast(const void *b, size_t n){ return crc_gen(1,b,n,0xFFFFFFFFu)^0xFFFFFFFFu; } static uint32_t datacrc(uint32_t ver, const void *b, size_t n) { return (ver >= HAMMER_VOL_VERSION_SEVEN) ? crc32_cast(b,n) : crc32_ieee(b,n); } #define OOF(x) offsetof(struct hammer_volume_ondisk, x) static uint64_t zoff_to_phys(struct hammer_volume_ondisk *hdr, int nvol, hammer_off_t off, uint64_t *volsize) { /* only zone 2/3-ish raw buffer translation we need: use the blockmap * phys_offset of the owning zone if set, else linear map over volumes. * For simplicity assume a single-volume image: phys = short offset. */ uint64_t zone = off & 0xF000000000000000ULL; hammer_off_t short_off = off & ~0xF000000000000000ULL; if (zone == HAMMER_ZONE_RAW_BUFFER) { return short_off; /* zone2 phys == short offset */ } /* btree/data zones: physical = blockmap.phys_offset + short % ?? * For freshly newfs'd single-volume images the freemap assigns * sequentially; instead scan each blockmap for phys base. */ for (int i = 0; i < HAMMER_MAX_ZONES; i++) { struct hammer_blockmap *bm = (struct hammer_blockmap *) ((char *)hdr + OOF(vol0_blockmap) + i * sizeof(struct hammer_blockmap)); if ((bm->phys_offset & 0xF000000000000000ULL) == HAMMER_ZONE_RAW_BUFFER && short_off >= (bm->phys_offset & HAMMER_OFF_SHORT_MASK) && short_off < (bm->phys_offset & HAMMER_OFF_SHORT_MASK) + (1ULL<<40)) { /* linear within this zone allocation area (fresh fs) */ return (bm->phys_offset & HAMMER_OFF_SHORT_MASK) + (short_off - (bm->phys_offset & HAMMER_OFF_SHORT_MASK)); } } return short_off; } int main(int ac, char **av) { struct hammer_volume_ondisk hdr; struct stat st; uint8_t *img; int fd, nvol = 1; uint32_t ver; uint64_t oid_filter = 0; int have_oid = 0; if (ac != 2 && ac != 3) { fprintf(stderr, "usage: craft_3033 <img> [oid]\n"); return 2; } if (ac == 3) { oid_filter = strtoull(av[2], NULL, 0); have_oid = 1; } init_tabs(); fd = open(av[1], O_RDWR); if (fd < 0) { perror("open"); return 1; } fstat(fd, &st); img = malloc(st.st_size); if (pread(fd, img, st.st_size, 0) != st.st_size) { perror("read"); return 1; } memcpy(&hdr, img, sizeof(hdr)); if (hdr.vol_signature != HAMMER_FSBUF_VOLUME) { fprintf(stderr, "not hammer\n"); return 1; } ver = hdr.vol_version; printf("vol_version=%u imgsize=%jd\n", ver, (intmax_t)st.st_size); /* walk vol0_next (btree root) is complex; instead scan every 16K * buffer for btree leaf nodes: leaf hdr flags&1? node_type in * hammer_node_ondisk.base? node->base.btype == HAMMER_BTREE_TYPE_LEAF * and signature bytes 'BTRE'? hammer stores crc first; validate by * testing node crc. */ int patched = 0; for (uint64_t off = 0; off + sizeof(struct hammer_node_ondisk) <= (uint64_t)st.st_size; off += 16384) { struct hammer_node_ondisk *node = (struct hammer_node_ondisk *)(img + off); if (node->type != HAMMER_BTREE_TYPE_LEAF) continue; /* validate crc */ uint32_t expect = datacrc(ver, ((uint8_t *)&node->crc) + sizeof(node->crc), HAMMER_BTREE_CRCSIZE); if (node->crc != expect) continue; if (node->count <= 0 || node->count > HAMMER_BTREE_LEAF_ELMS) continue; for (int e = 0; e < node->count; e++) { struct hammer_btree_leaf_elm *leaf = &node->elms[e].leaf; if (leaf->base.rec_type != HAMMER_RECTYPE_DATA) continue; if (have_oid && (uint64_t)leaf->base.obj_id != oid_filter) continue; if (leaf->data_len != 16384) continue; printf("leaf @phys %#jx elm %d: obj_id=%016jx key=%016jx data_off=%016jx data_len=%u\n", (uintmax_t)off, e, (uintmax_t)leaf->base.obj_id, (uintmax_t)leaf->base.key, (uintmax_t)leaf->data_offset, leaf->data_len); /* data phys offset */ uint64_t dphys = zoff_to_phys(&hdr, nvol, leaf->data_offset, NULL); printf(" data phys=%#jx\n", (uintmax_t)dphys); if (dphys + 0x20000 >= (uint64_t)st.st_size) { fprintf(stderr, " data phys +128K beyond image; extend image first\n"); continue; } leaf->data_len = 0x20000; leaf->data_crc = datacrc(ver, img + dphys, 0x20000); /* recompute node crc */ node->crc = datacrc(ver, ((uint8_t *)&node->crc) + sizeof(node->crc), HAMMER_BTREE_CRCSIZE); printf(" PATCHED: data_len=0x20000 data_crc=%08x node crc=%08x\n", leaf->data_crc, node->crc); patched++; } } if (!patched) { fprintf(stderr, "no DATA leaf patched\n"); return 1; } if (pwrite(fd, img, st.st_size, 0) != st.st_size) { perror("pwrite"); return 1; } close(fd); printf("OK patched %d leaf(s)\n", patched); return 0; } |