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