diff options
Diffstat (limited to 'lib/libdisk/chunk.c')
| -rw-r--r-- | lib/libdisk/chunk.c | 508 | 
1 files changed, 508 insertions, 0 deletions
| diff --git a/lib/libdisk/chunk.c b/lib/libdisk/chunk.c new file mode 100644 index 000000000000..a2b18ec25157 --- /dev/null +++ b/lib/libdisk/chunk.c @@ -0,0 +1,508 @@ +/* + * ---------------------------------------------------------------------------- + * "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 + * ---------------------------------------------------------------------------- + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#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; +#ifndef PC98 +		case extended: +			for(c1 = chunks->part; c1; c1 = c1->next) { +				if (c1->type != type) +					continue; +				if (Chunk_Inside(c1, &ct)) +					return c1; +			} +			return 0; +#endif +		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 type in Find_Mother_Chunk"); +			return 0; +	} +} + +void +Free_Chunk(struct chunk *c1) +{ +	if(!c1) return; +	if(c1->private_data && c1->private_free) +		(*c1->private_free)(c1->private_data); +	if(c1->part) +		Free_Chunk(c1->part); +	if(c1->next) +		Free_Chunk(c1->next); +	free(c1->name); +#ifdef PC98 +	free(c1->sname); +#endif +	free(c1); +} + +struct chunk * +Clone_Chunk(struct chunk *c1) +{ +	struct chunk *c2; + +	if(!c1) +		return 0; +	c2 = new_chunk(); +	if (!c2) 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); +#ifdef PC98 +	c2->sname = strdup(c2->sname); +#endif +	c2->next = Clone_Chunk(c2->next); +	c2->part = Clone_Chunk(c2->part); +	return c2; +} + +int +#ifdef PC98 +Insert_Chunk(struct chunk *c2, u_long offset, u_long size, const char *name, +	chunk_e type, int subtype, u_long flags, const char *sname) +#else +Insert_Chunk(struct chunk *c2, u_long offset, u_long size, const char *name, +	chunk_e type, int subtype, u_long flags) +#endif +{ +	struct chunk *ct,*cs; + +	/* We will only insert into empty spaces */ +	if (c2->type != unused) +		return __LINE__; + +	ct = new_chunk(); +	if (!ct) return __LINE__; +	memset(ct, 0, sizeof *ct); +	ct->disk = c2->disk; +	ct->offset = offset; +	ct->size = size; +	ct->end = offset + size - 1; +	ct->type = type; +#ifdef PC98 +	ct->sname = strdup(sname); +#endif +	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) return __LINE__; +		memset(cs, 0, sizeof *cs); +		cs->disk = c2->disk; +		cs->offset = offset; +		cs->size = size; +		cs->end = offset + size - 1; +		cs->type = unused; +#ifdef PC98 +		cs->sname = strdup(sname); +#endif +		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) return __LINE__; +		*cs = *c2; +		cs->disk = c2->disk; +		cs->offset = ct->end + 1; +		cs->size = c2->end - ct->end; +#ifdef PC98 +		if(c2->sname) +			cs->sname = strdup(c2->sname); +#endif +		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) { +#ifdef PC98 +		c2->sname = ct->sname; +#endif +		c2->name = ct->name; +		c2->type = ct->type; +		c2->part = ct->part; +		c2->subtype = ct->subtype; +		c2->flags = ct->flags; +#ifdef PC98 +		ct->sname = 0; +#endif +		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 +#ifdef PC98 +Add_Chunk(struct disk *d, long offset, u_long size, const char *name, +	chunk_e type, int subtype, u_long flags, const char *sname) +#else +Add_Chunk(struct disk *d, long offset, u_long size, const char *name, +	chunk_e type, int subtype, u_long flags) +#endif +{ +	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) return __LINE__; +		memset(c1, 0, sizeof *c1); +		c2 = c1->part = new_chunk(); +		if (!c2) return __LINE__; +		memset(c2,0,sizeof *c2); +		c2->disk = c1->disk = d; +		c2->offset = c1->offset = offset; +		c2->size = c1->size = size; +		c2->end = c1->end = end; +#ifdef PC98 +		c1->sname = strdup(sname); +		c2->sname = strdup("-"); +#endif +		c1->name = strdup(name); +		c2->name = strdup("-"); +		c1->type = type; +		c2->type = unused; +		c1->flags = flags; +		c1->subtype = subtype; +		return 0; +	} +	if (type == freebsd) +#ifdef PC98 +		subtype = 0xc494; +#else +		subtype = 0xa5; +#endif +	c1 = 0; +#ifndef PC98 +	if(!c1 && (type == freebsd || type == fat || type == unknown)) +		c1 = Find_Mother_Chunk(d->chunks, offset, end, extended); +#endif +	if(!c1 && (type == freebsd || type == fat || type == unknown)) +		c1 = Find_Mother_Chunk(d->chunks, offset, end, whole); +#ifndef PC98 +	if(!c1 && type == extended) +		c1 = Find_Mother_Chunk(d->chunks, offset, end, whole); +#endif +	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: +#ifdef PC98 +			return Insert_Chunk(c2, offset, size, name, +				type, subtype, flags, sname); +#else +			return Insert_Chunk(c2, offset, size, name, +				type, subtype, flags); +#endif +		} +	} +	return __LINE__; +} + +char * +ShowChunkFlags(struct chunk *c) +{ +	static char ret[10]; + +	int i=0; +	if (c->flags & CHUNK_BSD_COMPAT)	ret[i++] = 'C'; +	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; +} + +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(' '); +#ifdef PC98 +	printf("%p %8ld %8lu %8lu %-8s %-16s %-8s 0x%02x %s", +		c1, c1->offset, c1->size, c1->end, c1->name, c1->sname, +#else +	printf("%p %8ld %8lu %8lu %-8s %-8s 0x%02x %s", +		c1, c1->offset, c1->size, c1->end, c1->name, +#endif +		chunk_n[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=0, *c2, *c3; +	chunk_e type = c->type; +	long offset = c->offset; + +	if(type == whole) +		return 1; +#ifndef PC98 +	if(!c1 && (type == freebsd || type == fat || type == unknown)) +		c1 = Find_Mother_Chunk(d->chunks, c->offset, c->end, extended); +#endif +	if(!c1 && (type == freebsd || type == fat || type == unknown)) +		c1 = Find_Mother_Chunk(d->chunks, c->offset, c->end, whole); +#ifndef PC98 +	if(!c1 && type == extended) +		c1 = Find_Mother_Chunk(d->chunks, c->offset, c->end, whole); +#endif +	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; +#ifdef PC98 +			free(c2->sname); +			c2->sname = strdup("-"); +#endif +			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) barfout(1, "malloc failed"); +		*c2 = *c1; +		c1->next = c2; +		c1->disk = d; +#ifdef PC98 +		c1->sname = strdup("-"); +#endif +		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 | 
