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