MFC: following 2 commits:
[dragonfly.git] / sbin / newfs_hammer / newfs_hammer.c
blobffdf1c55e624c3541034f32a101768996d11dbc6
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:
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.
34 * $DragonFly: src/sbin/newfs_hammer/newfs_hammer.c,v 1.39 2008/07/09 20:48:33 dillon Exp $
37 #include "newfs_hammer.h"
39 static int64_t getsize(const char *str, int64_t minval, int64_t maxval, int pw);
40 static const char *sizetostr(off_t size);
41 static void check_volume(struct volume_info *vol);
42 static void format_volume(struct volume_info *vol, int nvols,const char *label,
43 off_t total_size);
44 static hammer_off_t format_root(const char *label);
45 static u_int64_t nowtime(void);
46 static void usage(void);
48 int
49 main(int ac, char **av)
51 u_int32_t status;
52 off_t total;
53 int ch;
54 int i;
55 const char *label = NULL;
56 struct volume_info *vol;
57 char *fsidstr;
60 * Sanity check basic filesystem structures. No cookies for us
61 * if it gets broken!
63 assert(sizeof(struct hammer_volume_ondisk) <= HAMMER_BUFSIZE);
64 assert(sizeof(struct hammer_blockmap_layer1) == 32);
65 assert(sizeof(struct hammer_blockmap_layer2) == 16);
68 * Generate a filesysem id and lookup the filesystem type
70 uuidgen(&Hammer_FSId, 1);
71 uuid_name_lookup(&Hammer_FSType, "DragonFly HAMMER", &status);
72 if (status != uuid_s_ok) {
73 errx(1, "uuids file does not have the DragonFly "
74 "HAMMER filesystem type");
78 * Parse arguments
80 while ((ch = getopt(ac, av, "L:b:m:u:")) != -1) {
81 switch(ch) {
82 case 'L':
83 label = optarg;
84 break;
85 case 'b':
86 BootAreaSize = getsize(optarg,
87 HAMMER_BUFSIZE,
88 HAMMER_BOOT_MAXBYTES, 2);
89 break;
90 case 'm':
91 MemAreaSize = getsize(optarg,
92 HAMMER_BUFSIZE,
93 HAMMER_MEM_MAXBYTES, 2);
94 break;
95 case 'u':
96 UndoBufferSize = getsize(optarg,
97 HAMMER_LARGEBLOCK_SIZE,
98 HAMMER_LARGEBLOCK_SIZE *
99 HAMMER_UNDO_LAYER2, 2);
100 break;
101 default:
102 usage();
103 break;
107 if (label == NULL) {
108 fprintf(stderr,
109 "newfs_hammer: A filesystem label must be specified\n");
110 exit(1);
114 * Collect volume information
116 ac -= optind;
117 av += optind;
118 NumVolumes = ac;
119 RootVolNo = 0;
121 if (NumVolumes == 0) {
122 fprintf(stderr,
123 "newfs_hammer: You must specify at least one volume\n");
124 exit(1);
127 total = 0;
128 for (i = 0; i < NumVolumes; ++i) {
129 vol = setup_volume(i, av[i], 1, O_RDWR);
132 * Load up information on the volume and initialize
133 * its remaining fields.
135 check_volume(vol);
136 total += vol->size;
140 * Calculate defaults for the boot and memory area sizes.
142 if (BootAreaSize == 0) {
143 BootAreaSize = HAMMER_BOOT_NOMBYTES;
144 while (BootAreaSize > total / NumVolumes / 256)
145 BootAreaSize >>= 1;
146 if (BootAreaSize < HAMMER_BOOT_MINBYTES)
147 BootAreaSize = 0;
148 } else if (BootAreaSize < HAMMER_BOOT_MINBYTES) {
149 BootAreaSize = HAMMER_BOOT_MINBYTES;
151 if (MemAreaSize == 0) {
152 MemAreaSize = HAMMER_MEM_NOMBYTES;
153 while (MemAreaSize > total / NumVolumes / 256)
154 MemAreaSize >>= 1;
155 if (MemAreaSize < HAMMER_MEM_MINBYTES)
156 MemAreaSize = 0;
157 } else if (MemAreaSize < HAMMER_MEM_MINBYTES) {
158 MemAreaSize = HAMMER_MEM_MINBYTES;
162 * Format the volumes. Format the root volume first so we can
163 * bootstrap the freemap.
165 format_volume(get_volume(RootVolNo), NumVolumes, label, total);
166 for (i = 0; i < NumVolumes; ++i) {
167 if (i != RootVolNo)
168 format_volume(get_volume(i), NumVolumes, label, total);
172 * Pre-size the blockmap layer1/layer2 infrastructure to the zone
173 * limit. If we do this the filesystem does not have to allocate
174 * new layer2 blocks which reduces the chances of the reblocker
175 * having to fallback to an extremely inefficient algorithm.
177 vol = get_volume(RootVolNo);
178 vol->ondisk->vol0_stat_bigblocks = vol->ondisk->vol0_stat_freebigblocks;
179 vol->cache.modified = 1;
180 uuid_to_string(&Hammer_FSId, &fsidstr, &status);
182 printf("---------------------------------------------\n");
183 printf("%d volume%s total size %s\n",
184 NumVolumes, (NumVolumes == 1 ? "" : "s"), sizetostr(total));
185 printf("boot-area-size: %s\n", sizetostr(BootAreaSize));
186 printf("memory-log-size: %s\n", sizetostr(MemAreaSize));
187 printf("undo-buffer-size: %s\n", sizetostr(UndoBufferSize));
188 printf("total-pre-allocated: %s\n",
189 sizetostr(vol->vol_free_off & HAMMER_OFF_SHORT_MASK));
190 printf("fsid: %s\n", fsidstr);
191 printf("\n");
193 flush_all_volumes();
194 return(0);
197 static
198 void
199 usage(void)
201 fprintf(stderr, "newfs_hammer vol0 [vol1 ...]\n");
202 exit(1);
206 * Convert the size in bytes to a human readable string.
208 static
209 const char *
210 sizetostr(off_t size)
212 static char buf[32];
214 if (size < 1024 / 2) {
215 snprintf(buf, sizeof(buf), "%6.2f", (double)size);
216 } else if (size < 1024 * 1024 / 2) {
217 snprintf(buf, sizeof(buf), "%6.2fKB",
218 (double)size / 1024);
219 } else if (size < 1024 * 1024 * 1024LL / 2) {
220 snprintf(buf, sizeof(buf), "%6.2fMB",
221 (double)size / (1024 * 1024));
222 } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) {
223 snprintf(buf, sizeof(buf), "%6.2fGB",
224 (double)size / (1024 * 1024 * 1024LL));
225 } else {
226 snprintf(buf, sizeof(buf), "%6.2fTB",
227 (double)size / (1024 * 1024 * 1024LL * 1024LL));
229 return(buf);
233 * Convert a string to a 64 bit signed integer with various requirements.
235 static int64_t
236 getsize(const char *str, int64_t minval, int64_t maxval, int powerof2)
238 int64_t val;
239 char *ptr;
241 val = strtoll(str, &ptr, 0);
242 switch(*ptr) {
243 case 't':
244 case 'T':
245 val *= 1024;
246 /* fall through */
247 case 'g':
248 case 'G':
249 val *= 1024;
250 /* fall through */
251 case 'm':
252 case 'M':
253 val *= 1024;
254 /* fall through */
255 case 'k':
256 case 'K':
257 val *= 1024;
258 break;
259 default:
260 errx(1, "Unknown suffix in number '%s'\n", str);
261 /* not reached */
263 if (ptr[1]) {
264 errx(1, "Unknown suffix in number '%s'\n", str);
265 /* not reached */
267 if (val < minval) {
268 errx(1, "Value too small: %s, min is %s\n",
269 str, sizetostr(minval));
270 /* not reached */
272 if (val > maxval) {
273 errx(1, "Value too large: %s, max is %s\n",
274 str, sizetostr(maxval));
275 /* not reached */
277 if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) {
278 errx(1, "Value not power of 2: %s\n", str);
279 /* not reached */
281 if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) {
282 errx(1, "Value not an integral multiple of %dK: %s",
283 HAMMER_BUFSIZE / 1024, str);
284 /* not reached */
286 return(val);
290 * Generate a transaction id. Transaction ids are no longer time-based.
291 * Put the nail in the coffin by not making the first one time-based.
293 * We could start at 1 here but start at 2^32 to reserve a small domain for
294 * possible future use.
296 static hammer_tid_t
297 createtid(void)
299 static hammer_tid_t lasttid;
301 if (lasttid == 0)
302 lasttid = 0x0000000100000000ULL;
303 return(lasttid++);
306 static u_int64_t
307 nowtime(void)
309 struct timeval tv;
310 u_int64_t xtime;
312 gettimeofday(&tv, NULL);
313 xtime = tv.tv_sec * 1000000LL + tv.tv_usec;
314 return(xtime);
318 * Check basic volume characteristics. HAMMER filesystems use a minimum
319 * of a 16KB filesystem buffer size.
321 static
322 void
323 check_volume(struct volume_info *vol)
325 struct partinfo pinfo;
326 struct stat st;
329 * Get basic information about the volume
331 vol->fd = open(vol->name, O_RDWR);
332 if (vol->fd < 0)
333 err(1, "Unable to open %s R+W", vol->name);
334 if (ioctl(vol->fd, DIOCGPART, &pinfo) < 0) {
336 * Allow the formatting of regular filews as HAMMER volumes
338 if (fstat(vol->fd, &st) < 0)
339 err(1, "Unable to stat %s", vol->name);
340 vol->size = st.st_size;
341 vol->type = "REGFILE";
342 } else {
344 * When formatting a block device as a HAMMER volume the
345 * sector size must be compatible. HAMMER uses 16384 byte
346 * filesystem buffers.
348 if (pinfo.reserved_blocks) {
349 errx(1, "HAMMER cannot be placed in a partition "
350 "which overlaps the disklabel or MBR");
352 if (pinfo.media_blksize > 16384 ||
353 16384 % pinfo.media_blksize) {
354 errx(1, "A media sector size of %d is not supported",
355 pinfo.media_blksize);
358 vol->size = pinfo.media_size;
359 vol->type = "DEVICE";
361 printf("Volume %d %s %-15s size %s\n",
362 vol->vol_no, vol->type, vol->name,
363 sizetostr(vol->size));
366 * Reserve space for (future) header junk, setup our poor-man's
367 * bigblock allocator.
369 vol->vol_alloc = HAMMER_BUFSIZE * 16;
373 * Format a HAMMER volume. Cluster 0 will be initially placed in volume 0.
375 static
376 void
377 format_volume(struct volume_info *vol, int nvols, const char *label,
378 off_t total_size __unused)
380 struct volume_info *root_vol;
381 struct hammer_volume_ondisk *ondisk;
382 int64_t freeblks;
383 int i;
386 * Initialize basic information in the on-disk volume structure.
388 ondisk = vol->ondisk;
390 ondisk->vol_fsid = Hammer_FSId;
391 ondisk->vol_fstype = Hammer_FSType;
392 snprintf(ondisk->vol_name, sizeof(ondisk->vol_name), "%s", label);
393 ondisk->vol_no = vol->vol_no;
394 ondisk->vol_count = nvols;
395 ondisk->vol_version = 1;
397 ondisk->vol_bot_beg = vol->vol_alloc;
398 vol->vol_alloc += BootAreaSize;
399 ondisk->vol_mem_beg = vol->vol_alloc;
400 vol->vol_alloc += MemAreaSize;
403 * The remaining area is the zone 2 buffer allocation area. These
404 * buffers
406 ondisk->vol_buf_beg = vol->vol_alloc;
407 ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK;
409 if (ondisk->vol_buf_end < ondisk->vol_buf_beg) {
410 errx(1, "volume %d %s is too small to hold the volume header",
411 vol->vol_no, vol->name);
414 ondisk->vol_nblocks = (ondisk->vol_buf_end - ondisk->vol_buf_beg) /
415 HAMMER_BUFSIZE;
416 ondisk->vol_blocksize = HAMMER_BUFSIZE;
418 ondisk->vol_rootvol = RootVolNo;
419 ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
421 vol->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0);
422 vol->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, (ondisk->vol_buf_end - ondisk->vol_buf_beg) & ~HAMMER_LARGEBLOCK_MASK64);
425 * Format the root volume.
427 if (vol->vol_no == RootVolNo) {
429 * Starting TID
431 ondisk->vol0_next_tid = createtid();
433 format_freemap(vol,
434 &ondisk->vol0_blockmap[HAMMER_ZONE_FREEMAP_INDEX]);
436 freeblks = initialize_freemap(vol);
437 ondisk->vol0_stat_freebigblocks = freeblks;
439 for (i = 8; i < HAMMER_MAX_ZONES; ++i) {
440 format_blockmap(&ondisk->vol0_blockmap[i],
441 HAMMER_ZONE_ENCODE(i, 0));
443 format_undomap(ondisk);
445 ondisk->vol0_btree_root = format_root(label);
446 ++ondisk->vol0_stat_inodes; /* root inode */
447 } else {
448 freeblks = initialize_freemap(vol);
449 root_vol = get_volume(RootVolNo);
450 root_vol->cache.modified = 1;
451 root_vol->ondisk->vol0_stat_freebigblocks += freeblks;
452 root_vol->ondisk->vol0_stat_bigblocks += freeblks;
453 rel_volume(root_vol);
458 * Format the root directory.
460 static
461 hammer_off_t
462 format_root(const char *label)
464 hammer_off_t btree_off;
465 hammer_off_t pfsd_off;
466 hammer_off_t data_off;
467 hammer_tid_t create_tid;
468 hammer_node_ondisk_t bnode;
469 struct hammer_inode_data *idata;
470 hammer_pseudofs_data_t pfsd;
471 struct buffer_info *data_buffer1 = NULL;
472 struct buffer_info *data_buffer2 = NULL;
473 hammer_btree_elm_t elm;
474 u_int64_t xtime;
476 bnode = alloc_btree_element(&btree_off);
477 idata = alloc_data_element(&data_off, sizeof(*idata), &data_buffer1);
478 pfsd = alloc_data_element(&pfsd_off, sizeof(*pfsd), &data_buffer2);
479 create_tid = createtid();
480 xtime = nowtime();
483 * Populate the inode data and inode record for the root directory.
485 idata->version = HAMMER_INODE_DATA_VERSION;
486 idata->mode = 0755;
487 idata->ctime = xtime;
488 idata->mtime = xtime;
489 idata->atime = xtime;
490 idata->obj_type = HAMMER_OBJTYPE_DIRECTORY;
491 idata->size = 0;
492 idata->nlinks = 1;
494 pfsd->sync_low_tid = 1;
495 pfsd->sync_beg_tid = 0;
496 pfsd->sync_end_tid = 0; /* overriden by vol0_next_tid on pfs0 */
497 pfsd->shared_uuid = Hammer_FSId;
498 pfsd->unique_uuid = Hammer_FSId;
499 pfsd->master_id = 0;
500 pfsd->mirror_flags = 0;
501 snprintf(pfsd->label, sizeof(pfsd->label), "%s", label);
504 * Create the root of the B-Tree. The root is a leaf node so we
505 * do not have to worry about boundary elements.
507 bnode->signature = HAMMER_BTREE_SIGNATURE_GOOD;
508 bnode->count = 2;
509 bnode->type = HAMMER_BTREE_TYPE_LEAF;
511 elm = &bnode->elms[0];
512 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
513 elm->leaf.base.localization = HAMMER_LOCALIZE_INODE;
514 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
515 elm->leaf.base.key = 0;
516 elm->leaf.base.create_tid = create_tid;
517 elm->leaf.base.delete_tid = 0;
518 elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE;
519 elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
520 elm->leaf.create_ts = (u_int32_t)time(NULL);
522 elm->leaf.data_offset = data_off;
523 elm->leaf.data_len = sizeof(*idata);
524 elm->leaf.data_crc = crc32(idata, HAMMER_INODE_CRCSIZE);
526 elm = &bnode->elms[1];
527 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
528 elm->leaf.base.localization = HAMMER_LOCALIZE_MISC;
529 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
530 elm->leaf.base.key = 0;
531 elm->leaf.base.create_tid = create_tid;
532 elm->leaf.base.delete_tid = 0;
533 elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS;
534 elm->leaf.base.obj_type = 0;
535 elm->leaf.create_ts = (u_int32_t)time(NULL);
537 elm->leaf.data_offset = pfsd_off;
538 elm->leaf.data_len = sizeof(*pfsd);
539 elm->leaf.data_crc = crc32(pfsd, sizeof(*pfsd));
541 bnode->crc = crc32(&bnode->crc + 1, HAMMER_BTREE_CRCSIZE);
543 return(btree_off);