sbin/hammer: Remove =0; for some global variables
[dragonfly.git] / sbin / newfs_hammer / newfs_hammer.c
1 /*
2  * Copyright (c) 2007 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
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
16  *    distribution.
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.
20  *
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
32  * SUCH DAMAGE.
33  */
34
35 #include <sys/sysctl.h>
36 #include <sys/ioctl_compat.h>
37
38 #include "hammer_util.h"
39
40 static int64_t getsize(const char *str, int64_t minval, int64_t maxval, int pw);
41 static const char *sizetostr(off_t size);
42 static void trim_volume(struct volume_info *vol);
43 static void format_volume(struct volume_info *vol, int nvols,const char *label);
44 static hammer_off_t format_root_directory(const char *label);
45 static uint64_t nowtime(void);
46 static void usage(void);
47
48 static int ForceOpt;
49 static int64_t BootAreaSize;
50 static int64_t MemAreaSize;
51 static int64_t UndoBufferSize;
52 static int HammerVersion = -1;
53
54 #define GIG     (1024LL*1024*1024)
55
56 int
57 main(int ac, char **av)
58 {
59         uint32_t status;
60         off_t total;
61         off_t avg_vol_size;
62         int ch;
63         int i;
64         int nvols;
65         int eflag = 0;
66         const char *label = NULL;
67         struct volume_info *vol;
68         char *fsidstr;
69
70         /*
71          * Sanity check basic filesystem structures.  No cookies for us
72          * if it gets broken!
73          */
74         assert(sizeof(struct hammer_volume_ondisk) <= HAMMER_BUFSIZE);
75         assert(sizeof(struct hammer_blockmap_layer1) == 32);
76         assert(sizeof(struct hammer_blockmap_layer2) == 16);
77
78         /*
79          * Generate a filesystem id and lookup the filesystem type
80          */
81         uuidgen(&Hammer_FSId, 1);
82         uuid_name_lookup(&Hammer_FSType, "DragonFly HAMMER", &status);
83         if (status != uuid_s_ok) {
84                 errx(1, "uuids file does not have the DragonFly "
85                         "HAMMER filesystem type");
86         }
87
88         /*
89          * Parse arguments
90          */
91         while ((ch = getopt(ac, av, "fEL:b:m:u:V:")) != -1) {
92                 switch(ch) {
93                 case 'f':
94                         ForceOpt = 1;
95                         break;
96                 case 'E':
97                         eflag = 1;
98                         break;
99                 case 'L':
100                         label = optarg;
101                         break;
102                 case 'b':
103                         BootAreaSize = getsize(optarg,
104                                          HAMMER_BUFSIZE,
105                                          HAMMER_BOOT_MAXBYTES, 2);
106                         break;
107                 case 'm':
108                         MemAreaSize = getsize(optarg,
109                                          HAMMER_BUFSIZE,
110                                          HAMMER_MEM_MAXBYTES, 2);
111                         break;
112                 case 'u':
113                         UndoBufferSize = getsize(optarg,
114                                          HAMMER_BIGBLOCK_SIZE,
115                                          HAMMER_BIGBLOCK_SIZE *
116                                          HAMMER_UNDO_LAYER2, 2);
117                         if (UndoBufferSize < 500*1024*1024 && ForceOpt == 0)
118                                 errx(1, "The minimum UNDO/REDO FIFO size is "
119                                         "500MB\n");
120                         if (UndoBufferSize < 500*1024*1024) {
121                                 fprintf(stderr,
122                                         "WARNING: you have specified an "
123                                         "UNDO/REDO FIFO size less than 500MB,\n"
124                                         "which may lead to VFS panics.\n");
125                         }
126                         break;
127                 case 'V':
128                         HammerVersion = strtol(optarg, NULL, 0);
129                         if (HammerVersion < HAMMER_VOL_VERSION_MIN ||
130                             HammerVersion >= HAMMER_VOL_VERSION_WIP) {
131                                 errx(1,
132                                      "I don't understand how to format "
133                                      "HAMMER version %d\n",
134                                      HammerVersion);
135                         }
136                         break;
137                 default:
138                         usage();
139                         break;
140                 }
141         }
142
143         if (label == NULL) {
144                 fprintf(stderr,
145                         "newfs_hammer: A filesystem label must be specified\n");
146                 usage();
147         }
148
149         if (HammerVersion < 0) {
150                 size_t olen = sizeof(HammerVersion);
151                 HammerVersion = HAMMER_VOL_VERSION_DEFAULT;
152                 if (sysctlbyname("vfs.hammer.supported_version",
153                                  &HammerVersion, &olen, NULL, 0) == 0) {
154                         if (HammerVersion >= HAMMER_VOL_VERSION_WIP) {
155                                 HammerVersion = HAMMER_VOL_VERSION_WIP - 1;
156                                 fprintf(stderr,
157                                         "newfs_hammer: WARNING: HAMMER VFS "
158                                         "supports higher version than I "
159                                         "understand,\n"
160                                         "using version %d\n",
161                                         HammerVersion);
162                         }
163                 } else {
164                         fprintf(stderr,
165                                 "newfs_hammer: WARNING: HAMMER VFS not "
166                                 "loaded, cannot get version info.\n"
167                                 "Using version %d\n",
168                                 HAMMER_VOL_VERSION_DEFAULT);
169                 }
170         }
171
172         /*
173          * Collect volume information
174          */
175         ac -= optind;
176         av += optind;
177         nvols = ac;
178
179         if (nvols == 0) {
180                 fprintf(stderr,
181                         "newfs_hammer: You must specify at least one "
182                         "special file (volume)\n");
183                 exit(1);
184         }
185
186         if (nvols > HAMMER_MAX_VOLUMES) {
187                 fprintf(stderr,
188                         "newfs_hammer: The maximum number of volumes is %d\n",
189                         HAMMER_MAX_VOLUMES);
190                 exit(1);
191         }
192
193         total = 0;
194         for (i = 0; i < nvols; ++i) {
195                 vol = init_volume(i, av[i], O_RDWR);
196
197                 /*
198                  * Load up information on the volume and initialize
199                  * its remaining fields.
200                  */
201                 check_volume(vol);
202                 printf("Volume %d %s %-15s size %s\n",
203                         vol->vol_no, vol->type, vol->name,
204                         sizetostr(vol->size));
205
206                 if (eflag) {
207                         if (strcmp(vol->type, "REGFILE") == 0) {
208                                 fprintf(stderr, "Cannot TRIM regular file %s\n",
209                                         vol->name);
210                                 exit(1);
211                         }
212
213                         char sysctl_name[64];
214                         int trim_enabled = 0;
215                         size_t olen = sizeof(trim_enabled);
216                         char *dev_name = strdup(vol->name);
217                         dev_name = strtok(dev_name + strlen("/dev/da"),"s");
218
219                         sprintf(sysctl_name, "kern.cam.da.%s.trim_enabled",
220                             dev_name);
221                         errno=0;
222                         sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0);
223                         if(errno == ENOENT) {
224                                 printf("%s %s (%s) does not support the TRIM "
225                                     "command\n",
226                                     vol->type, vol->name, sysctl_name);
227                                 usage();
228                         }
229                         if(!trim_enabled) {
230                                 printf("Erase device option selected, but "
231                                     "sysctl (%s) is not enabled\n", sysctl_name);
232                                 usage();
233
234                         }
235                         trim_volume(vol);
236                 }
237                 total += vol->size;
238         }
239
240         /*
241          * Calculate defaults for the boot and memory area sizes.
242          */
243         avg_vol_size = total / nvols;
244         BootAreaSize = init_boot_area_size(BootAreaSize, avg_vol_size);
245         MemAreaSize = init_mem_area_size(MemAreaSize, avg_vol_size);
246
247         /*
248          * Format the volumes.  Format the root volume first so we can
249          * bootstrap the freemap.
250          */
251         format_volume(get_root_volume(), nvols, label);
252         for (i = 0; i < nvols; ++i) {
253                 if (i != HAMMER_ROOT_VOLNO)
254                         format_volume(get_volume(i), nvols, label);
255         }
256
257         /*
258          * Print information stored in the root volume header.
259          */
260         vol = get_root_volume();
261         vol->cache.modified = 1;
262         uuid_to_string(&Hammer_FSId, &fsidstr, &status);
263
264         printf("---------------------------------------------\n");
265         printf("%d volume%s total size %s version %d\n",
266                 nvols, (nvols == 1 ? "" : "s"),
267                 sizetostr(total), HammerVersion);
268         printf("root-volume:         %s\n", vol->name);
269         printf("boot-area-size:      %s\n", sizetostr(BootAreaSize));
270         printf("memory-log-size:     %s\n", sizetostr(MemAreaSize));
271         printf("undo-buffer-size:    %s\n", sizetostr(UndoBufferSize));
272         printf("total-pre-allocated: %s\n",
273                 sizetostr(vol->vol_free_off & HAMMER_OFF_SHORT_MASK));
274         printf("fsid:                %s\n", fsidstr);
275         printf("\n");
276         printf("NOTE: Please remember that you may have to manually set up a\n"
277                 "cron(8) job to prune and reblock the filesystem regularly.\n"
278                 "By default, the system automatically runs 'hammer cleanup'\n"
279                 "on a nightly basis.  The periodic.conf(5) variable\n"
280                 "'daily_clean_hammer_enable' can be unset to disable this.\n"
281                 "Also see 'man hammer' and 'man HAMMER' for more information.\n");
282         if (total < 10*GIG) {
283                 printf("\nWARNING: The minimum UNDO/REDO FIFO is 500MB, you "
284                        "really should not\n"
285                        "try to format a HAMMER filesystem this small.\n");
286         }
287         if (total < 50*GIG) {
288                 printf("\nWARNING: HAMMER filesystems less than 50GB are "
289                         "not recommended!\n"
290                         "You may have to run 'hammer prune-everything' and "
291                         "'hammer reblock'\n"
292                         "quite often, even if using a nohistory mount.\n");
293         }
294         flush_all_volumes();
295         return(0);
296 }
297
298 static
299 void
300 usage(void)
301 {
302         fprintf(stderr,
303                 "usage: newfs_hammer -L label [-Ef] [-b bootsize] [-m savesize] [-u undosize]\n"
304                 "                    [-V version] special ...\n"
305         );
306         exit(1);
307 }
308
309 /*
310  * Convert the size in bytes to a human readable string.
311  */
312 static
313 const char *
314 sizetostr(off_t size)
315 {
316         static char buf[32];
317
318         if (size < 1024 / 2) {
319                 snprintf(buf, sizeof(buf), "%6.2f", (double)size);
320         } else if (size < 1024 * 1024 / 2) {
321                 snprintf(buf, sizeof(buf), "%6.2fKB",
322                         (double)size / 1024);
323         } else if (size < 1024 * 1024 * 1024LL / 2) {
324                 snprintf(buf, sizeof(buf), "%6.2fMB",
325                         (double)size / (1024 * 1024));
326         } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) {
327                 snprintf(buf, sizeof(buf), "%6.2fGB",
328                         (double)size / (1024 * 1024 * 1024LL));
329         } else {
330                 snprintf(buf, sizeof(buf), "%6.2fTB",
331                         (double)size / (1024 * 1024 * 1024LL * 1024LL));
332         }
333         return(buf);
334 }
335
336 /*
337  * Convert a string to a 64 bit signed integer with various requirements.
338  */
339 static int64_t
340 getsize(const char *str, int64_t minval, int64_t maxval, int powerof2)
341 {
342         int64_t val;
343         char *ptr;
344
345         val = strtoll(str, &ptr, 0);
346         switch(*ptr) {
347         case 't':
348         case 'T':
349                 val *= 1024;
350                 /* fall through */
351         case 'g':
352         case 'G':
353                 val *= 1024;
354                 /* fall through */
355         case 'm':
356         case 'M':
357                 val *= 1024;
358                 /* fall through */
359         case 'k':
360         case 'K':
361                 val *= 1024;
362                 break;
363         default:
364                 errx(1, "Unknown suffix in number '%s'\n", str);
365                 /* not reached */
366         }
367         if (ptr[1]) {
368                 errx(1, "Unknown suffix in number '%s'\n", str);
369                 /* not reached */
370         }
371         if (val < minval) {
372                 errx(1, "Value too small: %s, min is %s\n",
373                      str, sizetostr(minval));
374                 /* not reached */
375         }
376         if (val > maxval) {
377                 errx(1, "Value too large: %s, max is %s\n",
378                      str, sizetostr(maxval));
379                 /* not reached */
380         }
381         if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) {
382                 errx(1, "Value not power of 2: %s\n", str);
383                 /* not reached */
384         }
385         if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) {
386                 errx(1, "Value not an integral multiple of %dK: %s",
387                      HAMMER_BUFSIZE / 1024, str);
388                 /* not reached */
389         }
390         return(val);
391 }
392
393 /*
394  * Generate a transaction id.  Transaction ids are no longer time-based.
395  * Put the nail in the coffin by not making the first one time-based.
396  *
397  * We could start at 1 here but start at 2^32 to reserve a small domain for
398  * possible future use.
399  */
400 static hammer_tid_t
401 createtid(void)
402 {
403         static hammer_tid_t lasttid;
404
405         if (lasttid == 0)
406                 lasttid = 0x0000000100000000ULL;
407         return(lasttid++);
408 }
409
410 static uint64_t
411 nowtime(void)
412 {
413         struct timeval tv;
414         uint64_t xtime;
415
416         gettimeofday(&tv, NULL);
417         xtime = tv.tv_sec * 1000000LL + tv.tv_usec;
418         return(xtime);
419 }
420
421 /*
422  * TRIM the volume, but only if the backing store is a DEVICE
423  */
424 static
425 void
426 trim_volume(struct volume_info *vol)
427 {
428         if (strncmp(vol->type, "DEVICE", sizeof("DEVICE")) == 0) {
429                 off_t ioarg[2];
430
431                 /* 1MB offset to prevent destroying disk-reserved area */
432                 ioarg[0] = vol->device_offset;
433                 ioarg[1] = vol->size;
434
435                 printf("Trimming %s %s, sectors (%llu -%llu)\n",
436                     vol->type, vol->name,
437                     (unsigned long long)ioarg[0]/512,
438                     (unsigned long long)ioarg[1]/512);
439
440                 if (ioctl(vol->fd, IOCTLTRIM, ioarg) < 0) {
441                         printf("Device trim failed\n");
442                         usage ();
443                 }
444         }
445 }
446
447 /*
448  * Format a HAMMER volume.
449  */
450 static
451 void
452 format_volume(struct volume_info *vol, int nvols, const char *label)
453 {
454         struct volume_info *root_vol;
455         struct hammer_volume_ondisk *ondisk;
456         int64_t freeblks;
457         int64_t freebytes;
458         int64_t vol_buf_size;
459         hammer_off_t vol_alloc;
460         int i;
461
462         /*
463          * Initialize basic information in the on-disk volume structure.
464          */
465         ondisk = vol->ondisk;
466
467         ondisk->vol_fsid = Hammer_FSId;
468         ondisk->vol_fstype = Hammer_FSType;
469         snprintf(ondisk->vol_name, sizeof(ondisk->vol_name), "%s", label);
470         ondisk->vol_no = vol->vol_no;
471         ondisk->vol_count = nvols;
472         ondisk->vol_version = HammerVersion;
473
474         /*
475          * Reserve space for (future) header junk, setup our poor-man's
476          * big-block allocator.
477          */
478         vol_alloc = HAMMER_BUFSIZE * 16;  /* 262144 */
479
480         ondisk->vol_bot_beg = vol_alloc;
481         vol_alloc += BootAreaSize;
482         ondisk->vol_mem_beg = vol_alloc;
483         vol_alloc += MemAreaSize;
484
485         /*
486          * The remaining area is the zone 2 buffer allocation area.
487          */
488         ondisk->vol_buf_beg = vol_alloc;
489         ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK;
490         vol_buf_size = ondisk->vol_buf_end - ondisk->vol_buf_beg;
491
492         if (vol_buf_size < 0) {
493                 errx(1, "volume %d %s is too small to hold the volume header",
494                      vol->vol_no, vol->name);
495         }
496
497         ondisk->vol_nblocks = vol_buf_size / HAMMER_BUFSIZE;
498         ondisk->vol_blocksize = HAMMER_BUFSIZE;
499
500         ondisk->vol_rootvol = HAMMER_ROOT_VOLNO;
501         ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
502
503         vol->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0);
504         vol->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no,
505                                 vol_buf_size & ~HAMMER_BIGBLOCK_MASK64);
506
507         /*
508          * Format the root volume.
509          */
510         if (vol->vol_no == HAMMER_ROOT_VOLNO) {
511                 /*
512                  * Check freemap counts before formatting
513                  */
514                 freeblks = count_freemap(vol);
515                 freebytes = freeblks * HAMMER_BIGBLOCK_SIZE64;
516                 if (freebytes < 10*GIG && ForceOpt == 0) {
517                         errx(1, "Cannot create a HAMMER filesystem less than 10GB "
518                                 "unless you use -f\n(for the size of Volume %d).  "
519                                 "HAMMER filesystems less than 50GB are not "
520                                 "recommended.\n", HAMMER_ROOT_VOLNO);
521                 }
522
523                 /*
524                  * Starting TID
525                  */
526                 ondisk->vol0_next_tid = createtid();
527
528                 /*
529                  * Format freemap.  vol0_stat_freebigblocks is
530                  * the number of big-blocks available for anything
531                  * other than freemap zone at this point.
532                  */
533                 format_freemap(vol);
534                 assert(ondisk->vol0_stat_freebigblocks == 0);
535                 ondisk->vol0_stat_freebigblocks = initialize_freemap(vol);
536
537                 /*
538                  * Format zones that are mapped to zone-2.
539                  */
540                 for (i = 0; i < HAMMER_MAX_ZONES; ++i) {
541                         if (hammer_is_zone2_mapped_index(i))
542                                 format_blockmap(vol, i, 0);
543                 }
544
545                 /*
546                  * Format undo zone.  Formatting decrements
547                  * vol0_stat_freebigblocks whenever a new big-block
548                  * is allocated for undo zone.
549                  */
550                 format_undomap(vol, &UndoBufferSize);
551                 assert(ondisk->vol0_stat_bigblocks == 0);
552                 ondisk->vol0_stat_bigblocks = ondisk->vol0_stat_freebigblocks;
553
554                 /*
555                  * Format the root directory.  Formatting decrements
556                  * vol0_stat_freebigblocks whenever a new big-block
557                  * is allocated for required zones.
558                  */
559                 ondisk->vol0_btree_root = format_root_directory(label);
560                 ++ondisk->vol0_stat_inodes;     /* root inode */
561
562                 rel_volume(vol);
563         } else {
564                 freeblks = initialize_freemap(vol);
565                 root_vol = get_root_volume();
566                 root_vol->cache.modified = 1;
567                 root_vol->ondisk->vol0_stat_freebigblocks += freeblks;
568                 root_vol->ondisk->vol0_stat_bigblocks += freeblks;
569                 rel_volume(root_vol);
570         }
571 }
572
573 /*
574  * Format the root directory.
575  */
576 static
577 hammer_off_t
578 format_root_directory(const char *label)
579 {
580         hammer_off_t btree_off;
581         hammer_off_t pfsd_off;
582         hammer_off_t data_off;
583         hammer_tid_t create_tid;
584         hammer_node_ondisk_t bnode;
585         struct hammer_inode_data *idata;
586         hammer_pseudofs_data_t pfsd;
587         struct buffer_info *data_buffer0 = NULL;
588         struct buffer_info *data_buffer1 = NULL;
589         struct buffer_info *data_buffer2 = NULL;
590         hammer_btree_elm_t elm;
591         uint64_t xtime;
592
593         /*
594          * Allocate zero-filled root btree node, inode and pfs
595          */
596         bnode = alloc_btree_element(&btree_off, &data_buffer0);
597         idata = alloc_meta_element(&data_off, sizeof(*idata), &data_buffer1);
598         pfsd = alloc_meta_element(&pfsd_off, sizeof(*pfsd), &data_buffer2);
599         create_tid = createtid();
600         xtime = nowtime();
601
602         /*
603          * Populate the inode data and inode record for the root directory.
604          */
605         idata->version = HAMMER_INODE_DATA_VERSION;
606         idata->mode = 0755;
607         idata->ctime = xtime;
608         idata->mtime = xtime;
609         idata->atime = xtime;
610         idata->obj_type = HAMMER_OBJTYPE_DIRECTORY;
611         idata->size = 0;
612         idata->nlinks = 1;
613         if (HammerVersion >= HAMMER_VOL_VERSION_TWO)
614                 idata->cap_flags |= HAMMER_INODE_CAP_DIR_LOCAL_INO;
615         if (HammerVersion >= HAMMER_VOL_VERSION_SIX)
616                 idata->cap_flags |= HAMMER_INODE_CAP_DIRHASH_ALG1;
617
618         /*
619          * Populate the PFS data for the root PFS.
620          */
621         pfsd->sync_low_tid = 1;
622         pfsd->sync_beg_tid = 0;
623         pfsd->sync_end_tid = 0; /* overriden by vol0_next_tid on pfs0 */
624         pfsd->shared_uuid = Hammer_FSId;
625         pfsd->unique_uuid = Hammer_FSId;
626         pfsd->reserved01 = 0;
627         pfsd->mirror_flags = 0;
628         snprintf(pfsd->label, sizeof(pfsd->label), "%s", label);
629
630         /*
631          * Create the root of the B-Tree.  The root is a leaf node so we
632          * do not have to worry about boundary elements.
633          */
634         bnode->count = 2;
635         bnode->type = HAMMER_BTREE_TYPE_LEAF;
636
637         /*
638          * Create the first node element for the inode.
639          */
640         elm = &bnode->elms[0];
641         elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
642         elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION |
643                                       HAMMER_LOCALIZE_INODE;
644         elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
645         elm->leaf.base.key = 0;
646         elm->leaf.base.create_tid = create_tid;
647         elm->leaf.base.delete_tid = 0;
648         elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE;
649         elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
650         elm->leaf.create_ts = (uint32_t)time(NULL);
651
652         elm->leaf.data_offset = data_off;
653         elm->leaf.data_len = sizeof(*idata);
654         elm->leaf.data_crc = crc32(idata, HAMMER_INODE_CRCSIZE);
655
656         /*
657          * Create the second node element for the PFS data.
658          */
659         elm = &bnode->elms[1];
660         elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
661         elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION |
662                                       HAMMER_LOCALIZE_MISC;
663         elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
664         elm->leaf.base.key = 0;
665         elm->leaf.base.create_tid = create_tid;
666         elm->leaf.base.delete_tid = 0;
667         elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS;
668         elm->leaf.base.obj_type = 0;
669         elm->leaf.create_ts = (uint32_t)time(NULL);
670
671         elm->leaf.data_offset = pfsd_off;
672         elm->leaf.data_len = sizeof(*pfsd);
673         elm->leaf.data_crc = crc32(pfsd, sizeof(*pfsd));
674
675         bnode->crc = crc32(&bnode->crc + 1, HAMMER_BTREE_CRCSIZE);
676
677         return(btree_off);
678 }