world - Fix sysctlbyname() errno handling cases
[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                         if (sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0) < 0) {
223                                 printf("%s %s (%s) does not support the TRIM "
224                                     "command\n",
225                                     vol->type, vol->name, sysctl_name);
226                                 usage();
227                         }
228                         if(!trim_enabled) {
229                                 printf("Erase device option selected, but "
230                                     "sysctl (%s) is not enabled\n", sysctl_name);
231                                 usage();
232
233                         }
234                         trim_volume(vol);
235                 }
236                 total += vol->size;
237         }
238
239         /*
240          * Calculate defaults for the boot and memory area sizes.
241          */
242         avg_vol_size = total / nvols;
243         BootAreaSize = init_boot_area_size(BootAreaSize, avg_vol_size);
244         MemAreaSize = init_mem_area_size(MemAreaSize, avg_vol_size);
245
246         /*
247          * Format the volumes.  Format the root volume first so we can
248          * bootstrap the freemap.
249          */
250         format_volume(get_root_volume(), nvols, label);
251         for (i = 0; i < nvols; ++i) {
252                 if (i != HAMMER_ROOT_VOLNO)
253                         format_volume(get_volume(i), nvols, label);
254         }
255
256         /*
257          * Print information stored in the root volume header.
258          */
259         vol = get_root_volume();
260         uuid_to_string(&Hammer_FSId, &fsidstr, &status);
261
262         printf("---------------------------------------------\n");
263         printf("%d volume%s total size %s version %d\n",
264                 nvols, (nvols == 1 ? "" : "s"),
265                 sizetostr(total), HammerVersion);
266         printf("root-volume:         %s\n", vol->name);
267         printf("boot-area-size:      %s\n", sizetostr(BootAreaSize));
268         printf("memory-log-size:     %s\n", sizetostr(MemAreaSize));
269         printf("undo-buffer-size:    %s\n", sizetostr(UndoBufferSize));
270         printf("total-pre-allocated: %s\n",
271                 sizetostr(vol->vol_free_off & HAMMER_OFF_SHORT_MASK));
272         printf("fsid:                %s\n", fsidstr);
273         printf("\n");
274         printf("NOTE: Please remember that you may have to manually set up a\n"
275                 "cron(8) job to prune and reblock the filesystem regularly.\n"
276                 "By default, the system automatically runs 'hammer cleanup'\n"
277                 "on a nightly basis.  The periodic.conf(5) variable\n"
278                 "'daily_clean_hammer_enable' can be unset to disable this.\n"
279                 "Also see 'man hammer' and 'man HAMMER' for more information.\n");
280         if (total < 10*GIG) {
281                 printf("\nWARNING: The minimum UNDO/REDO FIFO is 500MB, you "
282                        "really should not\n"
283                        "try to format a HAMMER filesystem this small.\n");
284         }
285         if (total < 50*GIG) {
286                 printf("\nWARNING: HAMMER filesystems less than 50GB are "
287                         "not recommended!\n"
288                         "You may have to run 'hammer prune-everything' and "
289                         "'hammer reblock'\n"
290                         "quite often, even if using a nohistory mount.\n");
291         }
292         flush_all_volumes();
293         return(0);
294 }
295
296 static
297 void
298 usage(void)
299 {
300         fprintf(stderr,
301                 "usage: newfs_hammer -L label [-Ef] [-b bootsize] [-m savesize] [-u undosize]\n"
302                 "                    [-V version] special ...\n"
303         );
304         exit(1);
305 }
306
307 /*
308  * Convert the size in bytes to a human readable string.
309  */
310 static
311 const char *
312 sizetostr(off_t size)
313 {
314         static char buf[32];
315
316         if (size < 1024 / 2) {
317                 snprintf(buf, sizeof(buf), "%6.2f", (double)size);
318         } else if (size < 1024 * 1024 / 2) {
319                 snprintf(buf, sizeof(buf), "%6.2fKB",
320                         (double)size / 1024);
321         } else if (size < 1024 * 1024 * 1024LL / 2) {
322                 snprintf(buf, sizeof(buf), "%6.2fMB",
323                         (double)size / (1024 * 1024));
324         } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) {
325                 snprintf(buf, sizeof(buf), "%6.2fGB",
326                         (double)size / (1024 * 1024 * 1024LL));
327         } else {
328                 snprintf(buf, sizeof(buf), "%6.2fTB",
329                         (double)size / (1024 * 1024 * 1024LL * 1024LL));
330         }
331         return(buf);
332 }
333
334 /*
335  * Convert a string to a 64 bit signed integer with various requirements.
336  */
337 static int64_t
338 getsize(const char *str, int64_t minval, int64_t maxval, int powerof2)
339 {
340         int64_t val;
341         char *ptr;
342
343         val = strtoll(str, &ptr, 0);
344         switch(*ptr) {
345         case 't':
346         case 'T':
347                 val *= 1024;
348                 /* fall through */
349         case 'g':
350         case 'G':
351                 val *= 1024;
352                 /* fall through */
353         case 'm':
354         case 'M':
355                 val *= 1024;
356                 /* fall through */
357         case 'k':
358         case 'K':
359                 val *= 1024;
360                 break;
361         default:
362                 errx(1, "Unknown suffix in number '%s'\n", str);
363                 /* not reached */
364         }
365         if (ptr[1]) {
366                 errx(1, "Unknown suffix in number '%s'\n", str);
367                 /* not reached */
368         }
369         if (val < minval) {
370                 errx(1, "Value too small: %s, min is %s\n",
371                      str, sizetostr(minval));
372                 /* not reached */
373         }
374         if (val > maxval) {
375                 errx(1, "Value too large: %s, max is %s\n",
376                      str, sizetostr(maxval));
377                 /* not reached */
378         }
379         if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) {
380                 errx(1, "Value not power of 2: %s\n", str);
381                 /* not reached */
382         }
383         if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) {
384                 errx(1, "Value not an integral multiple of %dK: %s",
385                      HAMMER_BUFSIZE / 1024, str);
386                 /* not reached */
387         }
388         return(val);
389 }
390
391 /*
392  * Generate a transaction id.  Transaction ids are no longer time-based.
393  * Put the nail in the coffin by not making the first one time-based.
394  *
395  * We could start at 1 here but start at 2^32 to reserve a small domain for
396  * possible future use.
397  */
398 static hammer_tid_t
399 createtid(void)
400 {
401         static hammer_tid_t lasttid;
402
403         if (lasttid == 0)
404                 lasttid = 0x0000000100000000ULL;
405         return(lasttid++);
406 }
407
408 static uint64_t
409 nowtime(void)
410 {
411         struct timeval tv;
412         uint64_t xtime;
413
414         gettimeofday(&tv, NULL);
415         xtime = tv.tv_sec * 1000000LL + tv.tv_usec;
416         return(xtime);
417 }
418
419 /*
420  * TRIM the volume, but only if the backing store is a DEVICE
421  */
422 static
423 void
424 trim_volume(struct volume_info *vol)
425 {
426         if (strncmp(vol->type, "DEVICE", sizeof("DEVICE")) == 0) {
427                 off_t ioarg[2];
428
429                 /* 1MB offset to prevent destroying disk-reserved area */
430                 ioarg[0] = vol->device_offset;
431                 ioarg[1] = vol->size;
432
433                 printf("Trimming %s %s, sectors (%llu -%llu)\n",
434                     vol->type, vol->name,
435                     (unsigned long long)ioarg[0]/512,
436                     (unsigned long long)ioarg[1]/512);
437
438                 if (ioctl(vol->fd, IOCTLTRIM, ioarg) < 0) {
439                         printf("Device trim failed\n");
440                         usage ();
441                 }
442         }
443 }
444
445 /*
446  * Format a HAMMER volume.
447  */
448 static
449 void
450 format_volume(struct volume_info *vol, int nvols, const char *label)
451 {
452         struct volume_info *root_vol;
453         struct hammer_volume_ondisk *ondisk;
454         int64_t freeblks;
455         int64_t freebytes;
456         int64_t vol_buf_size;
457         hammer_off_t vol_alloc;
458         int i;
459
460         /*
461          * Initialize basic information in the on-disk volume structure.
462          */
463         ondisk = vol->ondisk;
464
465         ondisk->vol_fsid = Hammer_FSId;
466         ondisk->vol_fstype = Hammer_FSType;
467         snprintf(ondisk->vol_label, sizeof(ondisk->vol_label), "%s", label);
468         ondisk->vol_no = vol->vol_no;
469         ondisk->vol_count = nvols;
470         ondisk->vol_version = HammerVersion;
471
472         /*
473          * Reserve space for (future) header junk, setup our poor-man's
474          * big-block allocator.
475          */
476         vol_alloc = HAMMER_VOL_ALLOC;
477
478         ondisk->vol_bot_beg = vol_alloc;
479         vol_alloc += BootAreaSize;
480         ondisk->vol_mem_beg = vol_alloc;
481         vol_alloc += MemAreaSize;
482
483         /*
484          * The remaining area is the zone 2 buffer allocation area.
485          */
486         ondisk->vol_buf_beg = vol_alloc;
487         ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK;
488         vol_buf_size = ondisk->vol_buf_end - ondisk->vol_buf_beg;
489
490         if (vol_buf_size < 0) {
491                 errx(1, "volume %d %s is too small to hold the volume header",
492                      vol->vol_no, vol->name);
493         }
494
495         if ((vol_buf_size & ~HAMMER_OFF_SHORT_MASK) != 0) {
496                 errx(1, "volume %d %s is too large", vol->vol_no, vol->name);
497         }
498
499         ondisk->vol_rootvol = HAMMER_ROOT_VOLNO;
500         ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
501
502         vol->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0);
503         vol->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no,
504                                 vol_buf_size & ~HAMMER_BIGBLOCK_MASK64);
505
506         /*
507          * Format the root volume.
508          */
509         if (vol->vol_no == HAMMER_ROOT_VOLNO) {
510                 /*
511                  * Check freemap counts before formatting
512                  */
513                 freeblks = count_freemap(vol);
514                 freebytes = freeblks * HAMMER_BIGBLOCK_SIZE64;
515                 if (freebytes < 10*GIG && ForceOpt == 0) {
516                         errx(1, "Cannot create a HAMMER filesystem less than 10GB "
517                                 "unless you use -f\n(for the size of Volume %d).  "
518                                 "HAMMER filesystems less than 50GB are not "
519                                 "recommended.\n", HAMMER_ROOT_VOLNO);
520                 }
521
522                 /*
523                  * Starting TID
524                  */
525                 ondisk->vol0_next_tid = createtid();
526
527                 /*
528                  * Format freemap.  vol0_stat_freebigblocks is
529                  * the number of big-blocks available for anything
530                  * other than freemap zone at this point.
531                  */
532                 format_freemap(vol);
533                 assert(ondisk->vol0_stat_freebigblocks == 0);
534                 ondisk->vol0_stat_freebigblocks = initialize_freemap(vol);
535
536                 /*
537                  * Format zones that are mapped to zone-2.
538                  */
539                 for (i = 0; i < HAMMER_MAX_ZONES; ++i) {
540                         if (hammer_is_zone2_mapped_index(i))
541                                 format_blockmap(vol, i, 0);
542                 }
543
544                 /*
545                  * Format undo zone.  Formatting decrements
546                  * vol0_stat_freebigblocks whenever a new big-block
547                  * is allocated for undo zone.
548                  */
549                 format_undomap(vol, &UndoBufferSize);
550                 assert(ondisk->vol0_stat_bigblocks == 0);
551                 ondisk->vol0_stat_bigblocks = ondisk->vol0_stat_freebigblocks;
552
553                 /*
554                  * Format the root directory.  Formatting decrements
555                  * vol0_stat_freebigblocks whenever a new big-block
556                  * is allocated for required zones.
557                  */
558                 ondisk->vol0_btree_root = format_root_directory(label);
559                 ++ondisk->vol0_stat_inodes;     /* root inode */
560
561                 rel_volume(vol);
562         } else {
563                 freeblks = initialize_freemap(vol);
564                 root_vol = get_root_volume();
565                 root_vol->ondisk->vol0_stat_freebigblocks += freeblks;
566                 root_vol->ondisk->vol0_stat_bigblocks += freeblks;
567                 rel_volume(root_vol);
568         }
569 }
570
571 /*
572  * Format the root directory.
573  */
574 static
575 hammer_off_t
576 format_root_directory(const char *label)
577 {
578         hammer_off_t btree_off;
579         hammer_off_t pfsd_off;
580         hammer_off_t data_off;
581         hammer_tid_t create_tid;
582         hammer_node_ondisk_t bnode;
583         struct hammer_inode_data *idata;
584         hammer_pseudofs_data_t pfsd;
585         struct buffer_info *data_buffer0 = NULL;
586         struct buffer_info *data_buffer1 = NULL;
587         struct buffer_info *data_buffer2 = NULL;
588         hammer_btree_elm_t elm;
589         uint64_t xtime;
590
591         /*
592          * Allocate zero-filled root btree node, inode and pfs
593          */
594         bnode = alloc_btree_element(&btree_off, &data_buffer0);
595         idata = alloc_meta_element(&data_off, sizeof(*idata), &data_buffer1);
596         pfsd = alloc_meta_element(&pfsd_off, sizeof(*pfsd), &data_buffer2);
597         create_tid = createtid();
598         xtime = nowtime();
599
600         /*
601          * Populate the inode data and inode record for the root directory.
602          */
603         idata->version = HAMMER_INODE_DATA_VERSION;
604         idata->mode = 0755;
605         idata->ctime = xtime;
606         idata->mtime = xtime;
607         idata->atime = xtime;
608         idata->obj_type = HAMMER_OBJTYPE_DIRECTORY;
609         idata->size = 0;
610         idata->nlinks = 1;
611         if (HammerVersion >= HAMMER_VOL_VERSION_TWO)
612                 idata->cap_flags |= HAMMER_INODE_CAP_DIR_LOCAL_INO;
613         if (HammerVersion >= HAMMER_VOL_VERSION_SIX)
614                 idata->cap_flags |= HAMMER_INODE_CAP_DIRHASH_ALG1;
615
616         /*
617          * Populate the PFS data for the root PFS.
618          */
619         pfsd->sync_low_tid = 1;
620         pfsd->sync_beg_tid = 0;
621         pfsd->sync_end_tid = 0; /* overriden by vol0_next_tid on pfs0 */
622         pfsd->shared_uuid = Hammer_FSId;
623         pfsd->unique_uuid = Hammer_FSId;
624         pfsd->reserved01 = 0;
625         pfsd->mirror_flags = 0;
626         snprintf(pfsd->label, sizeof(pfsd->label), "%s", label);
627
628         /*
629          * Create the root of the B-Tree.  The root is a leaf node so we
630          * do not have to worry about boundary elements.
631          */
632         bnode->count = 2;
633         bnode->type = HAMMER_BTREE_TYPE_LEAF;
634
635         /*
636          * Create the first node element for the inode.
637          */
638         elm = &bnode->elms[0];
639         elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
640         elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION |
641                                       HAMMER_LOCALIZE_INODE;
642         elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
643         elm->leaf.base.key = 0;
644         elm->leaf.base.create_tid = create_tid;
645         elm->leaf.base.delete_tid = 0;
646         elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE;
647         elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
648         elm->leaf.create_ts = (uint32_t)time(NULL);
649
650         elm->leaf.data_offset = data_off;
651         elm->leaf.data_len = sizeof(*idata);
652         elm->leaf.data_crc = crc32(idata, HAMMER_INODE_CRCSIZE);
653
654         /*
655          * Create the second node element for the PFS data.
656          */
657         elm = &bnode->elms[1];
658         elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
659         elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION |
660                                       HAMMER_LOCALIZE_MISC;
661         elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
662         elm->leaf.base.key = 0;
663         elm->leaf.base.create_tid = create_tid;
664         elm->leaf.base.delete_tid = 0;
665         elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS;
666         elm->leaf.base.obj_type = 0;
667         elm->leaf.create_ts = (uint32_t)time(NULL);
668
669         elm->leaf.data_offset = pfsd_off;
670         elm->leaf.data_len = sizeof(*pfsd);
671         elm->leaf.data_crc = crc32(pfsd, sizeof(*pfsd));
672
673         bnode->crc = crc32(&bnode->crc + 1, HAMMER_BTREE_CRCSIZE);
674
675         rel_buffer(data_buffer0);
676         rel_buffer(data_buffer1);
677         rel_buffer(data_buffer2);
678
679         return(btree_off);
680 }