<sys/stat.h>: Clean up the POSIX namespace.
[dragonfly.git] / sbin / newfs_hammer / newfs_hammer.c
blob2c1c98f45ded5d261898927a54e6195dbc54ad80
1 /*
2 * Copyright (c) 2007 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
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.
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.
35 #include <sys/sysctl.h>
36 #include <sys/ioctl_compat.h>
38 #include "hammer_util.h"
40 static int64_t getsize(const char *str, int64_t minval, int64_t maxval, int pw);
41 static const char *sizetostr(off_t size);
42 static int trim_volume(struct volume_info *vol);
43 static void format_volume(struct volume_info *vol, int nvols,const char *label);
44 static hammer_off_t format_root_directory(const char *label);
45 static uint64_t nowtime(void);
46 static void usage(void);
48 static int ForceOpt;
49 static int64_t BootAreaSize;
50 static int64_t MemAreaSize;
51 static int64_t UndoBufferSize;
52 static int HammerVersion = -1;
54 #define GIG (1024LL*1024*1024)
56 int
57 main(int ac, char **av)
59 uint32_t status;
60 off_t total;
61 off_t avg_vol_size;
62 int ch;
63 int i;
64 int nvols;
65 int eflag = 0;
66 const char *label = NULL;
67 struct volume_info *vol;
68 char *fsidstr;
71 * Sanity check basic filesystem structures. No cookies for us
72 * if it gets broken!
74 assert(sizeof(struct hammer_volume_ondisk) <= HAMMER_BUFSIZE);
75 assert(sizeof(struct hammer_blockmap_layer1) == 32);
76 assert(sizeof(struct hammer_blockmap_layer2) == 16);
79 * Generate a filesystem id and lookup the filesystem type
81 uuidgen(&Hammer_FSId, 1);
82 uuid_name_lookup(&Hammer_FSType, "DragonFly HAMMER", &status);
83 if (status != uuid_s_ok) {
84 errx(1, "uuids file does not have the DragonFly "
85 "HAMMER filesystem type");
89 * Parse arguments
91 while ((ch = getopt(ac, av, "fEL:b:m:u:V:")) != -1) {
92 switch(ch) {
93 case 'f':
94 ForceOpt = 1;
95 break;
96 case 'E':
97 eflag = 1;
98 break;
99 case 'L':
100 label = optarg;
101 break;
102 case 'b':
103 BootAreaSize = getsize(optarg,
104 HAMMER_BUFSIZE,
105 HAMMER_BOOT_MAXBYTES, 2);
106 break;
107 case 'm':
108 MemAreaSize = getsize(optarg,
109 HAMMER_BUFSIZE,
110 HAMMER_MEM_MAXBYTES, 2);
111 break;
112 case 'u':
113 UndoBufferSize = getsize(optarg,
114 HAMMER_BIGBLOCK_SIZE,
115 HAMMER_BIGBLOCK_SIZE *
116 HAMMER_UNDO_LAYER2, 2);
117 if (UndoBufferSize < 500*1024*1024 && ForceOpt == 0)
118 errx(1, "The minimum UNDO/REDO FIFO size is "
119 "500MB\n");
120 if (UndoBufferSize < 500*1024*1024) {
121 fprintf(stderr,
122 "WARNING: you have specified an "
123 "UNDO/REDO FIFO size less than 500MB,\n"
124 "which may lead to VFS panics.\n");
126 break;
127 case 'V':
128 HammerVersion = strtol(optarg, NULL, 0);
129 if (HammerVersion < HAMMER_VOL_VERSION_MIN ||
130 HammerVersion >= HAMMER_VOL_VERSION_WIP) {
131 errx(1,
132 "I don't understand how to format "
133 "HAMMER version %d\n",
134 HammerVersion);
136 break;
137 default:
138 usage();
139 break;
143 if (label == NULL) {
144 fprintf(stderr,
145 "newfs_hammer: A filesystem label must be specified\n");
146 usage();
149 if (HammerVersion < 0) {
150 size_t olen = sizeof(HammerVersion);
151 HammerVersion = HAMMER_VOL_VERSION_DEFAULT;
152 if (sysctlbyname("vfs.hammer.supported_version",
153 &HammerVersion, &olen, NULL, 0) == 0) {
154 if (HammerVersion >= HAMMER_VOL_VERSION_WIP) {
155 HammerVersion = HAMMER_VOL_VERSION_WIP - 1;
156 fprintf(stderr,
157 "newfs_hammer: WARNING: HAMMER VFS "
158 "supports higher version than I "
159 "understand,\n"
160 "using version %d\n",
161 HammerVersion);
163 } else {
164 fprintf(stderr,
165 "newfs_hammer: WARNING: HAMMER VFS not "
166 "loaded, cannot get version info.\n"
167 "Using version %d\n",
168 HAMMER_VOL_VERSION_DEFAULT);
173 * Collect volume information
175 ac -= optind;
176 av += optind;
177 nvols = ac;
179 if (nvols == 0) {
180 fprintf(stderr,
181 "newfs_hammer: You must specify at least one "
182 "special file (volume)\n");
183 exit(1);
186 if (nvols > HAMMER_MAX_VOLUMES) {
187 fprintf(stderr,
188 "newfs_hammer: The maximum number of volumes is %d\n",
189 HAMMER_MAX_VOLUMES);
190 exit(1);
193 total = 0;
194 for (i = 0; i < nvols; ++i) {
195 vol = init_volume(i, av[i], O_RDWR);
198 * Load up information on the volume and initialize
199 * its remaining fields.
201 check_volume(vol);
202 printf("Volume %d %s %-15s size %s\n",
203 vol->vol_no, vol->type, vol->name,
204 sizetostr(vol->size));
206 if (eflag) {
207 int res = trim_volume(vol);
208 if (res == -1 || (res == 1 && ForceOpt == 0))
209 exit(1);
211 total += vol->size;
215 * Calculate defaults for the boot and memory area sizes.
217 avg_vol_size = total / nvols;
218 BootAreaSize = init_boot_area_size(BootAreaSize, avg_vol_size);
219 MemAreaSize = init_mem_area_size(MemAreaSize, avg_vol_size);
222 * Format the volumes. Format the root volume first so we can
223 * bootstrap the freemap.
225 format_volume(get_root_volume(), nvols, label);
226 for (i = 0; i < nvols; ++i) {
227 if (i != HAMMER_ROOT_VOLNO)
228 format_volume(get_volume(i), nvols, label);
232 * Print information stored in the root volume header.
234 vol = get_root_volume();
235 uuid_to_string(&Hammer_FSId, &fsidstr, &status);
237 printf("---------------------------------------------\n");
238 printf("%d volume%s total size %s version %d\n",
239 nvols, (nvols == 1 ? "" : "s"),
240 sizetostr(total), HammerVersion);
241 printf("root-volume: %s\n", vol->name);
242 printf("boot-area-size: %s\n", sizetostr(BootAreaSize));
243 printf("memory-log-size: %s\n", sizetostr(MemAreaSize));
244 printf("undo-buffer-size: %s\n", sizetostr(UndoBufferSize));
245 printf("total-pre-allocated: %s\n",
246 sizetostr(vol->vol_free_off & HAMMER_OFF_SHORT_MASK));
247 printf("fsid: %s\n", fsidstr);
248 printf("\n");
249 printf("NOTE: Please remember that you may have to manually set up a\n"
250 "cron(8) job to prune and reblock the filesystem regularly.\n"
251 "By default, the system automatically runs 'hammer cleanup'\n"
252 "on a nightly basis. The periodic.conf(5) variable\n"
253 "'daily_clean_hammer_enable' can be unset to disable this.\n"
254 "Also see 'man hammer' and 'man HAMMER' for more information.\n");
255 if (total < 10*GIG) {
256 printf("\nWARNING: The minimum UNDO/REDO FIFO is 500MB, you "
257 "really should not\n"
258 "try to format a HAMMER filesystem this small.\n");
260 if (total < 50*GIG) {
261 printf("\nWARNING: HAMMER filesystems less than 50GB are "
262 "not recommended!\n"
263 "You may have to run 'hammer prune-everything' and "
264 "'hammer reblock'\n"
265 "quite often, even if using a nohistory mount.\n");
267 flush_all_volumes();
268 return(0);
271 static
272 void
273 usage(void)
275 fprintf(stderr,
276 "usage: newfs_hammer -L label [-Ef] [-b bootsize] [-m savesize] [-u undosize]\n"
277 " [-V version] special ...\n"
279 exit(1);
283 * Convert the size in bytes to a human readable string.
285 static
286 const char *
287 sizetostr(off_t size)
289 static char buf[32];
291 if (size < 1024 / 2) {
292 snprintf(buf, sizeof(buf), "%6.2f", (double)size);
293 } else if (size < 1024 * 1024 / 2) {
294 snprintf(buf, sizeof(buf), "%6.2fKB",
295 (double)size / 1024);
296 } else if (size < 1024 * 1024 * 1024LL / 2) {
297 snprintf(buf, sizeof(buf), "%6.2fMB",
298 (double)size / (1024 * 1024));
299 } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) {
300 snprintf(buf, sizeof(buf), "%6.2fGB",
301 (double)size / (1024 * 1024 * 1024LL));
302 } else {
303 snprintf(buf, sizeof(buf), "%6.2fTB",
304 (double)size / (1024 * 1024 * 1024LL * 1024LL));
306 return(buf);
310 * Convert a string to a 64 bit signed integer with various requirements.
312 static int64_t
313 getsize(const char *str, int64_t minval, int64_t maxval, int powerof2)
315 int64_t val;
316 char *ptr;
318 val = strtoll(str, &ptr, 0);
319 switch(*ptr) {
320 case 't':
321 case 'T':
322 val *= 1024;
323 /* fall through */
324 case 'g':
325 case 'G':
326 val *= 1024;
327 /* fall through */
328 case 'm':
329 case 'M':
330 val *= 1024;
331 /* fall through */
332 case 'k':
333 case 'K':
334 val *= 1024;
335 break;
336 default:
337 errx(1, "Unknown suffix in number '%s'\n", str);
338 /* not reached */
340 if (ptr[1]) {
341 errx(1, "Unknown suffix in number '%s'\n", str);
342 /* not reached */
344 if (val < minval) {
345 errx(1, "Value too small: %s, min is %s\n",
346 str, sizetostr(minval));
347 /* not reached */
349 if (val > maxval) {
350 errx(1, "Value too large: %s, max is %s\n",
351 str, sizetostr(maxval));
352 /* not reached */
354 if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) {
355 errx(1, "Value not power of 2: %s\n", str);
356 /* not reached */
358 if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) {
359 errx(1, "Value not an integral multiple of %dK: %s",
360 HAMMER_BUFSIZE / 1024, str);
361 /* not reached */
363 return(val);
367 * Generate a transaction id. Transaction ids are no longer time-based.
368 * Put the nail in the coffin by not making the first one time-based.
370 * We could start at 1 here but start at 2^32 to reserve a small domain for
371 * possible future use.
373 static hammer_tid_t
374 createtid(void)
376 static hammer_tid_t lasttid;
378 if (lasttid == 0)
379 lasttid = 0x0000000100000000ULL;
380 return(lasttid++);
383 static uint64_t
384 nowtime(void)
386 struct timeval tv;
387 uint64_t xtime;
389 gettimeofday(&tv, NULL);
390 xtime = tv.tv_sec * 1000000LL + tv.tv_usec;
391 return(xtime);
395 * TRIM the volume, but only if the backing store is a DEVICE
397 static
399 trim_volume(struct volume_info *vol)
401 size_t olen;
402 char *dev_name, *p;
403 char sysctl_name[64];
404 int trim_enabled;
405 off_t ioarg[2];
407 if (strcmp(vol->type, "DEVICE")) {
408 fprintf(stderr, "Cannot TRIM regular file %s\n", vol->name);
409 return(1);
411 if (strncmp(vol->name, "/dev/da", 7)) {
412 fprintf(stderr, "%s does not support the TRIM command\n",
413 vol->name);
414 return(1);
417 /* Extract a number from /dev/da?s? */
418 dev_name = strdup(vol->name);
419 p = strtok(dev_name + strlen("/dev/da"), "s");
420 sprintf(sysctl_name, "kern.cam.da.%s.trim_enabled", p);
421 free(dev_name);
423 trim_enabled = 0;
424 olen = sizeof(trim_enabled);
426 if (sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0) == -1) {
427 fprintf(stderr, "%s (%s) does not support the TRIM command\n",
428 vol->name, sysctl_name);
429 return(1);
431 if (!trim_enabled) {
432 fprintf(stderr, "Erase device option selected, but sysctl (%s) "
433 "is not enabled\n", sysctl_name);
434 return(1);
437 ioarg[0] = vol->device_offset;
438 ioarg[1] = vol->size;
440 printf("Trimming %s %s, sectors %llu-%llu\n",
441 vol->type, vol->name,
442 (unsigned long long)ioarg[0] / 512,
443 (unsigned long long)ioarg[1] / 512);
445 if (ioctl(vol->fd, IOCTLTRIM, ioarg) == -1) {
446 fprintf(stderr, "Device trim failed\n");
447 return(-1);
450 return(0);
454 * Format a HAMMER volume.
456 static
457 void
458 format_volume(struct volume_info *vol, int nvols, const char *label)
460 struct volume_info *root_vol;
461 struct hammer_volume_ondisk *ondisk;
462 int64_t freeblks;
463 int64_t freebytes;
464 int64_t vol_buf_size;
465 hammer_off_t vol_alloc;
466 int i;
469 * Initialize basic information in the on-disk volume structure.
471 ondisk = vol->ondisk;
473 ondisk->vol_fsid = Hammer_FSId;
474 ondisk->vol_fstype = Hammer_FSType;
475 snprintf(ondisk->vol_label, sizeof(ondisk->vol_label), "%s", label);
476 ondisk->vol_no = vol->vol_no;
477 ondisk->vol_count = nvols;
478 ondisk->vol_version = HammerVersion;
481 * Reserve space for (future) header junk, setup our poor-man's
482 * big-block allocator.
484 vol_alloc = HAMMER_VOL_ALLOC;
486 ondisk->vol_bot_beg = vol_alloc;
487 vol_alloc += BootAreaSize;
488 ondisk->vol_mem_beg = vol_alloc;
489 vol_alloc += MemAreaSize;
492 * The remaining area is the zone 2 buffer allocation area.
494 ondisk->vol_buf_beg = vol_alloc;
495 ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK;
496 vol_buf_size = ondisk->vol_buf_end - ondisk->vol_buf_beg;
498 if (vol_buf_size < 0) {
499 errx(1, "volume %d %s is too small to hold the volume header",
500 vol->vol_no, vol->name);
503 if ((vol_buf_size & ~HAMMER_OFF_SHORT_MASK) != 0) {
504 errx(1, "volume %d %s is too large", vol->vol_no, vol->name);
507 ondisk->vol_rootvol = HAMMER_ROOT_VOLNO;
508 ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
510 vol->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0);
511 vol->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no,
512 vol_buf_size & ~HAMMER_BIGBLOCK_MASK64);
515 * Format the root volume.
517 if (vol->vol_no == HAMMER_ROOT_VOLNO) {
519 * Check freemap counts before formatting
521 freeblks = count_freemap(vol);
522 freebytes = freeblks * HAMMER_BIGBLOCK_SIZE64;
523 if (freebytes < 10*GIG && ForceOpt == 0) {
524 errx(1, "Cannot create a HAMMER filesystem less than 10GB "
525 "unless you use -f\n(for the size of Volume %d). "
526 "HAMMER filesystems less than 50GB are not "
527 "recommended.\n", HAMMER_ROOT_VOLNO);
531 * Starting TID
533 ondisk->vol0_next_tid = createtid();
536 * Format freemap. vol0_stat_freebigblocks is
537 * the number of big-blocks available for anything
538 * other than freemap zone at this point.
540 format_freemap(vol);
541 assert(ondisk->vol0_stat_freebigblocks == 0);
542 ondisk->vol0_stat_freebigblocks = initialize_freemap(vol);
545 * Format zones that are mapped to zone-2.
547 for (i = 0; i < HAMMER_MAX_ZONES; ++i) {
548 if (hammer_is_zone2_mapped_index(i))
549 format_blockmap(vol, i, 0);
553 * Format undo zone. Formatting decrements
554 * vol0_stat_freebigblocks whenever a new big-block
555 * is allocated for undo zone.
557 format_undomap(vol, &UndoBufferSize);
558 assert(ondisk->vol0_stat_bigblocks == 0);
559 ondisk->vol0_stat_bigblocks = ondisk->vol0_stat_freebigblocks;
562 * Format the root directory. Formatting decrements
563 * vol0_stat_freebigblocks whenever a new big-block
564 * is allocated for required zones.
566 ondisk->vol0_btree_root = format_root_directory(label);
567 ++ondisk->vol0_stat_inodes; /* root inode */
569 rel_volume(vol);
570 } else {
571 freeblks = initialize_freemap(vol);
572 root_vol = get_root_volume();
573 root_vol->ondisk->vol0_stat_freebigblocks += freeblks;
574 root_vol->ondisk->vol0_stat_bigblocks += freeblks;
575 rel_volume(root_vol);
580 * Format the root directory.
582 static
583 hammer_off_t
584 format_root_directory(const char *label)
586 hammer_off_t btree_off;
587 hammer_off_t pfsd_off;
588 hammer_off_t data_off;
589 hammer_tid_t create_tid;
590 hammer_node_ondisk_t bnode;
591 struct hammer_inode_data *idata;
592 hammer_pseudofs_data_t pfsd;
593 struct buffer_info *data_buffer0 = NULL;
594 struct buffer_info *data_buffer1 = NULL;
595 struct buffer_info *data_buffer2 = NULL;
596 hammer_btree_elm_t elm;
597 uint64_t xtime;
600 * Allocate zero-filled root btree node, inode and pfs
602 bnode = alloc_btree_element(&btree_off, &data_buffer0);
603 idata = alloc_meta_element(&data_off, sizeof(*idata), &data_buffer1);
604 pfsd = alloc_meta_element(&pfsd_off, sizeof(*pfsd), &data_buffer2);
605 create_tid = createtid();
606 xtime = nowtime();
609 * Populate the inode data and inode record for the root directory.
611 idata->version = HAMMER_INODE_DATA_VERSION;
612 idata->mode = 0755;
613 idata->ctime = xtime;
614 idata->mtime = xtime;
615 idata->atime = xtime;
616 idata->obj_type = HAMMER_OBJTYPE_DIRECTORY;
617 idata->size = 0;
618 idata->nlinks = 1;
619 if (HammerVersion >= HAMMER_VOL_VERSION_TWO)
620 idata->cap_flags |= HAMMER_INODE_CAP_DIR_LOCAL_INO;
621 if (HammerVersion >= HAMMER_VOL_VERSION_SIX)
622 idata->cap_flags |= HAMMER_INODE_CAP_DIRHASH_ALG1;
625 * Populate the PFS data for the root PFS.
627 pfsd->sync_low_tid = 1;
628 pfsd->sync_beg_tid = 0;
629 pfsd->sync_end_tid = 0; /* overriden by vol0_next_tid on pfs0 */
630 pfsd->shared_uuid = Hammer_FSId;
631 pfsd->unique_uuid = Hammer_FSId;
632 pfsd->reserved01 = 0;
633 pfsd->mirror_flags = 0;
634 snprintf(pfsd->label, sizeof(pfsd->label), "%s", label);
637 * Create the root of the B-Tree. The root is a leaf node so we
638 * do not have to worry about boundary elements.
640 bnode->count = 2;
641 bnode->type = HAMMER_BTREE_TYPE_LEAF;
644 * Create the first node element for the inode.
646 elm = &bnode->elms[0];
647 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
648 elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION |
649 HAMMER_LOCALIZE_INODE;
650 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
651 elm->leaf.base.key = 0;
652 elm->leaf.base.create_tid = create_tid;
653 elm->leaf.base.delete_tid = 0;
654 elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE;
655 elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
656 elm->leaf.create_ts = (uint32_t)time(NULL);
658 elm->leaf.data_offset = data_off;
659 elm->leaf.data_len = sizeof(*idata);
660 elm->leaf.data_crc = crc32(idata, HAMMER_INODE_CRCSIZE);
663 * Create the second node element for the PFS data.
665 elm = &bnode->elms[1];
666 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
667 elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION |
668 HAMMER_LOCALIZE_MISC;
669 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
670 elm->leaf.base.key = 0;
671 elm->leaf.base.create_tid = create_tid;
672 elm->leaf.base.delete_tid = 0;
673 elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS;
674 elm->leaf.base.obj_type = 0;
675 elm->leaf.create_ts = (uint32_t)time(NULL);
677 elm->leaf.data_offset = pfsd_off;
678 elm->leaf.data_len = sizeof(*pfsd);
679 elm->leaf.data_crc = crc32(pfsd, sizeof(*pfsd));
681 bnode->crc = crc32(&bnode->crc + 1, HAMMER_BTREE_CRCSIZE);
683 rel_buffer(data_buffer0);
684 rel_buffer(data_buffer1);
685 rel_buffer(data_buffer2);
687 return(btree_off);