o Add functionality to add a GPT partition,

o  Use DCE compliant UUID functions and provide local
   implementations if they don't exist,
o  Move dumping of the map to show.c and print the
   partition type,
o  Some cleanups and rearrangements.

The default GPT partition type is UFS. When no starting block
or size are specified, the tool will create a partition in the
first free space it find (or that fits, depending on the size).
This commit is contained in:
Marcel Moolenaar 2002-10-27 03:23:05 +00:00
parent eb0f0a174a
commit 5d5e1c2b12
9 changed files with 389 additions and 104 deletions

View File

@ -1,7 +1,7 @@
# $FreeBSD$
PROG= gpt
SRCS= create.c destroy.c gpt.c map.c migrate.c recover.c show.c
SRCS= add.c create.c destroy.c gpt.c map.c migrate.c recover.c show.c
WARNS= 4
NOMAN= not yet

192
sbin/gpt/add.c Normal file
View File

@ -0,0 +1,192 @@
/*
* Copyright (c) 2002 Marcel Moolenaar
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <sys/types.h>
#include <sys/disklabel.h>
#include <sys/uuid.h>
#include <sys/gpt.h>
#include <err.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "map.h"
#include "gpt.h"
off_t block, size;
uuid_t type;
static void
usage_add(void)
{
fprintf(stderr,
"usage: %s [-bst] device\n", getprogname());
exit(1);
}
static void
add(int fd)
{
map_t *gpt, *tpg;
map_t *tbl, *lbt;
map_t *map;
struct gpt_hdr *hdr;
struct gpt_ent *ent;
unsigned int i;
gpt = map_find(MAP_TYPE_PRI_GPT_HDR);
if (gpt == NULL) {
warnx("%s: error: device does not contain a GPT", device_name);
return;
}
tpg = map_find(MAP_TYPE_SEC_GPT_HDR);
if (tpg == NULL) {
warnx("%s: error: no secundary table; run recover",
device_name);
return;
}
tbl = map_find(MAP_TYPE_PRI_GPT_TBL);
lbt = map_find(MAP_TYPE_SEC_GPT_TBL);
if (tbl == NULL || lbt == NULL) {
warnx("%s: error: run recover -- trust me", device_name);
return;
}
/* Create UFS partitions by default. */
if (uuid_is_nil(&type, NULL)) {
uuid_t ufs = GPT_ENT_TYPE_FREEBSD_UFS;
type = ufs;
}
map = map_alloc(block, size);
if (map == NULL) {
warnx("%s: error: no space available on device", device_name);
return;
}
/* Find empty slot in GPT table. */
hdr = gpt->map_data;
for (i = 0; i < hdr->hdr_entries; i++) {
ent = (void*)((char*)tbl->map_data + i * hdr->hdr_entsz);
if (uuid_is_nil(&ent->ent_type, NULL))
break;
}
if (i == hdr->hdr_entries) {
warnx("%s: error: no available table entries", device_name);
return;
}
ent->ent_type = type;
ent->ent_lba_start = map->map_start;
ent->ent_lba_end = map->map_start + map->map_size - 1LL;
hdr->hdr_crc_table = crc32(tbl->map_data,
hdr->hdr_entries * hdr->hdr_entsz);
hdr->hdr_crc_self = 0;
hdr->hdr_crc_self = crc32(hdr, hdr->hdr_size);
gpt_write(fd, gpt);
gpt_write(fd, tbl);
hdr = tpg->map_data;
ent = (void*)((char*)lbt->map_data + i * hdr->hdr_entsz);
ent->ent_type = type;
ent->ent_lba_start = map->map_start;
ent->ent_lba_end = map->map_start + map->map_size - 1LL;
hdr->hdr_crc_table = crc32(lbt->map_data,
hdr->hdr_entries * hdr->hdr_entsz);
hdr->hdr_crc_self = 0;
hdr->hdr_crc_self = crc32(hdr, hdr->hdr_size);
gpt_write(fd, lbt);
gpt_write(fd, tpg);
}
int
cmd_add(int argc, char *argv[])
{
char *p;
int ch, fd;
uint32_t status;
/* Get the migrate options */
while ((ch = getopt(argc, argv, "b:s:t:")) != -1) {
switch(ch) {
case 'b':
if (block > 0)
usage_add();
block = strtol(optarg, &p, 10);
if (*p != 0 || block < 1)
usage_add();
break;
case 's':
if (size > 0)
usage_add();
size = strtol(optarg, &p, 10);
if (*p != 0 || size < 1)
usage_add();
break;
case 't':
if (!uuid_is_nil(&type, NULL))
usage_add();
uuid_from_string(optarg, &type, &status);
if (status != uuid_s_ok) {
/* TODO: accept aliases. */
usage_add();
}
break;
default:
usage_add();
}
}
if (argc == optind)
usage_add();
while (optind < argc) {
fd = gpt_open(argv[optind++]);
if (fd == -1) {
warn("unable to open device '%s'", device_name);
continue;
}
add(fd);
gpt_close(fd);
}
return (0);
}

