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