1 /* 2 * 3 * SPDX-License-Identifier: BSD-2-Clause 4 */ 5 6 /* 7 * Set a GPT attribute on a partition of a disk image file. 8 * 9 * gpart(8) does the same thing, but only on a real device and only as root. 10 * boot-test.sh builds every image as an unprivileged user, so it needs to 11 * edit the on-disk GPT itself. Both the primary and the secondary entry 12 * table are updated: the loader only reads the primary, but the kernel 13 * checks both, and a mismatch would be reported as a corrupt GPT during the 14 * boot under test. 15 * 16 * Usage: gptattr <image> <index> <attribute-bit> 17 * where <index> is 1-based, matching gpart(8) partition numbering. 18 */ 19 20 #include <sys/param.h> 21 #include <sys/disk/gpt.h> 22 #include <sys/endian.h> 23 24 #include <err.h> 25 #include <errno.h> 26 #include <fcntl.h> 27 #include <stdint.h> 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <string.h> 31 #include <unistd.h> 32 #include <zlib.h> 33 34 #define SECSZ 512 35 36 static uint8_t *image; 37 static size_t imagesz; 38 39 /* 40 * Apply the attribute to one entry table and refresh the CRCs of the header 41 * at hdroff that describes it. 42 */ 43 static void 44 patch_table(uint64_t hdroff, u_int index, uint64_t attr) 45 { 46 struct gpt_hdr *hdr; 47 struct gpt_ent *ent; 48 uint64_t tbloff; 49 uint32_t entries, entsz, hdrsz; 50 51 if (hdroff + SECSZ > imagesz) 52 errx(1, "GPT header at offset %ju is past end of image", 53 (uintmax_t)hdroff); 54 55 hdr = (struct gpt_hdr *)(image + hdroff); 56 if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)) != 0) 57 errx(1, "no GPT header at offset %ju", (uintmax_t)hdroff); 58 59 hdrsz = le32toh(hdr->hdr_size); 60 entries = le32toh(hdr->hdr_entries); 61 entsz = le32toh(hdr->hdr_entsz); 62 tbloff = le64toh(hdr->hdr_lba_table) * SECSZ; 63 64 if (index < 1 || index > entries) 65 errx(1, "partition index %u out of range (1..%u)", index, 66 entries); 67 if (tbloff + (uint64_t)entries * entsz > imagesz) 68 errx(1, "GPT entry table is past end of image"); 69 70 ent = (struct gpt_ent *)(image + tbloff + (uint64_t)(index - 1) * entsz); 71 le64enc(&ent->ent_attr, le64toh(ent->ent_attr) | attr); 72 73 /* 74 * The table CRC covers the entries, and the header CRC covers the 75 * header including that table CRC, so recompute them in that order. 76 * hdr_crc_self is zeroed while it is being computed over itself. 77 */ 78 le32enc(&hdr->hdr_crc_table, crc32(0, image + tbloff, 79 entries * entsz)); 80 hdr->hdr_crc_self = 0; 81 le32enc(&hdr->hdr_crc_self, crc32(0, (const uint8_t *)hdr, hdrsz)); 82 } 83 84 int 85 main(int argc, char *argv[]) 86 { 87 FILE *fp; 88 char *end; 89 uint64_t attr; 90 u_long bit, index; 91 92 if (argc != 4) { 93 fprintf(stderr, 94 "usage: %s <image> <index> <attribute-bit>\n", argv[0]); 95 return (1); 96 } 97 98 errno = 0; 99 index = strtoul(argv[2], &end, 0); 100 if (errno != 0 || *end != '\0' || index == 0) 101 errx(1, "bad partition index: %s", argv[2]); 102 bit = strtoul(argv[3], &end, 0); 103 if (errno != 0 || *end != '\0' || bit > 63) 104 errx(1, "bad attribute bit: %s", argv[3]); 105 attr = (uint64_t)1 << bit; 106 107 if ((fp = fopen(argv[1], "r+")) == NULL) 108 err(1, "%s", argv[1]); 109 if (fseek(fp, 0, SEEK_END) != 0) 110 err(1, "%s", argv[1]); 111 imagesz = (size_t)ftell(fp); 112 rewind(fp); 113 if (imagesz < 2 * SECSZ) 114 errx(1, "%s is too small to hold a GPT", argv[1]); 115 if ((image = malloc(imagesz)) == NULL) 116 err(1, "malloc"); 117 if (fread(image, 1, imagesz, fp) != imagesz) 118 err(1, "%s: read", argv[1]); 119 120 /* Primary header is always at LBA 1; it names the secondary. */ 121 patch_table(SECSZ, index, attr); 122 patch_table(le64toh(((struct gpt_hdr *)(image + SECSZ))->hdr_lba_alt) * 123 SECSZ, index, attr); 124 125 rewind(fp); 126 if (fwrite(image, 1, imagesz, fp) != imagesz) 127 err(1, "%s: write", argv[1]); 128 if (fclose(fp) != 0) 129 err(1, "%s: close", argv[1]); 130 free(image); 131 132 return (0); 133 } 134