aboutsummaryrefslogblamecommitdiff
path: root/lib/libdisk/chunk.c
blob: e81c646ae3b394ebcd87d95b1ef8d0ad09d6de59 (plain) (tree)
1
2
3
4
5
6
7
8
9







                                                                               
                                                   







                      



                    
 

























































































































































































                                                                                                               



                                                                      

















                                                                                    

                                                             

























                                                                   
                                             

                                   
                               



                                                                  
                                                                            
                                                                  
                                                                         
                                   
                                                                         
                               
                                                                           


                                         
                              
























































                                                                        
                                   
































                                         
/*
 * ----------------------------------------------------------------------------
 * "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.2 1995/04/29 01:55:19 phk Exp $
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <err.h>
#include "libdisk.h"

CHAR_N;

#define new_chunk() malloc(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;
			break;
		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) {
					for(c2=c1->part;c2;c2=c2->next)
						if (c2->type == type)
							if (Chunk_Inside(c2,&ct))
								return c2;
				}
			}
			return 0;
			break;
		default:
			err(1,"Mumble!");
	}
}

void
Free_Chunk(struct chunk *c1)
{
	/* XXX remove all chunks which "ref" us */
	if(!c1) return;	
	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;
	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;

	ct = new_chunk();
	if (!ct) err(1,"malloc failed");
	ct->offset = offset;
	ct->size = size;
	ct->end = offset + size - 1;
	ct->type = type;
	ct->name = strdup(name);
	ct->next = 0;
	ct->part = 0;
	ct->subtype = subtype;
	ct->flags = flags;

	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("-");
		cs->next = 0;
		cs->part = 0;
		ct->part = cs;
	}

	if (c2->type != unused)
		return __LINE__;
	if (Chunk_Inside(c2,ct)) {
		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 (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;
		}
		c2->end = ct->offset - 1;
		c2->size -= ct->size;
		ct->next = c2->next;
		c2->next = ct;
		return 0;
	}
	return __LINE__;
}


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(name);
		c1->type = type;
		c2->type = unused;
		c1->flags = flags;
		c1->subtype = subtype;
		return 0; 
	}
	c1 = 0;
	if(!c1 && (type == freebsd || type == fat || type == foo))
		c1 = Find_Mother_Chunk(d->chunks,offset,end,extended);
	if(!c1 && (type == freebsd || type == fat || type == foo))
		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 && type == reserved)
		c1 = Find_Mother_Chunk(d->chunks,offset,end,extended);
	if(!c1 && type == reserved)
		c1 = Find_Mother_Chunk(d->chunks,offset,end,whole);
	if(!c1)
		return __LINE__;
	for(c2=c1->part;c2;c2=c2->next) {
		if (c2->type != unused)
			continue;
		if(Chunk_Inside(c2,&ct))
			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;i++) putchar('>');
	for(;i<10;i++) putchar(' ');
	printf("%p %10lu %10lu %10lu %-8s %d %-8s %d %lx\n",
		c1, c1->offset, c1->size, c1->end, c1->name, 
		c1->type, chunk_n[c1->type],c1->subtype,c1->flags);
	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 == foo))
		c1 = Find_Mother_Chunk(d->chunks,c->offset,c->end,extended);
	if(!c1 && (type == freebsd || type == fat || type == foo))
		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;
	}
	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;
}