#include <stdio.h>
#ifndef __linux__
#include <stdlib.h>
#else
#include <malloc.h>
#endif
#include <fcntl.h>
#include <errno.h>
#include "validate.h"
#include "deblock_media.h"
#include "pathname.h"
#include "convert.h"
#include "io.h"
#include "errors.h"
enum range_state {
kUnallocated,
kAllocated,
kMultiplyAllocated
};
struct range_list {
struct range_list *next;
struct range_list *prev;
enum range_state state;
int valid;
u32 start;
u32 end;
};
typedef struct range_list range_list;
static char *buffer;
static Block0 *b0;
static DPME *mb;
static partition_map_header *the_map;
static MEDIA the_media;
static int g;
int get_block_zero(void);
int get_block_n(int n);
range_list *new_range_list_item(enum range_state state, int valid, u32 low, u32 high);
void initialize_list(range_list **list);
void add_range(range_list **list, u32 base, u32 len, int allocate);
void print_range_list(range_list *list);
void delete_list(range_list *list);
void coalesce_list(range_list *list);
int
get_block_zero(void)
{
int rtn_value;
if (the_map != NULL) {
b0 = the_map->misc;
rtn_value = 1;
} else {
if (read_media(the_media, (long long) 0, PBLOCK_SIZE, buffer) == 0) {
rtn_value = 0;
} else {
b0 = (Block0 *) buffer;
convert_block0(b0, 1);
rtn_value = 1;
}
}
return rtn_value;
}
int
get_block_n(int n)
{
partition_map * entry;
int rtn_value;
if (the_map != NULL) {
entry = find_entry_by_disk_address(n, the_map);
if (entry != 0) {
mb = entry->data;
rtn_value = 1;
} else {
rtn_value = 0;
}
} else {
if (read_media(the_media, ((long long) n) * g, PBLOCK_SIZE, (void *)buffer) == 0) {
rtn_value = 0;
} else {
mb = (DPME *) buffer;
convert_dpme(mb, 1);
rtn_value = 1;
}
}
return rtn_value;
}
range_list *
new_range_list_item(enum range_state state, int valid, u32 low, u32 high)
{
range_list *item;
item = (range_list *) malloc(sizeof(struct range_list));
item->next = 0;
item->prev = 0;
item->state = state;
item->valid = valid;
item->start = low;
item->end = high;
return item;
}
void
initialize_list(range_list **list)
{
range_list *item;
item = new_range_list_item(kUnallocated, 0, 0, 0xFFFFFFFF);
*list = item;
}
void
delete_list(range_list *list)
{
range_list *item;
range_list *cur;
for (cur = list; cur != 0; ) {
item = cur;
cur = cur->next;
free(item);
}
}
void
add_range(range_list **list, u32 base, u32 len, int allocate)
{
range_list *item;
range_list *cur;
u32 low;
u32 high;
if (list == 0 || *list == 0) {
return;
}
low = base;
high = base + len - 1;
if (len == 0 || high < len - 1) {
return;
}
cur = *list;
while (low <= high) {
if (cur == 0) {
break;
}
if (low <= cur->end) {
if (cur->start < low) {
item = new_range_list_item(cur->state, cur->valid, cur->start, low-1);
if (cur->prev == 0) {
item->prev = 0;
*list = item;
} else {
item->prev = cur->prev;
item->prev->next = item;
}
cur->prev = item;
item->next = cur;
cur->start = low;
}
if (high < cur->end) {
item = new_range_list_item(cur->state, cur->valid, high+1, cur->end);
if (cur->next == 0) {
item->next = 0;
} else {
item->next = cur->next;
item->next->prev = item;
}
cur->next = item;
item->prev = cur;
cur->end = high;
}
if (allocate) {
switch (cur->state) {
case kUnallocated:
cur->state = kAllocated;
break;
case kAllocated:
case kMultiplyAllocated:
cur->state = kMultiplyAllocated;
break;
}
} else {
cur->valid = 1;
}
low = cur->end + 1;
}
cur = cur->next;
}
}
void
coalesce_list(range_list *list)
{
range_list *cur;
range_list *item;
for (cur = list; cur != 0; ) {
item = cur->next;
if (item == 0) {
break;
}
if (cur->valid == item->valid
&& cur->state == item->state) {
cur->end = item->end;
cur->next = item->next;
if (item->next != 0) {
item->next->prev = cur;
}
free(item);
} else {
cur = cur->next;
}
}
}
void
print_range_list(range_list *list)
{
range_list *cur;
int printed;
const char *s;
s = NULL;
if (list == 0) {
printf("Empty range list\n");
return;
}
printf("Range list:\n");
printed = 0;
for (cur = list; cur != 0; cur = cur->next) {
if (cur->valid) {
switch (cur->state) {
case kUnallocated:
s = "unallocated";
break;
case kAllocated:
continue;
case kMultiplyAllocated:
s = "multiply allocated";
break;
}
printed = 1;
printf("\t%u:%u %s\n", cur->start, cur->end, s);
} else {
switch (cur->state) {
case kUnallocated:
continue;
case kAllocated:
s = "allocated";
break;
case kMultiplyAllocated:
s = "multiply allocated";
break;
}
printed = 1;
printf("\t%u:%u out of range, but %s\n", cur->start, cur->end, s);
}
}
if (printed == 0) {
printf("\tokay\n");
}
}
void
validate_map(partition_map_header *map)
{
range_list *list;
char *name;
unsigned int i;
u32 limit;
int printed;
if (map == NULL) {
the_map = 0;
if (get_string_argument("Name of device: ", &name, 1) == 0) {
bad_input("Bad name");
return;
}
the_media = open_pathname_as_media(name, O_RDONLY);
if (the_media == 0) {
error(errno, "can't open file '%s'", name);
free(name);
return;
}
g = media_granularity(the_media);
if (g < PBLOCK_SIZE) {
g = PBLOCK_SIZE;
}
the_media = open_deblock_media(PBLOCK_SIZE, the_media);
buffer = malloc(PBLOCK_SIZE);
if (buffer == NULL) {
error(errno, "can't allocate memory for disk buffer");
goto done;
}
} else {
name = 0;
the_map = map;
g = map->logical_block;
}
initialize_list(&list);
if (get_block_zero() == 0) {
printf("unable to read block 0\n");
goto check_map;
}
add_range(&list, 1, b0->sbBlkCount-1, 0);
check_map:
if (map != NULL) {
limit = the_map->blocks_in_map;
} else {
if (get_block_n(1) == 0) {
printf("unable to get first block\n");
goto done;
} else {
if (mb->dpme_signature != DPME_SIGNATURE) {
limit = -1;
} else {
limit = mb->dpme_map_entries;
}
}
}
for (i = 1; ; i++) {
#if 0
if (limit < 0) {
if (i > 5) {
break;
}
} else
#endif
if (i > limit) {
break;
}
printf("block %d:\n", i);
if (get_block_n(i) == 0) {
printf("\tunable to get\n");
goto post_processing;
}
printed = 0;
if (mb->dpme_signature != DPME_SIGNATURE) {
printed = 1;
printf("\tsignature is 0x%x, should be 0x%x\n", mb->dpme_signature, DPME_SIGNATURE);
}
if (mb->dpme_reserved_1 != 0) {
printed = 1;
printf("\treserved word is 0x%x, should be 0\n", mb->dpme_reserved_1);
}
#if 0
if (limit < 0) {
printed = 1;
printf("\tentry count is 0x%lx, real value unknown\n", mb->dpme_map_entries);
} else
#endif
if (mb->dpme_map_entries != limit) {
printed = 1;
printf("\tentry count is 0x%x, should be %d\n", mb->dpme_map_entries, limit);
}
if (mb->dpme_lblock_start >= mb->dpme_pblocks
|| mb->dpme_lblocks > mb->dpme_pblocks - mb->dpme_lblock_start) {
printed = 1;
printf("\tlogical blocks (%d for %d) not within physical size (%d)\n",
mb->dpme_lblock_start, mb->dpme_lblocks, mb->dpme_pblocks);
}
add_range(&list, mb->dpme_pblock_start, mb->dpme_pblocks, 1);
if (printed == 0) {
printf("\tokay\n");
}
}
post_processing:
coalesce_list(list);
print_range_list(list);
done:
if (map == NULL) {
close_media(the_media);
free(buffer);
free(name);
}
}