gpt.c revision 1.48 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.48 2015/12/01 02:03:55 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
62 char device_path[MAXPATHLEN];
63 const char *device_arg;
64 const char *device_name;
65
66 off_t mediasz;
67
68 u_int parts;
69 u_int secsz;
70
71 int readonly, verbose, quiet, nosync;
72
73 static int modified;
74
75 static uint32_t crc32_tab[] = {
76 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
77 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
78 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
79 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
80 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
81 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
82 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
83 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
84 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
85 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
86 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
87 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
88 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
89 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
90 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
91 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
92 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
93 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
94 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
95 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
96 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
97 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
98 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
99 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
100 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
101 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
102 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
103 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
104 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
105 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
106 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
107 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
108 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
109 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
110 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
111 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
112 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
113 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
114 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
115 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
116 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
117 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
118 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
119 };
120
121 uint32_t
122 crc32(const void *buf, size_t size)
123 {
124 const uint8_t *p;
125 uint32_t crc;
126
127 p = buf;
128 crc = ~0U;
129
130 while (size--)
131 crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
132
133 return crc ^ ~0U;
134 }
135
136 uint8_t *
137 utf16_to_utf8(uint16_t *s16)
138 {
139 static uint8_t *s8 = NULL;
140 static size_t s8len = 0;
141 size_t s8idx, s16idx, s16len;
142 uint32_t utfchar;
143 unsigned int c;
144
145 s16len = 0;
146 while (s16[s16len++] != 0)
147 ;
148 if (s8len < s16len * 3) {
149 if (s8 != NULL)
150 free(s8);
151 s8len = s16len * 3;
152 s8 = calloc(s16len, 3);
153 }
154 s8idx = s16idx = 0;
155 while (s16idx < s16len) {
156 utfchar = le16toh(s16[s16idx++]);
157 if ((utfchar & 0xf800) == 0xd800) {
158 c = le16toh(s16[s16idx]);
159 if ((utfchar & 0x400) != 0 || (c & 0xfc00) != 0xdc00)
160 utfchar = 0xfffd;
161 else
162 s16idx++;
163 }
164 if (utfchar < 0x80) {
165 s8[s8idx++] = utfchar;
166 } else if (utfchar < 0x800) {
167 s8[s8idx++] = 0xc0 | (utfchar >> 6);
168 s8[s8idx++] = 0x80 | (utfchar & 0x3f);
169 } else if (utfchar < 0x10000) {
170 s8[s8idx++] = 0xe0 | (utfchar >> 12);
171 s8[s8idx++] = 0x80 | ((utfchar >> 6) & 0x3f);
172 s8[s8idx++] = 0x80 | (utfchar & 0x3f);
173 } else if (utfchar < 0x200000) {
174 s8[s8idx++] = 0xf0 | (utfchar >> 18);
175 s8[s8idx++] = 0x80 | ((utfchar >> 12) & 0x3f);
176 s8[s8idx++] = 0x80 | ((utfchar >> 6) & 0x3f);
177 s8[s8idx++] = 0x80 | (utfchar & 0x3f);
178 }
179 }
180 return (s8);
181 }
182
183 void
184 utf8_to_utf16(const uint8_t *s8, uint16_t *s16, size_t s16len)
185 {
186 size_t s16idx, s8idx, s8len;
187 uint32_t utfchar = 0;
188 unsigned int c, utfbytes;
189
190 s8len = 0;
191 while (s8[s8len++] != 0)
192 ;
193 s8idx = s16idx = 0;
194 utfbytes = 0;
195 do {
196 c = s8[s8idx++];
197 if ((c & 0xc0) != 0x80) {
198 /* Initial characters. */
199 if (utfbytes != 0) {
200 /* Incomplete encoding. */
201 s16[s16idx++] = htole16(0xfffd);
202 if (s16idx == s16len) {
203 s16[--s16idx] = 0;
204 return;
205 }
206 }
207 if ((c & 0xf8) == 0xf0) {
208 utfchar = c & 0x07;
209 utfbytes = 3;
210 } else if ((c & 0xf0) == 0xe0) {
211 utfchar = c & 0x0f;
212 utfbytes = 2;
213 } else if ((c & 0xe0) == 0xc0) {
214 utfchar = c & 0x1f;
215 utfbytes = 1;
216 } else {
217 utfchar = c & 0x7f;
218 utfbytes = 0;
219 }
220 } else {
221 /* Followup characters. */
222 if (utfbytes > 0) {
223 utfchar = (utfchar << 6) + (c & 0x3f);
224 utfbytes--;
225 } else if (utfbytes == 0)
226 utfbytes = -1;
227 }
228 if (utfbytes == 0) {
229 if (utfchar >= 0x10000 && s16idx + 2 >= s16len)
230 utfchar = 0xfffd;
231 if (utfchar >= 0x10000) {
232 s16[s16idx++] =
233 htole16(0xd800 | ((utfchar>>10)-0x40));
234 s16[s16idx++] =
235 htole16(0xdc00 | (utfchar & 0x3ff));
236 } else
237 s16[s16idx++] = htole16(utfchar);
238 if (s16idx == s16len) {
239 s16[--s16idx] = 0;
240 return;
241 }
242 }
243 } while (c != 0);
244 }
245
246 void*
247 gpt_read(int fd, off_t lba, size_t count)
248 {
249 off_t ofs;
250 void *buf;
251
252 count *= secsz;
253 buf = malloc(count);
254 if (buf == NULL)
255 return (NULL);
256
257 ofs = lba * secsz;
258 if (lseek(fd, ofs, SEEK_SET) == ofs &&
259 read(fd, buf, count) == (ssize_t)count)
260 return (buf);
261
262 free(buf);
263 return (NULL);
264 }
265
266 int
267 gpt_write(int fd, map_t *map)
268 {
269 off_t ofs;
270 size_t count;
271
272 count = map->map_size * secsz;
273 ofs = map->map_start * secsz;
274 if (lseek(fd, ofs, SEEK_SET) != ofs ||
275 write(fd, map->map_data, count) != (ssize_t)count)
276 return -1;
277 modified = 1;
278 return 0;
279 }
280
281 static int
282 gpt_mbr(int fd, off_t lba)
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(fd, lba, 1);
290 if (mbr == NULL) {
291 if (!quiet)
292 warn("%s: read failed", device_name);
293 return (-1);
294 }
295
296 if (mbr->mbr_sig != htole16(MBR_SIG)) {
297 if (verbose)
298 gpt_msg("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
315 break;
316 }
317 if (pmbr && i == 4 && lba == 0) {
318 if (pmbr != 1 && !quiet)
319 warnx("%s: Suspicious PMBR at sector %ju",
320 device_name, (uintmax_t)lba);
321 else if (verbose > 1)
322 gpt_msg("PMBR at sector %ju", (uintmax_t)lba);
323 p = map_add(lba, 1LL, MAP_TYPE_PMBR, mbr);
324 return ((p == NULL) ? -1 : 0);
325 }
326 if (pmbr && !quiet)
327 warnx("%s: Suspicious MBR at sector %ju", device_name,
328 (uintmax_t)lba);
329 else if (verbose > 1)
330 gpt_msg("MBR at sector %ju", (uintmax_t)lba);
331
332 p = map_add(lba, 1LL, MAP_TYPE_MBR, mbr);
333 if (p == NULL)
334 return (-1);
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 warnx("%s: Malformed MBR at sector %llu", device_name,
345 (long long)lba);
346 continue;
347 }
348 /* start is relative to the offset of the MBR itself. */
349 start += lba;
350 if (verbose > 2)
351 gpt_msg("MBR part: type=%d, start=%ju, size=%ju",
352 mbr->mbr_part[i].part_typ,
353 (uintmax_t)start, (uintmax_t)size);
354 if (mbr->mbr_part[i].part_typ != MBR_PTYPE_EXT_LBA) {
355 m = map_add(start, size, MAP_TYPE_MBR_PART, p);
356 if (m == NULL)
357 return (-1);
358 m->map_index = i + 1;
359 } else {
360 if (gpt_mbr(fd, start) == -1)
361 return (-1);
362 }
363 }
364 return (0);
365 }
366
367 int
368 gpt_gpt(int fd, off_t lba, int found)
369 {
370 off_t size;
371 struct gpt_ent *ent;
372 struct gpt_hdr *hdr;
373 char *p;
374 map_t *m;
375 size_t blocks, tblsz;
376 unsigned int i;
377 uint32_t crc;
378
379 hdr = gpt_read(fd, lba, 1);
380 if (hdr == NULL)
381 return (-1);
382
383 if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)))
384 goto fail_hdr;
385
386 crc = le32toh(hdr->hdr_crc_self);
387 hdr->hdr_crc_self = 0;
388 if (crc32(hdr, le32toh(hdr->hdr_size)) != crc) {
389 if (verbose)
390 warnx("%s: Bad CRC in GPT header at sector %llu",
391 device_name, (long long)lba);
392 goto fail_hdr;
393 }
394
395 tblsz = le32toh(hdr->hdr_entries) * le32toh(hdr->hdr_entsz);
396 blocks = tblsz / secsz + ((tblsz % secsz) ? 1 : 0);
397
398 /* Use generic pointer to deal with hdr->hdr_entsz != sizeof(*ent). */
399 p = gpt_read(fd, le64toh(hdr->hdr_lba_table), blocks);
400 if (p == NULL) {
401 if (found) {
402 if (verbose)
403 warn("%s: Cannot read LBA table at sector %llu",
404 device_name, (unsigned long long)
405 le64toh(hdr->hdr_lba_table));
406 return (-1);
407 }
408 goto fail_hdr;
409 }
410
411 if (crc32(p, tblsz) != le32toh(hdr->hdr_crc_table)) {
412 if (verbose)
413 warnx("%s: Bad CRC in GPT table at sector %llu",
414 device_name,
415 (long long)le64toh(hdr->hdr_lba_table));
416 goto fail_ent;
417 }
418
419 if (verbose > 1)
420 warnx("%s: %s GPT at sector %llu", device_name,
421 (lba == 1) ? "Pri" : "Sec", (long long)lba);
422
423 m = map_add(lba, 1, (lba == 1)
424 ? MAP_TYPE_PRI_GPT_HDR : MAP_TYPE_SEC_GPT_HDR, hdr);
425 if (m == NULL)
426 return (-1);
427
428 m = map_add(le64toh(hdr->hdr_lba_table), blocks, (lba == 1)
429 ? MAP_TYPE_PRI_GPT_TBL : MAP_TYPE_SEC_GPT_TBL, p);
430 if (m == NULL)
431 return (-1);
432
433 if (lba != 1)
434 return (1);
435
436 for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
437 ent = (void*)(p + i * le32toh(hdr->hdr_entsz));
438 if (gpt_uuid_is_nil(ent->ent_type))
439 continue;
440
441 size = le64toh(ent->ent_lba_end) - le64toh(ent->ent_lba_start) +
442 1LL;
443 if (verbose > 2) {
444 char buf[128];
445 gpt_uuid_snprintf(buf, sizeof(buf), "%s",
446 ent->ent_type);
447 warnx("%s: GPT partition: type=%s, start=%llu, "
448 "size=%llu", device_name, buf,
449 (long long)le64toh(ent->ent_lba_start),
450 (long long)size);
451 }
452 m = map_add(le64toh(ent->ent_lba_start), size,
453 MAP_TYPE_GPT_PART, ent);
454 if (m == NULL)
455 return (-1);
456 m->map_index = i + 1;
457 }
458 return (1);
459
460 fail_ent:
461 free(p);
462
463 fail_hdr:
464 free(hdr);
465 return (0);
466 }
467
468 int
469 gpt_open(const char *dev, int flags)
470 {
471 struct stat sb;
472 int fd, mode, found;
473 off_t devsz;
474
475 mode = readonly ? O_RDONLY : O_RDWR|O_EXCL;
476
477 device_arg = device_name = dev;
478 fd = opendisk(dev, mode, device_path, sizeof(device_path), 0);
479 if (fd == -1) {
480 if (!quiet)
481 warn("Cannot open `%s'", device_name);
482 return -1;
483 }
484 device_name = device_path;
485
486 if (fstat(fd, &sb) == -1) {
487 if (!quiet)
488 warn("Cannot stat `%s'", device_name);
489 goto close;
490 }
491
492 if ((sb.st_mode & S_IFMT) != S_IFREG) {
493 if (secsz == 0) {
494 #ifdef DIOCGSECTORSIZE
495 if (ioctl(fd, DIOCGSECTORSIZE, &secsz) == -1) {
496 if (!quiet)
497 warn("Cannot get sector size for `%s'",
498 device_name);
499 goto close;
500 }
501 #endif
502 if (secsz == 0) {
503 if (!quiet)
504 warnx("Sector size for `%s' can't be 0",
505 device_name);
506 goto close;
507 }
508 }
509 if (mediasz == 0) {
510 #ifdef DIOCGMEDIASIZE
511 if (ioctl(fd, DIOCGMEDIASIZE, &mediasz) == -1) {
512 if (!quiet)
513 warn("Cannot get media size for `%s'",
514 device_name);
515 goto close;
516 }
517 #endif
518 if (mediasz == 0) {
519 if (!quiet)
520 warnx("Media size for `%s' can't be 0",
521 device_name);
522 goto close;
523 }
524 }
525 } else {
526 if (secsz == 0)
527 secsz = 512; /* Fixed size for files. */
528 if (mediasz == 0) {
529 if (sb.st_size % secsz) {
530 errno = EINVAL;
531 goto close;
532 }
533 mediasz = sb.st_size;
534 }
535 }
536
537 /*
538 * We require an absolute minimum of 6 sectors. One for the MBR,
539 * 2 for the GPT header, 2 for the GPT table and one to hold some
540 * user data. Let's catch this extreme border case here so that
541 * we don't have to worry about it later.
542 */
543 devsz = mediasz / secsz;
544 if (devsz < 6) {
545 if (!quiet)
546 warnx("Need 6 sectors on '%s' we have %ju",
547 device_name, (uintmax_t)devsz);
548 goto close;
549 }
550
551 if (verbose) {
552 gpt_msg("mediasize=%ju; sectorsize=%u; blocks=%ju",
553 (uintmax_t)mediasz, secsz, (uintmax_t)devsz);
554 }
555
556 map_init(devsz);
557
558 if (gpt_mbr(fd, 0LL) == -1)
559 goto close;
560 if ((found = gpt_gpt(fd, 1LL, 1)) == -1)
561 goto close;
562 if (gpt_gpt(fd, devsz - 1LL, found) == -1)
563 goto close;
564
565 return (fd);
566
567 close:
568 close(fd);
569 return (-1);
570 }
571
572 void
573 gpt_close(int fd)
574 {
575
576 if (!modified)
577 goto out;
578
579 if (!nosync) {
580 #ifdef DIOCMWEDGES
581 int bits;
582 if (ioctl(fd, DIOCMWEDGES, &bits) == -1)
583 warn("Can't update wedge information");
584 else
585 goto out;
586 #endif
587 }
588 gpt_msg("You need to run \"dkctl %s makewedges\""
589 " for the changes to take effect\n", device_name);
590
591 out:
592 close(fd);
593 }
594
595 void
596 gpt_msg(const char *fmt, ...)
597 {
598 va_list ap;
599
600 if (quiet)
601 return;
602 printf("%s: ", device_name);
603 va_start(ap, fmt);
604 vprintf(fmt, ap);
605 va_end(ap);
606 printf("\n");
607 }
608