e0e7753e22
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
596 lines
12 KiB
C
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
|