1 /*
2 * Copyright (c) 2001-2006 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 /*
30 * shadow.c
31 *
32 * Implement copy-on-write shadow map to allow a disk image to be
33 * mounted read-only, yet be writable by transferring writes to a
34 * "shadow" file. Subsequent reads from blocks that have been
35 * written will then go the "shadow" file.
36 *
37 * The map has two parts:
38 * 1) a bit map to track which blocks have been written
39 * 2) a band map to map a "band" within the original file to a corresponding
40 * "band" in the shadow file. Each band has the same size.
41 *
42 * The band map is used to ensure that blocks that are contiguous in the
43 * original file will remain contiguous in the shadow file.
44 *
45 * For debugging purposes, this file can be compiled standalone using:
46 * cc -o shadow shadow.c -DTEST_SHADOW
47 */
48
49 /*
50 * Modification History
51 *
52 * December 21, 2001 Dieter Siegmund ([email protected])
53 * - initial revision
54 */
55 #include <sys/param.h>
56 #include <sys/types.h>
57 #include <mach/boolean.h>
58
59 #include <string.h>
60
61 #ifdef TEST_SHADOW
62 #include <unistd.h>
63 #include <stdlib.h>
64 #else /* !TEST_SHADOW */
65 #include <sys/malloc.h>
66 #include <libkern/libkern.h>
67 #endif /* TEST_SHADOW */
68
69 #include "shadow.h"
70
71 #define UINT32_ALL_ONES ((uint32_t)(-1))
72 #define USHORT_ALL_ONES ((u_short)(-1))
73 #define UCHAR_ALL_ONES ((u_char)(-1))
74
75 #define my_trunc(value, divisor) ((value) / (divisor) * (divisor))
76
77 /* a band size of 128K can represent a file up to 8GB */
78 #define BAND_SIZE_DEFAULT_POWER_2 17
79 #define BAND_SIZE_DEFAULT (1 << BAND_SIZE_DEFAULT_POWER_2)
80
81 typedef u_short band_number_t;
82 #define BAND_ZERO ((band_number_t)0)
83 #define BAND_MAX ((band_number_t)65535)
84
85 struct shadow_map {
86 uint32_t blocks_per_band;/* size in blocks */
87 uint32_t block_size;
88 u_char * block_bitmap; /* 1 bit per block; 1=written */
89 band_number_t * bands; /* band map array */
90 uint32_t file_size_blocks; /* size of file in bands */
91 uint32_t shadow_size_bands; /* size of shadow in bands */
92 uint32_t next_band; /* next free band */
93 uint32_t zeroth_band; /* special-case 0th band */
94 };
95
96
97 typedef struct {
98 uint64_t byte;
99 uint32_t bit;
100 } bitmap_offset_t;
101
102 static __inline__ u_char
bit(int b)103 bit(int b)
104 {
105 return (u_char)(1 << b);
106 }
107
108 /*
109 * Function: bits_lower
110 * Purpose:
111 * Return a byte value in which bits numbered lower than 'b' are set.
112 */
113 static __inline__ u_char
bits_lower(int b)114 bits_lower(int b)
115 {
116 return (u_char)(bit(b) - 1);
117 }
118
119 /*
120 * Function: byte_set_bits
121 * Purpose:
122 * Set the given range of bits within a byte.
123 */
124 static __inline__ u_char
byte_set_bits(int start,int end)125 byte_set_bits(int start, int end)
126 {
127 return (u_char)((~bits_lower(start)) & (bits_lower(end) | bit(end)));
128 }
129
130 static __inline__ bitmap_offset_t
bitmap_offset(off_t where)131 bitmap_offset(off_t where)
132 {
133 bitmap_offset_t b;
134
135 b.byte = where / NBBY;
136 b.bit = where % NBBY;
137 return b;
138 }
139
140 /*
141 * Function: bitmap_set
142 *
143 * Purpose:
144 * Set the given range of bits.
145 *
146 * This algorithm tries to set the extents using the biggest
147 * units, using longs, then a short, then a byte, then bits.
148 */
149 static void
bitmap_set(u_char * map,off_t start_bit,size_t bit_count)150 bitmap_set(u_char * map, off_t start_bit, size_t bit_count)
151 {
152 bitmap_offset_t start;
153 bitmap_offset_t end;
154
155 start = bitmap_offset(start_bit);
156 end = bitmap_offset(start_bit + bit_count);
157 if (start.byte < end.byte) {
158 uint64_t n_bytes;
159
160 if (start.bit) {
161 map[start.byte] |= byte_set_bits(start.bit, NBBY - 1);
162 start.bit = 0;
163 start.byte++;
164 if (start.byte == end.byte) {
165 goto end;
166 }
167 }
168
169 n_bytes = end.byte - start.byte;
170
171 while (n_bytes >= (sizeof(uint32_t))) {
172 *((uint32_t *)(map + start.byte)) = UINT32_ALL_ONES;
173 start.byte += sizeof(uint32_t);
174 n_bytes -= sizeof(uint32_t);
175 }
176 if (n_bytes >= sizeof(u_short)) {
177 *((u_short *)(map + start.byte)) = USHORT_ALL_ONES;
178 start.byte += sizeof(u_short);
179 n_bytes -= sizeof(u_short);
180 }
181 if (n_bytes == 1) {
182 map[start.byte] = UCHAR_ALL_ONES;
183 start.byte++;
184 n_bytes = 0;
185 }
186 }
187
188 end:
189 if (end.bit > start.bit) {
190 map[start.byte] |= byte_set_bits(start.bit, end.bit - 1);
191 }
192
193 return;
194 }
195
196 /*
197 * Function: bitmap_get
198 *
199 * Purpose:
200 * Return the number of bits in the range that are the same e.g.
201 * 11101 returns 3 because the first 3 bits are the same (1's), whereas
202 * 001100 returns 2 because the first 2 bits are the same.
203 * This algorithm tries to count things in as big a chunk as possible,
204 * first aligning to a byte offset, then trying to count longs, a short,
205 * a byte, then any remaining bits to find the bit that is different.
206 */
207
208 static uint32_t
bitmap_get(u_char * map,off_t start_bit,size_t bit_count,boolean_t * ret_is_set)209 bitmap_get(u_char * map, off_t start_bit, size_t bit_count,
210 boolean_t * ret_is_set)
211 {
212 uint32_t count;
213 int i;
214 boolean_t is_set;
215 bitmap_offset_t start;
216 bitmap_offset_t end;
217
218 start = bitmap_offset(start_bit);
219 end = bitmap_offset(start_bit + bit_count);
220
221 is_set = (map[start.byte] & bit(start.bit)) ? TRUE : FALSE;
222 count = 0;
223
224 if (start.byte < end.byte) {
225 uint64_t n_bytes;
226
227 if (start.bit) { /* try to align to a byte */
228 for (i = start.bit; i < NBBY; i++) {
229 boolean_t this_is_set;
230
231 this_is_set = (map[start.byte] & bit(i)) ? TRUE : FALSE;
232 if (this_is_set != is_set) {
233 goto done; /* found bit that was different, we're done */
234 }
235 count++;
236 }
237 start.bit = 0; /* made it to the next byte */
238 start.byte++;
239 if (start.byte == end.byte) {
240 goto end; /* no more bytes, check for any leftover bits */
241 }
242 }
243 /* calculate how many bytes are left in the range */
244 n_bytes = end.byte - start.byte;
245
246 /* check for 4 bytes of the same bits */
247 while (n_bytes >= sizeof(uint32_t)) {
248 uint32_t * valPtr = (uint32_t *)(map + start.byte);
249 if ((is_set && *valPtr == UINT32_ALL_ONES)
250 || (!is_set && *valPtr == 0)) {
251 count += sizeof(*valPtr) * NBBY;
252 start.byte += sizeof(*valPtr);
253 n_bytes -= sizeof(*valPtr);
254 } else {
255 break; /* bits differ */
256 }
257 }
258 /* check for 2 bytes of the same bits */
259 if (n_bytes >= sizeof(u_short)) {
260 u_short * valPtr = (u_short *)(map + start.byte);
261
262 if ((is_set && *valPtr == USHORT_ALL_ONES)
263 || (!is_set && (*valPtr == 0))) {
264 count += sizeof(*valPtr) * NBBY;
265 start.byte += sizeof(*valPtr);
266 n_bytes -= sizeof(*valPtr);
267 }
268 }
269
270 /* check for 1 byte of the same bits */
271 if (n_bytes) {
272 if ((is_set && map[start.byte] == UCHAR_ALL_ONES)
273 || (!is_set && map[start.byte] == 0)) {
274 count += NBBY;
275 start.byte++;
276 n_bytes--;
277 }
278 /* we found bits that were different, find the first one */
279 if (n_bytes) {
280 for (i = 0; i < NBBY; i++) {
281 boolean_t this_is_set;
282
283 this_is_set = (map[start.byte] & bit(i)) ? TRUE : FALSE;
284 if (this_is_set != is_set) {
285 break;
286 }
287 count++;
288 }
289 goto done;
290 }
291 }
292 }
293
294 end:
295 for (i = start.bit; i < (int)end.bit; i++) {
296 boolean_t this_is_set = (map[start.byte] & bit(i)) ? TRUE : FALSE;
297
298 if (this_is_set != is_set) {
299 break;
300 }
301 count++;
302 }
303
304 done:
305 *ret_is_set = is_set;
306 return count;
307 }
308
309 static __inline__ band_number_t
shadow_map_block_to_band(shadow_map_t * map,off_t block)310 shadow_map_block_to_band(shadow_map_t * map, off_t block)
311 {
312 return (band_number_t)(block / map->blocks_per_band);
313 }
314
315 /*
316 * Function: shadow_map_mapped_band
317 * Purpose:
318 * Return the mapped band for the given band.
319 * If map_it is FALSE, and the band is not mapped, return FALSE.
320 * If map_it is TRUE, then this function will always return TRUE.
321 */
322 static boolean_t
shadow_map_mapped_band(shadow_map_t * map,band_number_t band,boolean_t map_it,band_number_t * mapped_band)323 shadow_map_mapped_band(shadow_map_t * map, band_number_t band,
324 boolean_t map_it, band_number_t * mapped_band)
325 {
326 boolean_t is_mapped = FALSE;
327
328 if (band == map->zeroth_band) {
329 *mapped_band = BAND_ZERO;
330 is_mapped = TRUE;
331 } else {
332 *mapped_band = map->bands[band];
333 if (*mapped_band == BAND_ZERO) {
334 if (map_it) {
335 /* grow the file */
336 if (map->next_band == 0) {
337 /* remember the zero'th band */
338 map->zeroth_band = band;
339 }
340 *mapped_band = map->bands[band] = (band_number_t)map->next_band++;
341 is_mapped = TRUE;
342 }
343 } else {
344 is_mapped = TRUE;
345 }
346 }
347 return is_mapped;
348 }
349
350 /*
351 * Function: shadow_map_contiguous
352 *
353 * Purpose:
354 * Return the first offset within the range position..(position + count)
355 * that is not a contiguous mapped band.
356 *
357 * If called with is_write = TRUE, this function will map bands as it goes.
358 */
359 static off_t
shadow_map_contiguous(shadow_map_t * map,off_t start_block,size_t num_blocks,boolean_t is_write)360 shadow_map_contiguous(shadow_map_t * map, off_t start_block,
361 size_t num_blocks, boolean_t is_write)
362 {
363 band_number_t band = shadow_map_block_to_band(map, start_block);
364 off_t end_block = start_block + num_blocks;
365 boolean_t is_mapped;
366 band_number_t mapped_band;
367 off_t ret_end_block = end_block;
368 off_t p;
369
370 is_mapped = shadow_map_mapped_band(map, band, is_write, &mapped_band);
371 if (is_write == FALSE && is_mapped == FALSE) {
372 static int happened = 0;
373 /* this can't happen */
374 if (happened == 0) {
375 printf("shadow_map_contiguous: this can't happen!\n");
376 happened = 1;
377 }
378 return start_block;
379 }
380 for (p = my_trunc(start_block + map->blocks_per_band,
381 map->blocks_per_band);
382 p < end_block; p += map->blocks_per_band) {
383 band_number_t next_mapped_band;
384
385 band++;
386 is_mapped = shadow_map_mapped_band(map, band, is_write,
387 &next_mapped_band);
388 if (is_write == FALSE && is_mapped == FALSE) {
389 return p;
390 }
391 if ((mapped_band + 1) != next_mapped_band) {
392 /* not contiguous */
393 ret_end_block = p;
394 break;
395 }
396 mapped_band = next_mapped_band;
397 }
398 return ret_end_block;
399 }
400
401
402 /*
403 * Function: block_bitmap_size
404 * Purpose:
405 * The number of bytes required in a block bitmap to represent a file of size
406 * file_size.
407 *
408 * The bytes required is the number of blocks in the file,
409 * divided by the number of bits per byte.
410 * Note:
411 * An 8GB file requires (assuming 512 byte block):
412 * 2^33 / 2^9 / 2^3 = 2^21 = 2MB
413 * of bitmap space. This is a non-trival amount of memory,
414 * particularly since most of the bits will be zero.
415 * A sparse bitmap would really help in this case.
416 */
417 static __inline__ size_t
block_bitmap_size(off_t file_size,uint32_t block_size)418 block_bitmap_size(off_t file_size, uint32_t block_size)
419 {
420 off_t blocks = howmany(file_size, block_size);
421 return howmany(blocks, NBBY);
422 }
423
424 /*
425 * Function: shadow_map_read
426 *
427 * Purpose:
428 * Calculate the block offset within the shadow to read, and the number
429 * blocks to read. The input values (block_offset, block_count) refer
430 * to the original file.
431 *
432 * The output values (*incr_block_offset, *incr_block_count) refer to the
433 * shadow file if the return value is TRUE. They refer to the original
434 * file if the return value is FALSE.
435 *
436 * Blocks within a band may or may not have been written, in addition,
437 * Bands are not necessarily contiguous, therefore:
438 * *incr_block_count <= block_count
439 * The caller must be prepared to call this function interatively
440 * to complete the whole i/o.
441 * Returns:
442 * TRUE if the shadow file should be read, FALSE if the original file
443 * should be read.
444 */
445 boolean_t
shadow_map_read(shadow_map_t * map,off_t block_offset,size_t block_count,off_t * incr_block_offset,size_t * incr_block_count)446 shadow_map_read(shadow_map_t * map, off_t block_offset, size_t block_count,
447 off_t * incr_block_offset, size_t * incr_block_count)
448 {
449 boolean_t written = FALSE;
450 uint32_t n_blocks;
451
452 if (block_offset >= map->file_size_blocks
453 || (block_offset + block_count) > map->file_size_blocks) {
454 printf("shadow_map_read: request (%lld, %lu) exceeds file size %d\n",
455 block_offset, block_count, map->file_size_blocks);
456 *incr_block_count = 0;
457 }
458 n_blocks = bitmap_get(map->block_bitmap, block_offset, block_count,
459 &written);
460 if (written == FALSE) {
461 *incr_block_count = n_blocks;
462 *incr_block_offset = block_offset;
463 } else { /* start has been written, and therefore mapped */
464 band_number_t mapped_band;
465 off_t band_limit;
466
467 mapped_band = map->bands[shadow_map_block_to_band(map, block_offset)];
468 *incr_block_offset = mapped_band * map->blocks_per_band
469 + (block_offset % map->blocks_per_band);
470 band_limit
471 = shadow_map_contiguous(map, block_offset, block_count, FALSE);
472 *incr_block_count = band_limit - block_offset;
473 if (*incr_block_count > n_blocks) {
474 *incr_block_count = n_blocks;
475 }
476 }
477 return written;
478 }
479
480 /*
481 * Function: shadow_map_write
482 *
483 * Purpose:
484 * Calculate the block offset within the shadow to write, and the number
485 * blocks to write. The input values (block_offset, block_count) refer
486 * to the original file. The output values
487 * (*incr_block_offset, *incr_block_count) refer to the shadow file.
488 *
489 * Bands are not necessarily contiguous, therefore:
490 * *incr_block_count <= block_count
491 * The caller must be prepared to call this function interatively
492 * to complete the whole i/o.
493 * Returns:
494 * TRUE if the shadow file was grown, FALSE otherwise.
495 */
496 boolean_t
shadow_map_write(shadow_map_t * map,off_t block_offset,size_t block_count,off_t * incr_block_offset,size_t * incr_block_count)497 shadow_map_write(shadow_map_t * map, off_t block_offset,
498 size_t block_count, off_t * incr_block_offset,
499 size_t * incr_block_count)
500 {
501 off_t band_limit;
502 band_number_t mapped_band;
503 boolean_t shadow_grew = FALSE;
504
505 if (block_offset >= map->file_size_blocks
506 || (block_offset + block_count) > map->file_size_blocks) {
507 printf("shadow_map_write: request (%lld, %zu) exceeds file size %d\n",
508 block_offset, block_count, map->file_size_blocks);
509 *incr_block_count = 0;
510 }
511
512 band_limit = shadow_map_contiguous(map, block_offset, block_count, TRUE);
513 mapped_band = map->bands[shadow_map_block_to_band(map, block_offset)];
514 *incr_block_offset = mapped_band * map->blocks_per_band
515 + (block_offset % map->blocks_per_band);
516 *incr_block_count = band_limit - block_offset;
517
518 /* mark these blocks as written */
519 bitmap_set(map->block_bitmap, block_offset, *incr_block_count);
520
521 if (map->next_band > map->shadow_size_bands) {
522 map->shadow_size_bands = map->next_band;
523 shadow_grew = TRUE;
524 }
525 return shadow_grew;
526 }
527
528 boolean_t
shadow_map_is_written(shadow_map_t * map,off_t block_offset)529 shadow_map_is_written(shadow_map_t * map, off_t block_offset)
530 {
531 bitmap_offset_t b;
532
533 b = bitmap_offset(block_offset);
534 return (map->block_bitmap[b.byte] & bit(b.bit)) ? TRUE : FALSE;
535 }
536
537 /*
538 * Function: shadow_map_shadow_size
539 *
540 * Purpose:
541 * To return the size of the shadow file in blocks.
542 */
543 uint32_t
shadow_map_shadow_size(shadow_map_t * map)544 shadow_map_shadow_size(shadow_map_t * map)
545 {
546 return map->shadow_size_bands * map->blocks_per_band;
547 }
548
549 /*
550 * Function: shadow_map_create
551 *
552 * Purpose:
553 * Allocate the dynamic data for keeping track of the shadow dirty blocks
554 * and the band mapping table.
555 * Returns:
556 * NULL if an error occurred.
557 */
558 shadow_map_t *
shadow_map_create(off_t file_size,off_t shadow_size,uint32_t band_size,uint32_t block_size)559 shadow_map_create(off_t file_size, off_t shadow_size,
560 uint32_t band_size, uint32_t block_size)
561 {
562 void * block_bitmap = NULL;
563 size_t bitmap_size;
564 band_number_t * bands = NULL;
565 shadow_map_t * map;
566 uint32_t n_bands = 0;
567
568 if (band_size == 0) {
569 band_size = BAND_SIZE_DEFAULT;
570 }
571
572 off_t many = howmany(file_size, band_size);
573 if (many > (BAND_MAX + 1)) {
574 printf("file is too big: %lld > %d\n",
575 many, BAND_MAX);
576 goto failure;
577 }
578 n_bands = (uint32_t)many;
579
580 /* create a block bitmap, one bit per block */
581 bitmap_size = block_bitmap_size(file_size, block_size);
582 block_bitmap = kalloc_data(bitmap_size, Z_ZERO);
583 if (block_bitmap == NULL) {
584 printf("failed to allocate bitmap\n");
585 goto failure;
586 }
587
588 /* get the band map */
589 bands = (band_number_t *)kalloc_data(n_bands * sizeof(band_number_t), Z_ZERO);
590 if (bands == NULL) {
591 printf("failed to allocate bands\n");
592 goto failure;
593 }
594
595 map = kalloc_type(shadow_map_t, 0);
596 if (map == NULL) {
597 printf("failed to allocate map\n");
598 goto failure;
599 }
600 map->blocks_per_band = band_size / block_size;
601 map->block_bitmap = block_bitmap;
602 map->bands = bands;
603 map->file_size_blocks = n_bands * map->blocks_per_band;
604 map->next_band = 0;
605 map->zeroth_band = -1;
606 map->shadow_size_bands = (uint32_t)howmany(shadow_size, band_size);
607 map->block_size = block_size;
608 return map;
609
610 failure:
611 if (block_bitmap) {
612 kfree_data(block_bitmap, bitmap_size);
613 }
614 if (bands) {
615 kfree_data(bands, n_bands * sizeof(band_number_t));
616 }
617 return NULL;
618 }
619
620 /*
621 * Function: shadow_map_free
622 * Purpose:
623 * Frees the data structure to deal with the shadow map.
624 */
625 void
shadow_map_free(shadow_map_t * map)626 shadow_map_free(shadow_map_t * map)
627 {
628 if (map->block_bitmap) {
629 kfree_data_addr(map->block_bitmap);
630 }
631 if (map->bands) {
632 kfree_data(map->bands, (map->file_size_blocks / map->blocks_per_band) * sizeof(band_number_t));
633 }
634 map->block_bitmap = NULL;
635 map->bands = NULL;
636 kfree_type(shadow_map_t, map);
637 return;
638 }
639
640 #ifdef TEST_SHADOW
641 #define BAND_SIZE_BLOCKS (BAND_SIZE_DEFAULT / 512)
642
643 enum {
644 ReadRequest,
645 WriteRequest,
646 };
647
648 typedef struct {
649 int type;
650 uint32_t offset;
651 uint32_t count;
652 } block_request_t;
653
654 int
main()655 main()
656 {
657 shadow_map_t * map;
658 int i;
659 block_request_t requests[] = {
660 { WriteRequest, BAND_SIZE_BLOCKS * 2, 1 },
661 { ReadRequest, BAND_SIZE_BLOCKS / 2, BAND_SIZE_BLOCKS * 2 - 2 },
662 { WriteRequest, BAND_SIZE_BLOCKS * 1, 5 * BAND_SIZE_BLOCKS + 3},
663 { ReadRequest, 0, BAND_SIZE_BLOCKS * 10 },
664 { WriteRequest, BAND_SIZE_BLOCKS * (BAND_MAX - 1),
665 BAND_SIZE_BLOCKS * 2},
666 { 0, 0 },
667 };
668
669 map = shadow_map_create(1024 * 1024 * 1024 * 8ULL, 0, 0, 512);
670 if (map == NULL) {
671 printf("shadow_map_create failed\n");
672 exit(1);
673 }
674 for (i = 0; TRUE; i++) {
675 uint32_t offset;
676 uint32_t resid;
677 boolean_t shadow_grew;
678 boolean_t read_shadow;
679
680 if (requests[i].count == 0) {
681 break;
682 }
683 offset = requests[i].offset;
684 resid = requests[i].count;
685 printf("\n%s REQUEST (%ld, %ld)\n",
686 requests[i].type == WriteRequest ? "WRITE" : "READ",
687 offset, resid);
688 switch (requests[i].type) {
689 case WriteRequest:
690 while (resid > 0) {
691 uint32_t this_offset;
692 uint32_t this_count;
693
694 shadow_grew = shadow_map_write(map, offset,
695 resid,
696 &this_offset,
697 &this_count);
698 printf("\t(%ld, %ld) => (%ld, %ld)",
699 offset, resid, this_offset, this_count);
700 resid -= this_count;
701 offset += this_count;
702 if (shadow_grew) {
703 printf(" shadow grew to %ld", shadow_map_shadow_size(map));
704 }
705 printf("\n");
706 }
707 break;
708 case ReadRequest:
709 while (resid > 0) {
710 uint32_t this_offset;
711 uint32_t this_count;
712
713 read_shadow = shadow_map_read(map, offset,
714 resid,
715 &this_offset,
716 &this_count);
717 printf("\t(%ld, %ld) => (%ld, %ld)%s\n",
718 offset, resid, this_offset, this_count,
719 read_shadow ? " from shadow" : "");
720 if (this_count == 0) {
721 printf("this_count is 0, aborting\n");
722 break;
723 }
724 resid -= this_count;
725 offset += this_count;
726 }
727 break;
728 default:
729 break;
730 }
731 }
732 if (map) {
733 shadow_map_free(map);
734 }
735 exit(0);
736 return 0;
737 }
738 #endif
739