freebsd-skq/lib/libdisk/chunk.c
marcel e0e7753e22 Don't barf when we encounter an UUID for GPT partitions. Instead, add
the GPT partition on i386 and adm64 as type=gpt, subtype=0 and with the
sname set to the UUID. This prevents sysinstall from bombing out. This
also makes sure the GPT partition shows up in sysinstall so as to avoid
accidental "clobberage".

PR:	bin/72896
2004-10-31 01:28:59 +00:00

596 lines
12 KiB
C

/*
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <phk@FreeBSD.org> wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
* ----------------------------------------------------------------------------
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <sys/stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <err.h>
#include "libdisk.h"
struct chunk *
New_Chunk(void)
{
struct chunk *c;
c = malloc(sizeof *c);
if (c != NULL)
memset(c, 0, sizeof *c);
return (c);
}
/* Is c2 completely inside c1 ? */
static int
Chunk_Inside(const struct chunk *c1, const struct chunk *c2)
{
/* if c1 ends before c2 do */
if (c1->end < c2->end)
return 0;
/* if c1 starts after c2 do */
if (c1->offset > c2->offset)
return 0;
return 1;
}
static struct chunk *
Find_Mother_Chunk(struct chunk *chunks, daddr_t offset, daddr_t end,
chunk_e type)
{
struct chunk *c1, *c2, ct;
ct.offset = offset;
ct.end = end;
switch (type) {
case whole:
if (Chunk_Inside(chunks, &ct))
return chunks;
case extended:
for (c1 = chunks->part; c1; c1 = c1->next) {
if (c1->type != type)
continue;
if (Chunk_Inside(c1, &ct))
return c1;
}
return 0;
case freebsd:
for (c1 = chunks->part; c1; c1 = c1->next) {
if (c1->type == type)
if (Chunk_Inside(c1, &ct))
return c1;
if (c1->type != extended)
continue;
for (c2 = c1->part; c2; c2 = c2->next)
if (c2->type == type && Chunk_Inside(c2, &ct))
return c2;
}
return 0;
#ifdef __powerpc__
case apple:
for (c1 = chunks->part; c1; c1 = c1->next) {
if (c1->type == type)
if (Chunk_Inside(c1, &ct))
return c1;
}
return 0;
#endif
default:
warn("Unsupported mother type in Find_Mother_Chunk");
return 0;
}
}
void
Free_Chunk(struct chunk *c1)
{
if(c1 == NULL)
return;
if(c1->private_data && c1->private_free)
(*c1->private_free)(c1->private_data);
if(c1->part != NULL)
Free_Chunk(c1->part);
if(c1->next != NULL)
Free_Chunk(c1->next);
if (c1->name != NULL)
free(c1->name);
if (c1->sname != NULL)
free(c1->sname);
free(c1);
}
struct chunk *
Clone_Chunk(const struct chunk *c1)
{
struct chunk *c2;
if(!c1)
return NULL;
c2 = New_Chunk();
if (c2 == NULL)
return NULL;
*c2 = *c1;
if (c1->private_data && c1->private_clone)
c2->private_data = c2->private_clone(c2->private_data);
c2->name = strdup(c2->name);
if (c2->sname != NULL)
c2->sname = strdup(c2->sname);
c2->next = Clone_Chunk(c2->next);
c2->part = Clone_Chunk(c2->part);
return c2;
}
int
Insert_Chunk(struct chunk *c2, daddr_t offset, daddr_t size, const char *name,
chunk_e type, int subtype, u_long flags, const char *sname)
{
struct chunk *ct,*cs;
/* We will only insert into empty spaces */
if (c2->type != unused)
return __LINE__;
ct = New_Chunk();
if (ct == NULL)
return __LINE__;
ct->disk = c2->disk;
ct->offset = offset;
ct->size = size;
ct->end = offset + size - 1;
ct->type = type;
if (sname != NULL)
ct->sname = strdup(sname);
ct->name = strdup(name);
ct->subtype = subtype;
ct->flags = flags;
if (!Chunk_Inside(c2, ct)) {
Free_Chunk(ct);
return __LINE__;
}
if ((type == freebsd || type == extended || type == apple)) {
cs = New_Chunk();
if (cs == NULL)
return __LINE__;
cs->disk = c2->disk;
cs->offset = offset;
cs->size = size;
cs->end = offset + size - 1;
cs->type = unused;
if (sname != NULL)
cs->sname = strdup(sname);
cs->name = strdup("-");
ct->part = cs;
}
/* Make a new chunk for any trailing unused space */
if (c2->end > ct->end) {
cs = New_Chunk();
if (cs == NULL)
return __LINE__;
*cs = *c2;
cs->disk = c2->disk;
cs->offset = ct->end + 1;
cs->size = c2->end - ct->end;
if (c2->sname != NULL)
cs->sname = strdup(c2->sname);
if (c2->name)
cs->name = strdup(c2->name);
c2->next = cs;
c2->size -= c2->end - ct->end;
c2->end = ct->end;
}
/* If no leading unused space just occupy the old chunk */
if (c2->offset == ct->offset) {
c2->sname = ct->sname;
c2->name = ct->name;
c2->type = ct->type;
c2->part = ct->part;
c2->subtype = ct->subtype;
c2->flags = ct->flags;
ct->sname = NULL;
ct->name = NULL;
ct->part = 0;
Free_Chunk(ct);
return 0;
}
/* else insert new chunk and adjust old one */
c2->end = ct->offset - 1;
c2->size -= ct->size;
ct->next = c2->next;
c2->next = ct;
return 0;
}
int
Add_Chunk(struct disk *d, daddr_t offset, daddr_t size, const char *name,
chunk_e type, int subtype, u_long flags, const char *sname)
{
struct chunk *c1, *c2, ct;
daddr_t end = offset + size - 1;
ct.offset = offset;
ct.end = end;
ct.size = size;
if (type == whole) {
d->chunks = c1 = New_Chunk();
if (c1 == NULL)
return __LINE__;
c2 = c1->part = New_Chunk();
if (c2 == NULL)
return __LINE__;
c2->disk = c1->disk = d;
c2->offset = c1->offset = offset;
c2->size = c1->size = size;
c2->end = c1->end = end;
c1->sname = strdup(sname);
c2->sname = strdup("-");
c1->name = strdup(name);
c2->name = strdup("-");
c1->type = type;
c2->type = unused;
c1->flags = flags;
c1->subtype = subtype;
return 0;
}
c1 = 0;
/* PLATFORM POLICY BEGIN ------------------------------------- */
switch(platform) {
case p_i386:
case p_amd64:
switch (type) {
case fat:
case gpt:
case mbr:
case extended:
case freebsd:
c1 = Find_Mother_Chunk(d->chunks, offset, end, whole);
break;
case part:
c1 = Find_Mother_Chunk(d->chunks, offset, end, freebsd);
break;
default:
return(-1);
}
break;
case p_ia64:
switch (type) {
case freebsd:
subtype = 0xa5;
/* FALL THROUGH */
case fat:
case efi:
case mbr:
c1 = Find_Mother_Chunk(d->chunks, offset, end, whole);
break;
case part:
c1 = Find_Mother_Chunk(d->chunks, offset, end,
freebsd);
if (!c1)
c1 = Find_Mother_Chunk(d->chunks, offset, end,
whole);
break;
default:
return (-1);
}
break;
case p_pc98:
switch (type) {
case fat:
case pc98:
case freebsd:
c1 = Find_Mother_Chunk(d->chunks, offset, end, whole);
break;
case part:
c1 = Find_Mother_Chunk(d->chunks, offset, end, freebsd);
break;
default:
return(-1);
}
break;
case p_sparc64:
case p_alpha:
switch (type) {
case freebsd:
c1 = Find_Mother_Chunk(d->chunks, offset, end, whole);
break;
case part:
c1 = Find_Mother_Chunk(d->chunks, offset, end, freebsd);
break;
default:
return(-1);
}
break;
case p_ppc:
switch (type) {
case apple:
c1 = Find_Mother_Chunk(d->chunks, offset, end, whole);
break;
case part:
c1 = Find_Mother_Chunk(d->chunks, offset, end, apple);
break;
default:
return (-1);
}
break;
default:
return (-1);
}
/* PLATFORM POLICY END ---------------------------------------- */
if(!c1)
return __LINE__;
for(c2 = c1->part; c2; c2 = c2->next) {
if (c2->type != unused)
continue;
if(!Chunk_Inside(c2, &ct))
continue;
/* PLATFORM POLICY BEGIN ------------------------------------- */
if (platform == p_sparc64) {
offset = Prev_Cyl_Aligned(d, offset);
size = Next_Cyl_Aligned(d, size);
} else if (platform == p_i386 || platform == p_pc98 ||
platform == p_amd64) {
if (type != freebsd)
break;
if (!(flags & CHUNK_ALIGN))
break;
if (offset == d->chunks->offset &&
end == d->chunks->end)
break;
/* Round down to prev cylinder */
offset = Prev_Cyl_Aligned(d,offset);
/* Stay inside the parent */
if (offset < c2->offset)
offset = c2->offset;
/* Round up to next cylinder */
offset = Next_Cyl_Aligned(d, offset);
/* Keep one track clear in front of parent */
if (offset == c1->offset)
offset = Next_Track_Aligned(d, offset + 1);
/* Work on the (end+1) */
size += offset;
/* Round up to cylinder */
size = Next_Cyl_Aligned(d, size);
/* Stay inside parent */
if ((size-1) > c2->end)
size = c2->end + 1;
/* Round down to cylinder */
size = Prev_Cyl_Aligned(d, size);
/* Convert back to size */
size -= offset;
}
break;
/* PLATFORM POLICY END ------------------------------------- */
}
if (c2 == NULL)
return (__LINE__);
return Insert_Chunk(c2, offset, size, name, type, subtype, flags,
sname);
}
char *
ShowChunkFlags(struct chunk *c)
{
static char ret[10];
int i = 0;
if (c->flags & CHUNK_ACTIVE)
ret[i++] = 'A';
if (c->flags & CHUNK_ALIGN)
ret[i++] = '=';
if (c->flags & CHUNK_IS_ROOT)
ret[i++] = 'R';
ret[i++] = '\0';
return ret;
}
static void
Print_Chunk(struct chunk *c1, int offset)
{
int i;
if (!c1)
return;
for (i = 0; i < offset - 2; i++)
putchar(' ');
for (; i < offset; i++)
putchar('-');
putchar('>');
for (; i < 10; i++)
putchar(' ');
#ifndef __ia64__
printf("%p ", c1);
#endif
printf("%8jd %8jd %8jd %-8s %-16s %-8s 0x%02x %s",
(intmax_t)c1->offset, (intmax_t)c1->size, (intmax_t)c1->end,
c1->name, c1->sname, chunk_name(c1->type), c1->subtype,
ShowChunkFlags(c1));
putchar('\n');
Print_Chunk(c1->part, offset + 2);
Print_Chunk(c1->next, offset);
}
void
Debug_Chunk(struct chunk *c1)
{
Print_Chunk(c1, 2);
}
int
Delete_Chunk(struct disk *d, struct chunk *c)
{
return (Delete_Chunk2(d, c, 0));
}
int
Delete_Chunk2(struct disk *d, struct chunk *c, int rflags)
{
struct chunk *c1, *c2, *c3;
daddr_t offset = c->offset;
switch (c->type) {
case whole:
case unused:
return 1;
case extended:
c1 = Find_Mother_Chunk(d->chunks, c->offset, c->end, whole);
break;
case part:
c1 = Find_Mother_Chunk(d->chunks, c->offset, c->end, freebsd);
#ifdef __ia64__
if (c1 == NULL)
c1 = Find_Mother_Chunk(d->chunks, c->offset, c->end,
whole);
#endif
#ifdef __powerpc__
if (c1 == NULL)
c1 = Find_Mother_Chunk(d->chunks, c->offset, c->end,
apple);
#endif
break;
default:
c1 = Find_Mother_Chunk(d->chunks, c->offset, c->end, extended);
if (c1 == NULL)
c1 = Find_Mother_Chunk(d->chunks, c->offset, c->end,
whole);
break;
}
if (c1 == NULL)
return 1;
for (c2 = c1->part; c2; c2 = c2->next) {
if (c2 == c) {
c2->type = unused;
c2->subtype = 0;
c2->flags = 0;
if (c2->sname != NULL)
free(c2->sname);
c2->sname = strdup("-");
free(c2->name);
c2->name = strdup("-");
Free_Chunk(c2->part);
c2->part =0;
goto scan;
}
}
return 1;
scan:
/*
* Collapse multiple unused elements together, and attempt
* to extend the previous chunk into the freed chunk.
*
* We only extend non-unused elements which are marked
* for newfs (we can't extend working filesystems), and
* only if we are called with DELCHUNK_RECOVER.
*/
for (c2 = c1->part; c2; c2 = c2->next) {
if (c2->type != unused) {
if (c2->offset + c2->size != offset ||
(rflags & DELCHUNK_RECOVER) == 0 ||
(c2->flags & CHUNK_NEWFS) == 0) {
continue;
}
/* else extend into free area */
}
if (!c2->next)
continue;
if (c2->next->type != unused)
continue;
c3 = c2->next;
c2->size += c3->size;
c2->end = c3->end;
c2->next = c3->next;
c3->next = 0;
Free_Chunk(c3);
goto scan;
}
Fixup_Names(d);
return 0;
}
#if 0
int
Collapse_Chunk(struct disk *d, struct chunk *c1)
{
struct chunk *c2, *c3;
if (c1->next && Collapse_Chunk(d, c1->next))
return 1;
if (c1->type == unused && c1->next && c1->next->type == unused) {
c3 = c1->next;
c1->size += c3->size;
c1->end = c3->end;
c1->next = c3->next;
c3->next = 0;
Free_Chunk(c3);
return 1;
}
c3 = c1->part;
if (!c3)
return 0;
if (Collapse_Chunk(d, c1->part))
return 1;
if (c1->type == whole)
return 0;
if (c3->type == unused && c3->size == c1->size) {
Delete_Chunk(d, c1);
return 1;
}
if (c3->type == unused) {
c2 = New_Chunk();
if (c2 == NULL)
barfout(1, "malloc failed");
*c2 = *c1;
c1->next = c2;
c1->disk = d;
c1->sname = strdup("-");
c1->name = strdup("-");
c1->part = 0;
c1->type = unused;
c1->flags = 0;
c1->subtype = 0;
c1->size = c3->size;
c1->end = c3->end;
c2->offset += c1->size;
c2->size -= c1->size;
c2->part = c3->next;
c3->next = 0;
Free_Chunk(c3);
return 1;
}
for (c2 = c3; c2->next; c2 = c2->next)
c3 = c2;
if (c2 && c2->type == unused) {
c3->next = 0;
c2->next = c1->next;
c1->next = c2;
c1->size -= c2->size;
c1->end -= c2->size;
return 1;
}
return 0;
}
#endif