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