2 * Copyright (c) 2002 Marcel Moolenaar
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
26 * CRC32 code derived from work by Gary S. Brown.
29 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD: src/sbin/gpt/gpt.c,v 1.16 2006/07/07 02:44:23 marcel Exp $");
37 #include <sys/param.h>
38 #include <sys/types.h>
41 #include <sys/ioctl.h>
55 #include <prop/proplib.h>
56 #include <sys/drvctlio.h>
62 char device_path
[MAXPATHLEN
];
63 const char *device_arg
;
71 int readonly
, verbose
;
73 static uint32_t crc32_tab
[] = {
74 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
75 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
76 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
77 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
78 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
79 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
80 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
81 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
82 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
83 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
84 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
85 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
86 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
87 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
88 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
89 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
90 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
91 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
92 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
93 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
94 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
95 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
96 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
97 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
98 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
99 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
100 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
101 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
102 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
103 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
104 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
105 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
106 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
107 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
108 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
109 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
110 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
111 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
112 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
113 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
114 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
115 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
116 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
120 crc32(const void *buf
, size_t size
)
129 crc
= crc32_tab
[(crc
^ *p
++) & 0xFF] ^ (crc
>> 8);
135 utf16_to_utf8(uint16_t *s16
)
137 static uint8_t *s8
= NULL
;
138 static size_t s8len
= 0;
139 size_t s8idx
, s16idx
, s16len
;
144 while (s16
[s16len
++] != 0)
146 if (s8len
< s16len
* 3) {
150 s8
= calloc(s16len
, 3);
153 while (s16idx
< s16len
) {
154 utfchar
= le16toh(s16
[s16idx
++]);
155 if ((utfchar
& 0xf800) == 0xd800) {
156 c
= le16toh(s16
[s16idx
]);
157 if ((utfchar
& 0x400) != 0 || (c
& 0xfc00) != 0xdc00)
162 if (utfchar
< 0x80) {
163 s8
[s8idx
++] = utfchar
;
164 } else if (utfchar
< 0x800) {
165 s8
[s8idx
++] = 0xc0 | (utfchar
>> 6);
166 s8
[s8idx
++] = 0x80 | (utfchar
& 0x3f);
167 } else if (utfchar
< 0x10000) {
168 s8
[s8idx
++] = 0xe0 | (utfchar
>> 12);
169 s8
[s8idx
++] = 0x80 | ((utfchar
>> 6) & 0x3f);
170 s8
[s8idx
++] = 0x80 | (utfchar
& 0x3f);
171 } else if (utfchar
< 0x200000) {
172 s8
[s8idx
++] = 0xf0 | (utfchar
>> 18);
173 s8
[s8idx
++] = 0x80 | ((utfchar
>> 12) & 0x3f);
174 s8
[s8idx
++] = 0x80 | ((utfchar
>> 6) & 0x3f);
175 s8
[s8idx
++] = 0x80 | (utfchar
& 0x3f);
182 utf8_to_utf16(const uint8_t *s8
, uint16_t *s16
, size_t s16len
)
184 size_t s16idx
, s8idx
, s8len
;
185 uint32_t utfchar
= 0;
186 unsigned int c
, utfbytes
;
189 while (s8
[s8len
++] != 0)
195 if ((c
& 0xc0) != 0x80) {
196 /* Initial characters. */
198 /* Incomplete encoding. */
199 s16
[s16idx
++] = 0xfffd;
200 if (s16idx
== s16len
) {
205 if ((c
& 0xf8) == 0xf0) {
208 } else if ((c
& 0xf0) == 0xe0) {
211 } else if ((c
& 0xe0) == 0xc0) {
219 /* Followup characters. */
221 utfchar
= (utfchar
<< 6) + (c
& 0x3f);
223 } else if (utfbytes
== 0)
227 if (utfchar
>= 0x10000 && s16idx
+ 2 >= s16len
)
229 if (utfchar
>= 0x10000) {
230 s16
[s16idx
++] = 0xd800 | ((utfchar
>>10)-0x40);
231 s16
[s16idx
++] = 0xdc00 | (utfchar
& 0x3ff);
233 s16
[s16idx
++] = utfchar
;
234 if (s16idx
== s16len
) {
243 le_uuid_dec(void const *buf
, uuid_t
*uuid
)
249 uuid
->time_low
= le32dec(p
);
250 uuid
->time_mid
= le16dec(p
+ 4);
251 uuid
->time_hi_and_version
= le16dec(p
+ 6);
252 uuid
->clock_seq_hi_and_reserved
= p
[8];
253 uuid
->clock_seq_low
= p
[9];
254 for (i
= 0; i
< _UUID_NODE_LEN
; i
++)
255 uuid
->node
[i
] = p
[10 + i
];
259 le_uuid_enc(void *buf
, uuid_t
const *uuid
)
265 le32enc(p
, uuid
->time_low
);
266 le16enc(p
+ 4, uuid
->time_mid
);
267 le16enc(p
+ 6, uuid
->time_hi_and_version
);
268 p
[8] = uuid
->clock_seq_hi_and_reserved
;
269 p
[9] = uuid
->clock_seq_low
;
270 for (i
= 0; i
< _UUID_NODE_LEN
; i
++)
271 p
[10 + i
] = uuid
->node
[i
];
275 parse_uuid(const char *s
, uuid_t
*uuid
)
279 uuid_from_string(s
, uuid
, &status
);
280 if (status
== uuid_s_ok
)
285 if (strcmp(s
, "bios") == 0) {
286 uuid_t bios
= GPT_ENT_TYPE_BIOS
;
292 if (strcmp(s
, "ccd") == 0) {
293 uuid_t ccd
= GPT_ENT_TYPE_NETBSD_CCD
;
296 } else if (strcmp(s
, "cgd") == 0) {
297 uuid_t cgd
= GPT_ENT_TYPE_NETBSD_CGD
;
303 if (strcmp(s
, "efi") == 0) {
304 uuid_t efi
= GPT_ENT_TYPE_EFI
;
310 if (strcmp(s
, "hfs") == 0) {
311 uuid_t hfs
= GPT_ENT_TYPE_APPLE_HFS
;
317 if (strcmp(s
, "lfs") == 0) {
318 uuid_t lfs
= GPT_ENT_TYPE_NETBSD_LFS
;
321 } else if (strcmp(s
, "linux") == 0) {
322 uuid_t lnx
= GPT_ENT_TYPE_MS_BASIC_DATA
;
328 if (strcmp(s
, "raid") == 0) {
329 uuid_t raid
= GPT_ENT_TYPE_NETBSD_RAIDFRAME
;
335 if (strcmp(s
, "swap") == 0) {
336 uuid_t sw
= GPT_ENT_TYPE_NETBSD_SWAP
;
342 if (strcmp(s
, "ufs") == 0) {
343 uuid_t ufs
= GPT_ENT_TYPE_NETBSD_FFS
;
349 if (strcmp(s
, "windows") == 0) {
350 uuid_t win
= GPT_ENT_TYPE_MS_BASIC_DATA
;
360 gpt_read(int fd
, off_t lba
, size_t count
)
371 if (lseek(fd
, ofs
, SEEK_SET
) == ofs
&&
372 read(fd
, buf
, count
) == (ssize_t
)count
)
380 gpt_write(int fd
, map_t
*map
)
385 count
= map
->map_size
* secsz
;
386 ofs
= map
->map_start
* secsz
;
387 if (lseek(fd
, ofs
, SEEK_SET
) == ofs
&&
388 write(fd
, map
->map_data
, count
) == (ssize_t
)count
)
394 gpt_mbr(int fd
, off_t lba
)
399 unsigned int i
, pmbr
;
401 mbr
= gpt_read(fd
, lba
, 1);
405 if (mbr
->mbr_sig
!= htole16(MBR_SIG
)) {
407 warnx("%s: MBR not found at sector %llu", device_name
,
414 * Differentiate between a regular MBR and a PMBR. This is more
415 * convenient in general. A PMBR is one with a single partition
419 for (i
= 0; i
< 4; i
++) {
420 if (mbr
->mbr_part
[i
].part_typ
== 0)
422 if (mbr
->mbr_part
[i
].part_typ
== 0xee)
427 if (pmbr
&& i
== 4 && lba
== 0) {
429 warnx("%s: Suspicious PMBR at sector %llu",
430 device_name
, (long long)lba
);
431 else if (verbose
> 1)
432 warnx("%s: PMBR at sector %llu", device_name
,
434 p
= map_add(lba
, 1LL, MAP_TYPE_PMBR
, mbr
);
435 return ((p
== NULL
) ? -1 : 0);
438 warnx("%s: Suspicious MBR at sector %llu", device_name
,
440 else if (verbose
> 1)
441 warnx("%s: MBR at sector %llu", device_name
, (long long)lba
);
443 p
= map_add(lba
, 1LL, MAP_TYPE_MBR
, mbr
);
446 for (i
= 0; i
< 4; i
++) {
447 if (mbr
->mbr_part
[i
].part_typ
== 0 ||
448 mbr
->mbr_part
[i
].part_typ
== 0xee)
450 start
= le16toh(mbr
->mbr_part
[i
].part_start_hi
);
451 start
= (start
<< 16) + le16toh(mbr
->mbr_part
[i
].part_start_lo
);
452 size
= le16toh(mbr
->mbr_part
[i
].part_size_hi
);
453 size
= (size
<< 16) + le16toh(mbr
->mbr_part
[i
].part_size_lo
);
454 if (start
== 0 && size
== 0) {
455 warnx("%s: Malformed MBR at sector %llu", device_name
,
459 /* start is relative to the offset of the MBR itself. */
462 warnx("%s: MBR part: type=%d, start=%llu, size=%llu",
463 device_name
, mbr
->mbr_part
[i
].part_typ
,
464 (long long)start
, (long long)size
);
465 if (mbr
->mbr_part
[i
].part_typ
!= 15) {
466 m
= map_add(start
, size
, MAP_TYPE_MBR_PART
, p
);
469 m
->map_index
= i
+ 1;
471 if (gpt_mbr(fd
, start
) == -1)
480 drvctl(const char *name
, u_int
*sector_size
, off_t
*media_size
)
482 prop_dictionary_t command_dict
, args_dict
, results_dict
, data_dict
,
484 prop_string_t string
;
485 prop_number_t number
;
489 if ((dfd
= open("/dev/drvctl", O_RDONLY
)) == -1) {
490 warn("%s: /dev/drvctl", __func__
);
494 command_dict
= prop_dictionary_create();
495 args_dict
= prop_dictionary_create();
497 string
= prop_string_create_cstring_nocopy("get-properties");
498 prop_dictionary_set(command_dict
, "drvctl-command", string
);
499 prop_object_release(string
);
501 if ((dname
= strdup(name
[0] == 'r' ? name
+ 1 : name
)) == NULL
) {
505 for (p
= dname
; *p
; p
++)
507 for (--p
; p
>= dname
&& !isdigit((unsigned char)*p
); *p
-- = '\0')
510 string
= prop_string_create_cstring(dname
);
512 prop_dictionary_set(args_dict
, "device-name", string
);
513 prop_object_release(string
);
515 prop_dictionary_set(command_dict
, "drvctl-arguments", args_dict
);
516 prop_object_release(args_dict
);
518 res
= prop_dictionary_sendrecv_ioctl(command_dict
, dfd
, DRVCTLCOMMAND
,
521 prop_object_release(command_dict
);
523 warn("%s: prop_dictionary_sendrecv_ioctl", __func__
);
528 number
= prop_dictionary_get(results_dict
, "drvctl-error");
529 if ((errno
= prop_number_integer_value(number
)) != 0)
532 data_dict
= prop_dictionary_get(results_dict
, "drvctl-result-data");
533 if (data_dict
== NULL
)
536 disk_info
= prop_dictionary_get(data_dict
, "disk-info");
537 if (disk_info
== NULL
)
540 geometry
= prop_dictionary_get(disk_info
, "geometry");
541 if (geometry
== NULL
)
544 number
= prop_dictionary_get(geometry
, "sector-size");
548 *sector_size
= prop_number_integer_value(number
);
550 number
= prop_dictionary_get(geometry
, "sectors-per-unit");
554 *media_size
= prop_number_integer_value(number
) * *sector_size
;
564 gpt_gpt(int fd
, off_t lba
)
572 size_t blocks
, tblsz
;
576 hdr
= gpt_read(fd
, lba
, 1);
580 if (memcmp(hdr
->hdr_sig
, GPT_HDR_SIG
, sizeof(hdr
->hdr_sig
)))
583 crc
= le32toh(hdr
->hdr_crc_self
);
584 hdr
->hdr_crc_self
= 0;
585 if (crc32(hdr
, le32toh(hdr
->hdr_size
)) != crc
) {
587 warnx("%s: Bad CRC in GPT header at sector %llu",
588 device_name
, (long long)lba
);
592 tblsz
= le32toh(hdr
->hdr_entries
) * le32toh(hdr
->hdr_entsz
);
593 blocks
= tblsz
/ secsz
+ ((tblsz
% secsz
) ? 1 : 0);
595 /* Use generic pointer to deal with hdr->hdr_entsz != sizeof(*ent). */
596 p
= gpt_read(fd
, le64toh(hdr
->hdr_lba_table
), blocks
);
600 if (crc32(p
, tblsz
) != le32toh(hdr
->hdr_crc_table
)) {
602 warnx("%s: Bad CRC in GPT table at sector %llu",
604 (long long)le64toh(hdr
->hdr_lba_table
));
609 warnx("%s: %s GPT at sector %llu", device_name
,
610 (lba
== 1) ? "Pri" : "Sec", (long long)lba
);
612 m
= map_add(lba
, 1, (lba
== 1)
613 ? MAP_TYPE_PRI_GPT_HDR
: MAP_TYPE_SEC_GPT_HDR
, hdr
);
617 m
= map_add(le64toh(hdr
->hdr_lba_table
), blocks
, (lba
== 1)
618 ? MAP_TYPE_PRI_GPT_TBL
: MAP_TYPE_SEC_GPT_TBL
, p
);
625 for (i
= 0; i
< le32toh(hdr
->hdr_entries
); i
++) {
626 ent
= (void*)(p
+ i
* le32toh(hdr
->hdr_entsz
));
627 if (uuid_is_nil((uuid_t
*)&ent
->ent_type
, NULL
))
630 size
= le64toh(ent
->ent_lba_end
) - le64toh(ent
->ent_lba_start
) +
633 le_uuid_dec(&ent
->ent_type
, &type
);
634 uuid_to_string(&type
, &s
, NULL
);
636 "%s: GPT partition: type=%s, start=%llu, size=%llu", device_name
, s
,
637 (long long)le64toh(ent
->ent_lba_start
),
641 m
= map_add(le64toh(ent
->ent_lba_start
), size
,
642 MAP_TYPE_GPT_PART
, ent
);
645 m
->map_index
= i
+ 1;
658 gpt_open(const char *dev
)
663 mode
= readonly
? O_RDONLY
: O_RDWR
|O_EXCL
;
667 strlcpy(device_path
, dev
, sizeof(device_path
));
668 if ((fd
= open(device_path
, mode
)) != -1)
671 snprintf(device_path
, sizeof(device_path
), "%s%s", _PATH_DEV
, dev
);
672 device_name
= device_path
+ strlen(_PATH_DEV
);
673 if ((fd
= open(device_path
, mode
)) != -1)
679 fd
= opendisk(dev
, mode
, device_path
, sizeof(device_path
), 0);
682 device_name
= device_path
+ strlen(_PATH_DEV
);
685 if (fstat(fd
, &sb
) == -1)
688 if ((sb
.st_mode
& S_IFMT
) != S_IFREG
) {
689 #ifdef DIOCGSECTORSIZE
690 if (ioctl(fd
, DIOCGSECTORSIZE
, &secsz
) == -1 ||
691 ioctl(fd
, DIOCGMEDIASIZE
, &mediasz
) == -1)
695 if (drvctl(device_name
, &secsz
, &mediasz
) == -1)
699 secsz
= 512; /* Fixed size for files. */
700 if (sb
.st_size
% secsz
) {
704 mediasz
= sb
.st_size
;
708 * We require an absolute minimum of 6 sectors. One for the MBR,
709 * 2 for the GPT header, 2 for the GPT table and one to hold some
710 * user data. Let's catch this extreme border case here so that
711 * we don't have to worry about it later.
713 if (mediasz
/ secsz
< 6) {
719 warnx("%s: mediasize=%llu; sectorsize=%u; blocks=%llu",
720 device_name
, (long long)mediasz
, secsz
,
721 (long long)(mediasz
/ secsz
));
723 map_init(mediasz
/ secsz
);
725 if (gpt_mbr(fd
, 0LL) == -1)
727 if (gpt_gpt(fd
, 1LL) == -1)
729 if (gpt_gpt(fd
, mediasz
/ secsz
- 1LL) == -1)
742 /* XXX post processing? */
747 int (*fptr
)(int, char *[]);
751 { cmd_create
, "create" },
752 { cmd_destroy
, "destroy" },
754 { cmd_label
, "label" },
755 { cmd_migrate
, "migrate" },
756 { cmd_recover
, "recover" },
757 { cmd_remove
, "remove" },
759 { cmd_show
, "show" },
767 extern const char addmsg
[], createmsg
[], destroymsg
[];
768 extern const char labelmsg1
[], labelmsg2
[], labelmsg3
[];
769 extern const char migratemsg
[], recovermsg
[], removemsg1
[];
770 extern const char removemsg2
[], showmsg
[];
784 getprogname(), addmsg
,
785 getprogname(), createmsg
,
786 getprogname(), destroymsg
,
787 getprogname(), labelmsg1
,
788 getprogname(), labelmsg2
,
789 (int)strlen(getprogname()), "", labelmsg3
,
790 getprogname(), migratemsg
,
791 getprogname(), recovermsg
,
792 getprogname(), removemsg1
,
793 getprogname(), removemsg2
,
794 getprogname(), showmsg
);
799 prefix(const char *cmd
)
805 pfx
= malloc(strlen(prg
) + strlen(cmd
) + 2);
806 /* Don't bother failing. It's not important */
810 sprintf(pfx
, "%s %s", prg
, cmd
);
815 main(int argc
, char *argv
[])
820 /* Get the generic options */
821 while ((ch
= getopt(argc
, argv
, "p:rv")) != -1) {
826 parts
= strtoul(optarg
, &p
, 10);
827 if (*p
!= 0 || parts
< 1)
846 cmd
= argv
[optind
++];
847 for (i
= 0; cmdsw
[i
].name
!= NULL
&& strcmp(cmd
, cmdsw
[i
].name
); i
++);
849 if (cmdsw
[i
].fptr
== NULL
)
850 errx(1, "unknown command: %s", cmd
);
853 return ((*cmdsw
[i
].fptr
)(argc
, argv
));