412 lines
9.1 KiB
C
412 lines
9.1 KiB
C
/*
|
|
* ----------------------------------------------------------------------------
|
|
* "THE BEER-WARE LICENSE" (Revision 42):
|
|
* <phk@login.dknet.dk> 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
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* $Id: chunk.c,v 1.9 1995/05/07 01:25:22 jkh Exp $
|
|
*
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <err.h>
|
|
#include "libdisk.h"
|
|
|
|
#define new_chunk() memset(malloc(sizeof(struct chunk)), 0, sizeof(struct chunk))
|
|
|
|
/* Is c2 completely inside c1 ? */
|
|
|
|
static int
|
|
Chunk_Inside(struct chunk *c1, 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;
|
|
}
|
|
|
|
struct chunk *
|
|
Find_Mother_Chunk(struct chunk *chunks, u_long offset, u_long 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;
|
|
default:
|
|
warn("Unsupported mother (0x%x) in Find_Mother_Chunk");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void
|
|
Free_Chunk(struct chunk *c1)
|
|
{
|
|
if(!c1) return;
|
|
if(c1->private && c1->private_free)
|
|
(*c1->private_free)(c1->private);
|
|
if(c1->part)
|
|
Free_Chunk(c1->part);
|
|
if(c1->next)
|
|
Free_Chunk(c1->next);
|
|
free(c1->name);
|
|
free(c1);
|
|
}
|
|
|
|
struct chunk *
|
|
Clone_Chunk(struct chunk *c1)
|
|
{
|
|
struct chunk *c2;
|
|
if(!c1)
|
|
return 0;
|
|
c2 = new_chunk();
|
|
if (!c2) err(1,"malloc failed");
|
|
*c2 = *c1;
|
|
if (c1->private && c1->private_clone)
|
|
c2->private = c2->private_clone(c2->private);
|
|
c2->name = strdup(c2->name);
|
|
c2->next = Clone_Chunk(c2->next);
|
|
c2->part = Clone_Chunk(c2->part);
|
|
return c2;
|
|
}
|
|
|
|
int
|
|
Insert_Chunk(struct chunk *c2, u_long offset, u_long size, char *name, chunk_e type, int subtype, u_long flags)
|
|
{
|
|
struct chunk *ct,*cs;
|
|
|
|
/* We will only insert into empty spaces */
|
|
if (c2->type != unused)
|
|
return __LINE__;
|
|
|
|
ct = new_chunk();
|
|
if (!ct) err(1,"malloc failed");
|
|
memset(ct,0,sizeof *ct);
|
|
ct->offset = offset;
|
|
ct->size = size;
|
|
ct->end = offset + size - 1;
|
|
ct->type = type;
|
|
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) {
|
|
cs = new_chunk();
|
|
if (!cs) err(1,"malloc failed");
|
|
memset(cs,0,sizeof *cs);
|
|
cs->offset = offset;
|
|
cs->size = size;
|
|
cs->end = offset + size - 1;
|
|
cs->type = unused;
|
|
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) err(1,"malloc failed");
|
|
*cs = *c2;
|
|
cs->offset = ct->end + 1;
|
|
cs->size = c2->end - ct->end;
|
|
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->name = ct->name;
|
|
c2->type = ct->type;
|
|
c2->part = ct->part;
|
|
c2->subtype = ct->subtype;
|
|
c2->flags = ct->flags;
|
|
ct->name = 0;
|
|
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, u_long offset, u_long size, char *name, chunk_e type,
|
|
int subtype, u_long flags)
|
|
{
|
|
struct chunk *c1,*c2,ct;
|
|
u_long end = offset + size - 1;
|
|
ct.offset = offset;
|
|
ct.end = end;
|
|
ct.size = size;
|
|
|
|
if (type == whole) {
|
|
d->chunks = c1 = new_chunk();
|
|
if (!c1) err(1,"malloc failed");
|
|
memset(c1,0,sizeof *c1);
|
|
c2 = c1->part = new_chunk();
|
|
if (!c2) err(1,"malloc failed");
|
|
memset(c2,0,sizeof *c2);
|
|
c2->offset = c1->offset = offset;
|
|
c2->size = c1->size = size;
|
|
c2->end = c1->end = end;
|
|
c1->name = strdup(name);
|
|
c2->name = strdup("-");
|
|
c1->type = type;
|
|
c2->type = unused;
|
|
c1->flags = flags;
|
|
c1->subtype = subtype;
|
|
return 0;
|
|
}
|
|
c1 = 0;
|
|
if(!c1 && (type == freebsd || type == fat || type == unknown))
|
|
c1 = Find_Mother_Chunk(d->chunks,offset,end,extended);
|
|
if(!c1 && (type == freebsd || type == fat || type == unknown))
|
|
c1 = Find_Mother_Chunk(d->chunks,offset,end,whole);
|
|
if(!c1 && type == extended)
|
|
c1 = Find_Mother_Chunk(d->chunks,offset,end,whole);
|
|
if(!c1 && type == part)
|
|
c1 = Find_Mother_Chunk(d->chunks,offset,end,freebsd);
|
|
if(!c1)
|
|
return __LINE__;
|
|
for(c2=c1->part;c2;c2=c2->next) {
|
|
if (c2->type != unused)
|
|
continue;
|
|
if(Chunk_Inside(c2,&ct)) {
|
|
if (type != freebsd)
|
|
goto doit;
|
|
if (!(flags & CHUNK_ALIGN))
|
|
goto doit;
|
|
if (offset == d->chunks->offset
|
|
&& end == d->chunks->end)
|
|
goto doit;
|
|
|
|
/* 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;
|
|
|
|
doit:
|
|
return Insert_Chunk(c2,offset,size,name,
|
|
type,subtype,flags);
|
|
}
|
|
}
|
|
return __LINE__;
|
|
}
|
|
|
|
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(' ');
|
|
printf("%p %8lu %8lu %8lu %-8s %-8s 0x%02x ",
|
|
c1, c1->offset, c1->size, c1->end, c1->name,
|
|
chunk_n[c1->type],c1->subtype);
|
|
if (c1->flags & CHUNK_ALIGN) putchar('=');
|
|
if (c1->flags & CHUNK_PAST_1024) putchar('>');
|
|
if (c1->flags & CHUNK_IS_ROOT) putchar('R');
|
|
if (c1->flags & CHUNK_BAD144) putchar('B');
|
|
if (c1->flags & CHUNK_BSD_COMPAT) putchar('C');
|
|
putchar('\n');
|
|
Print_Chunk(c1->part,offset + 2);
|
|
Print_Chunk(c1->next,offset);
|
|
}
|
|
|
|
void
|
|
Debug_Chunk(struct chunk *c1)
|
|
{
|
|
Print_Chunk(c1,2);
|
|
}
|
|
|
|
void
|
|
Bios_Limit_Chunk(struct chunk *c1, u_long limit)
|
|
{
|
|
if (c1->part)
|
|
Bios_Limit_Chunk(c1->part,limit);
|
|
if (c1->next)
|
|
Bios_Limit_Chunk(c1->next,limit);
|
|
if (c1->end >= limit) {
|
|
c1->flags |= CHUNK_PAST_1024;
|
|
} else {
|
|
c1->flags &= ~CHUNK_PAST_1024;
|
|
}
|
|
}
|
|
|
|
int
|
|
Delete_Chunk(struct disk *d, struct chunk *c)
|
|
{
|
|
struct chunk *c1=0,*c2,*c3;
|
|
chunk_e type = c->type;
|
|
|
|
if(type == whole)
|
|
return 1;
|
|
if(!c1 && (type == freebsd || type == fat || type == unknown))
|
|
c1 = Find_Mother_Chunk(d->chunks,c->offset,c->end,extended);
|
|
if(!c1 && (type == freebsd || type == fat || type == unknown))
|
|
c1 = Find_Mother_Chunk(d->chunks,c->offset,c->end,whole);
|
|
if(!c1 && type == extended)
|
|
c1 = Find_Mother_Chunk(d->chunks,c->offset,c->end,whole);
|
|
if(!c1 && type == part)
|
|
c1 = Find_Mother_Chunk(d->chunks,c->offset,c->end,freebsd);
|
|
if(!c1)
|
|
return 1;
|
|
for(c2=c1->part;c2;c2=c2->next) {
|
|
if (c2 == c) {
|
|
c2->type = unused;
|
|
c2->subtype = 0;
|
|
c2->flags = 0;
|
|
free(c2->name);
|
|
c2->name = strdup("-");
|
|
Free_Chunk(c2->part);
|
|
c2->part =0;
|
|
goto scan;
|
|
}
|
|
}
|
|
return 1;
|
|
scan:
|
|
for(c2=c1->part;c2;c2=c2->next) {
|
|
if (c2->type != unused)
|
|
continue;
|
|
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;
|
|
}
|
|
|
|
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();
|
|
*c2 = *c1;
|
|
c1->next = c2;
|
|
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;
|
|
}
|