15819a8c0Schristos /*- 25819a8c0Schristos * Copyright (c) 2002 Marcel Moolenaar 35819a8c0Schristos * All rights reserved. 45819a8c0Schristos * 55819a8c0Schristos * Redistribution and use in source and binary forms, with or without 65819a8c0Schristos * modification, are permitted provided that the following conditions 75819a8c0Schristos * are met: 85819a8c0Schristos * 95819a8c0Schristos * 1. Redistributions of source code must retain the above copyright 105819a8c0Schristos * notice, this list of conditions and the following disclaimer. 115819a8c0Schristos * 2. Redistributions in binary form must reproduce the above copyright 125819a8c0Schristos * notice, this list of conditions and the following disclaimer in the 135819a8c0Schristos * documentation and/or other materials provided with the distribution. 145819a8c0Schristos * 155819a8c0Schristos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 165819a8c0Schristos * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 175819a8c0Schristos * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 185819a8c0Schristos * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 195819a8c0Schristos * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 205819a8c0Schristos * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 215819a8c0Schristos * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 225819a8c0Schristos * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 235819a8c0Schristos * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 245819a8c0Schristos * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 255819a8c0Schristos * 265819a8c0Schristos * CRC32 code derived from work by Gary S. Brown. 275819a8c0Schristos */ 285819a8c0Schristos 29a50708a1Schristos #if HAVE_NBTOOL_CONFIG_H 30a50708a1Schristos #include "nbtool_config.h" 31a50708a1Schristos #endif 32a50708a1Schristos 335819a8c0Schristos #include <sys/cdefs.h> 349b522365Schristos #ifdef __FBSDID 355819a8c0Schristos __FBSDID("$FreeBSD: src/sbin/gpt/gpt.c,v 1.16 2006/07/07 02:44:23 marcel Exp $"); 369b522365Schristos #endif 379b522365Schristos #ifdef __RCSID 38*10edc71fSmlelstv __RCSID("$NetBSD: gpt.c,v 1.90 2024/10/20 08:21:30 mlelstv Exp $"); 399b522365Schristos #endif 405819a8c0Schristos 415819a8c0Schristos #include <sys/param.h> 425819a8c0Schristos #include <sys/types.h> 435819a8c0Schristos #include <sys/stat.h> 449b522365Schristos #include <sys/ioctl.h> 4516550bc8Sjakllsch #include <sys/bootblock.h> 465819a8c0Schristos 475819a8c0Schristos #include <err.h> 485819a8c0Schristos #include <errno.h> 495819a8c0Schristos #include <fcntl.h> 505819a8c0Schristos #include <paths.h> 515819a8c0Schristos #include <stddef.h> 52ca4e0dcdSchristos #include <stdarg.h> 535819a8c0Schristos #include <stdio.h> 545819a8c0Schristos #include <stdlib.h> 555819a8c0Schristos #include <string.h> 565819a8c0Schristos #include <unistd.h> 579b522365Schristos #include <ctype.h> 585819a8c0Schristos 595819a8c0Schristos #include "map.h" 605819a8c0Schristos #include "gpt.h" 610b43d398Schristos #include "gpt_private.h" 625819a8c0Schristos 635819a8c0Schristos static uint32_t crc32_tab[] = { 645819a8c0Schristos 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 655819a8c0Schristos 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 665819a8c0Schristos 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 675819a8c0Schristos 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 685819a8c0Schristos 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 695819a8c0Schristos 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 705819a8c0Schristos 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 715819a8c0Schristos 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 725819a8c0Schristos 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 735819a8c0Schristos 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 745819a8c0Schristos 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 755819a8c0Schristos 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 765819a8c0Schristos 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 775819a8c0Schristos 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 785819a8c0Schristos 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 795819a8c0Schristos 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 805819a8c0Schristos 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 815819a8c0Schristos 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 825819a8c0Schristos 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 835819a8c0Schristos 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 845819a8c0Schristos 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 855819a8c0Schristos 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 865819a8c0Schristos 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 875819a8c0Schristos 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 885819a8c0Schristos 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 895819a8c0Schristos 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 905819a8c0Schristos 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 915819a8c0Schristos 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 925819a8c0Schristos 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 935819a8c0Schristos 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 945819a8c0Schristos 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 955819a8c0Schristos 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 965819a8c0Schristos 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 975819a8c0Schristos 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 985819a8c0Schristos 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 995819a8c0Schristos 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 1005819a8c0Schristos 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 1015819a8c0Schristos 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 1025819a8c0Schristos 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 1035819a8c0Schristos 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 1045819a8c0Schristos 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 1055819a8c0Schristos 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 1065819a8c0Schristos 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d 1075819a8c0Schristos }; 1085819a8c0Schristos 1095819a8c0Schristos uint32_t 1105819a8c0Schristos crc32(const void *buf, size_t size) 1115819a8c0Schristos { 1125819a8c0Schristos const uint8_t *p; 1135819a8c0Schristos uint32_t crc; 1145819a8c0Schristos 1155819a8c0Schristos p = buf; 1165819a8c0Schristos crc = ~0U; 1175819a8c0Schristos 1185819a8c0Schristos while (size--) 1195819a8c0Schristos crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8); 1205819a8c0Schristos 1215819a8c0Schristos return crc ^ ~0U; 1225819a8c0Schristos } 1235819a8c0Schristos 124d6862190Schristos /* 125d6862190Schristos * Produce a NUL-terminated utf-8 string from the non-NUL-terminated 126d6862190Schristos * utf16 string. 127d6862190Schristos */ 128cdf86847Schristos void 129d6862190Schristos utf16_to_utf8(const uint16_t *s16, size_t s16len, uint8_t *s8, size_t s8len) 1305819a8c0Schristos { 131d6862190Schristos size_t s8idx, s16idx; 1325819a8c0Schristos uint32_t utfchar; 1335819a8c0Schristos unsigned int c; 1345819a8c0Schristos 135d6862190Schristos for (s16idx = 0; s16idx < s16len; s16idx++) 136d6862190Schristos if (s16[s16idx] == 0) 137d6862190Schristos break; 138d6862190Schristos 139d6862190Schristos s16len = s16idx; 1405819a8c0Schristos s8idx = s16idx = 0; 1415819a8c0Schristos while (s16idx < s16len) { 1425819a8c0Schristos utfchar = le16toh(s16[s16idx++]); 1435819a8c0Schristos if ((utfchar & 0xf800) == 0xd800) { 1445819a8c0Schristos c = le16toh(s16[s16idx]); 1455819a8c0Schristos if ((utfchar & 0x400) != 0 || (c & 0xfc00) != 0xdc00) 1465819a8c0Schristos utfchar = 0xfffd; 1475819a8c0Schristos else 1485819a8c0Schristos s16idx++; 1495819a8c0Schristos } 1505819a8c0Schristos if (utfchar < 0x80) { 151cdf86847Schristos if (s8idx + 1 >= s8len) 152cdf86847Schristos break; 1535c1ccc6eSchristos s8[s8idx++] = (uint8_t)utfchar; 1545819a8c0Schristos } else if (utfchar < 0x800) { 155cdf86847Schristos if (s8idx + 2 >= s8len) 156cdf86847Schristos break; 1575c1ccc6eSchristos s8[s8idx++] = (uint8_t)(0xc0 | (utfchar >> 6)); 1585c1ccc6eSchristos s8[s8idx++] = (uint8_t)(0x80 | (utfchar & 0x3f)); 1595819a8c0Schristos } else if (utfchar < 0x10000) { 160cdf86847Schristos if (s8idx + 3 >= s8len) 161cdf86847Schristos break; 1625c1ccc6eSchristos s8[s8idx++] = (uint8_t)(0xe0 | (utfchar >> 12)); 1635c1ccc6eSchristos s8[s8idx++] = (uint8_t)(0x80 | ((utfchar >> 6) & 0x3f)); 1645c1ccc6eSchristos s8[s8idx++] = (uint8_t)(0x80 | (utfchar & 0x3f)); 1655819a8c0Schristos } else if (utfchar < 0x200000) { 166cdf86847Schristos if (s8idx + 4 >= s8len) 167cdf86847Schristos break; 1685c1ccc6eSchristos s8[s8idx++] = (uint8_t)(0xf0 | (utfchar >> 18)); 1695c1ccc6eSchristos s8[s8idx++] = (uint8_t)(0x80 | ((utfchar >> 12) & 0x3f)); 1705c1ccc6eSchristos s8[s8idx++] = (uint8_t)(0x80 | ((utfchar >> 6) & 0x3f)); 1715c1ccc6eSchristos s8[s8idx++] = (uint8_t)(0x80 | (utfchar & 0x3f)); 1725819a8c0Schristos } 1735819a8c0Schristos } 174cdf86847Schristos s8[s8idx] = 0; 1755819a8c0Schristos } 1765819a8c0Schristos 177d6862190Schristos /* 178d6862190Schristos * Produce a non-NUL-terminated utf-16 string from the NUL-terminated 179d6862190Schristos * utf8 string. 180d6862190Schristos */ 1815819a8c0Schristos void 1825819a8c0Schristos utf8_to_utf16(const uint8_t *s8, uint16_t *s16, size_t s16len) 1835819a8c0Schristos { 1845819a8c0Schristos size_t s16idx, s8idx, s8len; 1859b522365Schristos uint32_t utfchar = 0; 1865819a8c0Schristos unsigned int c, utfbytes; 1875819a8c0Schristos 1885819a8c0Schristos s8len = 0; 1895819a8c0Schristos while (s8[s8len++] != 0) 1905819a8c0Schristos ; 1915819a8c0Schristos s8idx = s16idx = 0; 1925819a8c0Schristos utfbytes = 0; 1935819a8c0Schristos do { 1945819a8c0Schristos c = s8[s8idx++]; 1955819a8c0Schristos if ((c & 0xc0) != 0x80) { 1965819a8c0Schristos /* Initial characters. */ 1975819a8c0Schristos if (utfbytes != 0) { 1985819a8c0Schristos /* Incomplete encoding. */ 19911ded42cSmatt s16[s16idx++] = htole16(0xfffd); 2005819a8c0Schristos if (s16idx == s16len) { 2015819a8c0Schristos s16[--s16idx] = 0; 2025819a8c0Schristos return; 2035819a8c0Schristos } 2045819a8c0Schristos } 2055819a8c0Schristos if ((c & 0xf8) == 0xf0) { 2065819a8c0Schristos utfchar = c & 0x07; 2075819a8c0Schristos utfbytes = 3; 2085819a8c0Schristos } else if ((c & 0xf0) == 0xe0) { 2095819a8c0Schristos utfchar = c & 0x0f; 2105819a8c0Schristos utfbytes = 2; 2115819a8c0Schristos } else if ((c & 0xe0) == 0xc0) { 2125819a8c0Schristos utfchar = c & 0x1f; 2135819a8c0Schristos utfbytes = 1; 2145819a8c0Schristos } else { 2155819a8c0Schristos utfchar = c & 0x7f; 2165819a8c0Schristos utfbytes = 0; 2175819a8c0Schristos } 2185819a8c0Schristos } else { 2195819a8c0Schristos /* Followup characters. */ 2205819a8c0Schristos if (utfbytes > 0) { 2215819a8c0Schristos utfchar = (utfchar << 6) + (c & 0x3f); 2225819a8c0Schristos utfbytes--; 2235819a8c0Schristos } else if (utfbytes == 0) 2245c1ccc6eSchristos utfbytes = (u_int)~0; 2255819a8c0Schristos } 2265819a8c0Schristos if (utfbytes == 0) { 2275819a8c0Schristos if (utfchar >= 0x10000 && s16idx + 2 >= s16len) 22811ded42cSmatt utfchar = 0xfffd; 2295819a8c0Schristos if (utfchar >= 0x10000) { 2305c1ccc6eSchristos s16[s16idx++] = htole16((uint16_t) 2315c1ccc6eSchristos (0xd800 | ((utfchar>>10) - 0x40))); 2325c1ccc6eSchristos s16[s16idx++] = htole16((uint16_t) 2335c1ccc6eSchristos (0xdc00 | (utfchar & 0x3ff))); 2345819a8c0Schristos } else 2355c1ccc6eSchristos s16[s16idx++] = htole16((uint16_t)utfchar); 2365819a8c0Schristos if (s16idx == s16len) { 2375819a8c0Schristos return; 2385819a8c0Schristos } 2395819a8c0Schristos } 2405819a8c0Schristos } while (c != 0); 241d6862190Schristos 242d6862190Schristos while (s16idx < s16len) 243d6862190Schristos s16[s16idx++] = 0; 2445819a8c0Schristos } 2455819a8c0Schristos 2465819a8c0Schristos void * 2470b43d398Schristos gpt_read(gpt_t gpt, off_t lba, size_t count) 2485819a8c0Schristos { 2495819a8c0Schristos off_t ofs; 2505819a8c0Schristos void *buf; 2515819a8c0Schristos 2520b43d398Schristos count *= gpt->secsz; 2535819a8c0Schristos buf = malloc(count); 2545819a8c0Schristos if (buf == NULL) 2550b43d398Schristos return NULL; 2565819a8c0Schristos 2570b43d398Schristos ofs = lba * gpt->secsz; 2580b43d398Schristos if (lseek(gpt->fd, ofs, SEEK_SET) == ofs && 2590b43d398Schristos read(gpt->fd, buf, count) == (ssize_t)count) 2600b43d398Schristos return buf; 2615819a8c0Schristos 2625819a8c0Schristos free(buf); 2630b43d398Schristos return NULL; 2645819a8c0Schristos } 2655819a8c0Schristos 2665819a8c0Schristos int 2670b43d398Schristos gpt_write(gpt_t gpt, map_t map) 2685819a8c0Schristos { 2695819a8c0Schristos off_t ofs; 2705819a8c0Schristos size_t count; 2715819a8c0Schristos 2725c1ccc6eSchristos count = (size_t)(map->map_size * gpt->secsz); 2730b43d398Schristos ofs = map->map_start * gpt->secsz; 2740b43d398Schristos if (lseek(gpt->fd, ofs, SEEK_SET) != ofs || 2750b43d398Schristos write(gpt->fd, map->map_data, count) != (ssize_t)count) 276e4ed2565Schristos return -1; 2770b43d398Schristos gpt->flags |= GPT_MODIFIED; 278e4ed2565Schristos return 0; 2795819a8c0Schristos } 2805819a8c0Schristos 2815819a8c0Schristos static int 282be31ceadSmartin gpt_mbr(gpt_t gpt, off_t lba, unsigned int *next_index, off_t ext_offset) 2835819a8c0Schristos { 2845819a8c0Schristos struct mbr *mbr; 2850b43d398Schristos map_t m, p; 2865819a8c0Schristos off_t size, start; 2875819a8c0Schristos unsigned int i, pmbr; 2885819a8c0Schristos 2890b43d398Schristos mbr = gpt_read(gpt, lba, 1); 290ca4e0dcdSchristos if (mbr == NULL) { 2910b43d398Schristos gpt_warn(gpt, "Read failed"); 2920b43d398Schristos return -1; 293ca4e0dcdSchristos } 2945819a8c0Schristos 2955819a8c0Schristos if (mbr->mbr_sig != htole16(MBR_SIG)) { 2960b43d398Schristos if (gpt->verbose) 2970b43d398Schristos gpt_msg(gpt, 2980b43d398Schristos "MBR not found at sector %ju", (uintmax_t)lba); 2995819a8c0Schristos free(mbr); 3000b43d398Schristos return 0; 3015819a8c0Schristos } 3025819a8c0Schristos 3035819a8c0Schristos /* 3045819a8c0Schristos * Differentiate between a regular MBR and a PMBR. This is more 3055819a8c0Schristos * convenient in general. A PMBR is one with a single partition 3065819a8c0Schristos * of type 0xee. 3075819a8c0Schristos */ 3085819a8c0Schristos pmbr = 0; 3095819a8c0Schristos for (i = 0; i < 4; i++) { 31016550bc8Sjakllsch if (mbr->mbr_part[i].part_typ == MBR_PTYPE_UNUSED) 3115819a8c0Schristos continue; 31216550bc8Sjakllsch if (mbr->mbr_part[i].part_typ == MBR_PTYPE_PMBR) 3135819a8c0Schristos pmbr++; 314e5906adeSjmcneill else if ((gpt->flags & GPT_HYBRID) == 0) 3155819a8c0Schristos break; 3165819a8c0Schristos } 3175819a8c0Schristos if (pmbr && i == 4 && lba == 0) { 3180b43d398Schristos if (pmbr != 1) 3190b43d398Schristos gpt_warnx(gpt, "Suspicious PMBR at sector %ju", 320ca4e0dcdSchristos (uintmax_t)lba); 3210b43d398Schristos else if (gpt->verbose > 1) 3220b43d398Schristos gpt_msg(gpt, "PMBR at sector %ju", (uintmax_t)lba); 3230f110115Schristos p = map_add(gpt, lba, 1LL, MAP_TYPE_PMBR, mbr, 1); 3240b43d398Schristos goto out; 3250b43d398Schristos } 3260b43d398Schristos if (pmbr) 3270b43d398Schristos gpt_warnx(gpt, "Suspicious MBR at sector %ju", (uintmax_t)lba); 3280b43d398Schristos else if (gpt->verbose > 1) 3290b43d398Schristos gpt_msg(gpt, "MBR at sector %ju", (uintmax_t)lba); 3305819a8c0Schristos 3310f110115Schristos p = map_add(gpt, lba, 1LL, MAP_TYPE_MBR, mbr, 1); 3325819a8c0Schristos if (p == NULL) 3330b43d398Schristos goto out; 3340b43d398Schristos 3355819a8c0Schristos for (i = 0; i < 4; i++) { 33616550bc8Sjakllsch if (mbr->mbr_part[i].part_typ == MBR_PTYPE_UNUSED || 33716550bc8Sjakllsch mbr->mbr_part[i].part_typ == MBR_PTYPE_PMBR) 3385819a8c0Schristos continue; 3395819a8c0Schristos start = le16toh(mbr->mbr_part[i].part_start_hi); 3405819a8c0Schristos start = (start << 16) + le16toh(mbr->mbr_part[i].part_start_lo); 3415819a8c0Schristos size = le16toh(mbr->mbr_part[i].part_size_hi); 3425819a8c0Schristos size = (size << 16) + le16toh(mbr->mbr_part[i].part_size_lo); 3435819a8c0Schristos if (start == 0 && size == 0) { 3440b43d398Schristos gpt_warnx(gpt, "Malformed MBR at sector %ju", 3450b43d398Schristos (uintmax_t)lba); 3465819a8c0Schristos continue; 3475819a8c0Schristos } 3480b43d398Schristos if (gpt->verbose > 2) 3493017a7a3Schristos gpt_msg(gpt, "MBR part: flag=%#x type=%d, start=%ju, " 3503017a7a3Schristos "size=%ju", mbr->mbr_part[i].part_flag, 351ca4e0dcdSchristos mbr->mbr_part[i].part_typ, 352ca4e0dcdSchristos (uintmax_t)start, (uintmax_t)size); 353be31ceadSmartin if (!MBR_IS_EXTENDED(mbr->mbr_part[i].part_typ)) { 354be31ceadSmartin start += lba; 3550f110115Schristos m = map_add(gpt, start, size, MAP_TYPE_MBR_PART, p, 0); 3565819a8c0Schristos if (m == NULL) 3570b43d398Schristos return -1; 358be31ceadSmartin m->map_index = *next_index; 359be31ceadSmartin (*next_index)++; 3605819a8c0Schristos } else { 361be31ceadSmartin start += ext_offset; 362be31ceadSmartin if (gpt_mbr(gpt, start, next_index, 363be31ceadSmartin ext_offset ? ext_offset : start) == -1) 3640b43d398Schristos return -1; 3655819a8c0Schristos } 3665819a8c0Schristos } 3670b43d398Schristos return 0; 3680b43d398Schristos out: 3690b43d398Schristos if (p == NULL) { 3700b43d398Schristos free(mbr); 3710b43d398Schristos return -1; 3720b43d398Schristos } 3730b43d398Schristos return 0; 3745819a8c0Schristos } 3755819a8c0Schristos 3762ed5cc26Sjnemeth int 3770b43d398Schristos gpt_gpt(gpt_t gpt, off_t lba, int found) 3785819a8c0Schristos { 3795819a8c0Schristos off_t size; 3805819a8c0Schristos struct gpt_ent *ent; 3815819a8c0Schristos struct gpt_hdr *hdr; 38221c34dbbSchristos char *p; 3830b43d398Schristos map_t m; 3845819a8c0Schristos size_t blocks, tblsz; 3855819a8c0Schristos unsigned int i; 3865819a8c0Schristos uint32_t crc; 3875819a8c0Schristos 3880b43d398Schristos hdr = gpt_read(gpt, lba, 1); 389f169dbc4Smlelstv if (hdr == NULL) { 390f169dbc4Smlelstv gpt_warn(gpt, "Read failed"); 3910b43d398Schristos return -1; 392f169dbc4Smlelstv } 3935819a8c0Schristos 3945819a8c0Schristos if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig))) 3955819a8c0Schristos goto fail_hdr; 3965819a8c0Schristos 3975819a8c0Schristos crc = le32toh(hdr->hdr_crc_self); 3985819a8c0Schristos hdr->hdr_crc_self = 0; 3995819a8c0Schristos if (crc32(hdr, le32toh(hdr->hdr_size)) != crc) { 4000b43d398Schristos if (gpt->verbose) 4010b43d398Schristos gpt_msg(gpt, "Bad CRC in GPT header at sector %ju", 4020b43d398Schristos (uintmax_t)lba); 4035819a8c0Schristos goto fail_hdr; 4045819a8c0Schristos } 4055819a8c0Schristos 4065819a8c0Schristos tblsz = le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz); 4070b43d398Schristos blocks = tblsz / gpt->secsz + ((tblsz % gpt->secsz) ? 1 : 0); 4085819a8c0Schristos 4095819a8c0Schristos /* Use generic pointer to deal with hdr->hdr_entsz != sizeof(*ent). */ 4105c1ccc6eSchristos p = gpt_read(gpt, (off_t)le64toh((uint64_t)hdr->hdr_lba_table), blocks); 41179bdd20aSchristos if (p == NULL) { 41279bdd20aSchristos if (found) { 4130b43d398Schristos if (gpt->verbose) 4140b43d398Schristos gpt_msg(gpt, 4150b43d398Schristos "Cannot read LBA table at sector %ju", 4160b43d398Schristos (uintmax_t)le64toh(hdr->hdr_lba_table)); 4170b43d398Schristos return -1; 41879bdd20aSchristos } 41979bdd20aSchristos goto fail_hdr; 42079bdd20aSchristos } 4215819a8c0Schristos 4225819a8c0Schristos if (crc32(p, tblsz) != le32toh(hdr->hdr_crc_table)) { 4230b43d398Schristos if (gpt->verbose) 4240b43d398Schristos gpt_msg(gpt, "Bad CRC in GPT table at sector %ju", 4250b43d398Schristos (uintmax_t)le64toh(hdr->hdr_lba_table)); 4265819a8c0Schristos goto fail_ent; 4275819a8c0Schristos } 4285819a8c0Schristos 4290b43d398Schristos if (gpt->verbose > 1) 4300b43d398Schristos gpt_msg(gpt, "%s GPT at sector %ju", 4310b43d398Schristos (lba == 1) ? "Pri" : "Sec", (uintmax_t)lba); 4325819a8c0Schristos 4330b43d398Schristos m = map_add(gpt, lba, 1, (lba == 1) 4340f110115Schristos ? MAP_TYPE_PRI_GPT_HDR : MAP_TYPE_SEC_GPT_HDR, hdr, 1); 4355819a8c0Schristos if (m == NULL) 4365819a8c0Schristos return (-1); 4375819a8c0Schristos 4385c1ccc6eSchristos m = map_add(gpt, (off_t)le64toh((uint64_t)hdr->hdr_lba_table), 4395c1ccc6eSchristos (off_t)blocks, 4400f110115Schristos lba == 1 ? MAP_TYPE_PRI_GPT_TBL : MAP_TYPE_SEC_GPT_TBL, p, 1); 4415819a8c0Schristos if (m == NULL) 4425819a8c0Schristos return (-1); 4435819a8c0Schristos 4445819a8c0Schristos if (lba != 1) 44579bdd20aSchristos return (1); 4465819a8c0Schristos 4475819a8c0Schristos for (i = 0; i < le32toh(hdr->hdr_entries); i++) { 4485819a8c0Schristos ent = (void*)(p + i * le32toh(hdr->hdr_entsz)); 44921c34dbbSchristos if (gpt_uuid_is_nil(ent->ent_type)) 4505819a8c0Schristos continue; 4515819a8c0Schristos 4525c1ccc6eSchristos size = (off_t)(le64toh((uint64_t)ent->ent_lba_end) - 4535c1ccc6eSchristos le64toh((uint64_t)ent->ent_lba_start) + 1LL); 4540b43d398Schristos if (gpt->verbose > 2) { 45521c34dbbSchristos char buf[128]; 45621c34dbbSchristos gpt_uuid_snprintf(buf, sizeof(buf), "%s", 45721c34dbbSchristos ent->ent_type); 4580b43d398Schristos gpt_msg(gpt, "GPT partition: type=%s, start=%ju, " 4590b43d398Schristos "size=%ju", buf, 4600b43d398Schristos (uintmax_t)le64toh(ent->ent_lba_start), 4610b43d398Schristos (uintmax_t)size); 4625819a8c0Schristos } 4635c1ccc6eSchristos m = map_add(gpt, (off_t)le64toh((uint64_t)ent->ent_lba_start), 4640f110115Schristos size, MAP_TYPE_GPT_PART, ent, 0); 4655819a8c0Schristos if (m == NULL) 4665819a8c0Schristos return (-1); 4675819a8c0Schristos m->map_index = i + 1; 4685819a8c0Schristos } 46979bdd20aSchristos return (1); 4705819a8c0Schristos 4715819a8c0Schristos fail_ent: 4725819a8c0Schristos free(p); 4735819a8c0Schristos 4745819a8c0Schristos fail_hdr: 4755819a8c0Schristos free(hdr); 4765819a8c0Schristos return (0); 4775819a8c0Schristos } 4785819a8c0Schristos 4790b43d398Schristos gpt_t 480ef9cffabSchristos gpt_open(const char *dev, int flags, int verbose, off_t mediasz, u_int secsz, 481ef9cffabSchristos time_t timestamp) 4825819a8c0Schristos { 4830b43d398Schristos int mode, found; 484c3132810Schristos off_t devsz; 4850b43d398Schristos gpt_t gpt; 486be31ceadSmartin unsigned int index; 4875819a8c0Schristos 4880b43d398Schristos if ((gpt = calloc(1, sizeof(*gpt))) == NULL) { 489777094fdSjnemeth if (!(flags & GPT_QUIET)) 4900b43d398Schristos warn("Cannot allocate `%s'", dev); 4910b43d398Schristos return NULL; 492ca4e0dcdSchristos } 4930b43d398Schristos gpt->flags = flags; 4940b43d398Schristos gpt->verbose = verbose; 4950b43d398Schristos gpt->mediasz = mediasz; 4960b43d398Schristos gpt->secsz = secsz; 497ef9cffabSchristos gpt->timestamp = timestamp; 498*10edc71fSmlelstv gpt->uuidgen = 0; 4999b522365Schristos 5000b43d398Schristos mode = (gpt->flags & GPT_READONLY) ? O_RDONLY : O_RDWR|O_EXCL; 5010b43d398Schristos 5020b43d398Schristos gpt->fd = opendisk(dev, mode, gpt->device_name, 5030b43d398Schristos sizeof(gpt->device_name), 0); 5040b43d398Schristos if (gpt->fd == -1) { 5050b43d398Schristos strlcpy(gpt->device_name, dev, sizeof(gpt->device_name)); 5060b43d398Schristos gpt_warn(gpt, "Cannot open"); 5075819a8c0Schristos goto close; 508ca4e0dcdSchristos } 5095819a8c0Schristos 5100b43d398Schristos if (fstat(gpt->fd, &gpt->sb) == -1) { 5110b43d398Schristos gpt_warn(gpt, "Cannot stat"); 5120b43d398Schristos goto close; 5130b43d398Schristos } 5140b43d398Schristos 5150b43d398Schristos if ((gpt->sb.st_mode & S_IFMT) != S_IFREG) { 5160b43d398Schristos if (gpt->secsz == 0) { 5171ca10bc7Sjnemeth #ifdef DIOCGSECTORSIZE 5180b43d398Schristos if (ioctl(gpt->fd, DIOCGSECTORSIZE, &gpt->secsz) == -1) { 5190b43d398Schristos gpt_warn(gpt, "Cannot get sector size"); 5209b522365Schristos goto close; 521ca4e0dcdSchristos } 5221ca10bc7Sjnemeth #endif 5230b43d398Schristos if (gpt->secsz == 0) { 5240b43d398Schristos gpt_warnx(gpt, "Sector size can't be 0"); 525f8906ab6Schristos goto close; 526f8906ab6Schristos } 527f8906ab6Schristos } 5280b43d398Schristos if (gpt->mediasz == 0) { 529f8906ab6Schristos #ifdef DIOCGMEDIASIZE 5300b43d398Schristos if (ioctl(gpt->fd, DIOCGMEDIASIZE, &gpt->mediasz) == -1) { 5310b43d398Schristos gpt_warn(gpt, "Cannot get media size"); 532f8906ab6Schristos goto close; 533f8906ab6Schristos } 534f8906ab6Schristos #endif 5350b43d398Schristos if (gpt->mediasz == 0) { 5360b43d398Schristos gpt_warnx(gpt, "Media size can't be 0"); 537f8906ab6Schristos goto close; 538f8906ab6Schristos } 539f8906ab6Schristos } 5405819a8c0Schristos } else { 54166710644Schristos gpt->flags |= GPT_FILE; 5420b43d398Schristos if (gpt->secsz == 0) 5430b43d398Schristos gpt->secsz = 512; /* Fixed size for files. */ 5440b43d398Schristos if (gpt->mediasz == 0) { 5450b43d398Schristos if (gpt->sb.st_size % gpt->secsz) { 546f169dbc4Smlelstv gpt_warn(gpt, "Media size not a multiple of sector size (%u)\n", gpt->secsz); 5475819a8c0Schristos errno = EINVAL; 5485819a8c0Schristos goto close; 5495819a8c0Schristos } 5500b43d398Schristos gpt->mediasz = gpt->sb.st_size; 5515819a8c0Schristos } 552ce4e8c44Schristos gpt->flags |= GPT_NOSYNC; 553279da393Schristos } 5545819a8c0Schristos 5555819a8c0Schristos /* 5565819a8c0Schristos * We require an absolute minimum of 6 sectors. One for the MBR, 5575819a8c0Schristos * 2 for the GPT header, 2 for the GPT table and one to hold some 5585819a8c0Schristos * user data. Let's catch this extreme border case here so that 5595819a8c0Schristos * we don't have to worry about it later. 5605819a8c0Schristos */ 5610b43d398Schristos devsz = gpt->mediasz / gpt->secsz; 562c3132810Schristos if (devsz < 6) { 56366710644Schristos gpt_warnx(gpt, "Need 6 sectors, we have %ju", 5640b43d398Schristos (uintmax_t)devsz); 5655819a8c0Schristos goto close; 5665819a8c0Schristos } 5675819a8c0Schristos 5680b43d398Schristos if (gpt->verbose) { 5690b43d398Schristos gpt_msg(gpt, "mediasize=%ju; sectorsize=%u; blocks=%ju", 5700b43d398Schristos (uintmax_t)gpt->mediasz, gpt->secsz, (uintmax_t)devsz); 571ca4e0dcdSchristos } 5725819a8c0Schristos 573c458b37cSchristos if (map_init(gpt, devsz) == -1) 574c458b37cSchristos goto close; 5755819a8c0Schristos 576be31ceadSmartin index = 1; 577be31ceadSmartin if (gpt_mbr(gpt, 0LL, &index, 0U) == -1) 5785819a8c0Schristos goto close; 5790b43d398Schristos if ((found = gpt_gpt(gpt, 1LL, 1)) == -1) 5805819a8c0Schristos goto close; 5810bf6d488Smlelstv 5820bf6d488Smlelstv if (found) { 5830bf6d488Smlelstv struct map *map; 5840bf6d488Smlelstv struct gpt_hdr *hdr; 58527a040a8Smlelstv uint64_t lba; 5860bf6d488Smlelstv 5870bf6d488Smlelstv /* 5880bf6d488Smlelstv * read secondary GPT from position stored in primary header 5890bf6d488Smlelstv * when possible 5900bf6d488Smlelstv */ 5910bf6d488Smlelstv map = map_find(gpt, MAP_TYPE_PRI_GPT_HDR); 5920bf6d488Smlelstv hdr = map ? map->map_data : NULL; 59327a040a8Smlelstv lba = le64toh(hdr->hdr_lba_alt); 59427a040a8Smlelstv if (hdr && lba > 0 && lba < (uint64_t)devsz) { 59527a040a8Smlelstv if (gpt_gpt(gpt, (off_t)lba, found) == -1) 5960bf6d488Smlelstv goto close; 5970bf6d488Smlelstv } 5980bf6d488Smlelstv } else { 5990b43d398Schristos if (gpt_gpt(gpt, devsz - 1LL, found) == -1) 6005819a8c0Schristos goto close; 6010bf6d488Smlelstv } 6025819a8c0Schristos 6030b43d398Schristos return gpt; 6045819a8c0Schristos 6055819a8c0Schristos close: 6060b43d398Schristos if (gpt->fd != -1) 6070b43d398Schristos close(gpt->fd); 608f169dbc4Smlelstv gpt_warn(gpt, "No GPT found"); 6095fcb5009Smrg free(gpt); 6100b43d398Schristos return NULL; 6115819a8c0Schristos } 6125819a8c0Schristos 6135819a8c0Schristos void 6140b43d398Schristos gpt_close(gpt_t gpt) 6155819a8c0Schristos { 616e4ed2565Schristos 61708878476Smartin if (gpt == NULL) 61808878476Smartin return; 61908878476Smartin 620aa3b5bb2Sjnemeth if (!(gpt->flags & GPT_MODIFIED) || !(gpt->flags & GPT_SYNC)) 62168bc3825Schristos goto out; 62268bc3825Schristos 6230b43d398Schristos if (!(gpt->flags & GPT_NOSYNC)) { 624835e6be8Schristos #ifdef DIOCMWEDGES 625835e6be8Schristos int bits; 6260b43d398Schristos if (ioctl(gpt->fd, DIOCMWEDGES, &bits) == -1) 6270b43d398Schristos gpt_warn(gpt, "Can't update wedge information"); 62868bc3825Schristos else 62968bc3825Schristos goto out; 630835e6be8Schristos #endif 631835e6be8Schristos } 63266710644Schristos if (!(gpt->flags & GPT_FILE)) 6330b43d398Schristos gpt_msg(gpt, "You need to run \"dkctl %s makewedges\"" 6340b43d398Schristos " for the changes to take effect\n", gpt->device_name); 635e4ed2565Schristos 63668bc3825Schristos out: 6370b43d398Schristos close(gpt->fd); 6385819a8c0Schristos } 6395819a8c0Schristos 640459b255bSjoerg __printflike(2, 0) 641f9db7548Schristos static void 642f9db7548Schristos gpt_vwarnx(gpt_t gpt, const char *fmt, va_list ap, const char *e) 643f9db7548Schristos { 644f9db7548Schristos if (gpt && (gpt->flags & GPT_QUIET)) 645f9db7548Schristos return; 646f9db7548Schristos fprintf(stderr, "%s: ", getprogname()); 647f9db7548Schristos if (gpt) 648f9db7548Schristos fprintf(stderr, "%s: ", gpt->device_name); 649f9db7548Schristos vfprintf(stderr, fmt, ap); 650f9db7548Schristos if (e) 651f9db7548Schristos fprintf(stderr, " (%s)\n", e); 652f9db7548Schristos else 653f9db7548Schristos fputc('\n', stderr); 654f9db7548Schristos } 655f9db7548Schristos 656ca4e0dcdSchristos void 6570b43d398Schristos gpt_warnx(gpt_t gpt, const char *fmt, ...) 658ca4e0dcdSchristos { 659ca4e0dcdSchristos va_list ap; 66068bc3825Schristos 6610b43d398Schristos va_start(ap, fmt); 662f9db7548Schristos gpt_vwarnx(gpt, fmt, ap, NULL); 6630b43d398Schristos va_end(ap); 6640b43d398Schristos } 6650b43d398Schristos 6660b43d398Schristos void 6670b43d398Schristos gpt_warn(gpt_t gpt, const char *fmt, ...) 6680b43d398Schristos { 6690b43d398Schristos va_list ap; 6700b43d398Schristos 6710b43d398Schristos va_start(ap, fmt); 672f9db7548Schristos gpt_vwarnx(gpt, fmt, ap, strerror(errno)); 6730b43d398Schristos va_end(ap); 6740b43d398Schristos } 6750b43d398Schristos 6760b43d398Schristos void 6770b43d398Schristos gpt_msg(gpt_t gpt, const char *fmt, ...) 6780b43d398Schristos { 6790b43d398Schristos va_list ap; 6800b43d398Schristos 681f9db7548Schristos if (gpt && (gpt->flags & GPT_QUIET)) 6820b43d398Schristos return; 683f9db7548Schristos if (gpt) 6840b43d398Schristos printf("%s: ", gpt->device_name); 685ca4e0dcdSchristos va_start(ap, fmt); 686ca4e0dcdSchristos vprintf(fmt, ap); 687ca4e0dcdSchristos va_end(ap); 688ca4e0dcdSchristos printf("\n"); 689ca4e0dcdSchristos } 6900b43d398Schristos 6910b43d398Schristos struct gpt_hdr * 6920b43d398Schristos gpt_hdr(gpt_t gpt) 6930b43d398Schristos { 6940b43d398Schristos gpt->gpt = map_find(gpt, MAP_TYPE_PRI_GPT_HDR); 6950b43d398Schristos if (gpt->gpt == NULL) { 6960b43d398Schristos gpt_warnx(gpt, "No primary GPT header; run create or recover"); 6970b43d398Schristos return NULL; 6980b43d398Schristos } 6990b43d398Schristos 7000b43d398Schristos gpt->tpg = map_find(gpt, MAP_TYPE_SEC_GPT_HDR); 7010b43d398Schristos if (gpt->tpg == NULL) { 7020b43d398Schristos gpt_warnx(gpt, "No secondary GPT header; run recover"); 7030b43d398Schristos return NULL; 7040b43d398Schristos } 7050b43d398Schristos 7060b43d398Schristos gpt->tbl = map_find(gpt, MAP_TYPE_PRI_GPT_TBL); 7070b43d398Schristos gpt->lbt = map_find(gpt, MAP_TYPE_SEC_GPT_TBL); 7080b43d398Schristos if (gpt->tbl == NULL || gpt->lbt == NULL) { 7090b43d398Schristos gpt_warnx(gpt, "Corrupt maps, run recover"); 7100b43d398Schristos return NULL; 7110b43d398Schristos } 7120b43d398Schristos 7130b43d398Schristos return gpt->gpt->map_data; 7140b43d398Schristos } 7150b43d398Schristos 7160b43d398Schristos int 7170b43d398Schristos gpt_write_crc(gpt_t gpt, map_t map, map_t tbl) 7180b43d398Schristos { 7190b43d398Schristos struct gpt_hdr *hdr = map->map_data; 7200b43d398Schristos 7210b43d398Schristos hdr->hdr_crc_table = htole32(crc32(tbl->map_data, 7220b43d398Schristos le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz))); 7230b43d398Schristos hdr->hdr_crc_self = 0; 7240b43d398Schristos hdr->hdr_crc_self = htole32(crc32(hdr, le32toh(hdr->hdr_size))); 7250b43d398Schristos 7260b43d398Schristos if (gpt_write(gpt, map) == -1) { 7270b43d398Schristos gpt_warn(gpt, "Error writing crc map"); 7280b43d398Schristos return -1; 7290b43d398Schristos } 7300b43d398Schristos 7310b43d398Schristos if (gpt_write(gpt, tbl) == -1) { 7320b43d398Schristos gpt_warn(gpt, "Error writing crc table"); 7330b43d398Schristos return -1; 7340b43d398Schristos } 7350b43d398Schristos 7360b43d398Schristos return 0; 7370b43d398Schristos } 7380b43d398Schristos 7390b43d398Schristos int 7400b43d398Schristos gpt_write_primary(gpt_t gpt) 7410b43d398Schristos { 7420b43d398Schristos return gpt_write_crc(gpt, gpt->gpt, gpt->tbl); 7430b43d398Schristos } 7440b43d398Schristos 7450b43d398Schristos 7460b43d398Schristos int 7470b43d398Schristos gpt_write_backup(gpt_t gpt) 7480b43d398Schristos { 7490b43d398Schristos return gpt_write_crc(gpt, gpt->tpg, gpt->lbt); 7500b43d398Schristos } 7510b43d398Schristos 7520b43d398Schristos void 7533017a7a3Schristos gpt_create_pmbr_part(struct mbr_part *part, off_t last, int active) 7540b43d398Schristos { 7553017a7a3Schristos part->part_flag = active ? 0x80 : 0; 7560b43d398Schristos part->part_shd = 0x00; 7570b43d398Schristos part->part_ssect = 0x02; 7580b43d398Schristos part->part_scyl = 0x00; 7590b43d398Schristos part->part_typ = MBR_PTYPE_PMBR; 7600b43d398Schristos part->part_ehd = 0xfe; 7610b43d398Schristos part->part_esect = 0xff; 7620b43d398Schristos part->part_ecyl = 0xff; 7630b43d398Schristos part->part_start_lo = htole16(1); 7640b43d398Schristos if (last > 0xffffffff) { 7650b43d398Schristos part->part_size_lo = htole16(0xffff); 7660b43d398Schristos part->part_size_hi = htole16(0xffff); 7670b43d398Schristos } else { 7685c1ccc6eSchristos part->part_size_lo = htole16((uint16_t)last); 7695c1ccc6eSchristos part->part_size_hi = htole16((uint16_t)(last >> 16)); 7700b43d398Schristos } 7710b43d398Schristos } 7720b43d398Schristos 7730b43d398Schristos struct gpt_ent * 7740b43d398Schristos gpt_ent(map_t map, map_t tbl, unsigned int i) 7750b43d398Schristos { 7760b43d398Schristos struct gpt_hdr *hdr = map->map_data; 7770b43d398Schristos return (void *)((char *)tbl->map_data + i * le32toh(hdr->hdr_entsz)); 7780b43d398Schristos } 7790b43d398Schristos 7800b43d398Schristos struct gpt_ent * 7810b43d398Schristos gpt_ent_primary(gpt_t gpt, unsigned int i) 7820b43d398Schristos { 7830b43d398Schristos return gpt_ent(gpt->gpt, gpt->tbl, i); 7840b43d398Schristos } 7850b43d398Schristos 7860b43d398Schristos struct gpt_ent * 7870b43d398Schristos gpt_ent_backup(gpt_t gpt, unsigned int i) 7880b43d398Schristos { 7890b43d398Schristos return gpt_ent(gpt->tpg, gpt->lbt, i); 7900b43d398Schristos } 7918ca93e46Schristos 7928ca93e46Schristos int 7938ca93e46Schristos gpt_usage(const char *prefix, const struct gpt_cmd *cmd) 7948ca93e46Schristos { 7958ca93e46Schristos const char **a = cmd->help; 7968ca93e46Schristos size_t hlen = cmd->hlen; 7978ca93e46Schristos size_t i; 7988ca93e46Schristos 7998ca93e46Schristos if (prefix == NULL) { 8008ca93e46Schristos const char *pname = getprogname(); 801272fa829Schristos const char *d1, *d2, *d = " <device>"; 8028ca93e46Schristos int len = (int)strlen(pname); 803272fa829Schristos if (strcmp(pname, "gpt") == 0) { 804272fa829Schristos d1 = ""; 805272fa829Schristos d2 = d; 806272fa829Schristos } else { 807272fa829Schristos d2 = ""; 808272fa829Schristos d1 = d; 809272fa829Schristos } 810272fa829Schristos fprintf(stderr, "Usage: %s%s %s %s%s\n", pname, 811272fa829Schristos d1, cmd->name, a[0], d2); 8128ca93e46Schristos for (i = 1; i < hlen; i++) { 8138ca93e46Schristos fprintf(stderr, 814272fa829Schristos " %*s%s %s %s%s\n", len, "", 815272fa829Schristos d1, cmd->name, a[i], d2); 8168ca93e46Schristos } 8178ca93e46Schristos } else { 8188ca93e46Schristos for (i = 0; i < hlen; i++) 8198ca93e46Schristos fprintf(stderr, "%s%s %s\n", prefix, cmd->name, a[i]); 8208ca93e46Schristos } 8218ca93e46Schristos return -1; 8228ca93e46Schristos } 823bbb4a8abSchristos 824bbb4a8abSchristos off_t 825bbb4a8abSchristos gpt_last(gpt_t gpt) 826bbb4a8abSchristos { 827bbb4a8abSchristos return gpt->mediasz / gpt->secsz - 1LL; 828bbb4a8abSchristos } 829bbb4a8abSchristos 8305c1ccc6eSchristos off_t 831bbb4a8abSchristos gpt_create(gpt_t gpt, off_t last, u_int parts, int primary_only) 832bbb4a8abSchristos { 833bbb4a8abSchristos off_t blocks; 834bbb4a8abSchristos map_t map; 835bbb4a8abSchristos struct gpt_hdr *hdr; 836bbb4a8abSchristos struct gpt_ent *ent; 837bbb4a8abSchristos unsigned int i; 838bbb4a8abSchristos void *p; 839bbb4a8abSchristos 840bbb4a8abSchristos if (map_find(gpt, MAP_TYPE_PRI_GPT_HDR) != NULL || 841bbb4a8abSchristos map_find(gpt, MAP_TYPE_SEC_GPT_HDR) != NULL) { 842f3439cf7Schristos gpt_warnx(gpt, "Device already contains a GPT, " 843f3439cf7Schristos "destroy it first"); 844bbb4a8abSchristos return -1; 845bbb4a8abSchristos } 846bbb4a8abSchristos 847bbb4a8abSchristos /* Get the amount of free space after the MBR */ 848bbb4a8abSchristos blocks = map_free(gpt, 1LL, 0LL); 849bbb4a8abSchristos if (blocks == 0LL) { 850bbb4a8abSchristos gpt_warnx(gpt, "No room for the GPT header"); 851bbb4a8abSchristos return -1; 852bbb4a8abSchristos } 853bbb4a8abSchristos 854bbb4a8abSchristos /* Don't create more than parts entries. */ 855bbb4a8abSchristos if ((uint64_t)(blocks - 1) * gpt->secsz > 856bbb4a8abSchristos parts * sizeof(struct gpt_ent)) { 8575c1ccc6eSchristos blocks = (off_t)((parts * sizeof(struct gpt_ent)) / gpt->secsz); 858bbb4a8abSchristos if ((parts * sizeof(struct gpt_ent)) % gpt->secsz) 859bbb4a8abSchristos blocks++; 860bbb4a8abSchristos blocks++; /* Don't forget the header itself */ 861bbb4a8abSchristos } 862bbb4a8abSchristos 863bbb4a8abSchristos /* Never cross the median of the device. */ 864bbb4a8abSchristos if ((blocks + 1LL) > ((last + 1LL) >> 1)) 865bbb4a8abSchristos blocks = ((last + 1LL) >> 1) - 1LL; 866bbb4a8abSchristos 867bbb4a8abSchristos /* 868bbb4a8abSchristos * Get the amount of free space at the end of the device and 869bbb4a8abSchristos * calculate the size for the GPT structures. 870bbb4a8abSchristos */ 871bbb4a8abSchristos map = map_last(gpt); 872bbb4a8abSchristos if (map->map_type != MAP_TYPE_UNUSED) { 873bbb4a8abSchristos gpt_warnx(gpt, "No room for the backup header"); 874bbb4a8abSchristos return -1; 875bbb4a8abSchristos } 876bbb4a8abSchristos 877bbb4a8abSchristos if (map->map_size < blocks) 878bbb4a8abSchristos blocks = map->map_size; 879bbb4a8abSchristos if (blocks == 1LL) { 880bbb4a8abSchristos gpt_warnx(gpt, "No room for the GPT table"); 881bbb4a8abSchristos return -1; 882bbb4a8abSchristos } 883bbb4a8abSchristos 884bbb4a8abSchristos blocks--; /* Number of blocks in the GPT table. */ 885bbb4a8abSchristos 8865b47b594Schristos if (gpt_add_hdr(gpt, MAP_TYPE_PRI_GPT_HDR, 1) == -1) 887bbb4a8abSchristos return -1; 888bbb4a8abSchristos 8895c1ccc6eSchristos if ((p = calloc((size_t)blocks, gpt->secsz)) == NULL) { 890bbb4a8abSchristos gpt_warnx(gpt, "Can't allocate the primary GPT table"); 891bbb4a8abSchristos return -1; 892bbb4a8abSchristos } 893bbb4a8abSchristos if ((gpt->tbl = map_add(gpt, 2LL, blocks, 8940f110115Schristos MAP_TYPE_PRI_GPT_TBL, p, 1)) == NULL) { 895bbb4a8abSchristos free(p); 896bbb4a8abSchristos gpt_warnx(gpt, "Can't add the primary GPT table"); 897bbb4a8abSchristos return -1; 898bbb4a8abSchristos } 899bbb4a8abSchristos 900bbb4a8abSchristos hdr = gpt->gpt->map_data; 901bbb4a8abSchristos memcpy(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)); 902bbb4a8abSchristos 903bbb4a8abSchristos /* 904bbb4a8abSchristos * XXX struct gpt_hdr is not a multiple of 8 bytes in size and thus 905bbb4a8abSchristos * contains padding we must not include in the size. 906bbb4a8abSchristos */ 907bbb4a8abSchristos hdr->hdr_revision = htole32(GPT_HDR_REVISION); 908bbb4a8abSchristos hdr->hdr_size = htole32(GPT_HDR_SIZE); 9095c1ccc6eSchristos hdr->hdr_lba_self = htole64((uint64_t)gpt->gpt->map_start); 9105c1ccc6eSchristos hdr->hdr_lba_alt = htole64((uint64_t)last); 9115c1ccc6eSchristos hdr->hdr_lba_start = htole64((uint64_t)(gpt->tbl->map_start + blocks)); 9125c1ccc6eSchristos hdr->hdr_lba_end = htole64((uint64_t)(last - blocks - 1LL)); 9130f004afeSchristos if (gpt_uuid_generate(gpt, hdr->hdr_guid) == -1) 9140f004afeSchristos return -1; 9155c1ccc6eSchristos hdr->hdr_lba_table = htole64((uint64_t)(gpt->tbl->map_start)); 9165c1ccc6eSchristos hdr->hdr_entries = htole32((uint32_t)(((uint64_t)blocks * gpt->secsz) / 9175c1ccc6eSchristos sizeof(struct gpt_ent))); 918bbb4a8abSchristos if (le32toh(hdr->hdr_entries) > parts) 919bbb4a8abSchristos hdr->hdr_entries = htole32(parts); 920bbb4a8abSchristos hdr->hdr_entsz = htole32(sizeof(struct gpt_ent)); 921bbb4a8abSchristos 922bbb4a8abSchristos ent = gpt->tbl->map_data; 923bbb4a8abSchristos for (i = 0; i < le32toh(hdr->hdr_entries); i++) { 9240f004afeSchristos if (gpt_uuid_generate(gpt, ent[i].ent_guid) == -1) 9250f004afeSchristos return -1; 926bbb4a8abSchristos } 927bbb4a8abSchristos 928bbb4a8abSchristos /* 929bbb4a8abSchristos * Create backup GPT if the user didn't suppress it. 930bbb4a8abSchristos */ 931bbb4a8abSchristos if (primary_only) 932bbb4a8abSchristos return last; 933bbb4a8abSchristos 9345b47b594Schristos if (gpt_add_hdr(gpt, MAP_TYPE_SEC_GPT_HDR, last) == -1) 935bbb4a8abSchristos return -1; 936bbb4a8abSchristos 937bbb4a8abSchristos if ((gpt->lbt = map_add(gpt, last - blocks, blocks, 9380f110115Schristos MAP_TYPE_SEC_GPT_TBL, gpt->tbl->map_data, 0)) == NULL) { 939bbb4a8abSchristos gpt_warnx(gpt, "Can't add the secondary GPT table"); 940bbb4a8abSchristos return -1; 941bbb4a8abSchristos } 942bbb4a8abSchristos 943bbb4a8abSchristos memcpy(gpt->tpg->map_data, gpt->gpt->map_data, gpt->secsz); 944bbb4a8abSchristos 945bbb4a8abSchristos hdr = gpt->tpg->map_data; 9465c1ccc6eSchristos hdr->hdr_lba_self = htole64((uint64_t)gpt->tpg->map_start); 9475c1ccc6eSchristos hdr->hdr_lba_alt = htole64((uint64_t)gpt->gpt->map_start); 9485c1ccc6eSchristos hdr->hdr_lba_table = htole64((uint64_t)gpt->lbt->map_start); 949bbb4a8abSchristos return last; 950bbb4a8abSchristos } 951bbb4a8abSchristos 9520f004afeSchristos static int 9530f004afeSchristos gpt_size_get(gpt_t gpt, off_t *size) 954bbb4a8abSchristos { 9550f004afeSchristos off_t sectors; 956bbb4a8abSchristos int64_t human_num; 957bbb4a8abSchristos char *p; 958bbb4a8abSchristos 9590f004afeSchristos if (*size > 0) 9600f004afeSchristos return -1; 9610f004afeSchristos sectors = strtoll(optarg, &p, 10); 9620f004afeSchristos if (sectors < 1) 9630f004afeSchristos return -1; 9640f004afeSchristos if (*p == '\0' || ((*p == 's' || *p == 'S') && p[1] == '\0')) { 9650f004afeSchristos *size = sectors * gpt->secsz; 9660f004afeSchristos return 0; 9670f004afeSchristos } 9680f004afeSchristos if ((*p == 'b' || *p == 'B') && p[1] == '\0') { 9690f004afeSchristos *size = sectors; 9700f004afeSchristos return 0; 9710f004afeSchristos } 9720f004afeSchristos if (dehumanize_number(optarg, &human_num) < 0) 9730f004afeSchristos return -1; 9740f004afeSchristos *size = human_num; 9750f004afeSchristos return 0; 9760f004afeSchristos } 9770f004afeSchristos 9780f004afeSchristos int 979f9db7548Schristos gpt_human_get(gpt_t gpt, off_t *human) 9800f004afeSchristos { 9810f004afeSchristos int64_t human_num; 9820f004afeSchristos 983f9db7548Schristos if (*human > 0) { 984f9db7548Schristos gpt_warn(gpt, "Already set to %jd new `%s'", (intmax_t)*human, 985f9db7548Schristos optarg); 9860f004afeSchristos return -1; 987f9db7548Schristos } 988f9db7548Schristos if (dehumanize_number(optarg, &human_num) < 0) { 989f9db7548Schristos gpt_warn(gpt, "Bad number `%s'", optarg); 9900f004afeSchristos return -1; 991f9db7548Schristos } 9920f004afeSchristos *human = human_num; 993f9db7548Schristos if (*human < 1) { 994f9db7548Schristos gpt_warn(gpt, "Number `%s' < 1", optarg); 9950f004afeSchristos return -1; 996f9db7548Schristos } 9970f004afeSchristos return 0; 9980f004afeSchristos } 9990f004afeSchristos 10000f004afeSchristos int 10010f004afeSchristos gpt_add_find(gpt_t gpt, struct gpt_find *find, int ch) 10020f004afeSchristos { 1003bbb4a8abSchristos switch (ch) { 1004bbb4a8abSchristos case 'a': 1005f9db7548Schristos if (find->all > 0) { 1006f9db7548Schristos gpt_warn(gpt, "-a is already set"); 1007bbb4a8abSchristos return -1; 1008f9db7548Schristos } 1009bbb4a8abSchristos find->all = 1; 1010bbb4a8abSchristos break; 1011bbb4a8abSchristos case 'b': 1012f9db7548Schristos if (gpt_human_get(gpt, &find->block) == -1) 1013bbb4a8abSchristos return -1; 1014bbb4a8abSchristos break; 1015bbb4a8abSchristos case 'i': 1016f9db7548Schristos if (gpt_uint_get(gpt, &find->entry) == -1) 1017bbb4a8abSchristos return -1; 1018bbb4a8abSchristos break; 1019bbb4a8abSchristos case 'L': 10200f004afeSchristos if (gpt_name_get(gpt, &find->label) == -1) 1021bbb4a8abSchristos return -1; 1022bbb4a8abSchristos break; 1023bbb4a8abSchristos case 's': 10240f004afeSchristos if (gpt_size_get(gpt, &find->size) == -1) 1025bbb4a8abSchristos return -1; 1026bbb4a8abSchristos break; 1027bbb4a8abSchristos case 't': 1028bbb4a8abSchristos if (!gpt_uuid_is_nil(find->type)) 1029bbb4a8abSchristos return -1; 1030bbb4a8abSchristos if (gpt_uuid_parse(optarg, find->type) != 0) 1031bbb4a8abSchristos return -1; 1032bbb4a8abSchristos break; 1033bbb4a8abSchristos default: 1034f9db7548Schristos gpt_warn(gpt, "Unknown find option `%c'", ch); 1035bbb4a8abSchristos return -1; 1036bbb4a8abSchristos } 1037bbb4a8abSchristos return 0; 1038bbb4a8abSchristos } 1039bbb4a8abSchristos 1040bbb4a8abSchristos int 1041bbb4a8abSchristos gpt_change_ent(gpt_t gpt, const struct gpt_find *find, 1042e003a26fSjnemeth void (*cfn)(struct gpt_ent *, void *, int), void *v) 1043bbb4a8abSchristos { 1044bbb4a8abSchristos map_t m; 1045bbb4a8abSchristos struct gpt_hdr *hdr; 1046bbb4a8abSchristos struct gpt_ent *ent; 1047bbb4a8abSchristos unsigned int i; 1048cdf86847Schristos uint8_t utfbuf[__arraycount(ent->ent_name) * 3 + 1]; 1049bbb4a8abSchristos 1050bbb4a8abSchristos if (!find->all ^ 1051bbb4a8abSchristos (find->block > 0 || find->entry > 0 || find->label != NULL 1052bbb4a8abSchristos || find->size > 0 || !gpt_uuid_is_nil(find->type))) 1053bbb4a8abSchristos return -1; 1054bbb4a8abSchristos 1055bbb4a8abSchristos if ((hdr = gpt_hdr(gpt)) == NULL) 1056bbb4a8abSchristos return -1; 1057bbb4a8abSchristos 1058bbb4a8abSchristos /* Relabel all matching entries in the map. */ 1059bbb4a8abSchristos for (m = map_first(gpt); m != NULL; m = m->map_next) { 1060bbb4a8abSchristos if (m->map_type != MAP_TYPE_GPT_PART || m->map_index < 1) 1061bbb4a8abSchristos continue; 1062bbb4a8abSchristos if (find->entry > 0 && find->entry != m->map_index) 1063bbb4a8abSchristos continue; 1064bbb4a8abSchristos if (find->block > 0 && find->block != m->map_start) 1065bbb4a8abSchristos continue; 1066bbb4a8abSchristos if (find->size > 0 && find->size != m->map_size) 1067bbb4a8abSchristos continue; 1068bbb4a8abSchristos 1069bbb4a8abSchristos i = m->map_index - 1; 1070bbb4a8abSchristos 1071bbb4a8abSchristos ent = gpt_ent_primary(gpt, i); 1072cdf86847Schristos if (find->label != NULL) { 107347a40809Schristos utf16_to_utf8(ent->ent_name, 107447a40809Schristos __arraycount(ent->ent_name), 107547a40809Schristos utfbuf, __arraycount(utfbuf)); 1076a66cbab4Smlelstv if (strcmp((char *)find->label, (char *)utfbuf) != 0) 1077bbb4a8abSchristos continue; 1078cdf86847Schristos } 1079bbb4a8abSchristos 1080bbb4a8abSchristos if (!gpt_uuid_is_nil(find->type) && 1081bbb4a8abSchristos !gpt_uuid_equal(find->type, ent->ent_type)) 1082bbb4a8abSchristos continue; 1083bbb4a8abSchristos 1084bbb4a8abSchristos /* Change the primary entry. */ 1085e003a26fSjnemeth (*cfn)(ent, v, 0); 1086bbb4a8abSchristos 1087bbb4a8abSchristos if (gpt_write_primary(gpt) == -1) 1088bbb4a8abSchristos return -1; 1089bbb4a8abSchristos 1090bbb4a8abSchristos ent = gpt_ent_backup(gpt, i); 1091bbb4a8abSchristos /* Change the secondary entry. */ 1092e003a26fSjnemeth (*cfn)(ent, v, 1); 1093bbb4a8abSchristos 1094bbb4a8abSchristos if (gpt_write_backup(gpt) == -1) 1095bbb4a8abSchristos return -1; 1096bbb4a8abSchristos 1097bbb4a8abSchristos gpt_msg(gpt, "Partition %d %s", m->map_index, find->msg); 1098bbb4a8abSchristos } 1099bbb4a8abSchristos return 0; 1100bbb4a8abSchristos } 1101bbb4a8abSchristos 1102bbb4a8abSchristos int 110346e73491Sjnemeth gpt_change_hdr(gpt_t gpt, const struct gpt_find *find, 110446e73491Sjnemeth void (*cfn)(struct gpt_hdr *, void *, int), void *v) 110546e73491Sjnemeth { 110646e73491Sjnemeth struct gpt_hdr *hdr; 110746e73491Sjnemeth 110846e73491Sjnemeth if ((hdr = gpt_hdr(gpt)) == NULL) 110946e73491Sjnemeth return -1; 111046e73491Sjnemeth 111146e73491Sjnemeth /* Change the primary header. */ 111246e73491Sjnemeth (*cfn)(hdr, v, 0); 111346e73491Sjnemeth 111446e73491Sjnemeth if (gpt_write_primary(gpt) == -1) 111546e73491Sjnemeth return -1; 111646e73491Sjnemeth 111746e73491Sjnemeth hdr = gpt->tpg->map_data; 111846e73491Sjnemeth /* Change the secondary header. */ 111946e73491Sjnemeth (*cfn)(hdr, v, 1); 112046e73491Sjnemeth 112146e73491Sjnemeth if (gpt_write_backup(gpt) == -1) 112246e73491Sjnemeth return -1; 112346e73491Sjnemeth 112446e73491Sjnemeth gpt_msg(gpt, "Header %s", find->msg); 112546e73491Sjnemeth 112646e73491Sjnemeth return 0; 112746e73491Sjnemeth } 112846e73491Sjnemeth 112946e73491Sjnemeth int 1130bbb4a8abSchristos gpt_add_ais(gpt_t gpt, off_t *alignment, u_int *entry, off_t *size, int ch) 1131bbb4a8abSchristos { 1132bbb4a8abSchristos switch (ch) { 1133bbb4a8abSchristos case 'a': 1134f9db7548Schristos if (gpt_human_get(gpt, alignment) == -1) 1135bbb4a8abSchristos return -1; 1136bbb4a8abSchristos return 0; 1137bbb4a8abSchristos case 'i': 1138f9db7548Schristos if (gpt_uint_get(gpt, entry) == -1) 1139bbb4a8abSchristos return -1; 1140bbb4a8abSchristos return 0; 1141bbb4a8abSchristos case 's': 11420f004afeSchristos if (gpt_size_get(gpt, size) == -1) 1143bbb4a8abSchristos return -1; 1144bbb4a8abSchristos return 0; 1145bbb4a8abSchristos default: 1146f9db7548Schristos gpt_warn(gpt, "Unknown alignment/index/size option `%c'", ch); 1147bbb4a8abSchristos return -1; 1148bbb4a8abSchristos } 1149bbb4a8abSchristos } 1150bbb4a8abSchristos 1151bbb4a8abSchristos off_t 1152bbb4a8abSchristos gpt_check_ais(gpt_t gpt, off_t alignment, u_int entry, off_t size) 1153bbb4a8abSchristos { 1154bbb4a8abSchristos if (entry == 0) { 1155bbb4a8abSchristos gpt_warnx(gpt, "Entry not specified"); 1156bbb4a8abSchristos return -1; 1157bbb4a8abSchristos } 1158bbb4a8abSchristos if (alignment % gpt->secsz != 0) { 1159bbb4a8abSchristos gpt_warnx(gpt, "Alignment (%#jx) must be a multiple of " 1160bbb4a8abSchristos "sector size (%#x)", (uintmax_t)alignment, gpt->secsz); 1161bbb4a8abSchristos return -1; 1162bbb4a8abSchristos } 1163bbb4a8abSchristos 1164bbb4a8abSchristos if (size % gpt->secsz != 0) { 1165bbb4a8abSchristos gpt_warnx(gpt, "Size (%#jx) must be a multiple of " 1166bbb4a8abSchristos "sector size (%#x)", (uintmax_t)size, gpt->secsz); 1167bbb4a8abSchristos return -1; 1168bbb4a8abSchristos } 1169bbb4a8abSchristos if (size > 0) 1170bbb4a8abSchristos return size / gpt->secsz; 1171bbb4a8abSchristos return 0; 1172bbb4a8abSchristos } 11734d523900Schristos 11744d523900Schristos static const struct nvd { 11754d523900Schristos const char *name; 11764d523900Schristos uint64_t mask; 11774d523900Schristos const char *description; 11784d523900Schristos } gpt_attr[] = { 1179bbb4a8abSchristos { 11804d523900Schristos "biosboot", 11814d523900Schristos GPT_ENT_ATTR_LEGACY_BIOS_BOOTABLE, 11824d523900Schristos "Legacy BIOS boot partition", 11834d523900Schristos }, 11844d523900Schristos { 11854d523900Schristos "bootme", 11864d523900Schristos GPT_ENT_ATTR_BOOTME, 11874d523900Schristos "Bootable partition", 11884d523900Schristos }, 11894d523900Schristos { 11904d523900Schristos "bootfailed", 11914d523900Schristos GPT_ENT_ATTR_BOOTFAILED, 11924d523900Schristos "Partition that marked bootonce failed to boot", 11934d523900Schristos }, 11944d523900Schristos { 11954d523900Schristos "bootonce", 11964d523900Schristos GPT_ENT_ATTR_BOOTONCE, 11974d523900Schristos "Attempt to boot this partition only once", 11984d523900Schristos }, 11994d523900Schristos { 12004d523900Schristos "noblockio", 12014d523900Schristos GPT_ENT_ATTR_NO_BLOCK_IO_PROTOCOL, 12024d523900Schristos "UEFI won't recognize file system for block I/O", 12034d523900Schristos }, 12044d523900Schristos { 12054d523900Schristos "required", 12064d523900Schristos GPT_ENT_ATTR_REQUIRED_PARTITION, 12074d523900Schristos "Partition required for platform to function", 12084d523900Schristos }, 12094d523900Schristos }; 12104d523900Schristos 12114d523900Schristos int 12124d523900Schristos gpt_attr_get(gpt_t gpt, uint64_t *attributes) 12134d523900Schristos { 12144d523900Schristos size_t i; 12154d523900Schristos int rv = 0; 12164d523900Schristos char *ptr; 12174d523900Schristos 12184d523900Schristos *attributes = 0; 12194d523900Schristos 12204d523900Schristos for (ptr = strtok(optarg, ","); ptr; ptr = strtok(NULL, ",")) { 12214d523900Schristos for (i = 0; i < __arraycount(gpt_attr); i++) 12224d523900Schristos if (strcmp(gpt_attr[i].name, ptr) == 0) 12234d523900Schristos break; 12244d523900Schristos if (i == __arraycount(gpt_attr)) { 1225331de78cSkre gpt_warnx(gpt, "Unrecognized attribute `%s'", ptr); 12264d523900Schristos rv = -1; 12274d523900Schristos } else 12284d523900Schristos *attributes |= gpt_attr[i].mask; 1229bbb4a8abSchristos } 12304d523900Schristos return rv; 12314d523900Schristos } 12324d523900Schristos 12334d523900Schristos void 12344d523900Schristos gpt_attr_help(const char *prefix) 12354d523900Schristos { 12364d523900Schristos size_t i; 12374d523900Schristos 12384d523900Schristos for (i = 0; i < __arraycount(gpt_attr); i++) 12394d523900Schristos printf("%s%10.10s\t%s\n", prefix, gpt_attr[i].name, 12404d523900Schristos gpt_attr[i].description); 12414d523900Schristos } 12424d523900Schristos 12434d523900Schristos const char * 12444d523900Schristos gpt_attr_list(char *buf, size_t len, uint64_t attributes) 12454d523900Schristos { 12464d523900Schristos size_t i; 12476468c262Skre /* 12486468c262Skre * a uint64_t (attributes) has at most 16 hex digits 12496468c262Skre * in its representation, add 2 for "0x", and 2 more 12506468c262Skre * for surrounding [ ], plus one for a trailing \0, 12516468c262Skre * and we need 21 bytes, round that up to 24 12526468c262Skre */ 12536468c262Skre char xbuf[24]; 12546468c262Skre 12554d523900Schristos strlcpy(buf, "", len); 12564d523900Schristos 12576468c262Skre for (i = 0; i < __arraycount(gpt_attr); i++) { 12586468c262Skre /* 12596468c262Skre * if the attribute is specified in one of bits 12606468c262Skre * 48..63, it should depend upon the defining 12616468c262Skre * partition type for that attribute. Currently 12626468c262Skre * we have no idea what that is, so... 12636468c262Skre * 12646468c262Skre * Also note that for some partition types, these 12656468c262Skre * fields are not a single bit boolean, but several 12666468c262Skre * bits to form a numeric value. That we could handle. 12676468c262Skre */ 12686468c262Skre 12694d523900Schristos if (attributes & gpt_attr[i].mask) { 12704d523900Schristos strlcat(buf, buf[0] ? ", " : "", len); 12714d523900Schristos strlcat(buf, gpt_attr[i].name, len); 12726468c262Skre #if 0 12736468c262Skre /* 12746468c262Skre * there are none currently defined, so this is untestable 12756468c262Skre * (it does build however). 12766468c262Skre */ 12776468c262Skre if (gpt_attr[i].mask & (gpt_attr[i].mask - 1)) { 1278331de78cSkre /* This only happens in bits 46..63 */ 12796468c262Skre 12806468c262Skre /* 12816468c262Skre * xbuf is big enough for "=65535\0" 12826468c262Skre * which is the biggest possible value 12836468c262Skre */ 12846468c262Skre snprintf(xbuf, sizeof xbuf, "=%ju", 12856468c262Skre (uintmax_t) ( 12866468c262Skre (attributes & gpt_attr[i].mask) >> 12876468c262Skre (ffs((int)(gpt_attr[i].mask >> 48)) + 47) 12886468c262Skre )); 12896468c262Skre 12906468c262Skre strlcat(buf, xbuf, len); 12914d523900Schristos } 12926468c262Skre #endif 12936468c262Skre attributes &=~ gpt_attr[i].mask; 12946468c262Skre } 12956468c262Skre } 12966468c262Skre 12976468c262Skre if (attributes != 0) { 12986468c262Skre snprintf(xbuf, sizeof xbuf, "[%#jx]", (uintmax_t)attributes); 12996468c262Skre strlcat(buf, buf[0] ? ", " : "", len); 13006468c262Skre strlcat(buf, xbuf, len); 13016468c262Skre } 13026468c262Skre 13034d523900Schristos return buf; 13044d523900Schristos } 13054d523900Schristos 1306bbb4a8abSchristos int 1307bbb4a8abSchristos gpt_attr_update(gpt_t gpt, u_int entry, uint64_t set, uint64_t clr) 1308bbb4a8abSchristos { 1309bbb4a8abSchristos struct gpt_hdr *hdr; 1310bbb4a8abSchristos struct gpt_ent *ent; 1311bbb4a8abSchristos unsigned int i; 1312bbb4a8abSchristos 1313f9db7548Schristos if (entry == 0 || (set == 0 && clr == 0)) { 1314f9db7548Schristos gpt_warnx(gpt, "Nothing to set"); 1315bbb4a8abSchristos return -1; 1316f9db7548Schristos } 1317bbb4a8abSchristos 1318bbb4a8abSchristos if ((hdr = gpt_hdr(gpt)) == NULL) 1319bbb4a8abSchristos return -1; 1320bbb4a8abSchristos 1321bbb4a8abSchristos if (entry > le32toh(hdr->hdr_entries)) { 1322bbb4a8abSchristos gpt_warnx(gpt, "Index %u out of range (%u max)", 1323bbb4a8abSchristos entry, le32toh(hdr->hdr_entries)); 1324bbb4a8abSchristos return -1; 1325bbb4a8abSchristos } 1326bbb4a8abSchristos 1327bbb4a8abSchristos i = entry - 1; 1328bbb4a8abSchristos ent = gpt_ent_primary(gpt, i); 1329bbb4a8abSchristos if (gpt_uuid_is_nil(ent->ent_type)) { 1330bbb4a8abSchristos gpt_warnx(gpt, "Entry at index %u is unused", entry); 1331bbb4a8abSchristos return -1; 1332bbb4a8abSchristos } 1333bbb4a8abSchristos 1334bbb4a8abSchristos ent->ent_attr &= ~clr; 1335bbb4a8abSchristos ent->ent_attr |= set; 1336bbb4a8abSchristos 1337bbb4a8abSchristos if (gpt_write_primary(gpt) == -1) 1338bbb4a8abSchristos return -1; 1339bbb4a8abSchristos 1340bbb4a8abSchristos ent = gpt_ent_backup(gpt, i); 1341bbb4a8abSchristos ent->ent_attr &= ~clr; 1342bbb4a8abSchristos ent->ent_attr |= set; 1343bbb4a8abSchristos 1344bbb4a8abSchristos if (gpt_write_backup(gpt) == -1) 1345bbb4a8abSchristos return -1; 1346bbb4a8abSchristos gpt_msg(gpt, "Partition %d attributes updated", entry); 1347bbb4a8abSchristos return 0; 1348bbb4a8abSchristos } 1349bbb4a8abSchristos 1350bbb4a8abSchristos int 1351f9db7548Schristos gpt_uint_get(gpt_t gpt, u_int *entry) 1352bbb4a8abSchristos { 1353bbb4a8abSchristos char *p; 1354bbb4a8abSchristos if (*entry > 0) 1355bbb4a8abSchristos return -1; 13565c1ccc6eSchristos *entry = (u_int)strtoul(optarg, &p, 10); 1357f9db7548Schristos if (*p != 0 || *entry < 1) { 1358f9db7548Schristos gpt_warn(gpt, "Bad number `%s'", optarg); 1359bbb4a8abSchristos return -1; 1360f9db7548Schristos } 1361bbb4a8abSchristos return 0; 1362bbb4a8abSchristos } 13630f004afeSchristos int 13640f004afeSchristos gpt_uuid_get(gpt_t gpt, gpt_uuid_t *uuid) 13650f004afeSchristos { 13660f004afeSchristos if (!gpt_uuid_is_nil(*uuid)) 13670f004afeSchristos return -1; 13680f004afeSchristos if (gpt_uuid_parse(optarg, *uuid) != 0) { 1369683824ccSchristos gpt_warnx(gpt, "Can't parse uuid/type `%s'", optarg); 13700f004afeSchristos return -1; 13710f004afeSchristos } 13720f004afeSchristos return 0; 13730f004afeSchristos } 13740f004afeSchristos 13750f004afeSchristos int 13760f004afeSchristos gpt_name_get(gpt_t gpt, void *v) 13770f004afeSchristos { 13780f004afeSchristos char **name = v; 13790f004afeSchristos if (*name != NULL) 13800f004afeSchristos return -1; 13810f004afeSchristos *name = strdup(optarg); 13820f004afeSchristos if (*name == NULL) { 13830f004afeSchristos gpt_warn(gpt, "Can't copy string"); 13840f004afeSchristos return -1; 13850f004afeSchristos } 13860f004afeSchristos return 0; 13870f004afeSchristos } 13885c1ccc6eSchristos 13895c1ccc6eSchristos void 13905c1ccc6eSchristos gpt_show_num(const char *prompt, uintmax_t num) 13915c1ccc6eSchristos { 13925c1ccc6eSchristos #ifdef HN_AUTOSCALE 13935c1ccc6eSchristos char human_num[5]; 13945c1ccc6eSchristos if (humanize_number(human_num, 5, (int64_t)num , 13955c1ccc6eSchristos "", HN_AUTOSCALE, HN_NOSPACE|HN_B) < 0) 13965c1ccc6eSchristos human_num[0] = '\0'; 13975c1ccc6eSchristos #endif 13985c1ccc6eSchristos printf("%s: %ju", prompt, num); 13995c1ccc6eSchristos #ifdef HN_AUTOSCALE 14005c1ccc6eSchristos if (human_num[0] != '\0') 14015c1ccc6eSchristos printf(" (%s)", human_num); 14025c1ccc6eSchristos #endif 14035c1ccc6eSchristos printf("\n"); 14045c1ccc6eSchristos } 14055b47b594Schristos 14065b47b594Schristos int 14075b47b594Schristos gpt_add_hdr(gpt_t gpt, int type, off_t loc) 14085b47b594Schristos { 14095b47b594Schristos void *p; 14105b47b594Schristos map_t *t; 14115b47b594Schristos const char *msg; 14125b47b594Schristos 14135b47b594Schristos switch (type) { 14145b47b594Schristos case MAP_TYPE_PRI_GPT_HDR: 14155b47b594Schristos t = &gpt->gpt; 14165b47b594Schristos msg = "primary"; 14175b47b594Schristos break; 14185b47b594Schristos case MAP_TYPE_SEC_GPT_HDR: 14195b47b594Schristos t = &gpt->tpg; 14205b47b594Schristos msg = "secondary"; 14215b47b594Schristos break; 14225b47b594Schristos default: 14235b47b594Schristos gpt_warnx(gpt, "Unknown GPT header type %d", type); 14245b47b594Schristos return -1; 14255b47b594Schristos } 14265b47b594Schristos 14275b47b594Schristos if ((p = calloc(1, gpt->secsz)) == NULL) { 14285b47b594Schristos gpt_warn(gpt, "Error allocating %s GPT header", msg); 14295b47b594Schristos return -1; 14305b47b594Schristos } 14315b47b594Schristos 14325b47b594Schristos *t = map_add(gpt, loc, 1LL, type, p, 1); 14335b47b594Schristos if (*t == NULL) { 14345b47b594Schristos gpt_warn(gpt, "Error adding %s GPT header", msg); 14355b47b594Schristos free(p); 14365b47b594Schristos return -1; 14375b47b594Schristos } 14385b47b594Schristos return 0; 14395b47b594Schristos } 1440