DF-3092 / tailcheck.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 | /* DF-3092 offline image verifier. * * Checks the crafted crafttail.c image (fixed geometry: 512B sectors, * SecPerClust=1, Res=1, FATs=1, FATsecs=17, RootDirEnts=16, Huge=4127 * => first data sector 19, pm_maxcluster=4109, FAT entry capacity 4352): * * C1: every FAT entry for a cluster > pm_maxcluster (4110..4351) is * still 0 (free) -- i.e. NO phantom cluster was ever allocated, * chained, or linked by any operation. * C2: no root-dir entry has a start cluster > pm_maxcluster. * C3: BIG.TXT chain walk stays within 2..pm_maxcluster and terminates * at an EOF marker without cycling; optional expected file size * check (argv[2]). * * build: cc -O -o tailcheck tailcheck.c * usage: ./tailcheck image.img [expected_BIG_TXT_size] * exit: 0 = all pass, 1 = a check FAILED (prints which) */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #define SECSZ 512 #define FATSECS 17 #define FIRSTCLUST_BLK 19 #define HUGE_SECS 4127 #define MAXCLUST 4109 /* pm_maxcluster */ #define FATENTS (FATSECS * 256)/* 4352 FAT16 entries fit in the FAT */ static uint8_t img[HUGE_SECS * SECSZ + 8 * SECSZ]; static size_t imgsiz; static uint16_t rd16(const uint8_t *p) { return ((uint16_t)p[0] | ((uint16_t)p[1] << 8)); } static uint32_t rd32(const uint8_t *p) { return ((uint32_t)rd16(p) | ((uint32_t)rd16(p + 2) << 16)); } int main(int argc, char **argv) { FILE *f; const uint8_t *fat, *root; uint16_t e; unsigned long n, c, steps; int fails = 0; if (argc < 2) { fprintf(stderr, "usage: %s image.img [expected_size]\n", argv[0]); return (1); } f = fopen(argv[1], "rb"); if (!f) { perror("fopen"); return (1); } imgsiz = fread(img, 1, sizeof(img), f); fclose(f); if (imgsiz < HUGE_SECS * SECSZ) { fprintf(stderr, "short image %zu\n", imgsiz); return (1); } /* sanity: BPB matches crafttail geometry */ if (img[13] != 1 || rd16(img + 14) != 1 || img[16] != 1 || rd16(img + 22) != FATSECS || rd32(img + 32) != HUGE_SECS) { fprintf(stderr, "image is not the crafttail geometry\n"); return (1); } fat = img + SECSZ; root = img + (FIRSTCLUST_BLK - 1) * SECSZ; /* C1: FAT entries beyond pm_maxcluster must all be free */ for (n = MAXCLUST + 1; n < FATENTS; n++) { e = rd16(fat + n * 2); if ((e & 0xffff) != 0) { printf("C1 FAIL: FAT[%lu]=0x%04x nonzero " "(phantom cluster was touched!)\n", n, e); fails++; } } if (!fails) printf("C1 pass: FAT entries %u..%u all zero " "(no phantom allocation ever reached the FAT)\n", MAXCLUST + 1, FATENTS - 1); /* C2: no dir entry starts beyond pm_maxcluster */ for (n = 0; n < 16; n++) { const uint8_t *de = root + n * 32; uint16_t s; if (de[0] == 0x00 || de[0] == 0xE5) continue; s = rd16(de + 26); if (s > MAXCLUST) { printf("C2 FAIL: dir entry %lu '%.11s' start=%u > " "maxcluster\n", n, (const char *)de, s); fails++; } } if (!fails) printf("C2 pass: all dir entries start within 2..%u\n", MAXCLUST); /* C3: BIG.TXT chain bounded + terminates at EOF */ for (n = 0; n < 16; n++) { const uint8_t *de = root + n * 32; if (de[0] == 0x00 || de[0] == 0xE5) continue; if (memcmp(de, "BIG TXT", 11) != 0) continue; c = rd16(de + 26); steps = 0; while (c >= 2 && c <= MAXCLUST) { e = rd16(fat + c * 2); if (e >= 0xFFF8) break; if (e < 2 || e > MAXCLUST) { printf("C3 FAIL: FAT[%lu]=0x%04x out of " "range\n", c, e); fails++; break; } c = e; if (++steps > MAXCLUST) { printf("C3 FAIL: BIG.TXT chain cycles\n"); fails++; break; } } printf("C3 %s: BIG.TXT chain ends at cluster %lu " "(EOF), dir size=%lu\n", (c <= MAXCLUST) ? "pass" : "FAIL", c, (unsigned long)rd32(de + 28)); if (c > MAXCLUST) fails++; if (argc > 2) { unsigned long want = strtoul(argv[2], NULL, 0); unsigned long got = rd32(de + 28); if (got != want) { printf("C3 FAIL: BIG.TXT size %lu != " "expected %lu\n", got, want); fails++; } } break; } printf("%s\n", fails ? "TAILCHECK: FAILED" : "TAILCHECK: ALL PASS"); return (fails ? 1 : 0); } |