sys/vfs/hammer: Use HAMMER_OFF_{SHORT,LONG}_ENCODE()
[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                 fprintf(stderr,
173                         "newfs_hammer: You must specify at least one "
174                         "special file (volume)\n");
175                 exit(1);
176         }
177
178         if (nvols > HAMMER_MAX_VOLUMES) {
179                 fprintf(stderr,
180                         "newfs_hammer: The maximum number of volumes is %d\n",
181                         HAMMER_MAX_VOLUMES);
182                 exit(1);
183         }
184
185         /*
186          * Generate a filesystem id and lookup the filesystem type
187          */
188         uuidgen(&Hammer_FSId, 1);
189         uuid_name_lookup(&Hammer_FSType, HAMMER_FSTYPE_STRING, &status);
190         if (status != uuid_s_ok) {
191                 errx(1, "uuids file does not have the DragonFly "
192                         "HAMMER filesystem type");
193         }
194
195         total = 0;
196         for (i = 0; i < nvols; ++i) {
197                 vol = init_volume(av[i], O_RDWR, i);
198                 printf("Volume %d %s %-15s size %s\n",
199                         vol->vol_no, vol->type, vol->name,
200                         sizetostr(vol->size));
201
202                 if (eflag) {
203                         int res = trim_volume(vol);
204                         if (res == -1 || (res == 1 && ForceOpt == 0))
205                                 exit(1);
206                 }
207                 total += vol->size;
208         }
209
210         /*
211          * Reserve space for (future) header junk, setup our poor-man's
212          * big-block allocator.  Note that the header junk space includes
213          * volume header which is 1928 bytes.
214          */
215         if (HeaderJunkSize == -1)
216                 HeaderJunkSize = HAMMER_VOL_JUNK_SIZE;
217         else if (HeaderJunkSize < (int64_t)sizeof(struct hammer_volume_ondisk))
218                 HeaderJunkSize = sizeof(struct hammer_volume_ondisk);
219         HeaderJunkSize = (HeaderJunkSize + HAMMER_BUFMASK) & ~HAMMER_BUFMASK;
220
221         /*
222          * Calculate defaults for the boot area and memory log sizes,
223          * only if not specified by -b or -m option.
224          */
225         avg_vol_size = total / nvols;
226         if (BootAreaSize == -1)
227                 BootAreaSize = init_boot_area_size(BootAreaSize, avg_vol_size);
228         if (MemoryLogSize == -1)
229                 MemoryLogSize = init_memory_log_size(MemoryLogSize, avg_vol_size);
230
231         /*
232          * Format the volumes.  Format the root volume first so we can
233          * bootstrap the freemap.
234          */
235         format_volume(get_root_volume(), nvols, label);
236         for (i = 0; i < nvols; ++i) {
237                 if (i != HAMMER_ROOT_VOLNO)
238                         format_volume(get_volume(i), nvols, label);
239         }
240
241         print_volume(get_root_volume());
242
243         flush_all_volumes();
244         return(0);
245 }
246
247 static
248 void
249 print_volume(const struct volume_info *vol)
250 {
251         hammer_volume_ondisk_t ondisk;
252         hammer_blockmap_t blockmap;
253         hammer_off_t total = 0;
254         int i, nvols;
255         uint32_t status;
256         const char *name = NULL;
257         char *fsidstr;
258
259         ondisk = vol->ondisk;
260         blockmap = &ondisk->vol0_blockmap[HAMMER_ZONE_UNDO_INDEX];
261
262         nvols = ondisk->vol_count;
263         for (i = 0; i < nvols; ++i) {
264                 struct volume_info *p = get_volume(i);
265                 total += p->size;
266                 if (p->vol_no == HAMMER_ROOT_VOLNO) {
267                         assert(name == NULL);
268                         name = p->name;
269                 }
270         }
271
272         uuid_to_string(&Hammer_FSId, &fsidstr, &status);
273
274         printf("---------------------------------------------\n");
275         printf("HAMMER version %d\n", HammerVersion);
276         printf("%d volume%s total size %s\n",
277                 nvols, (nvols == 1 ? "" : "s"), sizetostr(total));
278         printf("root-volume:         %s\n", name);
279         if (DebugOpt)
280                 printf("header-junk-size:    %s\n",
281                         sizetostr(ondisk->vol_bot_beg));
282         printf("boot-area-size:      %s\n",
283                 sizetostr(ondisk->vol_mem_beg - ondisk->vol_bot_beg));
284         printf("memory-log-size:     %s\n",
285                 sizetostr(ondisk->vol_buf_beg - ondisk->vol_mem_beg));
286         printf("undo-buffer-size:    %s\n",
287                 sizetostr(HAMMER_OFF_LONG_ENCODE(blockmap->alloc_offset)));
288         printf("total-pre-allocated: %s\n",
289                 sizetostr(HAMMER_OFF_SHORT_ENCODE(vol->vol_free_off)));
290         printf("fsid:                %s\n", fsidstr);
291         printf("\n");
292         printf("NOTE: Please remember that you may have to manually set up a\n"
293                 "cron(8) job to prune and reblock the filesystem regularly.\n"
294                 "By default, the system automatically runs 'hammer cleanup'\n"
295                 "on a nightly basis.  The periodic.conf(5) variable\n"
296                 "'daily_clean_hammer_enable' can be unset to disable this.\n"
297                 "Also see 'man hammer' and 'man HAMMER' for more information.\n");
298         if (total < 10*GIG) {
299                 printf("\nWARNING: The minimum UNDO/REDO FIFO is %s, "
300                         "you really should not\n"
301                         "try to format a HAMMER filesystem this small.\n",
302                         sizetostr(HAMMER_BIGBLOCK_SIZE *
303                                HAMMER_MIN_UNDO_BIGBLOCKS));
304         }
305         if (total < 50*GIG) {
306                 printf("\nWARNING: HAMMER filesystems less than 50GB are "
307                         "not recommended!\n"
308                         "You may have to run 'hammer prune-everything' and "
309                         "'hammer reblock'\n"
310                         "quite often, even if using a nohistory mount.\n");
311         }
312 }
313
314 static
315 void
316 usage(int exit_code)
317 {
318         fprintf(stderr,
319                 "usage: newfs_hammer -L label [-Efh] [-b bootsize] [-m savesize] [-u undosize]\n"
320                 "                    [-C cachesize[:readahead]] [-V version] special ...\n"
321         );
322         exit(exit_code);
323 }
324
325 static void
326 test_header_junk_size(int64_t size)
327 {
328         if (size < HAMMER_MIN_VOL_JUNK) {
329                 if (ForceOpt == 0) {
330                         errx(1, "The minimum header junk size is %s",
331                                 sizetostr(HAMMER_MIN_VOL_JUNK));
332                 } else {
333                         fprintf(stderr,
334                                 "WARNING: you have specified "
335                                 "header junk size less than %s.\n",
336                                 sizetostr(HAMMER_MIN_VOL_JUNK));
337                 }
338         } else if (size > HAMMER_MAX_VOL_JUNK) {
339                 errx(1, "The maximum header junk size is %s",
340                         sizetostr(HAMMER_MAX_VOL_JUNK));
341         }
342 }
343
344 static void
345 test_boot_area_size(int64_t size)
346 {
347         if (size < HAMMER_BOOT_MINBYTES) {
348                 if (ForceOpt == 0) {
349                         errx(1, "The minimum boot area size is %s",
350                                 sizetostr(HAMMER_BOOT_MINBYTES));
351                 } else {
352                         fprintf(stderr,
353                                 "WARNING: you have specified "
354                                 "boot area size less than %s.\n",
355                                 sizetostr(HAMMER_BOOT_MINBYTES));
356                 }
357         } else if (size > HAMMER_BOOT_MAXBYTES) {
358                 errx(1, "The maximum boot area size is %s",
359                         sizetostr(HAMMER_BOOT_MAXBYTES));
360         }
361 }
362
363 static void
364 test_memory_log_size(int64_t size)
365 {
366         if (size < HAMMER_MEM_MINBYTES) {
367                 if (ForceOpt == 0) {
368                         errx(1, "The minimum memory log size is %s",
369                                 sizetostr(HAMMER_MEM_MINBYTES));
370                 } else {
371                         fprintf(stderr,
372                                 "WARNING: you have specified "
373                                 "memory log size less than %s.\n",
374                                 sizetostr(HAMMER_MEM_MINBYTES));
375                 }
376         } else if (size > HAMMER_MEM_MAXBYTES) {
377                 errx(1, "The maximum memory log size is %s",
378                         sizetostr(HAMMER_MEM_MAXBYTES));
379         }
380 }
381
382 static void
383 test_undo_buffer_size(int64_t size)
384 {
385         int64_t minbuf, maxbuf;
386
387         minbuf = HAMMER_BIGBLOCK_SIZE * HAMMER_MIN_UNDO_BIGBLOCKS;
388         maxbuf = HAMMER_BIGBLOCK_SIZE * HAMMER_MAX_UNDO_BIGBLOCKS;
389
390         if (size < minbuf) {
391                 if (ForceOpt == 0) {
392                         errx(1, "The minimum UNDO/REDO FIFO size is %s",
393                                 sizetostr(minbuf));
394                 } else {
395                         fprintf(stderr,
396                                 "WARNING: you have specified an "
397                                 "UNDO/REDO FIFO size less than %s,\n"
398                                 "which may lead to VFS panics.\n",
399                                 sizetostr(minbuf));
400                 }
401         } else if (size > maxbuf) {
402                 errx(1, "The maximum UNDO/REDO FIFO size is %s",
403                         sizetostr(maxbuf));
404         }
405 }
406
407 /*
408  * Convert a string to a 64 bit signed integer with various requirements.
409  */
410 static 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 hammer_tid_t
463 createtid(void)
464 {
465         static hammer_tid_t lasttid;
466
467         if (lasttid == 0)
468                 lasttid = 0x0000000100000000ULL;
469         return(lasttid++);
470 }
471
472 static uint64_t
473 nowtime(void)
474 {
475         struct timeval tv;
476         uint64_t xtime;
477
478         gettimeofday(&tv, NULL);
479         xtime = tv.tv_sec * 1000000LL + tv.tv_usec;
480         return(xtime);
481 }
482
483 /*
484  * TRIM the volume, but only if the backing store is a DEVICE
485  */
486 static
487 int
488 trim_volume(struct volume_info *vol)
489 {
490         size_t olen;
491         char *dev_name, *p;
492         char sysctl_name[64];
493         int trim_enabled;
494         off_t ioarg[2];
495
496         if (strcmp(vol->type, "DEVICE")) {
497                 fprintf(stderr, "Cannot TRIM regular file %s\n", vol->name);
498                 return(1);
499         }
500         if (strncmp(vol->name, "/dev/da", 7)) {
501                 fprintf(stderr, "%s does not support the TRIM command\n",
502                         vol->name);
503                 return(1);
504         }
505
506         /* Extract a number from /dev/da?s? */
507         dev_name = strdup(vol->name);
508         p = strtok(dev_name + strlen("/dev/da"), "s");
509         sprintf(sysctl_name, "kern.cam.da.%s.trim_enabled", p);
510         free(dev_name);
511
512         trim_enabled = 0;
513         olen = sizeof(trim_enabled);
514
515         if (sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0) == -1) {
516                 fprintf(stderr, "%s (%s) does not support the TRIM command\n",
517                         vol->name, sysctl_name);
518                 return(1);
519         }
520         if (!trim_enabled) {
521                 fprintf(stderr, "Erase device option selected, but sysctl (%s) "
522                         "is not enabled\n", sysctl_name);
523                 return(1);
524         }
525
526         ioarg[0] = vol->device_offset;
527         ioarg[1] = vol->size;
528
529         printf("Trimming %s %s, sectors %llu-%llu\n",
530                 vol->type, vol->name,
531                 (unsigned long long)ioarg[0] / 512,
532                 (unsigned long long)ioarg[1] / 512);
533
534         if (ioctl(vol->fd, IOCTLTRIM, ioarg) == -1) {
535                 fprintf(stderr, "Device trim failed\n");
536                 return(-1);
537         }
538
539         return(0);
540 }
541
542 /*
543  * Format a HAMMER volume.
544  */
545 static
546 void
547 format_volume(struct volume_info *vol, int nvols, const char *label)
548 {
549         struct volume_info *root_vol;
550         hammer_volume_ondisk_t ondisk;
551         int64_t freeblks;
552         int64_t freebytes;
553         int64_t vol_buf_size;
554         hammer_off_t vol_alloc;
555         int i;
556
557         /*
558          * Initialize basic information in the on-disk volume structure.
559          */
560         ondisk = vol->ondisk;
561
562         ondisk->vol_fsid = Hammer_FSId;
563         ondisk->vol_fstype = Hammer_FSType;
564         snprintf(ondisk->vol_label, sizeof(ondisk->vol_label), "%s", label);
565         ondisk->vol_no = vol->vol_no;
566         ondisk->vol_count = nvols;
567         ondisk->vol_version = HammerVersion;
568
569         vol_alloc = HeaderJunkSize;
570         ondisk->vol_bot_beg = vol_alloc;
571         vol_alloc += BootAreaSize;
572         ondisk->vol_mem_beg = vol_alloc;
573         vol_alloc += MemoryLogSize;
574
575         /*
576          * The remaining area is the zone 2 buffer allocation area.
577          */
578         ondisk->vol_buf_beg = vol_alloc;
579         ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK;
580         vol_buf_size = HAMMER_VOL_BUF_SIZE(ondisk);
581
582         if (vol_buf_size < 0) {
583                 errx(1, "volume %d %s is too small to hold the volume header",
584                      vol->vol_no, vol->name);
585         }
586
587         if ((vol_buf_size & ~HAMMER_OFF_SHORT_MASK) != 0) {
588                 errx(1, "volume %d %s is too large", vol->vol_no, vol->name);
589         }
590
591         ondisk->vol_rootvol = HAMMER_ROOT_VOLNO;
592         ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
593
594         vol->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0);
595         vol->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no,
596                                 vol_buf_size & ~HAMMER_BIGBLOCK_MASK64);
597
598         /*
599          * Format the root volume.
600          */
601         if (vol->vol_no == HAMMER_ROOT_VOLNO) {
602                 /*
603                  * Check freemap counts before formatting
604                  */
605                 freeblks = count_freemap(vol);
606                 freebytes = freeblks * HAMMER_BIGBLOCK_SIZE64;
607                 if (freebytes < 10*GIG && ForceOpt == 0) {
608                         errx(1, "Cannot create a HAMMER filesystem less than 10GB "
609                                 "unless you use -f\n(for the size of Volume %d).  "
610                                 "HAMMER filesystems less than 50GB are not "
611                                 "recommended.", HAMMER_ROOT_VOLNO);
612                 }
613
614                 /*
615                  * Starting TID
616                  */
617                 ondisk->vol0_next_tid = createtid();
618
619                 /*
620                  * Format freemap.  vol0_stat_freebigblocks is
621                  * the number of big-blocks available for anything
622                  * other than freemap zone at this point.
623                  */
624                 format_freemap(vol);
625                 assert(ondisk->vol0_stat_freebigblocks == 0);
626                 ondisk->vol0_stat_freebigblocks = initialize_freemap(vol);
627
628                 /*
629                  * Format zones that are mapped to zone-2.
630                  */
631                 for (i = 0; i < HAMMER_MAX_ZONES; ++i) {
632                         if (hammer_is_zone2_mapped_index(i))
633                                 format_blockmap(vol, i, 0);
634                 }
635
636                 /*
637                  * Format undo zone.  Formatting decrements
638                  * vol0_stat_freebigblocks whenever a new big-block
639                  * is allocated for undo zone.
640                  */
641                 format_undomap(vol, &UndoBufferSize);
642                 assert(ondisk->vol0_stat_bigblocks == 0);
643                 ondisk->vol0_stat_bigblocks = ondisk->vol0_stat_freebigblocks;
644
645                 /*
646                  * Format the root directory.  Formatting decrements
647                  * vol0_stat_freebigblocks whenever a new big-block
648                  * is allocated for required zones.
649                  */
650                 ondisk->vol0_btree_root = format_root_directory(label);
651                 ++ondisk->vol0_stat_inodes;     /* root inode */
652         } else {
653                 freeblks = initialize_freemap(vol);
654                 root_vol = get_root_volume();
655                 root_vol->ondisk->vol0_stat_freebigblocks += freeblks;
656                 root_vol->ondisk->vol0_stat_bigblocks += freeblks;
657         }
658 }
659
660 /*
661  * Format the root directory.
662  */
663 static
664 hammer_off_t
665 format_root_directory(const char *label)
666 {
667         hammer_off_t btree_off;
668         hammer_off_t idata_off;
669         hammer_off_t pfsd_off;
670         hammer_tid_t create_tid;
671         hammer_node_ondisk_t bnode;
672         hammer_inode_data_t idata;
673         hammer_pseudofs_data_t pfsd;
674         struct buffer_info *data_buffer0 = NULL;
675         struct buffer_info *data_buffer1 = NULL;
676         struct buffer_info *data_buffer2 = NULL;
677         hammer_btree_elm_t elm;
678         uint64_t xtime;
679
680         /*
681          * Allocate zero-filled root btree node, inode and pfs
682          */
683         bnode = alloc_btree_node(&btree_off, &data_buffer0);
684         idata = alloc_meta_element(&idata_off, sizeof(*idata), &data_buffer1);
685         pfsd = alloc_meta_element(&pfsd_off, sizeof(*pfsd), &data_buffer2);
686         create_tid = createtid();
687         xtime = nowtime();
688
689         /*
690          * Populate the inode data and inode record for the root directory.
691          */
692         idata->version = HAMMER_INODE_DATA_VERSION;
693         idata->mode = 0755;
694         idata->ctime = xtime;
695         idata->mtime = xtime;
696         idata->atime = xtime;
697         idata->obj_type = HAMMER_OBJTYPE_DIRECTORY;
698         idata->size = 0;
699         idata->nlinks = 1;
700         if (HammerVersion >= HAMMER_VOL_VERSION_TWO)
701                 idata->cap_flags |= HAMMER_INODE_CAP_DIR_LOCAL_INO;
702         if (HammerVersion >= HAMMER_VOL_VERSION_SIX)
703                 idata->cap_flags |= HAMMER_INODE_CAP_DIRHASH_ALG1;
704
705         /*
706          * Populate the PFS data for the root PFS.
707          */
708         pfsd->sync_low_tid = 1;
709         pfsd->sync_beg_tid = 0;
710         pfsd->sync_end_tid = 0; /* overriden by vol0_next_tid on pfs0 */
711         pfsd->shared_uuid = Hammer_FSId;
712         pfsd->unique_uuid = Hammer_FSId;
713         pfsd->mirror_flags = 0;
714         snprintf(pfsd->label, sizeof(pfsd->label), "%s", label);
715
716         /*
717          * Create the root of the B-Tree.  The root is a leaf node so we
718          * do not have to worry about boundary elements.
719          */
720         bnode->parent = 0;  /* no parent */
721         bnode->count = 2;
722         bnode->type = HAMMER_BTREE_TYPE_LEAF;
723         bnode->mirror_tid = 0;
724
725         /*
726          * Create the first node element for the inode.
727          */
728         elm = &bnode->elms[0];
729         elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
730         elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION |
731                                       HAMMER_LOCALIZE_INODE;
732         elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
733         elm->leaf.base.key = 0;
734         elm->leaf.base.create_tid = create_tid;
735         elm->leaf.base.delete_tid = 0;
736         elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE;
737         elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
738         elm->leaf.create_ts = (uint32_t)time(NULL);
739
740         elm->leaf.data_offset = idata_off;
741         elm->leaf.data_len = sizeof(*idata);
742         hammer_crc_set_leaf(idata, &elm->leaf);
743
744         /*
745          * Create the second node element for the PFS data.
746          * This is supposed to be a record part of the root ip (inode),
747          * so it should have the same obj_type value as above.
748          */
749         elm = &bnode->elms[1];
750         elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
751         elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION |
752                                       HAMMER_LOCALIZE_MISC;
753         elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
754         elm->leaf.base.key = 0;
755         elm->leaf.base.create_tid = create_tid;
756         elm->leaf.base.delete_tid = 0;
757         elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS;
758         elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
759         elm->leaf.create_ts = (uint32_t)time(NULL);
760
761         elm->leaf.data_offset = pfsd_off;
762         elm->leaf.data_len = sizeof(*pfsd);
763         hammer_crc_set_leaf(pfsd, &elm->leaf);
764
765         hammer_crc_set_btree(bnode);
766
767         rel_buffer(data_buffer0);
768         rel_buffer(data_buffer1);
769         rel_buffer(data_buffer2);
770
771         return(btree_off);
772 }