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