diff options
Diffstat (limited to 'lib/libdisk/chunk.c')
-rw-r--r-- | lib/libdisk/chunk.c | 433 |
1 files changed, 433 insertions, 0 deletions
diff --git a/lib/libdisk/chunk.c b/lib/libdisk/chunk.c new file mode 100644 index 0000000..6116f54 --- /dev/null +++ b/lib/libdisk/chunk.c @@ -0,0 +1,433 @@ +/* + * ---------------------------------------------------------------------------- + * "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 + * ---------------------------------------------------------------------------- + * + * $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; + 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 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); + 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_data && c1->private_clone) + c2->private_data = c2->private_clone(c2->private_data); + 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, const 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->disk = c2->disk; + 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->disk = c2->disk; + 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->disk = c2->disk; + 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, long offset, u_long size, const 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->disk = c1->disk = d; + 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; + } + if (type == freebsd) + subtype = 0xa5; + 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__; +} + +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_PAST_1024) 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(' '); + printf("%p %8ld %8lu %8lu %-8s %-8s 0x%02x %s", + c1, c1->offset, c1->size, c1->end, c1->name, + 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); +} + +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; +} + +#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) err(1,"malloc failed"); + *c2 = *c1; + c1->next = c2; + c1->disk = d; + 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 |