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