1//
2// validate.c -
3//
4// Written by Eryk Vershen
5//
6
7/*
8 * Copyright 1997,1998 by Apple Computer, Inc.
9 *              All Rights Reserved
10 *
11 * Permission to use, copy, modify, and distribute this software and
12 * its documentation for any purpose and without fee is hereby granted,
13 * provided that the above copyright notice appears in all copies and
14 * that both the copyright notice and this permission notice appear in
15 * supporting documentation.
16 *
17 * APPLE COMPUTER DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
18 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19 * FOR A PARTICULAR PURPOSE.
20 *
21 * IN NO EVENT SHALL APPLE COMPUTER BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
22 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
23 * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
24 * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
25 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
26 */
27
28
29// for *printf()
30#include <stdio.h>
31// for malloc(), free()
32#ifndef __linux__
33#include <stdlib.h>
34#else
35#include <malloc.h>
36#endif
37// for O_RDONLY
38#include <fcntl.h>
39// for errno
40#include <errno.h>
41
42#include "validate.h"
43#include "deblock_media.h"
44#include "pathname.h"
45#include "convert.h"
46#include "io.h"
47#include "errors.h"
48
49
50//
51// Defines
52//
53
54
55//
56// Types
57//
58enum range_state {
59    kUnallocated,
60    kAllocated,
61    kMultiplyAllocated
62};
63
64struct range_list {
65    struct range_list *next;
66    struct range_list *prev;
67    enum range_state state;
68    int valid;
69    u32 start;
70    u32 end;
71};
72typedef struct range_list range_list;
73
74
75//
76// Global Constants
77//
78
79
80//
81// Global Variables
82//
83static char *buffer;
84static Block0 *b0;
85static DPME *mb;
86static partition_map_header *the_map;
87static MEDIA the_media;
88static int g;
89
90
91//
92// Forward declarations
93//
94int get_block_zero(void);
95int get_block_n(int n);
96range_list *new_range_list_item(enum range_state state, int valid, u32 low, u32 high);
97void initialize_list(range_list **list);
98void add_range(range_list **list, u32 base, u32 len, int allocate);
99void print_range_list(range_list *list);
100void delete_list(range_list *list);
101void coalesce_list(range_list *list);
102
103
104//
105// Routines
106//
107int
108get_block_zero(void)
109{
110    int rtn_value;
111
112    if (the_map != NULL) {
113	b0 = the_map->misc;
114	rtn_value = 1;
115    } else {
116	if (read_media(the_media, (long long) 0, PBLOCK_SIZE, buffer) == 0) {
117	    rtn_value = 0;
118	} else {
119	    b0 = (Block0 *) buffer;
120	    convert_block0(b0, 1);
121	    rtn_value = 1;
122	}
123    }
124    return rtn_value;
125}
126
127
128int
129get_block_n(int n)
130{
131    partition_map * entry;
132    int rtn_value;
133
134    if (the_map != NULL) {
135	entry = find_entry_by_disk_address(n, the_map);
136	if (entry != 0) {
137	    mb = entry->data;
138	    rtn_value = 1;
139	} else {
140	    rtn_value = 0;
141	}
142    } else {
143	if (read_media(the_media, ((long long) n) * g, PBLOCK_SIZE, (void *)buffer) == 0) {
144	    rtn_value = 0;
145	} else {
146	    mb = (DPME *) buffer;
147	    convert_dpme(mb, 1);
148	    rtn_value = 1;
149	}
150    }
151    return rtn_value;
152}
153
154
155range_list *
156new_range_list_item(enum range_state state, int valid, u32 low, u32 high)
157{
158    range_list *item;
159
160    item = (range_list *) malloc(sizeof(struct range_list));
161    item->next = 0;
162    item->prev = 0;
163    item->state = state;
164    item->valid = valid;
165    item->start = low;
166    item->end = high;
167    return item;
168}
169
170
171void
172initialize_list(range_list **list)
173{
174    range_list *item;
175
176    item = new_range_list_item(kUnallocated, 0, 0, 0xFFFFFFFF);
177    *list = item;
178}
179
180
181void
182delete_list(range_list *list)
183{
184    range_list *item;
185    range_list *cur;
186
187    for (cur = list; cur != 0; ) {
188	item = cur;
189	cur = cur->next;
190	free(item);
191    }
192}
193
194
195void
196add_range(range_list **list, u32 base, u32 len, int allocate)
197{
198    range_list *item;
199    range_list *cur;
200    u32 low;
201    u32 high;
202
203    if (list == 0 || *list == 0) {
204    	/* XXX initialized list will always have one element */
205    	return;
206    }
207
208    low = base;
209    high = base + len - 1;
210    if (len == 0 || high < len - 1) {
211	/* XXX wrapped around */
212	return;
213    }
214
215    cur = *list;
216    while (low <= high) {
217	if (cur == 0) {
218	    /* XXX should never occur */
219	    break;
220	}
221	if (low <= cur->end) {
222	    if (cur->start < low) {
223		item = new_range_list_item(cur->state, cur->valid, cur->start, low-1);
224		/* insert before here */
225		if (cur->prev == 0) {
226		    item->prev = 0;
227		    *list = item;
228		} else {
229		    item->prev = cur->prev;
230		    item->prev->next = item;
231		}
232		cur->prev = item;
233		item->next = cur;
234
235		cur->start = low;
236	    }
237	    if (high < cur->end) {
238		item = new_range_list_item(cur->state, cur->valid, high+1, cur->end);
239		/* insert after here */
240		if (cur->next == 0) {
241		    item->next = 0;
242		} else {
243		    item->next = cur->next;
244		    item->next->prev = item;
245		}
246		cur->next = item;
247		item->prev = cur;
248
249		cur->end = high;
250	    }
251
252	    if (allocate) {
253		switch (cur->state) {
254		case kUnallocated:
255		    cur->state = kAllocated;
256		    break;
257		case kAllocated:
258		case kMultiplyAllocated:
259		    cur->state = kMultiplyAllocated;
260		    break;
261		}
262	    } else {
263		cur->valid = 1;
264	    }
265	    low = cur->end + 1;
266	}
267	cur = cur->next;
268    }
269}
270
271
272void
273coalesce_list(range_list *list)
274{
275    range_list *cur;
276    range_list *item;
277
278    for (cur = list; cur != 0; ) {
279	item = cur->next;
280	if (item == 0) {
281	    break;
282	}
283	if (cur->valid == item->valid
284		&& cur->state == item->state) {
285	    cur->end = item->end;
286	    cur->next = item->next;
287	    if (item->next != 0) {
288		item->next->prev = cur;
289	    }
290	    free(item);
291	} else {
292	    cur = cur->next;
293	}
294    }
295}
296
297
298void
299print_range_list(range_list *list)
300{
301    range_list *cur;
302    int printed;
303    const char *s;
304
305    s = NULL;		/* XXXGCC -Wuninitialized [powerpc] */
306
307    if (list == 0) {
308	printf("Empty range list\n");
309	return;
310    }
311    printf("Range list:\n");
312    printed = 0;
313    for (cur = list; cur != 0; cur = cur->next) {
314	if (cur->valid) {
315	    switch (cur->state) {
316	    case kUnallocated:
317		s = "unallocated";
318		break;
319	    case kAllocated:
320		continue;
321		//s = "allocated";
322		//break;
323	    case kMultiplyAllocated:
324		s = "multiply allocated";
325		break;
326	    }
327	    printed = 1;
328	    printf("\t%u:%u %s\n", cur->start, cur->end, s);
329	} else {
330	    switch (cur->state) {
331	    case kUnallocated:
332		continue;
333		//s = "unallocated";
334		//break;
335	    case kAllocated:
336		s = "allocated";
337		break;
338	    case kMultiplyAllocated:
339		s = "multiply allocated";
340		break;
341	    }
342	    printed = 1;
343	    printf("\t%u:%u out of range, but %s\n", cur->start, cur->end, s);
344	}
345    }
346    if (printed == 0) {
347	printf("\tokay\n");
348    }
349}
350
351
352void
353validate_map(partition_map_header *map)
354{
355    range_list *list;
356    char *name;
357    unsigned int i;
358    u32 limit;
359    int printed;
360
361    //printf("Validation not implemented yet.\n");
362
363    if (map == NULL) {
364    	the_map = 0;
365	if (get_string_argument("Name of device: ", &name, 1) == 0) {
366	    bad_input("Bad name");
367	    return;
368	}
369	the_media = open_pathname_as_media(name, O_RDONLY);
370	if (the_media == 0) {
371	    error(errno, "can't open file '%s'", name);
372	    free(name);
373	    return;
374	}
375	g = media_granularity(the_media);
376	if (g < PBLOCK_SIZE) {
377	    g = PBLOCK_SIZE;
378	}
379   	the_media = open_deblock_media(PBLOCK_SIZE, the_media);
380
381	buffer = malloc(PBLOCK_SIZE);
382	if (buffer == NULL) {
383	    error(errno, "can't allocate memory for disk buffer");
384	    goto done;
385	}
386
387    } else {
388    	name = 0;
389	the_map = map;
390	g = map->logical_block;
391    }
392
393    initialize_list(&list);
394
395    // get block 0
396    if (get_block_zero() == 0) {
397	printf("unable to read block 0\n");
398	goto check_map;
399    }
400    // XXX signature valid
401    // XXX size & count match DeviceCapacity
402    // XXX number of descriptors matches array size
403    // XXX each descriptor wholly contained in a partition
404    // XXX the range below here is in physical blocks but the map is in logical blocks!!!
405    add_range(&list, 1, b0->sbBlkCount-1, 0);	/* subtract one since args are base & len */
406
407check_map:
408    // compute size of map
409    if (map != NULL) {
410	limit = the_map->blocks_in_map;
411    } else {
412	if (get_block_n(1) == 0) {
413	    printf("unable to get first block\n");
414	    goto done;
415	} else {
416	    if (mb->dpme_signature != DPME_SIGNATURE) {
417	        limit = -1;
418	    } else {
419		limit = mb->dpme_map_entries;
420	    }
421	}
422    }
423
424    // for each entry
425    for (i = 1; ; i++) {
426#if 0
427	if (limit < 0) {
428	    /* XXX what to use for end of list? */
429	    if (i > 5) {
430	    	break;
431	    }
432	} else
433#endif
434	if (i > limit) {
435	    break;
436	}
437
438	printf("block %d:\n", i);
439
440	// get entry
441	if (get_block_n(i) == 0) {
442	    printf("\tunable to get\n");
443	    goto post_processing;
444	}
445	printed = 0;
446
447	// signature matches
448	if (mb->dpme_signature != DPME_SIGNATURE) {
449	    printed = 1;
450	    printf("\tsignature is 0x%x, should be 0x%x\n", mb->dpme_signature, DPME_SIGNATURE);
451	}
452	// reserved1 == 0
453	if (mb->dpme_reserved_1 != 0) {
454	    printed = 1;
455	    printf("\treserved word is 0x%x, should be 0\n", mb->dpme_reserved_1);
456	}
457	// entry count matches
458#if 0
459	if (limit < 0) {
460	    printed = 1;
461	    printf("\tentry count is 0x%lx, real value unknown\n", mb->dpme_map_entries);
462	} else
463#endif
464	if (mb->dpme_map_entries != limit) {
465	    printed = 1;
466	    printf("\tentry count is 0x%x, should be %d\n", mb->dpme_map_entries, limit);
467	}
468	// lblocks contained within physical
469	if (mb->dpme_lblock_start >= mb->dpme_pblocks
470		|| mb->dpme_lblocks > mb->dpme_pblocks - mb->dpme_lblock_start) {
471	    printed = 1;
472	    printf("\tlogical blocks (%d for %d) not within physical size (%d)\n",
473		    mb->dpme_lblock_start, mb->dpme_lblocks, mb->dpme_pblocks);
474	}
475	// remember stuff for post processing
476	add_range(&list, mb->dpme_pblock_start, mb->dpme_pblocks, 1);
477
478	// XXX type is known type?
479	// XXX no unknown flags?
480	// XXX boot blocks either within or outside of logical
481	// XXX checksum matches contents
482	// XXX other fields zero if boot_bytes  is zero
483	// XXX processor id is known value?
484	// XXX no data in reserved3
485	if (printed == 0) {
486	    printf("\tokay\n");
487	}
488    }
489
490post_processing:
491    // properties of whole map
492
493    // every block on disk in one & only one partition
494    coalesce_list(list);
495    print_range_list(list);
496    // there is a partition for the map
497    // map fits within partition that contains it
498
499    // try to detect 512/2048 mixed partition map?
500
501done:
502    if (map == NULL) {
503	close_media(the_media);
504	free(buffer);
505	free(name);
506    }
507}
508