View File

@ -69,7 +69,7 @@ create(int fd)
}
/* Get the amount of free space after the MBR */
blocks = map_unused(1LL, 0LL);
blocks = map_free(1LL, 0LL);
if (blocks == 0LL) {
warnx("%s: error: no room for the GPT header", device_name);
return;
@ -125,7 +125,7 @@ create(int fd)
hdr->hdr_lba_alt = last;
hdr->hdr_lba_start = tbl->map_start + blocks;
hdr->hdr_lba_end = last - blocks - 1LL;
uuidgen(&hdr->hdr_uuid, 1);
uuid_create(&hdr->hdr_uuid, NULL);
hdr->hdr_lba_table = tbl->map_start;
hdr->hdr_entries = (blocks * secsz) / sizeof(struct gpt_ent);
if (hdr->hdr_entries > parts)
@ -134,7 +134,7 @@ create(int fd)
ent = tbl->map_data;
for (i = 0; i < hdr->hdr_entries; i++)
uuidgen(&ent[i].ent_uuid, 1);
uuid_create(&ent[i].ent_uuid, NULL);
hdr->hdr_crc_table = crc32(ent, hdr->hdr_entries * hdr->hdr_entsz);
hdr->hdr_crc_self = crc32(hdr, hdr->hdr_size);

View File

@ -127,18 +127,62 @@ unicode16(short *dst, const wchar_t *src, size_t len)
*dst = 0;
}
static char *
uuid_string(uuid_t *uuid)
#ifdef NEED_UUID_FUNCTIONS
void
uuid_create(uuid_t *u, uint32_t *st __unused)
{
static char buf[48];
uuidgen(u, 1);
}
void
uuid_from_string(const char *s, uuid_t *u, uint32_t *st)
{
int n;
*st = uuid_s_invalid_string_uuid;
if (strlen(s) != 36 || s[8] != '-')
return;
n = sscanf(s,
"%8x-%4hx-%4hx-%2hhx%2hhx-%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx",
&u->time_low, &u->time_mid, &u->time_hi_and_version,
&u->clock_seq_hi_and_reserved, &u->clock_seq_low, &u->node[0],
&u->node[1], &u->node[2], &u->node[3], &u->node[4], &u->node[5]);
if (n != 11)
return;
n = u->clock_seq_hi_and_reserved;
if ((n & 0x80) != 0x00 && /* variant 0? */
(n & 0xc0) != 0x80 && /* variant 1? */
(n & 0xe0) != 0xc0) /* variant 2? */
*st = uuid_s_bad_version;
else
*st = uuid_s_ok;
}
int32_t
uuid_is_nil(uuid_t *u, uint32_t *st __unused)
{
uint32_t *p;
p = (uint32_t*)u;
return ((p[0] == 0 && p[1] == 0 && p[2] == 0 && p[3] == 0) ? 1 : 0);
}
void
uuid_to_string(uuid_t *u, char **s, uint32_t *st __unused)
{
char buf[40];
sprintf(buf, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
uuid->time_low, uuid->time_mid, uuid->time_hi_and_version,
uuid->clock_seq_hi_and_reserved, uuid->clock_seq_low,
uuid->node[0], uuid->node[1], uuid->node[2], uuid->node[3],
uuid->node[4], uuid->node[5]);
return buf;
u->time_low, u->time_mid, u->time_hi_and_version,
u->clock_seq_hi_and_reserved, u->clock_seq_low, u->node[0],
u->node[1], u->node[2], u->node[3], u->node[4], u->node[5]);
*s = strdup(buf);
}
#endif
void*
gpt_read(int fd, off_t lba, size_t count)
@ -180,7 +224,7 @@ gpt_gpt(int fd, off_t lba)
off_t size;
struct gpt_ent *ent;
struct gpt_hdr *hdr;
char *p;
char *p, *s;
map_t *m;
size_t blocks, tblsz;
unsigned int i;
@ -235,20 +279,19 @@ gpt_gpt(int fd, off_t lba)
return (0);
for (i = 0; i < hdr->hdr_entries; i++) {
uuid_t unused = GPT_ENT_TYPE_UNUSED;
ent = (void*)(p + i * hdr->hdr_entsz);
if (!memcmp(&ent->ent_type, &unused, sizeof(uuid_t)))
if (uuid_is_nil(&ent->ent_type, NULL))
continue;
size = ent->ent_lba_end - ent->ent_lba_start + 1LL;
if (verbose > 2)
if (verbose > 2) {
uuid_to_string(&ent->ent_type, &s, NULL);
warnx(
"%s: GPT partition: type=%s, start=%llu, size=%llu",
device_name, uuid_string(&ent->ent_type),
"%s: GPT partition: type=%s, start=%llu, size=%llu", device_name, s,
(long long)ent->ent_lba_start, (long long)size);
m = map_add(ent->ent_lba_start, size, MAP_TYPE_GPT_PART, NULL);
free(s);
}
m = map_add(ent->ent_lba_start, size, MAP_TYPE_GPT_PART, ent);
if (m == NULL)
return (-1);
}
@ -338,19 +381,18 @@ gpt_open(const char *dev)
start = (start << 16) + mbr->mbr_part[i].part_start_lo;
size = mbr->mbr_part[i].part_size_hi;
size = (size << 16) + mbr->mbr_part[i].part_size_lo;
if (start != 0 || size != 0) {
if (verbose > 2)
warnx(
"%s: MBR partition: type=%d, start=%llu, size=%llu", device_name,
mbr->mbr_part[i].part_typ,
(long long)start, (long long)size);
if (mbr->mbr_part[i].part_typ != 0xee) {
m = map_add(start, size,
MAP_TYPE_MBR_PART, NULL);
if (m == NULL)
goto close;
}
}
if (start == 0 && size == 0)
continue;
if (verbose > 2)
warnx("%s: MBR partition: type=%d, start=%llu, size=%llu",
device_name, mbr->mbr_part[i].part_typ,
(long long)start, (long long)size);
if (mbr->mbr_part[i].part_typ == 0xee)
continue;
m = map_add(start, size, MAP_TYPE_MBR_PART,
mbr->mbr_part + i);
if (m == NULL)
goto close;
}
} else {
if (verbose)
@ -384,7 +426,7 @@ static struct {
int (*fptr)(int, char *[]);
const char *name;
} cmdsw[] = {
{ NULL, "add" },
{ cmd_add, "add" },
{ cmd_create, "create" },
{ NULL, "delete" },
{ cmd_destroy, "destroy" },

View File

@ -29,26 +29,39 @@
#ifndef _GPT_H_
#define _GPT_H_
struct mbr_part {
uint8_t part_flag; /* bootstrap flags */
uint8_t part_shd; /* starting head */
uint8_t part_ssect; /* starting sector */
uint8_t part_scyl; /* starting cylinder */
uint8_t part_typ; /* partition type */
uint8_t part_ehd; /* end head */
uint8_t part_esect; /* end sector */
uint8_t part_ecyl; /* end cylinder */
uint16_t part_start_lo; /* absolute starting ... */
uint16_t part_start_hi; /* ... sector number */
uint16_t part_size_lo; /* partition size ... */
uint16_t part_size_hi; /* ... in sectors */
};
struct mbr {
uint16_t mbr_code[223];
struct {
uint8_t part_flag; /* bootstrap flags */
uint8_t part_shd; /* starting head */
uint8_t part_ssect; /* starting sector */
uint8_t part_scyl; /* starting cylinder */
uint8_t part_typ; /* partition type */
uint8_t part_ehd; /* end head */
uint8_t part_esect; /* end sector */
uint8_t part_ecyl; /* end cylinder */
uint16_t part_start_lo; /* absolute starting ... */
uint16_t part_start_hi; /* ... sector number */
uint16_t part_size_lo; /* partition size ... */
uint16_t part_size_hi; /* ... in sectors */
} mbr_part[4];
struct mbr_part mbr_part[4];
uint16_t mbr_sig;
#define MBR_SIG 0xAA55
};
#ifndef uuid_s_ok
#define NEED_UUID_FUNCTIONS
#define uuid_s_ok 0
#define uuid_s_bad_version 1
#define uuid_s_invalid_string_uuid 2
void uuid_create(uuid_t *, uint32_t *);
void uuid_from_string(const char *, uuid_t *, uint32_t *);
int32_t uuid_is_nil(uuid_t *, uint32_t *);
void uuid_to_string(uuid_t *, char **, uint32_t *);
#endif
extern char device_name[];
extern off_t mediasz;
extern u_int parts;
@ -62,6 +75,7 @@ void* gpt_read(int, off_t, size_t);
int gpt_write(int, map_t *);
void unicode16(short *, const wchar_t *, size_t);
int cmd_add(int, char *[]);
int cmd_create(int, char *[]);
int cmd_destroy(int, char *[]);
int cmd_migrate(int, char *[]);

View File

@ -33,7 +33,8 @@
#include "map.h"
static int lbawidth;
int lbawidth;
static map_t *mediamap;
static map_t *
@ -132,6 +133,31 @@ map_add(off_t start, off_t size, int type, void *data)
return (m);
}
map_t *
map_alloc(off_t start, off_t size)
{
off_t delta;
map_t *m;
for (m = mediamap; m != NULL; m = m->map_next) {
if (m->map_type != MAP_TYPE_UNUSED || m->map_start < 2)
continue;
if (start != 0 && m->map_start > start)
return (NULL);
delta = (start != 0) ? start - m->map_start : 0;
if (size == 0 || m->map_size - delta >= size) {
if (m->map_size - delta <= 0)
continue;
if (size == 0)
size = m->map_size - delta;
return (map_add(m->map_start + delta, size,
MAP_TYPE_GPT_PART, NULL));
}
}
return (NULL);
}
map_t *
map_find(int type)
{
@ -161,7 +187,7 @@ map_last(void)
}
off_t
map_unused(off_t start, off_t size)
map_free(off_t start, off_t size)
{
map_t *m;
@ -176,53 +202,6 @@ map_unused(off_t start, off_t size)
return (m->map_size - (start - m->map_start));
}
void
map_dump(void)
{
off_t end;
map_t *m;
printf(" %*s", lbawidth, "start");
printf(" %*s", lbawidth, "end");
printf(" %*s", lbawidth, "size");
printf(" %s\n", "contents");
m = mediamap;
while (m != NULL) {
end = m->map_start + m->map_size - 1;
printf(" %*llu", lbawidth, (long long)m->map_start);
printf(" %*llu", lbawidth, (long long)end);
printf(" %*llu", lbawidth, (long long)m->map_size);
putchar(' '); putchar(' ');
switch (m->map_type) {
case MAP_TYPE_MBR:
printf("MBR");
break;
case MAP_TYPE_PRI_GPT_HDR:
printf("Pri GPT header");
break;
case MAP_TYPE_SEC_GPT_HDR:
printf("Sec GPT header");
break;
case MAP_TYPE_PRI_GPT_TBL:
printf("Pri GPT table");
break;
case MAP_TYPE_SEC_GPT_TBL:
printf("Sec GPT table");
break;
case MAP_TYPE_MBR_PART:
printf("MBR partition");
break;
case MAP_TYPE_GPT_PART:
printf("GPT partition");
break;
}
putchar('\n');
m = m->map_next;
}
}
void
map_init(off_t size)
{

View File

@ -46,14 +46,16 @@ typedef struct map {
void *map_data;
} map_t;
extern int lbawidth;
map_t *map_add(off_t, off_t, int, void*);
map_t *map_alloc(off_t, off_t);
map_t *map_find(int);
map_t *map_first(void);
map_t *map_last(void);
off_t map_unused(off_t, off_t);
off_t map_free(off_t, off_t);
void map_dump(void);
void map_init(off_t);
#endif /* _MAP_H_ */

View File

@ -135,7 +135,7 @@ migrate(int fd)
}
/* Get the amount of free space after the MBR */
blocks = map_unused(1LL, 0LL);
blocks = map_free(1LL, 0LL);
if (blocks == 0LL) {
warnx("%s: error: no room for the GPT header", device_name);
return;
@ -193,7 +193,7 @@ migrate(int fd)
hdr->hdr_lba_alt = tpg->map_start;
hdr->hdr_lba_start = tbl->map_start + blocks;
hdr->hdr_lba_end = lbt->map_start - 1LL;
uuidgen(&hdr->hdr_uuid, 1);
uuid_create(&hdr->hdr_uuid, NULL);
hdr->hdr_lba_table = tbl->map_start;
hdr->hdr_entries = (blocks * secsz) / sizeof(struct gpt_ent);
if (hdr->hdr_entries > parts)
@ -202,7 +202,7 @@ migrate(int fd)
ent = tbl->map_data;
for (i = 0; i < hdr->hdr_entries; i++)
uuidgen(&ent[i].ent_uuid, 1);
uuid_create(&ent[i].ent_uuid, NULL);
/* Mirror partitions. */
for (i = 0; i < 4; i++) {

View File

@ -49,6 +49,62 @@ usage_show(void)
exit(1);
}
static void
show(int fd __unused)
{
off_t end;
map_t *m;
struct mbr_part *part;
struct gpt_ent *ent;
char *s;
printf(" %*s", lbawidth, "start");
printf(" %*s", lbawidth, "end");
printf(" %*s", lbawidth, "size");
printf(" %s\n", "contents");
m = map_first();
while (m != NULL) {
end = m->map_start + m->map_size - 1;
printf(" %*llu", lbawidth, (long long)m->map_start);
printf(" %*llu", lbawidth, (long long)end);
printf(" %*llu", lbawidth, (long long)m->map_size);
putchar(' '); putchar(' ');
switch (m->map_type) {
case MAP_TYPE_MBR:
printf("MBR");
break;
case MAP_TYPE_PRI_GPT_HDR:
printf("Pri GPT header");
break;
case MAP_TYPE_SEC_GPT_HDR:
printf("Sec GPT header");
break;
case MAP_TYPE_PRI_GPT_TBL:
printf("Pri GPT table");
break;
case MAP_TYPE_SEC_GPT_TBL:
printf("Sec GPT table");
break;
case MAP_TYPE_MBR_PART:
printf("MBR partition: ");
part = m->map_data;
printf("type=%d", part->part_typ);
break;
case MAP_TYPE_GPT_PART:
printf("GPT partition: ");
ent = m->map_data;
uuid_to_string(&ent->ent_type, &s, NULL);
printf("type=%s", s);
free(s);
break;
}
putchar('\n');
m = m->map_next;
}
}
int
cmd_show(int argc, char *argv[])
{
@ -71,7 +127,7 @@ cmd_show(int argc, char *argv[])
continue;
}
map_dump();
show(fd);
gpt_close(fd);
}