TRIM support
[dragonfly.git] / sbin / newfs_hammer / newfs_hammer.c
CommitLineData
9a8bf4a7
MD
1/*
2 * Copyright (c) 2007 The DragonFly Project. All rights reserved.
dac7444f 3 *
9a8bf4a7
MD
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
dac7444f 6 *
9a8bf4a7
MD
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
dac7444f 10 *
9a8bf4a7
MD
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.
dac7444f 20 *
9a8bf4a7
MD
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.
dac7444f 33 *
2fd7accd 34 * $DragonFly: src/sbin/newfs_hammer/newfs_hammer.c,v 1.44 2008/08/21 23:32:27 thomas Exp $
9a8bf4a7
MD
35 */
36
ed3afcca 37#include "newfs_hammer.h"
9a8bf4a7 38
ed3afcca 39static int64_t getsize(const char *str, int64_t minval, int64_t maxval, int pw);
9a8bf4a7 40static const char *sizetostr(off_t size);
e0fb398b 41static void trim_volume(struct volume_info *vol);
ed3afcca 42static void check_volume(struct volume_info *vol);
416571a0
MD
43static void format_volume(struct volume_info *vol, int nvols,const char *label,
44 off_t total_size);
d8935c61 45static hammer_off_t format_root(const char *label);
7849afbc 46static u_int64_t nowtime(void);
ed3afcca 47static void usage(void);
9a8bf4a7 48
0d86737d 49static int ForceOpt = 0;
47921633 50static int HammerVersion = -1;
e0fb398b 51static int Eflag = 0;
0d86737d
MD
52
53#define GIG (1024LL*1024*1024)
54
9a8bf4a7
MD
55int
56main(int ac, char **av)
57{
9a8bf4a7
MD
58 u_int32_t status;
59 off_t total;
7e1ba5da
MD
60 int ch;
61 int i;
9a8bf4a7 62 const char *label = NULL;
416571a0 63 struct volume_info *vol;
ddc8e722 64 char *fsidstr;
9a8bf4a7
MD
65
66 /*
ed3afcca
MD
67 * Sanity check basic filesystem structures. No cookies for us
68 * if it gets broken!
9a8bf4a7 69 */
9a8bf4a7 70 assert(sizeof(struct hammer_volume_ondisk) <= HAMMER_BUFSIZE);
c3be93f2
MD
71 assert(sizeof(struct hammer_blockmap_layer1) == 32);
72 assert(sizeof(struct hammer_blockmap_layer2) == 16);
9a8bf4a7
MD
73
74 /*
93667675 75 * Generate a filesystem id and lookup the filesystem type
9a8bf4a7 76 */
ed3afcca 77 uuidgen(&Hammer_FSId, 1);
9a8bf4a7
MD
78 uuid_name_lookup(&Hammer_FSType, "DragonFly HAMMER", &status);
79 if (status != uuid_s_ok) {
ed3afcca
MD
80 errx(1, "uuids file does not have the DragonFly "
81 "HAMMER filesystem type");
9a8bf4a7
MD
82 }
83
9a8bf4a7
MD
84 /*
85 * Parse arguments
86 */
e0fb398b 87 while ((ch = getopt(ac, av, "fEL:b:m:u:V:")) != -1) {
9a8bf4a7 88 switch(ch) {
0d86737d
MD
89 case 'f':
90 ForceOpt = 1;
91 break;
e0fb398b
T
92 case 'E':
93 Eflag = 1;
94 break;
9a8bf4a7
MD
95 case 'L':
96 label = optarg;
97 break;
a89aec1b
MD
98 case 'b':
99 BootAreaSize = getsize(optarg,
100 HAMMER_BUFSIZE,
101 HAMMER_BOOT_MAXBYTES, 2);
102 break;
a89aec1b
MD
103 case 'm':
104 MemAreaSize = getsize(optarg,
105 HAMMER_BUFSIZE,
106 HAMMER_MEM_MAXBYTES, 2);
107 break;
64c21cf3
MD
108 case 'u':
109 UndoBufferSize = getsize(optarg,
110 HAMMER_LARGEBLOCK_SIZE,
111 HAMMER_LARGEBLOCK_SIZE *
112 HAMMER_UNDO_LAYER2, 2);
dac7444f
TN
113 if (UndoBufferSize < 500*1024*1024 && ForceOpt == 0)
114 errx(1, "The minimum UNDO/REDO FIFO size is "
115 "500MB\n");
116 if (UndoBufferSize < 500*1024*1024) {
117 fprintf(stderr,
118 "WARNING: you have specified an "
119 "UNDO/REDO FIFO size less than 500MB,\n"
120 "which may lead to VFS panics.\n");
0d86737d 121 }
64c21cf3 122 break;
47921633
MD
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\n",
130 HammerVersion);
131 }
132 break;
9a8bf4a7
MD
133 default:
134 usage();
135 break;
136 }
137 }
138
139 if (label == NULL) {
140 fprintf(stderr,
141 "newfs_hammer: A filesystem label must be specified\n");
240a4e41 142 usage();
9a8bf4a7
MD
143 }
144
47921633
MD
145 if (HammerVersion < 0) {
146 size_t olen = sizeof(HammerVersion);
147 HammerVersion = HAMMER_VOL_VERSION_DEFAULT;
148 if (sysctlbyname("vfs.hammer.supported_version",
149 &HammerVersion, &olen, NULL, 0) == 0) {
150 if (HammerVersion >= HAMMER_VOL_VERSION_WIP) {
151 HammerVersion = HAMMER_VOL_VERSION_WIP - 1;
152 fprintf(stderr,
153 "newfs_hammer: WARNING: HAMMER VFS "
240a4e41 154 "supports higher version than I "
47921633
MD
155 "understand,\n"
156 "using version %d\n",
157 HammerVersion);
158 }
159 } else {
160 fprintf(stderr,
161 "newfs_hammer: WARNING: HAMMER VFS not "
162 "loaded, cannot get version info.\n"
163 "Using version %d\n",
164 HAMMER_VOL_VERSION_DEFAULT);
165 }
166 }
167
9a8bf4a7
MD
168 /*
169 * Collect volume information
170 */
171 ac -= optind;
172 av += optind;
ed3afcca 173 NumVolumes = ac;
47197d71 174 RootVolNo = 0;
9a8bf4a7 175
d8935c61 176 if (NumVolumes == 0) {
acb809eb 177 fprintf(stderr,
133fbfd7 178 "newfs_hammer: You must specify at least one special file (volume)\n");
acb809eb
MD
179 exit(1);
180 }
181
94509284
MD
182 if (NumVolumes > HAMMER_MAX_VOLUMES) {
183 fprintf(stderr,
184 "newfs_hammer: The maximum number of volumes is %d\n", HAMMER_MAX_VOLUMES);
185 exit(1);
186 }
187
9a8bf4a7 188 total = 0;
ed3afcca 189 for (i = 0; i < NumVolumes; ++i) {
61aeeb33 190 vol = setup_volume(i, av[i], 1, O_RDWR);
ed3afcca
MD
191
192 /*
193 * Load up information on the volume and initialize
194 * its remaining fields.
195 */
196 check_volume(vol);
e0fb398b
T
197 if (Eflag) {
198 char sysctl_name[64];
199 int trim_enabled = 0;
200 size_t olen = sizeof(trim_enabled);
201 char *dev_name = strdup(vol->name);
202 dev_name = strtok(dev_name + strlen("/dev/da"),"s");
203
204 sprintf(sysctl_name, "kern.cam.da.%s.trim_enabled",
205 dev_name);
206 errno=0;
207 sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0);
208 if(errno == ENOENT) {
209 printf("Device:%s (%s) does not support the "
210 "TRIM command\n", vol->name,sysctl_name);
211 usage();
212 }
213 if(!trim_enabled) {
214 printf("Erase device option selected, but "
215 "sysctl (%s) is not enabled\n", sysctl_name);
216 usage();
217
218 }
219 trim_volume(vol);
220 }
ed3afcca 221 total += vol->size;
9a8bf4a7
MD
222 }
223
224 /*
a89aec1b
MD
225 * Calculate defaults for the boot and memory area sizes.
226 */
227 if (BootAreaSize == 0) {
228 BootAreaSize = HAMMER_BOOT_NOMBYTES;
94509284 229 while (BootAreaSize > total / NumVolumes / HAMMER_MAX_VOLUMES)
a89aec1b
MD
230 BootAreaSize >>= 1;
231 if (BootAreaSize < HAMMER_BOOT_MINBYTES)
232 BootAreaSize = 0;
233 } else if (BootAreaSize < HAMMER_BOOT_MINBYTES) {
234 BootAreaSize = HAMMER_BOOT_MINBYTES;
235 }
236 if (MemAreaSize == 0) {
237 MemAreaSize = HAMMER_MEM_NOMBYTES;
94509284 238 while (MemAreaSize > total / NumVolumes / HAMMER_MAX_VOLUMES)
a89aec1b
MD
239 MemAreaSize >>= 1;
240 if (MemAreaSize < HAMMER_MEM_MINBYTES)
241 MemAreaSize = 0;
242 } else if (MemAreaSize < HAMMER_MEM_MINBYTES) {
243 MemAreaSize = HAMMER_MEM_MINBYTES;
244 }
245
9a8bf4a7 246 /*
c3be93f2
MD
247 * Format the volumes. Format the root volume first so we can
248 * bootstrap the freemap.
9a8bf4a7 249 */
416571a0 250 format_volume(get_volume(RootVolNo), NumVolumes, label, total);
ed3afcca 251 for (i = 0; i < NumVolumes; ++i) {
c3be93f2 252 if (i != RootVolNo)
416571a0 253 format_volume(get_volume(i), NumVolumes, label, total);
9a8bf4a7 254 }
7e1ba5da
MD
255
256 /*
257 * Pre-size the blockmap layer1/layer2 infrastructure to the zone
258 * limit. If we do this the filesystem does not have to allocate
259 * new layer2 blocks which reduces the chances of the reblocker
260 * having to fallback to an extremely inefficient algorithm.
261 */
262 vol = get_volume(RootVolNo);
e926bade
MD
263 vol->ondisk->vol0_stat_bigblocks = vol->ondisk->vol0_stat_freebigblocks;
264 vol->cache.modified = 1;
ddc8e722 265 uuid_to_string(&Hammer_FSId, &fsidstr, &status);
7e1ba5da 266
64c21cf3 267 printf("---------------------------------------------\n");
47921633
MD
268 printf("%d volume%s total size %s version %d\n",
269 NumVolumes, (NumVolumes == 1 ? "" : "s"),
270 sizetostr(total), HammerVersion);
64c21cf3
MD
271 printf("boot-area-size: %s\n", sizetostr(BootAreaSize));
272 printf("memory-log-size: %s\n", sizetostr(MemAreaSize));
273 printf("undo-buffer-size: %s\n", sizetostr(UndoBufferSize));
e926bade
MD
274 printf("total-pre-allocated: %s\n",
275 sizetostr(vol->vol_free_off & HAMMER_OFF_SHORT_MASK));
ddc8e722 276 printf("fsid: %s\n", fsidstr);
64c21cf3 277 printf("\n");
50393a60 278 printf("NOTE: Please remember that you may have to manually set up a\n"
240a4e41 279 "cron(8) job to prune and reblock the filesystem regularly.\n"
50393a60 280 "By default, the system automatically runs 'hammer cleanup'\n"
dac7444f 281 "on a nightly basis. The periodic.conf(5) variable\n"
50393a60
SW
282 "'daily_clean_hammer_enable' can be unset to disable this.\n"
283 "Also see 'man hammer' and 'man HAMMER' for more information.\n");
bc5587bb 284 if (total < 10*GIG) {
dac7444f 285 printf("\nWARNING: The minimum UNDO/REDO FIFO is 500MB, you "
bc5587bb 286 "really should not\n"
dac7444f 287 "try to format a HAMMER filesystem this small.\n");
bc5587bb 288 }
0d86737d 289 if (total < 50*GIG) {
240a4e41 290 printf("\nWARNING: HAMMER filesystems less than 50GB are "
0d86737d
MD
291 "not recommended!\n"
292 "You may have to run 'hammer prune-everything' and "
293 "'hammer reblock'\n"
294 "quite often, even if using a nohistory mount.\n");
295 }
ed3afcca 296 flush_all_volumes();
9a8bf4a7
MD
297 return(0);
298}
299
300static
301void
302usage(void)
303{
133fbfd7 304 fprintf(stderr,
e0fb398b 305 "usage: newfs_hammer -L label [-fE] [-b bootsize] [-m savesize] [-u undosize]\n"
240a4e41 306 " [-V version] special ...\n"
133fbfd7 307 );
9a8bf4a7
MD
308 exit(1);
309}
310
311/*
312 * Convert the size in bytes to a human readable string.
313 */
64c21cf3
MD
314static
315const char *
9a8bf4a7
MD
316sizetostr(off_t size)
317{
318 static char buf[32];
319
320 if (size < 1024 / 2) {
321 snprintf(buf, sizeof(buf), "%6.2f", (double)size);
322 } else if (size < 1024 * 1024 / 2) {
323 snprintf(buf, sizeof(buf), "%6.2fKB",
324 (double)size / 1024);
325 } else if (size < 1024 * 1024 * 1024LL / 2) {
326 snprintf(buf, sizeof(buf), "%6.2fMB",
327 (double)size / (1024 * 1024));
328 } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) {
329 snprintf(buf, sizeof(buf), "%6.2fGB",
330 (double)size / (1024 * 1024 * 1024LL));
331 } else {
332 snprintf(buf, sizeof(buf), "%6.2fTB",
333 (double)size / (1024 * 1024 * 1024LL * 1024LL));
334 }
335 return(buf);
336}
337
ed3afcca
MD
338/*
339 * Convert a string to a 64 bit signed integer with various requirements.
340 */
9a8bf4a7 341static int64_t
ed3afcca 342getsize(const char *str, int64_t minval, int64_t maxval, int powerof2)
9a8bf4a7
MD
343{
344 int64_t val;
345 char *ptr;
346
347 val = strtoll(str, &ptr, 0);
348 switch(*ptr) {
349 case 't':
350 case 'T':
351 val *= 1024;
352 /* fall through */
353 case 'g':
354 case 'G':
355 val *= 1024;
356 /* fall through */
357 case 'm':
358 case 'M':
359 val *= 1024;
360 /* fall through */
361 case 'k':
362 case 'K':
363 val *= 1024;
364 break;
365 default:
ed3afcca
MD
366 errx(1, "Unknown suffix in number '%s'\n", str);
367 /* not reached */
9a8bf4a7
MD
368 }
369 if (ptr[1]) {
ed3afcca
MD
370 errx(1, "Unknown suffix in number '%s'\n", str);
371 /* not reached */
9a8bf4a7
MD
372 }
373 if (val < minval) {
ed3afcca
MD
374 errx(1, "Value too small: %s, min is %s\n",
375 str, sizetostr(minval));
376 /* not reached */
9a8bf4a7
MD
377 }
378 if (val > maxval) {
ed3afcca
MD
379 errx(1, "Value too large: %s, max is %s\n",
380 str, sizetostr(maxval));
381 /* not reached */
382 }
a89aec1b 383 if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) {
ed3afcca
MD
384 errx(1, "Value not power of 2: %s\n", str);
385 /* not reached */
9a8bf4a7 386 }
a89aec1b 387 if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) {
dac7444f 388 errx(1, "Value not an integral multiple of %dK: %s",
a89aec1b
MD
389 HAMMER_BUFSIZE / 1024, str);
390 /* not reached */
391 }
9a8bf4a7
MD
392 return(val);
393}
394
ed3afcca 395/*
b5aaba7f
MD
396 * Generate a transaction id. Transaction ids are no longer time-based.
397 * Put the nail in the coffin by not making the first one time-based.
398 *
399 * We could start at 1 here but start at 2^32 to reserve a small domain for
400 * possible future use.
ed3afcca 401 */
9a8bf4a7
MD
402static hammer_tid_t
403createtid(void)
404{
405 static hammer_tid_t lasttid;
9a8bf4a7 406
1d9f6aa1 407 if (lasttid == 0)
b5aaba7f 408 lasttid = 0x0000000100000000ULL;
9a8bf4a7
MD
409 return(lasttid++);
410}
411
7849afbc
MD
412static u_int64_t
413nowtime(void)
414{
415 struct timeval tv;
416 u_int64_t xtime;
417
418 gettimeofday(&tv, NULL);
419 xtime = tv.tv_sec * 1000000LL + tv.tv_usec;
420 return(xtime);
421}
422
9a8bf4a7 423/*
e0fb398b
T
424 * TRIM the volume, but only if the backing store is a DEVICE
425 */
426static
427void
428trim_volume(struct volume_info *vol)
429{
430 if (strncmp(vol->type, "DEVICE", sizeof(vol->type)) == 0) {
431 off_t ioarg[2];
432
433 /* 1MB offset to prevent destroying disk-reserved area */
434 ioarg[0] = vol->device_offset;
435 ioarg[1] = vol->size;
436 printf("Trimming Device:%s, sectors (%llu -%llu)\n",vol->name,
437 (unsigned long long)ioarg[0]/512,
438 (unsigned long long)ioarg[1]/512);
439 if (ioctl(vol->fd, IOCTLTRIM, ioarg) < 0) {
440 printf("Device trim failed\n");
441 usage ();
442 }
443 }
444}
445
446/*
9a8bf4a7
MD
447 * Check basic volume characteristics. HAMMER filesystems use a minimum
448 * of a 16KB filesystem buffer size.
449 */
450static
451void
ed3afcca 452check_volume(struct volume_info *vol)
9a8bf4a7
MD
453{
454 struct partinfo pinfo;
455 struct stat st;
456
457 /*
458 * Get basic information about the volume
459 */
ed3afcca
MD
460 vol->fd = open(vol->name, O_RDWR);
461 if (vol->fd < 0)
462 err(1, "Unable to open %s R+W", vol->name);
463 if (ioctl(vol->fd, DIOCGPART, &pinfo) < 0) {
9a8bf4a7 464 /*
133fbfd7 465 * Allow the formatting of regular files as HAMMER volumes
9a8bf4a7 466 */
ed3afcca
MD
467 if (fstat(vol->fd, &st) < 0)
468 err(1, "Unable to stat %s", vol->name);
469 vol->size = st.st_size;
470 vol->type = "REGFILE";
e0fb398b
T
471
472 if (Eflag)
473 errx(1,"Cannot TRIM regular file %s\n", vol->name);
474
9a8bf4a7
MD
475 } else {
476 /*
477 * When formatting a block device as a HAMMER volume the
478 * sector size must be compatible. HAMMER uses 16384 byte
479 * filesystem buffers.
480 */
481 if (pinfo.reserved_blocks) {
ed3afcca
MD
482 errx(1, "HAMMER cannot be placed in a partition "
483 "which overlaps the disklabel or MBR");
9a8bf4a7
MD
484 }
485 if (pinfo.media_blksize > 16384 ||
486 16384 % pinfo.media_blksize) {
ed3afcca
MD
487 errx(1, "A media sector size of %d is not supported",
488 pinfo.media_blksize);
9a8bf4a7
MD
489 }
490
ed3afcca 491 vol->size = pinfo.media_size;
e0fb398b 492 vol->device_offset = pinfo.media_offset;
ed3afcca 493 vol->type = "DEVICE";
9a8bf4a7 494 }
ed3afcca
MD
495 printf("Volume %d %s %-15s size %s\n",
496 vol->vol_no, vol->type, vol->name,
497 sizetostr(vol->size));
498
499 /*
c3be93f2
MD
500 * Reserve space for (future) header junk, setup our poor-man's
501 * bigblock allocator.
ed3afcca 502 */
a89aec1b 503 vol->vol_alloc = HAMMER_BUFSIZE * 16;
9a8bf4a7
MD
504}
505
506/*
507 * Format a HAMMER volume. Cluster 0 will be initially placed in volume 0.
508 */
509static
510void
416571a0 511format_volume(struct volume_info *vol, int nvols, const char *label,
68e079b8 512 off_t total_size __unused)
9a8bf4a7 513{
c3be93f2 514 struct volume_info *root_vol;
ed3afcca 515 struct hammer_volume_ondisk *ondisk;
c3be93f2 516 int64_t freeblks;
0d86737d 517 int64_t freebytes;
68e079b8 518 int i;
9a8bf4a7 519
ed3afcca
MD
520 /*
521 * Initialize basic information in the on-disk volume structure.
522 */
523 ondisk = vol->ondisk;
524
525 ondisk->vol_fsid = Hammer_FSId;
526 ondisk->vol_fstype = Hammer_FSType;
527 snprintf(ondisk->vol_name, sizeof(ondisk->vol_name), "%s", label);
528 ondisk->vol_no = vol->vol_no;
529 ondisk->vol_count = nvols;
47921633 530 ondisk->vol_version = HammerVersion;
9a8bf4a7 531
a89aec1b
MD
532 ondisk->vol_bot_beg = vol->vol_alloc;
533 vol->vol_alloc += BootAreaSize;
534 ondisk->vol_mem_beg = vol->vol_alloc;
535 vol->vol_alloc += MemAreaSize;
40043e7f
MD
536
537 /*
538 * The remaining area is the zone 2 buffer allocation area. These
539 * buffers
540 */
47197d71
MD
541 ondisk->vol_buf_beg = vol->vol_alloc;
542 ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK;
9a8bf4a7 543
47197d71 544 if (ondisk->vol_buf_end < ondisk->vol_buf_beg) {
ed3afcca
MD
545 errx(1, "volume %d %s is too small to hold the volume header",
546 vol->vol_no, vol->name);
9a8bf4a7
MD
547 }
548
47197d71
MD
549 ondisk->vol_nblocks = (ondisk->vol_buf_end - ondisk->vol_buf_beg) /
550 HAMMER_BUFSIZE;
fbc6e32a 551 ondisk->vol_blocksize = HAMMER_BUFSIZE;
9a8bf4a7 552
c3be93f2
MD
553 ondisk->vol_rootvol = RootVolNo;
554 ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
555
556 vol->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0);
dac7444f
TN
557 vol->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no,
558 (ondisk->vol_buf_end - ondisk->vol_buf_beg) &
559 ~HAMMER_LARGEBLOCK_MASK64);
c3be93f2 560
9a8bf4a7 561 /*
c3be93f2 562 * Format the root volume.
9a8bf4a7 563 */
c3be93f2 564 if (vol->vol_no == RootVolNo) {
416571a0
MD
565 /*
566 * Starting TID
567 */
47197d71 568 ondisk->vol0_next_tid = createtid();
c3be93f2
MD
569
570 format_freemap(vol,
571 &ondisk->vol0_blockmap[HAMMER_ZONE_FREEMAP_INDEX]);
572
573 freeblks = initialize_freemap(vol);
574 ondisk->vol0_stat_freebigblocks = freeblks;
c3be93f2 575
0d86737d 576 freebytes = freeblks * HAMMER_LARGEBLOCK_SIZE64;
dac7444f 577 if (freebytes < 10*GIG && ForceOpt == 0) {
0a9528b9 578 errx(1, "Cannot create a HAMMER filesystem less than "
dac7444f 579 "10GB unless you use -f. HAMMER filesystems\n"
240a4e41 580 "less than 50GB are not recommended\n");
0d86737d
MD
581 }
582
68e079b8
MD
583 for (i = 8; i < HAMMER_MAX_ZONES; ++i) {
584 format_blockmap(&ondisk->vol0_blockmap[i],
585 HAMMER_ZONE_ENCODE(i, 0));
586 }
3f673d5c
MD
587 format_undomap(ondisk);
588
d8935c61 589 ondisk->vol0_btree_root = format_root(label);
47197d71 590 ++ondisk->vol0_stat_inodes; /* root inode */
c3be93f2
MD
591 } else {
592 freeblks = initialize_freemap(vol);
593 root_vol = get_volume(RootVolNo);
594 root_vol->cache.modified = 1;
595 root_vol->ondisk->vol0_stat_freebigblocks += freeblks;
596 root_vol->ondisk->vol0_stat_bigblocks += freeblks;
597 rel_volume(root_vol);
9a8bf4a7 598 }
9a8bf4a7
MD
599}
600
601/*
ed3afcca 602 * Format the root directory.
9a8bf4a7
MD
603 */
604static
47197d71 605hammer_off_t
d8935c61 606format_root(const char *label)
9a8bf4a7 607{
47197d71 608 hammer_off_t btree_off;
d8935c61 609 hammer_off_t pfsd_off;
11ad5ade
MD
610 hammer_off_t data_off;
611 hammer_tid_t create_tid;
8cd0a023 612 hammer_node_ondisk_t bnode;
ed3afcca 613 struct hammer_inode_data *idata;
d8935c61
MD
614 hammer_pseudofs_data_t pfsd;
615 struct buffer_info *data_buffer1 = NULL;
616 struct buffer_info *data_buffer2 = NULL;
8cd0a023 617 hammer_btree_elm_t elm;
7849afbc 618 u_int64_t xtime;
ed3afcca 619
47197d71 620 bnode = alloc_btree_element(&btree_off);
d8935c61
MD
621 idata = alloc_data_element(&data_off, sizeof(*idata), &data_buffer1);
622 pfsd = alloc_data_element(&pfsd_off, sizeof(*pfsd), &data_buffer2);
11ad5ade 623 create_tid = createtid();
7849afbc 624 xtime = nowtime();
9a8bf4a7
MD
625
626 /*
ed3afcca 627 * Populate the inode data and inode record for the root directory.
9a8bf4a7 628 */
ed3afcca
MD
629 idata->version = HAMMER_INODE_DATA_VERSION;
630 idata->mode = 0755;
7849afbc
MD
631 idata->ctime = xtime;
632 idata->mtime = xtime;
633 idata->atime = xtime;
11ad5ade
MD
634 idata->obj_type = HAMMER_OBJTYPE_DIRECTORY;
635 idata->size = 0;
636 idata->nlinks = 1;
47921633
MD
637 if (HammerVersion >= HAMMER_VOL_VERSION_TWO)
638 idata->cap_flags |= HAMMER_INODE_CAP_DIR_LOCAL_INO;
e523b881
MD
639 if (HammerVersion >= HAMMER_VOL_VERSION_SIX)
640 idata->cap_flags |= HAMMER_INODE_CAP_DIRHASH_ALG1;
9a8bf4a7 641
d8935c61 642 pfsd->sync_low_tid = 1;
ddc8e722 643 pfsd->sync_beg_tid = 0;
d8935c61
MD
644 pfsd->sync_end_tid = 0; /* overriden by vol0_next_tid on pfs0 */
645 pfsd->shared_uuid = Hammer_FSId;
646 pfsd->unique_uuid = Hammer_FSId;
dfd94fe0 647 pfsd->reserved01 = 0;
d8935c61
MD
648 pfsd->mirror_flags = 0;
649 snprintf(pfsd->label, sizeof(pfsd->label), "%s", label);
650
9a8bf4a7 651 /*
620822d2
MD
652 * Create the root of the B-Tree. The root is a leaf node so we
653 * do not have to worry about boundary elements.
9a8bf4a7 654 */
52bae4a6 655 bnode->signature = HAMMER_BTREE_SIGNATURE_GOOD;
d8935c61 656 bnode->count = 2;
8cd0a023 657 bnode->type = HAMMER_BTREE_TYPE_LEAF;
620822d2 658
8cd0a023 659 elm = &bnode->elms[0];
11ad5ade 660 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
58c17893 661 elm->leaf.base.localization = HAMMER_LOCALIZE_INODE;
11ad5ade
MD
662 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
663 elm->leaf.base.key = 0;
664 elm->leaf.base.create_tid = create_tid;
665 elm->leaf.base.delete_tid = 0;
666 elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE;
667 elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
1d9f6aa1 668 elm->leaf.create_ts = (u_int32_t)time(NULL);
11ad5ade 669
11ad5ade
MD
670 elm->leaf.data_offset = data_off;
671 elm->leaf.data_len = sizeof(*idata);
7849afbc 672 elm->leaf.data_crc = crc32(idata, HAMMER_INODE_CRCSIZE);
11ad5ade 673
d8935c61
MD
674 elm = &bnode->elms[1];
675 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
676 elm->leaf.base.localization = HAMMER_LOCALIZE_MISC;
677 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
d9b1ce90 678 elm->leaf.base.key = 0;
d8935c61
MD
679 elm->leaf.base.create_tid = create_tid;
680 elm->leaf.base.delete_tid = 0;
d9b1ce90 681 elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS;
d8935c61
MD
682 elm->leaf.base.obj_type = 0;
683 elm->leaf.create_ts = (u_int32_t)time(NULL);
684
685 elm->leaf.data_offset = pfsd_off;
686 elm->leaf.data_len = sizeof(*pfsd);
687 elm->leaf.data_crc = crc32(pfsd, sizeof(*pfsd));
688
11ad5ade 689 bnode->crc = crc32(&bnode->crc + 1, HAMMER_BTREE_CRCSIZE);
52bae4a6 690
47197d71 691 return(btree_off);
9a8bf4a7 692}