2 * Copyright (c) 2007 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific, prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * $DragonFly: src/sbin/newfs_hammer/newfs_hammer.c,v 1.10 2007/12/14 08:05:37 dillon Exp $
37 #include "newfs_hammer.h"
39 static int64_t getsize(const char *str
, int64_t minval
, int64_t maxval
, int pw
);
40 static const char *sizetostr(off_t size
);
41 static void check_volume(struct volume_info
*vol
);
42 static void format_volume(struct volume_info
*vol
, int nvols
,const char *label
);
43 static int32_t format_cluster(struct volume_info
*vol
, int isroot
);
44 static void format_root(struct cluster_info
*cluster
);
45 static void usage(void);
47 struct hammer_alist_config Buf_alist_config
;
48 struct hammer_alist_config Vol_normal_alist_config
;
49 struct hammer_alist_config Vol_super_alist_config
;
50 struct hammer_alist_config Supercl_alist_config
;
51 struct hammer_alist_config Clu_master_alist_config
;
52 struct hammer_alist_config Clu_slave_alist_config
;
58 int UsingSuperClusters
;
60 struct volume_info
*VolBase
;
63 main(int ac
, char **av
)
69 int64_t max_volume_size
;
70 const char *label
= NULL
;
73 * Sanity check basic filesystem structures. No cookies for us
76 assert(sizeof(struct hammer_almeta
) == HAMMER_ALMETA_SIZE
);
77 assert(sizeof(struct hammer_fsbuf_head
) == HAMMER_FSBUF_HEAD_SIZE
);
78 assert(sizeof(struct hammer_volume_ondisk
) <= HAMMER_BUFSIZE
);
79 assert(sizeof(struct hammer_cluster_ondisk
) <= HAMMER_BUFSIZE
);
80 assert(sizeof(struct hammer_fsbuf_data
) == HAMMER_BUFSIZE
);
81 assert(sizeof(struct hammer_fsbuf_recs
) == HAMMER_BUFSIZE
);
82 assert(sizeof(struct hammer_fsbuf_btree
) == HAMMER_BUFSIZE
);
83 assert(sizeof(union hammer_fsbuf_ondisk
) == HAMMER_BUFSIZE
);
86 * Generate a filesysem id and lookup the filesystem type
88 uuidgen(&Hammer_FSId
, 1);
89 uuid_name_lookup(&Hammer_FSType
, "DragonFly HAMMER", &status
);
90 if (status
!= uuid_s_ok
) {
91 errx(1, "uuids file does not have the DragonFly "
92 "HAMMER filesystem type");
96 * Initialize the alist templates we will be using
98 hammer_alist_template(&Buf_alist_config
, HAMMER_FSBUF_MAXBLKS
,
99 1, HAMMER_FSBUF_METAELMS
);
100 hammer_alist_template(&Vol_normal_alist_config
, HAMMER_VOL_MAXCLUSTERS
,
101 1, HAMMER_VOL_METAELMS_1LYR
);
102 hammer_alist_template(&Vol_super_alist_config
,
103 HAMMER_VOL_MAXSUPERCLUSTERS
* HAMMER_SCL_MAXCLUSTERS
,
104 HAMMER_SCL_MAXCLUSTERS
, HAMMER_VOL_METAELMS_2LYR
);
105 hammer_super_alist_template(&Vol_super_alist_config
);
106 hammer_alist_template(&Supercl_alist_config
, HAMMER_VOL_MAXCLUSTERS
,
107 1, HAMMER_SUPERCL_METAELMS
);
108 hammer_alist_template(&Clu_master_alist_config
, HAMMER_CLU_MAXBUFFERS
,
109 1, HAMMER_CLU_MASTER_METAELMS
);
110 hammer_alist_template(&Clu_slave_alist_config
,
111 HAMMER_CLU_MAXBUFFERS
* HAMMER_FSBUF_MAXBLKS
,
112 HAMMER_FSBUF_MAXBLKS
, HAMMER_CLU_SLAVE_METAELMS
);
113 hammer_buffer_alist_template(&Clu_slave_alist_config
);
118 while ((ch
= getopt(ac
, av
, "L:b:c:m:S")) != -1) {
124 BootAreaSize
= getsize(optarg
,
126 HAMMER_BOOT_MAXBYTES
, 2);
129 ClusterSize
= getsize(optarg
,
130 HAMMER_BUFSIZE
* 256LL,
131 HAMMER_CLU_MAXBYTES
, 1);
134 MemAreaSize
= getsize(optarg
,
136 HAMMER_MEM_MAXBYTES
, 2);
140 * Force the use of super-clusters
142 UsingSuperClusters
= 1;
152 "newfs_hammer: A filesystem label must be specified\n");
157 * Collect volume information
164 for (i
= 0; i
< NumVolumes
; ++i
) {
165 struct volume_info
*vol
;
167 vol
= calloc(1, sizeof(struct volume_info
));
175 * Load up information on the volume and initialize
176 * its remaining fields.
183 * Calculate the size of a cluster. A cluster is broken
184 * down into 256 chunks which must be at least filesystem buffer
185 * sized. This gives us a minimum chunk size of around 4MB.
187 if (ClusterSize
== 0) {
188 ClusterSize
= HAMMER_BUFSIZE
* 256;
189 while (ClusterSize
< total
/ NumVolumes
/ 256 &&
190 ClusterSize
< HAMMER_CLU_MAXBYTES
) {
196 * Calculate defaults for the boot and memory area sizes.
198 if (BootAreaSize
== 0) {
199 BootAreaSize
= HAMMER_BOOT_NOMBYTES
;
200 while (BootAreaSize
> total
/ NumVolumes
/ 256)
202 if (BootAreaSize
< HAMMER_BOOT_MINBYTES
)
204 } else if (BootAreaSize
< HAMMER_BOOT_MINBYTES
) {
205 BootAreaSize
= HAMMER_BOOT_MINBYTES
;
207 if (MemAreaSize
== 0) {
208 MemAreaSize
= HAMMER_MEM_NOMBYTES
;
209 while (MemAreaSize
> total
/ NumVolumes
/ 256)
211 if (MemAreaSize
< HAMMER_MEM_MINBYTES
)
213 } else if (MemAreaSize
< HAMMER_MEM_MINBYTES
) {
214 MemAreaSize
= HAMMER_MEM_MINBYTES
;
217 printf("---------------------------------------------\n");
218 printf("%d volume%s total size %s\n",
219 NumVolumes
, (NumVolumes
== 1 ? "" : "s"), sizetostr(total
));
220 printf("cluster-size: %s\n", sizetostr(ClusterSize
));
222 if (UsingSuperClusters
) {
223 max_volume_size
= (int64_t)HAMMER_VOL_MAXSUPERCLUSTERS
* \
224 HAMMER_SCL_MAXCLUSTERS
* ClusterSize
;
226 max_volume_size
= HAMMER_VOL_MAXCLUSTERS
* ClusterSize
;
228 printf("max-volume-size: %s\n", sizetostr(max_volume_size
));
230 printf("max-filesystem-size: %s\n",
231 (max_volume_size
* 32768LL < max_volume_size
) ?
233 sizetostr(max_volume_size
* 32768LL));
234 printf("boot-area-size: %s\n", sizetostr(BootAreaSize
));
235 printf("memory-log-size: %s\n", sizetostr(MemAreaSize
));
239 * Format the volumes.
241 for (i
= 0; i
< NumVolumes
; ++i
) {
242 format_volume(get_volume(i
), NumVolumes
, label
);
252 fprintf(stderr
, "newfs_hammer vol0 [vol1 ...]\n");
257 * Convert the size in bytes to a human readable string.
260 sizetostr(off_t size
)
264 if (size
< 1024 / 2) {
265 snprintf(buf
, sizeof(buf
), "%6.2f", (double)size
);
266 } else if (size
< 1024 * 1024 / 2) {
267 snprintf(buf
, sizeof(buf
), "%6.2fKB",
268 (double)size
/ 1024);
269 } else if (size
< 1024 * 1024 * 1024LL / 2) {
270 snprintf(buf
, sizeof(buf
), "%6.2fMB",
271 (double)size
/ (1024 * 1024));
272 } else if (size
< 1024 * 1024 * 1024LL * 1024LL / 2) {
273 snprintf(buf
, sizeof(buf
), "%6.2fGB",
274 (double)size
/ (1024 * 1024 * 1024LL));
276 snprintf(buf
, sizeof(buf
), "%6.2fTB",
277 (double)size
/ (1024 * 1024 * 1024LL * 1024LL));
283 * Convert a string to a 64 bit signed integer with various requirements.
286 getsize(const char *str
, int64_t minval
, int64_t maxval
, int powerof2
)
291 val
= strtoll(str
, &ptr
, 0);
310 errx(1, "Unknown suffix in number '%s'\n", str
);
314 errx(1, "Unknown suffix in number '%s'\n", str
);
318 errx(1, "Value too small: %s, min is %s\n",
319 str
, sizetostr(minval
));
323 errx(1, "Value too large: %s, max is %s\n",
324 str
, sizetostr(maxval
));
327 if ((powerof2
& 1) && (val
^ (val
- 1)) != ((val
<< 1) - 1)) {
328 errx(1, "Value not power of 2: %s\n", str
);
331 if ((powerof2
& 2) && (val
& HAMMER_BUFMASK
)) {
332 errx(1, "Value not an integral multiple of %dK: %s",
333 HAMMER_BUFSIZE
/ 1024, str
);
340 * Generate a transaction id
345 static hammer_tid_t lasttid
;
349 gettimeofday(&tv
, NULL
);
350 lasttid
= tv
.tv_sec
* 1000000000LL +
357 * Check basic volume characteristics. HAMMER filesystems use a minimum
358 * of a 16KB filesystem buffer size.
362 check_volume(struct volume_info
*vol
)
364 struct partinfo pinfo
;
368 * Get basic information about the volume
370 vol
->fd
= open(vol
->name
, O_RDWR
);
372 err(1, "Unable to open %s R+W", vol
->name
);
373 if (ioctl(vol
->fd
, DIOCGPART
, &pinfo
) < 0) {
375 * Allow the formatting of regular filews as HAMMER volumes
377 if (fstat(vol
->fd
, &st
) < 0)
378 err(1, "Unable to stat %s", vol
->name
);
379 vol
->size
= st
.st_size
;
380 vol
->type
= "REGFILE";
383 * When formatting a block device as a HAMMER volume the
384 * sector size must be compatible. HAMMER uses 16384 byte
385 * filesystem buffers.
387 if (pinfo
.reserved_blocks
) {
388 errx(1, "HAMMER cannot be placed in a partition "
389 "which overlaps the disklabel or MBR");
391 if (pinfo
.media_blksize
> 16384 ||
392 16384 % pinfo
.media_blksize
) {
393 errx(1, "A media sector size of %d is not supported",
394 pinfo
.media_blksize
);
397 vol
->size
= pinfo
.media_size
;
398 vol
->type
= "DEVICE";
400 printf("Volume %d %s %-15s size %s\n",
401 vol
->vol_no
, vol
->type
, vol
->name
,
402 sizetostr(vol
->size
));
405 * Strictly speaking we do not need to enable super clusters unless
406 * we have volumes > 2TB, but turning them on doesn't really hurt
407 * and if we don't the user may get confused if he tries to expand
408 * the size of an existing volume.
410 if (vol
->size
> 200LL * 1024 * 1024 * 1024 && !UsingSuperClusters
) {
411 UsingSuperClusters
= 1;
412 printf("Enabling super-clusters\n");
416 * Reserve space for (future) header junk
418 vol
->vol_alloc
= HAMMER_BUFSIZE
* 16;
422 * Format a HAMMER volume. Cluster 0 will be initially placed in volume 0.
426 format_volume(struct volume_info
*vol
, int nvols
, const char *label
)
428 struct hammer_volume_ondisk
*ondisk
;
432 int64_t scl_group_size
;
433 int64_t scl_header_size
;
437 * The last cluster in a volume may wind up truncated. It must be
438 * at least minclsize to really be workable as a cluster.
440 minclsize
= (int32_t)(ClusterSize
/ 4);
441 if (minclsize
< HAMMER_BUFSIZE
* 64)
442 minclsize
= HAMMER_BUFSIZE
* 64;
445 * Initialize basic information in the on-disk volume structure.
447 ondisk
= vol
->ondisk
;
449 ondisk
->vol_fsid
= Hammer_FSId
;
450 ondisk
->vol_fstype
= Hammer_FSType
;
451 snprintf(ondisk
->vol_name
, sizeof(ondisk
->vol_name
), "%s", label
);
452 ondisk
->vol_no
= vol
->vol_no
;
453 ondisk
->vol_count
= nvols
;
454 ondisk
->vol_version
= 1;
455 ondisk
->vol_clsize
= (int32_t)ClusterSize
;
456 if (UsingSuperClusters
)
457 ondisk
->vol_flags
= HAMMER_VOLF_USINGSUPERCL
;
459 ondisk
->vol_bot_beg
= vol
->vol_alloc
;
460 vol
->vol_alloc
+= BootAreaSize
;
461 ondisk
->vol_mem_beg
= vol
->vol_alloc
;
462 vol
->vol_alloc
+= MemAreaSize
;
463 ondisk
->vol_clo_beg
= vol
->vol_alloc
;
464 ondisk
->vol_clo_end
= vol
->size
;
466 if (ondisk
->vol_clo_end
< ondisk
->vol_clo_beg
) {
467 errx(1, "volume %d %s is too small to hold the volume header",
468 vol
->vol_no
, vol
->name
);
472 * Our A-lists have been initialized but are marked all-allocated.
473 * Calculate the actual number of clusters in the volume and free
474 * them to get the filesystem ready for work. The clusters will
475 * be initialized on-demand.
477 * If using super-clusters we must still calculate nclusters but
478 * we only need to initialize superclusters that are not going
479 * to wind up in the all-free state, which will only be the last
480 * supercluster. hammer_alist_free() will recurse into the
481 * supercluster infrastructure and create the necessary superclusters.
483 * NOTE: The nclusters calculation ensures that the volume EOF does
484 * not occur in the middle of a supercluster buffer array.
486 if (UsingSuperClusters
) {
488 * Figure out how many full super-cluster groups we will have.
489 * This calculation does not include the partial supercluster
492 scl_header_size
= (int64_t)HAMMER_BUFSIZE
*
493 HAMMER_VOL_SUPERCLUSTER_GROUP
;
494 scl_group_size
= scl_header_size
+
495 (int64_t)HAMMER_VOL_SUPERCLUSTER_GROUP
*
496 ClusterSize
* HAMMER_SCL_MAXCLUSTERS
;
497 nscl_groups
= (ondisk
->vol_clo_end
- ondisk
->vol_clo_beg
) /
499 nclusters
= nscl_groups
* HAMMER_SCL_MAXCLUSTERS
*
500 HAMMER_VOL_SUPERCLUSTER_GROUP
;
503 * Figure out how much space we have left and calculate the
504 * remaining number of clusters.
506 n64
= (ondisk
->vol_clo_end
- ondisk
->vol_clo_beg
) -
507 (nscl_groups
* scl_group_size
);
508 if (n64
> scl_header_size
) {
509 nclusters
+= (n64
+ minclsize
) / ClusterSize
;
511 printf("%d clusters, %d full super-cluster groups\n",
512 nclusters
, nscl_groups
);
513 hammer_alist_free(&vol
->clu_alist
, 0, nclusters
);
515 nclusters
= (ondisk
->vol_clo_end
- ondisk
->vol_clo_beg
+
516 minclsize
) / ClusterSize
;
517 if (nclusters
> HAMMER_VOL_MAXCLUSTERS
) {
518 errx(1, "Volume is too large, max %s\n",
519 sizetostr(nclusters
* ClusterSize
));
521 hammer_alist_free(&vol
->clu_alist
, 0, nclusters
);
523 ondisk
->vol_nclusters
= nclusters
;
524 ondisk
->vol_nblocks
= nclusters
* ClusterSize
/ HAMMER_BUFSIZE
-
526 ondisk
->vol_blocksize
= HAMMER_BUFSIZE
;
529 * Place the root cluster in volume 0.
531 ondisk
->vol_rootvol
= 0;
532 if (ondisk
->vol_no
== ondisk
->vol_rootvol
) {
533 ondisk
->vol0_root_clu_id
= format_cluster(vol
, 1);
534 ondisk
->vol0_recid
= 1;
535 /* global next TID */
536 ondisk
->vol0_nexttid
= createtid();
541 * Format a hammer cluster. Returns byte offset in volume of cluster.
545 format_cluster(struct volume_info
*vol
, int isroot
)
547 hammer_tid_t clu_id
= createtid();
548 struct cluster_info
*cluster
;
549 struct hammer_cluster_ondisk
*ondisk
;
556 clno
= hammer_alist_alloc(&vol
->clu_alist
, 1);
557 if (clno
== HAMMER_ALIST_BLOCK_NONE
) {
558 fprintf(stderr
, "volume %d %s has insufficient space\n",
559 vol
->vol_no
, vol
->name
);
562 cluster
= get_cluster(vol
, clno
);
563 printf("allocate cluster id=%016llx %d@%08llx\n",
564 clu_id
, clno
, cluster
->clu_offset
);
566 ondisk
= cluster
->ondisk
;
568 ondisk
->vol_fsid
= vol
->ondisk
->vol_fsid
;
569 ondisk
->vol_fstype
= vol
->ondisk
->vol_fstype
;
571 ondisk
->clu_id
= clu_id
;
572 ondisk
->clu_no
= clno
;
573 ondisk
->clu_flags
= 0;
574 ondisk
->clu_start
= HAMMER_BUFSIZE
;
575 if (vol
->size
- cluster
->clu_offset
> ClusterSize
)
576 ondisk
->clu_limit
= (u_int32_t
)ClusterSize
;
578 ondisk
->clu_limit
= (u_int32_t
)(vol
->size
- cluster
->clu_offset
);
581 * In-band filesystem buffer management A-List. The first filesystem
582 * buffer is the cluster header itself.
584 nbuffers
= ondisk
->clu_limit
/ HAMMER_BUFSIZE
;
585 hammer_alist_free(&cluster
->alist_master
, 1, nbuffers
- 1);
586 printf("cluster %d has %d buffers\n", cluster
->clu_no
, nbuffers
);
589 * Buffer Iterators in elements. Each buffer has 256 elements.
590 * The data and B-Tree indices are forward allocations while the
591 * record index allocates backwards.
593 ondisk
->idx_data
= 1 * HAMMER_FSBUF_MAXBLKS
;
594 ondisk
->idx_index
= 0 * HAMMER_FSBUF_MAXBLKS
;
595 ondisk
->idx_record
= nbuffers
* HAMMER_FSBUF_MAXBLKS
;
598 * Iterator for whole-buffer data allocations. The iterator is
601 ondisk
->idx_ldata
= 1;
604 * Initialize root cluster's parent cluster info. -1's
605 * indicate we are the root cluster and no parent exists.
607 ondisk
->clu_btree_parent_vol_no
= -1;
608 ondisk
->clu_btree_parent_clu_no
= -1;
609 ondisk
->clu_btree_parent_offset
= -1;
610 ondisk
->clu_btree_parent_clu_gen
= -1;
613 * Cluster 0 is the root cluster. Set the B-Tree range for this
614 * cluster to the entire key space and format the root directory.
616 * Note that delete_tid for the ending range must be set to 0,
617 * 0 indicates 'not deleted', aka 'the most recent'. See
618 * hammer_btree_cmp() in sys/vfs/hammer/hammer_btree.c.
620 * The root cluster's key space represents the entire key space for
621 * the filesystem. The btree_end element appears to be inclusive
622 * only because we can't overflow our variables. It's actually
623 * non-inclusive... that is, it is a right-side boundary element.
626 ondisk
->clu_btree_beg
.obj_id
= -0x8000000000000000LL
;
627 ondisk
->clu_btree_beg
.key
= -0x8000000000000000LL
;
628 ondisk
->clu_btree_beg
.create_tid
= 0;
629 ondisk
->clu_btree_beg
.delete_tid
= 0;
630 ondisk
->clu_btree_beg
.rec_type
= 0;
631 ondisk
->clu_btree_beg
.obj_type
= 0;
633 ondisk
->clu_btree_end
.obj_id
= 0x7FFFFFFFFFFFFFFFLL
;
634 ondisk
->clu_btree_end
.key
= 0x7FFFFFFFFFFFFFFFLL
;
635 ondisk
->clu_btree_end
.create_tid
= 0xFFFFFFFFFFFFFFFFULL
;
636 ondisk
->clu_btree_end
.delete_tid
= 0; /* special case */
637 ondisk
->clu_btree_end
.rec_type
= 0xFFFFU
;
638 ondisk
->clu_btree_end
.obj_type
= 0;
640 format_root(cluster
);
644 * Write-out and update the index, record, and cluster buffers
650 * Format the root directory.
654 format_root(struct cluster_info
*cluster
)
659 hammer_node_ondisk_t bnode
;
660 union hammer_record_ondisk
*rec
;
661 struct hammer_inode_data
*idata
;
662 hammer_btree_elm_t elm
;
664 bnode
= alloc_btree_element(cluster
, &btree_off
);
665 rec
= alloc_record_element(cluster
, &rec_off
);
666 idata
= alloc_data_element(cluster
, sizeof(*idata
), &data_off
);
669 * Populate the inode data and inode record for the root directory.
671 idata
->version
= HAMMER_INODE_DATA_VERSION
;
674 rec
->base
.base
.obj_id
= 1;
675 rec
->base
.base
.key
= 0;
676 rec
->base
.base
.create_tid
= createtid();
677 rec
->base
.base
.delete_tid
= 0;
678 rec
->base
.base
.rec_type
= HAMMER_RECTYPE_INODE
;
679 rec
->base
.base
.obj_type
= HAMMER_OBJTYPE_DIRECTORY
;
680 rec
->base
.data_offset
= data_off
;
681 rec
->base
.data_len
= sizeof(*idata
);
682 rec
->base
.data_crc
= crc32(idata
, sizeof(*idata
));
683 rec
->inode
.ino_atime
= rec
->base
.base
.create_tid
;
684 rec
->inode
.ino_mtime
= rec
->base
.base
.create_tid
;
685 rec
->inode
.ino_size
= 0;
686 rec
->inode
.ino_nlinks
= 1;
688 ++cluster
->volume
->ondisk
->vol0_stat_inodes
;
691 * Assign the cluster's root B-Tree node.
693 assert(cluster
->ondisk
->clu_btree_root
== 0);
694 cluster
->ondisk
->clu_btree_root
= btree_off
;
697 * Create the root of the B-Tree. The root is a leaf node so we
698 * do not have to worry about boundary elements.
701 bnode
->type
= HAMMER_BTREE_TYPE_LEAF
;
703 elm
= &bnode
->elms
[0];
704 elm
->base
= rec
->base
.base
;
705 elm
->leaf
.rec_offset
= rec_off
;
706 elm
->leaf
.data_offset
= rec
->base
.data_offset
;
707 elm
->leaf
.data_len
= rec
->base
.data_len
;
708 elm
->leaf
.data_crc
= rec
->base
.data_crc
;
712 panic(const char *ctl
, ...)
717 vfprintf(stderr
, ctl
, va
);
719 fprintf(stderr
, "\n");