1 /*-
2  * Copyright (c) 2002 Marcel Moolenaar
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  * CRC32 code derived from work by Gary S. Brown.
27  */
28 
29 #if HAVE_NBTOOL_CONFIG_H
30 #include "nbtool_config.h"
31 #endif
32 
33 #include <sys/cdefs.h>
34 #ifdef __FBSDID
35 __FBSDID("$FreeBSD: src/sbin/gpt/gpt.c,v 1.16 2006/07/07 02:44:23 marcel Exp $");
36 #endif
37 #ifdef __RCSID
38 __RCSID("$NetBSD: gpt.c,v 1.91 2025/02/23 20:47:19 christos Exp $");
39 #endif
40 
41 #include <sys/param.h>
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <sys/ioctl.h>
45 #include <sys/bootblock.h>
46 
47 #include <err.h>
48 #include <errno.h>
49 #include <fcntl.h>
50 #include <paths.h>
51 #include <stddef.h>
52 #include <stdarg.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <unistd.h>
57 #include <ctype.h>
58 
59 #include "map.h"
60 #include "gpt.h"
61 #include "gpt_private.h"
62 
63 static uint32_t crc32_tab[] = {
64           0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
65           0xe963a535, 0x9e6495a3,       0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
66           0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
67           0xf3b97148, 0x84be41de,       0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
68           0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,   0x14015c4f, 0x63066cd9,
69           0xfa0f3d63, 0x8d080df5,       0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
70           0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,   0x35b5a8fa, 0x42b2986c,
71           0xdbbbc9d6, 0xacbcf940,       0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
72           0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
73           0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
74           0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,   0x76dc4190, 0x01db7106,
75           0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
76           0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
77           0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
78           0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
79           0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
80           0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
81           0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
82           0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
83           0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
84           0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
85           0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
86           0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
87           0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
88           0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
89           0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
90           0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
91           0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
92           0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
93           0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
94           0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
95           0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
96           0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
97           0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
98           0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
99           0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
100           0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
101           0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
102           0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
103           0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
104           0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
105           0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
106           0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
107 };
108 
109 uint32_t
crc32(const void * buf,size_t size)110 crc32(const void *buf, size_t size)
111 {
112           const uint8_t *p;
113           uint32_t crc;
114 
115           p = buf;
116           crc = ~0U;
117 
118           while (size--)
119                     crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
120 
121           return crc ^ ~0U;
122 }
123 
124 /*
125  * Produce a NUL-terminated utf-8 string from the non-NUL-terminated
126  * utf16 string.
127  */
128 void
utf16_to_utf8(const uint16_t * s16,size_t s16len,uint8_t * s8,size_t s8len)129 utf16_to_utf8(const uint16_t *s16, size_t s16len, uint8_t *s8, size_t s8len)
130 {
131           size_t s8idx, s16idx;
132           uint32_t utfchar;
133           unsigned int c;
134 
135           for (s16idx = 0; s16idx < s16len; s16idx++)
136                     if (s16[s16idx] == 0)
137                               break;
138 
139           s16len = s16idx;
140           s8idx = s16idx = 0;
141           while (s16idx < s16len) {
142                     utfchar = le16toh(s16[s16idx++]);
143                     if ((utfchar & 0xf800) == 0xd800) {
144                               c = le16toh(s16[s16idx]);
145                               if ((utfchar & 0x400) != 0 || (c & 0xfc00) != 0xdc00)
146                                         utfchar = 0xfffd;
147                               else
148                                         s16idx++;
149                     }
150                     if (utfchar < 0x80) {
151                               if (s8idx + 1 >= s8len)
152                                         break;
153                               s8[s8idx++] = (uint8_t)utfchar;
154                     } else if (utfchar < 0x800) {
155                               if (s8idx + 2 >= s8len)
156                                         break;
157                               s8[s8idx++] = (uint8_t)(0xc0 | (utfchar >> 6));
158                               s8[s8idx++] = (uint8_t)(0x80 | (utfchar & 0x3f));
159                     } else if (utfchar < 0x10000) {
160                               if (s8idx + 3 >= s8len)
161                                         break;
162                               s8[s8idx++] = (uint8_t)(0xe0 | (utfchar >> 12));
163                               s8[s8idx++] = (uint8_t)(0x80 | ((utfchar >> 6) & 0x3f));
164                               s8[s8idx++] = (uint8_t)(0x80 | (utfchar & 0x3f));
165                     } else if (utfchar < 0x200000) {
166                               if (s8idx + 4 >= s8len)
167                                         break;
168                               s8[s8idx++] = (uint8_t)(0xf0 | (utfchar >> 18));
169                               s8[s8idx++] = (uint8_t)(0x80 | ((utfchar >> 12) & 0x3f));
170                               s8[s8idx++] = (uint8_t)(0x80 | ((utfchar >> 6) & 0x3f));
171                               s8[s8idx++] = (uint8_t)(0x80 | (utfchar & 0x3f));
172                     }
173           }
174           s8[s8idx] = 0;
175 }
176 
177 /*
178  * Produce a non-NUL-terminated utf-16 string from the NUL-terminated
179  * utf8 string.
180  */
181 void
utf8_to_utf16(const uint8_t * s8,uint16_t * s16,size_t s16len)182 utf8_to_utf16(const uint8_t *s8, uint16_t *s16, size_t s16len)
183 {
184           size_t s16idx, s8idx, s8len;
185           uint32_t utfchar = 0;
186           unsigned int c, utfbytes;
187 
188           s8len = 0;
189           while (s8[s8len++] != 0)
190                     ;
191           s8idx = s16idx = 0;
192           utfbytes = 0;
193           do {
194                     c = s8[s8idx++];
195                     if ((c & 0xc0) != 0x80) {
196                               /* Initial characters. */
197                               if (utfbytes != 0) {
198                                         /* Incomplete encoding. */
199                                         s16[s16idx++] = htole16(0xfffd);
200                                         if (s16idx == s16len) {
201                                                   s16[--s16idx] = 0;
202                                                   return;
203                                         }
204                               }
205                               if ((c & 0xf8) == 0xf0) {
206                                         utfchar = c & 0x07;
207                                         utfbytes = 3;
208                               } else if ((c & 0xf0) == 0xe0) {
209                                         utfchar = c & 0x0f;
210                                         utfbytes = 2;
211                               } else if ((c & 0xe0) == 0xc0) {
212                                         utfchar = c & 0x1f;
213                                         utfbytes = 1;
214                               } else {
215                                         utfchar = c & 0x7f;
216                                         utfbytes = 0;
217                               }
218                     } else {
219                               /* Followup characters. */
220                               if (utfbytes > 0) {
221                                         utfchar = (utfchar << 6) + (c & 0x3f);
222                                         utfbytes--;
223                               } else if (utfbytes == 0)
224                                         utfbytes = (u_int)~0;
225                     }
226                     if (utfbytes == 0) {
227                               if (utfchar >= 0x10000 && s16idx + 2 >= s16len)
228                                         utfchar = 0xfffd;
229                               if (utfchar >= 0x10000) {
230                                         s16[s16idx++] = htole16((uint16_t)
231                                             (0xd800 | ((utfchar>>10) - 0x40)));
232                                         s16[s16idx++] = htole16((uint16_t)
233                                             (0xdc00 | (utfchar & 0x3ff)));
234                               } else
235                                         s16[s16idx++] = htole16((uint16_t)utfchar);
236                               if (s16idx == s16len) {
237                                         return;
238                               }
239                     }
240           } while (c != 0);
241 
242           while (s16idx < s16len)
243                     s16[s16idx++] = 0;
244 }
245 
246 void *
gpt_read(gpt_t gpt,off_t lba,size_t count)247 gpt_read(gpt_t gpt, off_t lba, size_t count)
248 {
249           off_t ofs;
250           void *buf;
251 
252           count *= gpt->secsz;
253           buf = malloc(count);
254           if (buf == NULL)
255                     return NULL;
256 
257           ofs = lba * gpt->secsz;
258           if (lseek(gpt->fd, ofs, SEEK_SET) == ofs &&
259               read(gpt->fd, buf, count) == (ssize_t)count)
260                     return buf;
261 
262           free(buf);
263           return NULL;
264 }
265 
266 int
gpt_write(gpt_t gpt,map_t map)267 gpt_write(gpt_t gpt, map_t map)
268 {
269           off_t ofs;
270           size_t count;
271 
272           count = (size_t)(map->map_size * gpt->secsz);
273           ofs = map->map_start * gpt->secsz;
274           if (lseek(gpt->fd, ofs, SEEK_SET) != ofs ||
275               write(gpt->fd, map->map_data, count) != (ssize_t)count)
276                     return -1;
277           gpt->flags |= GPT_MODIFIED;
278           return 0;
279 }
280 
281 static int
gpt_mbr(gpt_t gpt,off_t lba,unsigned int * next_index,off_t ext_offset)282 gpt_mbr(gpt_t gpt, off_t lba, unsigned int *next_index, off_t ext_offset)
283 {
284           struct mbr *mbr;
285           map_t m, p;
286           off_t size, start;
287           unsigned int i, pmbr;
288 
289           mbr = gpt_read(gpt, lba, 1);
290           if (mbr == NULL) {
291                     gpt_warn(gpt, "Read failed");
292                     return -1;
293           }
294 
295           if (mbr->mbr_sig != htole16(MBR_SIG)) {
296                     if (gpt->verbose)
297                               gpt_msg(gpt,
298                                   "MBR not found at sector %ju", (uintmax_t)lba);
299                     free(mbr);
300                     return 0;
301           }
302 
303           /*
304            * Differentiate between a regular MBR and a PMBR. This is more
305            * convenient in general. A PMBR is one with a single partition
306            * of type 0xee.
307            */
308           pmbr = 0;
309           for (i = 0; i < 4; i++) {
310                     if (mbr->mbr_part[i].part_typ == MBR_PTYPE_UNUSED)
311                               continue;
312                     if (mbr->mbr_part[i].part_typ == MBR_PTYPE_PMBR)
313                               pmbr++;
314                     else if ((gpt->flags & GPT_HYBRID) == 0)
315                               break;
316           }
317           if (pmbr && i == 4 && lba == 0) {
318                     if (pmbr != 1)
319                               gpt_warnx(gpt, "Suspicious PMBR at sector %ju",
320                                   (uintmax_t)lba);
321                     else if (gpt->verbose > 1)
322                               gpt_msg(gpt, "PMBR at sector %ju", (uintmax_t)lba);
323                     p = map_add(gpt, lba, 1LL, MAP_TYPE_PMBR, mbr, 1);
324                     goto out;
325           }
326           if (pmbr)
327                     gpt_warnx(gpt, "Suspicious MBR at sector %ju", (uintmax_t)lba);
328           else if (gpt->verbose > 1)
329                     gpt_msg(gpt, "MBR at sector %ju", (uintmax_t)lba);
330 
331           p = map_add(gpt, lba, 1LL, MAP_TYPE_MBR, mbr, 1);
332           if (p == NULL)
333                     goto out;
334 
335           for (i = 0; i < 4; i++) {
336                     if (mbr->mbr_part[i].part_typ == MBR_PTYPE_UNUSED ||
337                         mbr->mbr_part[i].part_typ == MBR_PTYPE_PMBR)
338                               continue;
339                     start = le16toh(mbr->mbr_part[i].part_start_hi);
340                     start = (start << 16) + le16toh(mbr->mbr_part[i].part_start_lo);
341                     size = le16toh(mbr->mbr_part[i].part_size_hi);
342                     size = (size << 16) + le16toh(mbr->mbr_part[i].part_size_lo);
343                     if (start == 0 && size == 0) {
344                               gpt_warnx(gpt, "Malformed MBR at sector %ju",
345                                   (uintmax_t)lba);
346                               continue;
347                     }
348                     if (gpt->verbose > 2)
349                               gpt_msg(gpt, "MBR part: flag=%#x type=%d, start=%ju, "
350                                   "size=%ju", mbr->mbr_part[i].part_flag,
351                                   mbr->mbr_part[i].part_typ,
352                                   (uintmax_t)start, (uintmax_t)size);
353                     if (!MBR_IS_EXTENDED(mbr->mbr_part[i].part_typ)) {
354                               start += lba;
355                               m = map_add(gpt, start, size, MAP_TYPE_MBR_PART, p, 0);
356                               if (m == NULL)
357                                         return -1;
358                               m->map_index = *next_index;
359                               (*next_index)++;
360                     } else {
361                               start += ext_offset;
362                               if (gpt_mbr(gpt, start, next_index,
363                                   ext_offset ? ext_offset : start) == -1)
364                                         return -1;
365                     }
366           }
367           return 0;
368 out:
369           if (p == NULL) {
370                     free(mbr);
371                     return -1;
372           }
373           return 0;
374 }
375 
376 int
gpt_gpt(gpt_t gpt,off_t lba,int found)377 gpt_gpt(gpt_t gpt, off_t lba, int found)
378 {
379           off_t size;
380           struct gpt_ent *ent;
381           struct gpt_hdr *hdr;
382           char *p;
383           map_t m;
384           size_t blocks, tblsz;
385           unsigned int i;
386           uint32_t crc;
387 
388           hdr = gpt_read(gpt, lba, 1);
389           if (hdr == NULL) {
390                     gpt_warn(gpt, "Read failed");
391                     return -1;
392           }
393 
394           if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)))
395                     goto fail_hdr;
396 
397           crc = le32toh(hdr->hdr_crc_self);
398           hdr->hdr_crc_self = 0;
399           if (crc32(hdr, le32toh(hdr->hdr_size)) != crc) {
400                     if (gpt->verbose)
401                               gpt_msg(gpt, "Bad CRC in GPT header at sector %ju",
402                                   (uintmax_t)lba);
403                     goto fail_hdr;
404           }
405 
406           tblsz = le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz);
407           blocks = tblsz / gpt->secsz + ((tblsz % gpt->secsz) ? 1 : 0);
408 
409           /* Use generic pointer to deal with hdr->hdr_entsz != sizeof(*ent). */
410           p = gpt_read(gpt, (off_t)le64toh((uint64_t)hdr->hdr_lba_table), blocks);
411           if (p == NULL) {
412                     if (found) {
413                               if (gpt->verbose)
414                                         gpt_msg(gpt,
415                                             "Cannot read LBA table at sector %ju",
416                                             (uintmax_t)le64toh(hdr->hdr_lba_table));
417                               return -1;
418                     }
419                     goto fail_hdr;
420           }
421 
422           if (crc32(p, tblsz) != le32toh(hdr->hdr_crc_table)) {
423                     if (gpt->verbose)
424                               gpt_msg(gpt, "Bad CRC in GPT table at sector %ju",
425                                   (uintmax_t)le64toh(hdr->hdr_lba_table));
426                     goto fail_ent;
427           }
428 
429           if (gpt->verbose > 1)
430                     gpt_msg(gpt, "%s GPT at sector %ju",
431                         (lba == 1) ? "Pri" : "Sec", (uintmax_t)lba);
432 
433           m = map_add(gpt, lba, 1, (lba == 1)
434               ? MAP_TYPE_PRI_GPT_HDR : MAP_TYPE_SEC_GPT_HDR, hdr, 1);
435           if (m == NULL)
436                     return (-1);
437 
438           m = map_add(gpt, (off_t)le64toh((uint64_t)hdr->hdr_lba_table),
439               (off_t)blocks,
440               lba == 1 ? MAP_TYPE_PRI_GPT_TBL : MAP_TYPE_SEC_GPT_TBL, p, 1);
441           if (m == NULL)
442                     return (-1);
443 
444           if (lba != 1)
445                     return (1);
446 
447           for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
448                     ent = (void*)(p + i * le32toh(hdr->hdr_entsz));
449                     if (gpt_uuid_is_nil(ent->ent_type))
450                               continue;
451 
452                     size = (off_t)(le64toh((uint64_t)ent->ent_lba_end) -
453                         le64toh((uint64_t)ent->ent_lba_start) + 1LL);
454                     if (gpt->verbose > 2) {
455                               char buf[128];
456                               gpt_uuid_snprintf(buf, sizeof(buf), "%s",
457                                   ent->ent_type);
458                               gpt_msg(gpt, "GPT partition: type=%s, start=%ju, "
459                                   "size=%ju", buf,
460                                   (uintmax_t)le64toh(ent->ent_lba_start),
461                                   (uintmax_t)size);
462                     }
463                     m = map_add(gpt, (off_t)le64toh((uint64_t)ent->ent_lba_start),
464                         size, MAP_TYPE_GPT_PART, ent, 0);
465                     if (m == NULL)
466                               return (-1);
467                     m->map_index = i + 1;
468           }
469           return (1);
470 
471  fail_ent:
472           free(p);
473 
474  fail_hdr:
475           free(hdr);
476           return (0);
477 }
478 
479 gpt_t
gpt_open(const char * dev,int flags,int verbose,off_t mediasz,u_int secsz,time_t timestamp)480 gpt_open(const char *dev, int flags, int verbose, off_t mediasz, u_int secsz,
481     time_t timestamp)
482 {
483           int mode, found;
484           off_t devsz;
485           gpt_t gpt;
486           unsigned int idx;
487 
488           if ((gpt = calloc(1, sizeof(*gpt))) == NULL) {
489                     if (!(flags & GPT_QUIET))
490                               warn("Cannot allocate `%s'", dev);
491                     return NULL;
492           }
493           gpt->flags = flags;
494           gpt->verbose = verbose;
495           gpt->mediasz = mediasz;
496           gpt->secsz = secsz;
497           gpt->timestamp = timestamp;
498           gpt->uuidgen = 0;
499 
500           mode = (gpt->flags & GPT_READONLY) ? O_RDONLY : O_RDWR|O_EXCL;
501 
502           gpt->fd = opendisk(dev, mode, gpt->device_name,
503               sizeof(gpt->device_name), 0);
504           if (gpt->fd == -1) {
505                     strlcpy(gpt->device_name, dev, sizeof(gpt->device_name));
506                     gpt_warn(gpt, "Cannot open");
507                     goto close;
508           }
509 
510           if (fstat(gpt->fd, &gpt->sb) == -1) {
511                     gpt_warn(gpt, "Cannot stat");
512                     goto close;
513           }
514 
515           if ((gpt->sb.st_mode & S_IFMT) != S_IFREG) {
516                     if (gpt->secsz == 0) {
517 #ifdef DIOCGSECTORSIZE
518                               if (ioctl(gpt->fd, DIOCGSECTORSIZE, &gpt->secsz) == -1) {
519                                         gpt_warn(gpt, "Cannot get sector size");
520                                         goto close;
521                               }
522 #endif
523                               if (gpt->secsz == 0) {
524                                         gpt_warnx(gpt, "Sector size can't be 0");
525                                         goto close;
526                               }
527                     }
528                     if (gpt->mediasz == 0) {
529 #ifdef DIOCGMEDIASIZE
530                               if (ioctl(gpt->fd, DIOCGMEDIASIZE, &gpt->mediasz) == -1) {
531                                         gpt_warn(gpt, "Cannot get media size");
532                                         goto close;
533                               }
534 #endif
535                               if (gpt->mediasz == 0) {
536                                         gpt_warnx(gpt, "Media size can't be 0");
537                                         goto close;
538                               }
539                     }
540           } else {
541                     gpt->flags |= GPT_FILE;
542                     if (gpt->secsz == 0)
543                               gpt->secsz = 512;   /* Fixed size for files. */
544                     if (gpt->mediasz == 0) {
545                               if (gpt->sb.st_size % gpt->secsz) {
546                                         gpt_warn(gpt, "Media size not a multiple of sector size (%u)\n", gpt->secsz);
547                                         errno = EINVAL;
548                                         goto close;
549                               }
550                               gpt->mediasz = gpt->sb.st_size;
551                     }
552                     gpt->flags |= GPT_NOSYNC;
553           }
554 
555           /*
556            * We require an absolute minimum of 6 sectors. One for the MBR,
557            * 2 for the GPT header, 2 for the GPT table and one to hold some
558            * user data. Let's catch this extreme border case here so that
559            * we don't have to worry about it later.
560            */
561           devsz = gpt->mediasz / gpt->secsz;
562           if (devsz < 6) {
563                     gpt_warnx(gpt, "Need 6 sectors, we have %ju",
564                         (uintmax_t)devsz);
565                     goto close;
566           }
567 
568           if (gpt->verbose) {
569                     gpt_msg(gpt, "mediasize=%ju; sectorsize=%u; blocks=%ju",
570                         (uintmax_t)gpt->mediasz, gpt->secsz, (uintmax_t)devsz);
571           }
572 
573           if (map_init(gpt, devsz) == -1)
574                     goto close;
575 
576           idx = 1;
577           if (gpt_mbr(gpt, 0LL, &idx, 0U) == -1)
578                     goto close;
579           if ((found = gpt_gpt(gpt, 1LL, 1)) == -1)
580                     goto close;
581 
582           if (found) {
583                     struct map *map;
584                     struct gpt_hdr *hdr;
585                     uint64_t lba;
586 
587                     /*
588                      * read secondary GPT from position stored in primary header
589                      * when possible
590                      */
591                     map = map_find(gpt, MAP_TYPE_PRI_GPT_HDR);
592                     hdr = map ? map->map_data : NULL;
593                     lba = le64toh(hdr->hdr_lba_alt);
594                     if (hdr && lba > 0 && lba < (uint64_t)devsz) {
595                               if (gpt_gpt(gpt, (off_t)lba, found) == -1)
596                                         goto close;
597                     }
598           } else {
599                     if (gpt_gpt(gpt, devsz - 1LL, found) == -1)
600                               goto close;
601           }
602 
603           return gpt;
604 
605  close:
606           if (gpt->fd != -1)
607                     close(gpt->fd);
608           gpt_warn(gpt, "No GPT found");
609           free(gpt);
610           return NULL;
611 }
612 
613 void
gpt_close(gpt_t gpt)614 gpt_close(gpt_t gpt)
615 {
616 
617           if (gpt == NULL)
618                     return;
619 
620           if (!(gpt->flags & GPT_MODIFIED) || !(gpt->flags & GPT_SYNC))
621                     goto out;
622 
623           if (!(gpt->flags & GPT_NOSYNC)) {
624 #ifdef DIOCMWEDGES
625                     int bits;
626                     if (ioctl(gpt->fd, DIOCMWEDGES, &bits) == -1)
627                               gpt_warn(gpt, "Can't update wedge information");
628                     else
629                               goto out;
630 #endif
631           }
632           if (!(gpt->flags & GPT_FILE))
633                     gpt_msg(gpt, "You need to run \"dkctl %s makewedges\""
634                         " for the changes to take effect\n", gpt->device_name);
635 
636 out:
637           close(gpt->fd);
638 }
639 
640 __printflike(2, 0)
641 static void
gpt_vwarnx(gpt_t gpt,const char * fmt,va_list ap,const char * e)642 gpt_vwarnx(gpt_t gpt, const char *fmt, va_list ap, const char *e)
643 {
644           if (gpt && (gpt->flags & GPT_QUIET))
645                     return;
646           fprintf(stderr, "%s: ", getprogname());
647           if (gpt)
648                     fprintf(stderr, "%s: ", gpt->device_name);
649           vfprintf(stderr, fmt, ap);
650           if (e)
651                     fprintf(stderr, " (%s)\n", e);
652           else
653                     fputc('\n', stderr);
654 }
655 
656 void
gpt_warnx(gpt_t gpt,const char * fmt,...)657 gpt_warnx(gpt_t gpt, const char *fmt, ...)
658 {
659           va_list ap;
660 
661           va_start(ap, fmt);
662           gpt_vwarnx(gpt, fmt, ap, NULL);
663           va_end(ap);
664 }
665 
666 void
gpt_warn(gpt_t gpt,const char * fmt,...)667 gpt_warn(gpt_t gpt, const char *fmt, ...)
668 {
669           va_list ap;
670 
671           va_start(ap, fmt);
672           gpt_vwarnx(gpt, fmt, ap, strerror(errno));
673           va_end(ap);
674 }
675 
676 void
gpt_msg(gpt_t gpt,const char * fmt,...)677 gpt_msg(gpt_t gpt, const char *fmt, ...)
678 {
679           va_list ap;
680 
681           if (gpt && (gpt->flags & GPT_QUIET))
682                     return;
683           if (gpt)
684                     printf("%s: ", gpt->device_name);
685           va_start(ap, fmt);
686           vprintf(fmt, ap);
687           va_end(ap);
688           printf("\n");
689 }
690 
691 struct gpt_hdr *
gpt_hdr(gpt_t gpt)692 gpt_hdr(gpt_t gpt)
693 {
694           gpt->gpt = map_find(gpt, MAP_TYPE_PRI_GPT_HDR);
695           if (gpt->gpt == NULL) {
696                     gpt_warnx(gpt, "No primary GPT header; run create or recover");
697                     return NULL;
698           }
699 
700           gpt->tpg = map_find(gpt, MAP_TYPE_SEC_GPT_HDR);
701           if (gpt->tpg == NULL) {
702                     gpt_warnx(gpt, "No secondary GPT header; run recover");
703                     return NULL;
704           }
705 
706           gpt->tbl = map_find(gpt, MAP_TYPE_PRI_GPT_TBL);
707           gpt->lbt = map_find(gpt, MAP_TYPE_SEC_GPT_TBL);
708           if (gpt->tbl == NULL || gpt->lbt == NULL) {
709                     gpt_warnx(gpt, "Corrupt maps, run recover");
710                     return NULL;
711           }
712 
713           return gpt->gpt->map_data;
714 }
715 
716 int
gpt_write_crc(gpt_t gpt,map_t map,map_t tbl)717 gpt_write_crc(gpt_t gpt, map_t map, map_t tbl)
718 {
719           struct gpt_hdr *hdr = map->map_data;
720 
721           hdr->hdr_crc_table = htole32(crc32(tbl->map_data,
722               le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz)));
723           hdr->hdr_crc_self = 0;
724           hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size)));
725 
726           if (gpt_write(gpt, map) == -1) {
727                     gpt_warn(gpt, "Error writing crc map");
728                     return -1;
729           }
730 
731           if (gpt_write(gpt, tbl) == -1) {
732                     gpt_warn(gpt, "Error writing crc table");
733                     return -1;
734           }
735 
736           return 0;
737 }
738 
739 int
gpt_write_primary(gpt_t gpt)740 gpt_write_primary(gpt_t gpt)
741 {
742           return gpt_write_crc(gpt, gpt->gpt, gpt->tbl);
743 }
744 
745 
746 int
gpt_write_backup(gpt_t gpt)747 gpt_write_backup(gpt_t gpt)
748 {
749           return gpt_write_crc(gpt, gpt->tpg, gpt->lbt);
750 }
751 
752 void
gpt_create_pmbr_part(struct mbr_part * part,off_t last,int active)753 gpt_create_pmbr_part(struct mbr_part *part, off_t last, int active)
754 {
755           part->part_flag = active ? 0x80 : 0;
756           part->part_shd = 0x00;
757           part->part_ssect = 0x02;
758           part->part_scyl = 0x00;
759           part->part_typ = MBR_PTYPE_PMBR;
760           part->part_ehd = 0xfe;
761           part->part_esect = 0xff;
762           part->part_ecyl = 0xff;
763           part->part_start_lo = htole16(1);
764           if (last > 0xffffffff) {
765                     part->part_size_lo = htole16(0xffff);
766                     part->part_size_hi = htole16(0xffff);
767           } else {
768                     part->part_size_lo = htole16((uint16_t)last);
769                     part->part_size_hi = htole16((uint16_t)((uint64_t)last >> 16));
770           }
771 }
772 
773 struct gpt_ent *
gpt_ent(map_t map,map_t tbl,unsigned int i)774 gpt_ent(map_t map, map_t tbl, unsigned int i)
775 {
776           struct gpt_hdr *hdr = map->map_data;
777           return (void *)((char *)tbl->map_data + i * le32toh(hdr->hdr_entsz));
778 }
779 
780 struct gpt_ent *
gpt_ent_primary(gpt_t gpt,unsigned int i)781 gpt_ent_primary(gpt_t gpt, unsigned int i)
782 {
783           return gpt_ent(gpt->gpt, gpt->tbl, i);
784 }
785 
786 struct gpt_ent *
gpt_ent_backup(gpt_t gpt,unsigned int i)787 gpt_ent_backup(gpt_t gpt, unsigned int i)
788 {
789           return gpt_ent(gpt->tpg, gpt->lbt, i);
790 }
791 
792 int
gpt_usage(const char * prefix,const struct gpt_cmd * cmd)793 gpt_usage(const char *prefix, const struct gpt_cmd *cmd)
794 {
795           const char **a = cmd->help;
796           size_t hlen = cmd->hlen;
797           size_t i;
798 
799           if (prefix == NULL) {
800                     const char *pname = getprogname();
801                     const char *d1, *d2, *d = " <device>";
802                     int len = (int)strlen(pname);
803                     if (strcmp(pname, "gpt") == 0) {
804                               d1 = "";
805                               d2 = d;
806                     } else {
807                               d2 = "";
808                               d1 = d;
809                     }
810                     fprintf(stderr, "Usage: %s%s %s %s%s\n", pname,
811                         d1, cmd->name, a[0], d2);
812                     for (i = 1; i < hlen; i++) {
813                               fprintf(stderr,
814                                   "       %*s%s %s %s%s\n", len, "",
815                                   d1, cmd->name, a[i], d2);
816                     }
817           } else {
818                     for (i = 0; i < hlen; i++)
819                         fprintf(stderr, "%s%s %s\n", prefix, cmd->name, a[i]);
820           }
821           return -1;
822 }
823 
824 off_t
gpt_last(gpt_t gpt)825 gpt_last(gpt_t gpt)
826 {
827           return gpt->mediasz / gpt->secsz - 1LL;
828 }
829 
830 off_t
gpt_create(gpt_t gpt,off_t last,u_int parts,int primary_only)831 gpt_create(gpt_t gpt, off_t last, u_int parts, int primary_only)
832 {
833           off_t blocks, lastoff;
834           map_t map;
835           struct gpt_hdr *hdr;
836           struct gpt_ent *ent;
837           unsigned int i;
838           void *p;
839 
840           if (map_find(gpt, MAP_TYPE_PRI_GPT_HDR) != NULL ||
841               map_find(gpt, MAP_TYPE_SEC_GPT_HDR) != NULL) {
842                     gpt_warnx(gpt, "Device already contains a GPT, "
843                         "destroy it first");
844                     return -1;
845           }
846 
847           /* Get the amount of free space after the MBR */
848           blocks = map_free(gpt, 1LL, 0LL);
849           if (blocks == 0LL) {
850                     gpt_warnx(gpt, "No room for the GPT header");
851                     return -1;
852           }
853 
854           /* Don't create more than parts entries. */
855           if ((uint64_t)(blocks - 1) * gpt->secsz >
856               parts * sizeof(struct gpt_ent)) {
857                     blocks = (off_t)((parts * sizeof(struct gpt_ent)) / gpt->secsz);
858                     if ((parts * sizeof(struct gpt_ent)) % gpt->secsz)
859                               blocks++;
860                     blocks++;           /* Don't forget the header itself */
861           }
862 
863           /* Never cross the median of the device. */
864           lastoff = (off_t)(((uint64_t)last + 1LL) >> 1);
865           if ((blocks + 1LL) > lastoff)
866                     blocks = lastoff - 1LL;
867 
868           /*
869            * Get the amount of free space at the end of the device and
870            * calculate the size for the GPT structures.
871            */
872           map = map_last(gpt);
873           if (map->map_type != MAP_TYPE_UNUSED) {
874                     gpt_warnx(gpt, "No room for the backup header");
875                     return -1;
876           }
877 
878           if (map->map_size < blocks)
879                     blocks = map->map_size;
880           if (blocks == 1LL) {
881                     gpt_warnx(gpt, "No room for the GPT table");
882                     return -1;
883           }
884 
885           blocks--;           /* Number of blocks in the GPT table. */
886 
887           if (gpt_add_hdr(gpt, MAP_TYPE_PRI_GPT_HDR, 1) == -1)
888                     return -1;
889 
890           if ((p = calloc((size_t)blocks, gpt->secsz)) == NULL) {
891                     gpt_warnx(gpt, "Can't allocate the primary GPT table");
892                     return -1;
893           }
894           if ((gpt->tbl = map_add(gpt, 2LL, blocks,
895               MAP_TYPE_PRI_GPT_TBL, p, 1)) == NULL) {
896                     free(p);
897                     gpt_warnx(gpt, "Can't add the primary GPT table");
898                     return -1;
899           }
900 
901           hdr = gpt->gpt->map_data;
902           memcpy(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig));
903 
904           /*
905            * XXX struct gpt_hdr is not a multiple of 8 bytes in size and thus
906            * contains padding we must not include in the size.
907            */
908           hdr->hdr_revision = htole32(GPT_HDR_REVISION);
909           hdr->hdr_size = htole32(GPT_HDR_SIZE);
910           hdr->hdr_lba_self = htole64((uint64_t)gpt->gpt->map_start);
911           hdr->hdr_lba_alt = htole64((uint64_t)last);
912           hdr->hdr_lba_start = htole64((uint64_t)(gpt->tbl->map_start + blocks));
913           hdr->hdr_lba_end = htole64((uint64_t)(last - blocks - 1LL));
914           if (gpt_uuid_generate(gpt, hdr->hdr_guid) == -1)
915                     return -1;
916           hdr->hdr_lba_table = htole64((uint64_t)(gpt->tbl->map_start));
917           hdr->hdr_entries = htole32((uint32_t)(((uint64_t)blocks * gpt->secsz) /
918               sizeof(struct gpt_ent)));
919           if (le32toh(hdr->hdr_entries) > parts)
920                     hdr->hdr_entries = htole32(parts);
921           hdr->hdr_entsz = htole32(sizeof(struct gpt_ent));
922 
923           ent = gpt->tbl->map_data;
924           for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
925                     if (gpt_uuid_generate(gpt, ent[i].ent_guid) == -1)
926                               return -1;
927           }
928 
929           /*
930            * Create backup GPT if the user didn't suppress it.
931            */
932           if (primary_only)
933                     return last;
934 
935           if (gpt_add_hdr(gpt, MAP_TYPE_SEC_GPT_HDR, last) == -1)
936                     return -1;
937 
938           if ((gpt->lbt = map_add(gpt, last - blocks, blocks,
939               MAP_TYPE_SEC_GPT_TBL, gpt->tbl->map_data, 0)) == NULL) {
940                     gpt_warnx(gpt, "Can't add the secondary GPT table");
941                     return -1;
942           }
943 
944           memcpy(gpt->tpg->map_data, gpt->gpt->map_data, gpt->secsz);
945 
946           hdr = gpt->tpg->map_data;
947           hdr->hdr_lba_self = htole64((uint64_t)gpt->tpg->map_start);
948           hdr->hdr_lba_alt = htole64((uint64_t)gpt->gpt->map_start);
949           hdr->hdr_lba_table = htole64((uint64_t)gpt->lbt->map_start);
950           return last;
951 }
952 
953 static int
gpt_size_get(gpt_t gpt,off_t * size)954 gpt_size_get(gpt_t gpt, off_t *size)
955 {
956           off_t sectors;
957           int64_t human_num;
958           char *p;
959 
960           if (*size > 0)
961                     return -1;
962           sectors = strtoll(optarg, &p, 10);
963           if (sectors < 1)
964                     return -1;
965           if (*p == '\0' || ((*p == 's' || *p == 'S') && p[1] == '\0')) {
966                     *size = sectors * gpt->secsz;
967                     return 0;
968           }
969           if ((*p == 'b' || *p == 'B') && p[1] == '\0') {
970                     *size = sectors;
971                     return 0;
972           }
973           if (dehumanize_number(optarg, &human_num) < 0)
974                     return -1;
975           *size = human_num;
976           return 0;
977 }
978 
979 int
gpt_human_get(gpt_t gpt,off_t * human)980 gpt_human_get(gpt_t gpt, off_t *human)
981 {
982           int64_t human_num;
983 
984           if (*human > 0) {
985                     gpt_warn(gpt, "Already set to %jd new `%s'", (intmax_t)*human,
986                         optarg);
987                     return -1;
988           }
989           if (dehumanize_number(optarg, &human_num) < 0) {
990                     gpt_warn(gpt, "Bad number `%s'", optarg);
991                     return -1;
992           }
993           *human = human_num;
994           if (*human < 1) {
995                     gpt_warn(gpt, "Number `%s' < 1", optarg);
996                     return -1;
997           }
998           return 0;
999 }
1000 
1001 int
gpt_add_find(gpt_t gpt,struct gpt_find * find,int ch)1002 gpt_add_find(gpt_t gpt, struct gpt_find *find, int ch)
1003 {
1004           switch (ch) {
1005           case 'a':
1006                     if (find->all > 0) {
1007                               gpt_warn(gpt, "-a is already set");
1008                               return -1;
1009                     }
1010                     find->all = 1;
1011                     break;
1012           case 'b':
1013                     if (gpt_human_get(gpt, &find->block) == -1)
1014                               return -1;
1015                     break;
1016           case 'i':
1017                     if (gpt_uint_get(gpt, &find->entry) == -1)
1018                               return -1;
1019                     break;
1020           case 'L':
1021                     if (gpt_name_get(gpt, &find->label) == -1)
1022                               return -1;
1023                     break;
1024           case 's':
1025                     if (gpt_size_get(gpt, &find->size) == -1)
1026                               return -1;
1027                     break;
1028           case 't':
1029                     if (!gpt_uuid_is_nil(find->type))
1030                               return -1;
1031                     if (gpt_uuid_parse(optarg, find->type) != 0)
1032                               return -1;
1033                     break;
1034           default:
1035                     gpt_warn(gpt, "Unknown find option `%c'", ch);
1036                     return -1;
1037           }
1038           return 0;
1039 }
1040 
1041 int
gpt_change_ent(gpt_t gpt,const struct gpt_find * find,void (* cfn)(struct gpt_ent *,void *,int),void * v)1042 gpt_change_ent(gpt_t gpt, const struct gpt_find *find,
1043     void (*cfn)(struct gpt_ent *, void *, int), void *v)
1044 {
1045           map_t m;
1046           struct gpt_ent *ent;
1047           unsigned int i;
1048           uint8_t utfbuf[__arraycount(ent->ent_name) * 3 + 1];
1049 
1050           if (!find->all ^
1051               (find->block > 0 || find->entry > 0 || find->label != NULL
1052               || find->size > 0 || !gpt_uuid_is_nil(find->type)))
1053                     return -1;
1054 
1055           if (gpt_hdr(gpt) == NULL)
1056                     return -1;
1057 
1058           /* Relabel all matching entries in the map. */
1059           for (m = map_first(gpt); m != NULL; m = m->map_next) {
1060                     if (m->map_type != MAP_TYPE_GPT_PART || m->map_index < 1)
1061                               continue;
1062                     if (find->entry > 0 && find->entry != m->map_index)
1063                               continue;
1064                     if (find->block > 0 && find->block != m->map_start)
1065                               continue;
1066                     if (find->size > 0 && find->size != m->map_size)
1067                               continue;
1068 
1069                     i = m->map_index - 1;
1070 
1071                     ent = gpt_ent_primary(gpt, i);
1072                     if (find->label != NULL) {
1073                               utf16_to_utf8(ent->ent_name,
1074                                   __arraycount(ent->ent_name),
1075                                   utfbuf, __arraycount(utfbuf));
1076                               if (strcmp((char *)find->label, (char *)utfbuf) != 0)
1077                                         continue;
1078                     }
1079 
1080                     if (!gpt_uuid_is_nil(find->type) &&
1081                         !gpt_uuid_equal(find->type, ent->ent_type))
1082                               continue;
1083 
1084                     /* Change the primary entry. */
1085                     (*cfn)(ent, v, 0);
1086 
1087                     if (gpt_write_primary(gpt) == -1)
1088                               return -1;
1089 
1090                     ent = gpt_ent_backup(gpt, i);
1091                     /* Change the secondary entry. */
1092                     (*cfn)(ent, v, 1);
1093 
1094                     if (gpt_write_backup(gpt) == -1)
1095                               return -1;
1096 
1097                     gpt_msg(gpt, "Partition %d %s", m->map_index, find->msg);
1098           }
1099           return 0;
1100 }
1101 
1102 int
gpt_change_hdr(gpt_t gpt,const struct gpt_find * find,void (* cfn)(struct gpt_hdr *,void *,int),void * v)1103 gpt_change_hdr(gpt_t gpt, const struct gpt_find *find,
1104     void (*cfn)(struct gpt_hdr *, void *, int), void *v)
1105 {
1106           struct gpt_hdr *hdr;
1107 
1108           if ((hdr = gpt_hdr(gpt)) == NULL)
1109                     return -1;
1110 
1111           /* Change the primary header. */
1112           (*cfn)(hdr, v, 0);
1113 
1114           if (gpt_write_primary(gpt) == -1)
1115                     return -1;
1116 
1117           hdr = gpt->tpg->map_data;
1118           /* Change the secondary header. */
1119           (*cfn)(hdr, v, 1);
1120 
1121           if (gpt_write_backup(gpt) == -1)
1122                     return -1;
1123 
1124           gpt_msg(gpt, "Header %s", find->msg);
1125 
1126           return 0;
1127 }
1128 
1129 int
gpt_add_ais(gpt_t gpt,off_t * alignment,u_int * entry,off_t * size,int ch)1130 gpt_add_ais(gpt_t gpt, off_t *alignment, u_int *entry, off_t *size, int ch)
1131 {
1132           switch (ch) {
1133           case 'a':
1134                     if (gpt_human_get(gpt, alignment) == -1)
1135                               return -1;
1136                     return 0;
1137           case 'i':
1138                     if (gpt_uint_get(gpt, entry) == -1)
1139                               return -1;
1140                     return 0;
1141           case 's':
1142                     if (gpt_size_get(gpt, size) == -1)
1143                               return -1;
1144                     return 0;
1145           default:
1146                     gpt_warn(gpt, "Unknown alignment/index/size option `%c'", ch);
1147                     return -1;
1148           }
1149 }
1150 
1151 off_t
gpt_check_ais(gpt_t gpt,off_t alignment,u_int entry,off_t size)1152 gpt_check_ais(gpt_t gpt, off_t alignment, u_int entry, off_t size)
1153 {
1154           if (entry == 0) {
1155                     gpt_warnx(gpt, "Entry not specified");
1156                     return -1;
1157           }
1158           if (alignment % gpt->secsz != 0) {
1159                     gpt_warnx(gpt, "Alignment (%#jx) must be a multiple of "
1160                         "sector size (%#x)", (uintmax_t)alignment, gpt->secsz);
1161                     return -1;
1162           }
1163 
1164           if (size % gpt->secsz != 0) {
1165                     gpt_warnx(gpt, "Size (%#jx) must be a multiple of "
1166                         "sector size (%#x)", (uintmax_t)size, gpt->secsz);
1167                     return -1;
1168           }
1169           if (size > 0)
1170                     return size / gpt->secsz;
1171           return 0;
1172 }
1173 
1174 static const struct nvd {
1175           const char *name;
1176           uint64_t mask;
1177           const char *description;
1178 } gpt_attr[] = {
1179           {
1180                     "biosboot",
1181                     GPT_ENT_ATTR_LEGACY_BIOS_BOOTABLE,
1182                     "Legacy BIOS boot partition",
1183           },
1184           {
1185                     "bootme",
1186                     GPT_ENT_ATTR_BOOTME,
1187                     "Bootable partition",
1188           },
1189           {
1190                     "bootfailed",
1191                     GPT_ENT_ATTR_BOOTFAILED,
1192                     "Partition that marked bootonce failed to boot",
1193           },
1194           {
1195                     "bootonce",
1196                     GPT_ENT_ATTR_BOOTONCE,
1197                     "Attempt to boot this partition only once",
1198           },
1199           {
1200                     "noblockio",
1201                     GPT_ENT_ATTR_NO_BLOCK_IO_PROTOCOL,
1202                     "UEFI won't recognize file system for block I/O",
1203           },
1204           {
1205                     "required",
1206                     GPT_ENT_ATTR_REQUIRED_PARTITION,
1207                     "Partition required for platform to function",
1208           },
1209 };
1210 
1211 int
gpt_attr_get(gpt_t gpt,uint64_t * attributes)1212 gpt_attr_get(gpt_t gpt, uint64_t *attributes)
1213 {
1214           size_t i;
1215           int rv = 0;
1216           char *ptr;
1217 
1218           *attributes = 0;
1219 
1220           for (ptr = strtok(optarg, ","); ptr; ptr = strtok(NULL, ",")) {
1221                     for (i = 0; i < __arraycount(gpt_attr); i++)
1222                               if (strcmp(gpt_attr[i].name, ptr) == 0)
1223                                         break;
1224                     if (i == __arraycount(gpt_attr)) {
1225                               gpt_warnx(gpt, "Unrecognized attribute `%s'", ptr);
1226                               rv = -1;
1227                     } else
1228                               *attributes |= gpt_attr[i].mask;
1229           }
1230           return rv;
1231 }
1232 
1233 void
gpt_attr_help(const char * prefix)1234 gpt_attr_help(const char *prefix)
1235 {
1236           size_t i;
1237 
1238           for (i = 0; i < __arraycount(gpt_attr); i++)
1239                     printf("%s%10.10s\t%s\n", prefix, gpt_attr[i].name,
1240                         gpt_attr[i].description);
1241 }
1242 
1243 const char *
gpt_attr_list(char * buf,size_t len,uint64_t attributes)1244 gpt_attr_list(char *buf, size_t len, uint64_t attributes)
1245 {
1246           size_t i;
1247           /*
1248            * a uint64_t (attributes) has at most 16 hex digits
1249            * in its representation, add 2 for "0x", and 2 more
1250            * for surrounding [ ], plus one for a trailing \0,
1251            * and we need 21 bytes, round that up to 24
1252            */
1253           char xbuf[24];
1254 
1255           strlcpy(buf, "", len);
1256 
1257           for (i = 0; i < __arraycount(gpt_attr); i++) {
1258                     /*
1259                      * if the attribute is specified in one of bits
1260                      * 48..63, it should depend upon the defining
1261                      * partition type for that attribute.   Currently
1262                      * we have no idea what that is, so...
1263                      *
1264                      * Also note that for some partition types, these
1265                      * fields are not a single bit boolean, but several
1266                      * bits to form a numeric value.  That we could handle.
1267                      */
1268 
1269                     if (attributes & gpt_attr[i].mask) {
1270                               strlcat(buf, buf[0] ? ", " : "", len);
1271                               strlcat(buf, gpt_attr[i].name, len);
1272 #if 0
1273           /*
1274            * there are none currently defined, so this is untestable
1275            * (it does build however).
1276            */
1277                               if (gpt_attr[i].mask & (gpt_attr[i].mask - 1)) {
1278                                         /* This only happens in bits 46..63 */
1279 
1280                                         /*
1281                                          * xbuf is big enough for "=65535\0"
1282                                          * which is the biggest possible value
1283                                          */
1284                                         snprintf(xbuf, sizeof xbuf, "=%ju",
1285                                             (uintmax_t) (
1286                                               (attributes & gpt_attr[i].mask) >>
1287                                               (ffs((int)(gpt_attr[i].mask >> 48)) + 47)
1288                                             ));
1289 
1290                                         strlcat(buf, xbuf, len);
1291                               }
1292 #endif
1293                               attributes &=~ gpt_attr[i].mask;
1294                     }
1295           }
1296 
1297           if (attributes != 0) {
1298                     snprintf(xbuf, sizeof xbuf, "[%#jx]", (uintmax_t)attributes);
1299                     strlcat(buf, buf[0] ? ", " : "", len);
1300                     strlcat(buf, xbuf, len);
1301           }
1302 
1303           return buf;
1304 }
1305 
1306 int
gpt_attr_update(gpt_t gpt,u_int entry,uint64_t set,uint64_t clr)1307 gpt_attr_update(gpt_t gpt, u_int entry, uint64_t set, uint64_t clr)
1308 {
1309           struct gpt_hdr *hdr;
1310           struct gpt_ent *ent;
1311           unsigned int i;
1312 
1313           if (entry == 0 || (set == 0 && clr == 0)) {
1314                     gpt_warnx(gpt, "Nothing to set");
1315                     return -1;
1316           }
1317 
1318           if ((hdr = gpt_hdr(gpt)) == NULL)
1319                     return -1;
1320 
1321           if (entry > le32toh(hdr->hdr_entries)) {
1322                     gpt_warnx(gpt, "Index %u out of range (%u max)",
1323                         entry, le32toh(hdr->hdr_entries));
1324                     return -1;
1325           }
1326 
1327           i = entry - 1;
1328           ent = gpt_ent_primary(gpt, i);
1329           if (gpt_uuid_is_nil(ent->ent_type)) {
1330                     gpt_warnx(gpt, "Entry at index %u is unused", entry);
1331                     return -1;
1332           }
1333 
1334           ent->ent_attr &= ~clr;
1335           ent->ent_attr |= set;
1336 
1337           if (gpt_write_primary(gpt) == -1)
1338                     return -1;
1339 
1340           ent = gpt_ent_backup(gpt, i);
1341           ent->ent_attr &= ~clr;
1342           ent->ent_attr |= set;
1343 
1344           if (gpt_write_backup(gpt) == -1)
1345                     return -1;
1346           gpt_msg(gpt, "Partition %d attributes updated", entry);
1347           return 0;
1348 }
1349 
1350 int
gpt_uint_get(gpt_t gpt,u_int * entry)1351 gpt_uint_get(gpt_t gpt, u_int *entry)
1352 {
1353           char *p;
1354           if (*entry > 0)
1355                     return -1;
1356           *entry = (u_int)strtoul(optarg, &p, 10);
1357           if (*p != 0 || *entry < 1) {
1358                     gpt_warn(gpt, "Bad number `%s'", optarg);
1359                     return -1;
1360           }
1361           return 0;
1362 }
1363 int
gpt_uuid_get(gpt_t gpt,gpt_uuid_t * uuid)1364 gpt_uuid_get(gpt_t gpt, gpt_uuid_t *uuid)
1365 {
1366           if (!gpt_uuid_is_nil(*uuid))
1367                     return -1;
1368           if (gpt_uuid_parse(optarg, *uuid) != 0) {
1369                     gpt_warnx(gpt, "Can't parse uuid/type `%s'", optarg);
1370                     return -1;
1371           }
1372           return 0;
1373 }
1374 
1375 int
gpt_name_get(gpt_t gpt,void * v)1376 gpt_name_get(gpt_t gpt, void *v)
1377 {
1378           char **name = v;
1379           if (*name != NULL)
1380                     return -1;
1381           *name = strdup(optarg);
1382           if (*name == NULL) {
1383                     gpt_warn(gpt, "Can't copy string");
1384                     return -1;
1385           }
1386           return 0;
1387 }
1388 
1389 void
gpt_show_num(const char * prompt,uintmax_t num)1390 gpt_show_num(const char *prompt, uintmax_t num)
1391 {
1392 #ifdef HN_AUTOSCALE
1393           char human_num[5];
1394           if (humanize_number(human_num, 5, (int64_t)num ,
1395               "", HN_AUTOSCALE, HN_NOSPACE|HN_B) < 0)
1396                     human_num[0] = '\0';
1397 #endif
1398           printf("%s: %ju", prompt, num);
1399 #ifdef HN_AUTOSCALE
1400           if (human_num[0] != '\0')
1401                     printf(" (%s)", human_num);
1402 #endif
1403           printf("\n");
1404 }
1405 
1406 int
gpt_add_hdr(gpt_t gpt,int type,off_t loc)1407 gpt_add_hdr(gpt_t gpt, int type, off_t loc)
1408 {
1409           void *p;
1410           map_t *t;
1411           const char *msg;
1412 
1413           switch (type) {
1414           case MAP_TYPE_PRI_GPT_HDR:
1415                     t = &gpt->gpt;
1416                     msg = "primary";
1417                     break;
1418           case MAP_TYPE_SEC_GPT_HDR:
1419                     t = &gpt->tpg;
1420                     msg = "secondary";
1421                     break;
1422           default:
1423                     gpt_warnx(gpt, "Unknown GPT header type %d", type);
1424                     return -1;
1425           }
1426 
1427           if ((p = calloc(1, gpt->secsz)) == NULL) {
1428                     gpt_warn(gpt, "Error allocating %s GPT header", msg);
1429                     return -1;
1430           }
1431 
1432           *t = map_add(gpt, loc, 1LL, type, p, 1);
1433           if (*t == NULL) {
1434                     gpt_warn(gpt, "Error adding %s GPT header", msg);
1435                     free(p);
1436                     return -1;
1437           }
1438           return 0;
1439 }
1440