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  * $DragonFly: src/sbin/newfs_hammer/newfs_hammer.c,v 1.38 2008/07/07 03:51:29 dillon Exp $
35  */
36 
37 #include "newfs_hammer.h"
38 
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);
47 
48 int
49 main(int ac, char **av)
50 {
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;
58 
59 	/*
60 	 * Sanity check basic filesystem structures.  No cookies for us
61 	 * if it gets broken!
62 	 */
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);
66 
67 	/*
68 	 * Generate a filesysem id and lookup the filesystem type
69 	 */
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");
75 	}
76 
77 	/*
78 	 * Parse arguments
79 	 */
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;
104 		}
105 	}
106 
107 	if (label == NULL) {
108 		fprintf(stderr,
109 			"newfs_hammer: A filesystem label must be specified\n");
110 		exit(1);
111 	}
112 
113 	/*
114 	 * Collect volume information
115 	 */
116 	ac -= optind;
117 	av += optind;
118 	NumVolumes = ac;
119 	RootVolNo = 0;
120 
121         if (NumVolumes == 0) {
122                 fprintf(stderr,
123                         "newfs_hammer: You must specify at least one volume\n");
124                 exit(1);
125         }
126 
127 	total = 0;
128 	for (i = 0; i < NumVolumes; ++i) {
129 		vol = setup_volume(i, av[i], 1, O_RDWR);
130 
131 		/*
132 		 * Load up information on the volume and initialize
133 		 * its remaining fields.
134 		 */
135 		check_volume(vol);
136 		total += vol->size;
137 	}
138 
139 	/*
140 	 * Calculate defaults for the boot and memory area sizes.
141 	 */
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;
150 	}
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;
159 	}
160 
161 	/*
162 	 * Format the volumes.  Format the root volume first so we can
163 	 * bootstrap the freemap.
164 	 */
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);
169 	}
170 
171 	/*
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.
176 	 */
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);
181 
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");
192 
193 	flush_all_volumes();
194 	return(0);
195 }
196 
197 static
198 void
199 usage(void)
200 {
201 	fprintf(stderr, "newfs_hammer vol0 [vol1 ...]\n");
202 	exit(1);
203 }
204 
205 /*
206  * Convert the size in bytes to a human readable string.
207  */
208 static
209 const char *
210 sizetostr(off_t size)
211 {
212 	static char buf[32];
213 
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));
228 	}
229 	return(buf);
230 }
231 
232 /*
233  * Convert a string to a 64 bit signed integer with various requirements.
234  */
235 static int64_t
236 getsize(const char *str, int64_t minval, int64_t maxval, int powerof2)
237 {
238 	int64_t val;
239 	char *ptr;
240 
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 */
262 	}
263 	if (ptr[1]) {
264 		errx(1, "Unknown suffix in number '%s'\n", str);
265 		/* not reached */
266 	}
267 	if (val < minval) {
268 		errx(1, "Value too small: %s, min is %s\n",
269 		     str, sizetostr(minval));
270 		/* not reached */
271 	}
272 	if (val > maxval) {
273 		errx(1, "Value too large: %s, max is %s\n",
274 		     str, sizetostr(maxval));
275 		/* not reached */
276 	}
277 	if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) {
278 		errx(1, "Value not power of 2: %s\n", str);
279 		/* not reached */
280 	}
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 */
285 	}
286 	return(val);
287 }
288 
289 /*
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.
292  *
293  * We could start at 1 here but start at 2^32 to reserve a small domain for
294  * possible future use.
295  */
296 static hammer_tid_t
297 createtid(void)
298 {
299 	static hammer_tid_t lasttid;
300 
301 	if (lasttid == 0)
302 		lasttid = 0x0000000100000000ULL;
303 	return(lasttid++);
304 }
305 
306 static u_int64_t
307 nowtime(void)
308 {
309 	struct timeval tv;
310 	u_int64_t xtime;
311 
312 	gettimeofday(&tv, NULL);
313 	xtime = tv.tv_sec * 1000000LL + tv.tv_usec;
314 	return(xtime);
315 }
316 
317 /*
318  * Check basic volume characteristics.  HAMMER filesystems use a minimum
319  * of a 16KB filesystem buffer size.
320  */
321 static
322 void
323 check_volume(struct volume_info *vol)
324 {
325 	struct partinfo pinfo;
326 	struct stat st;
327 
328 	/*
329 	 * Get basic information about the volume
330 	 */
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) {
335 		/*
336 		 * Allow the formatting of regular filews as HAMMER volumes
337 		 */
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 {
343 		/*
344 		 * When formatting a block device as a HAMMER volume the
345 		 * sector size must be compatible.  HAMMER uses 16384 byte
346 		 * filesystem buffers.
347 		 */
348 		if (pinfo.reserved_blocks) {
349 			errx(1, "HAMMER cannot be placed in a partition "
350 				"which overlaps the disklabel or MBR");
351 		}
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);
356 		}
357 
358 		vol->size = pinfo.media_size;
359 		vol->type = "DEVICE";
360 	}
361 	printf("Volume %d %s %-15s size %s\n",
362 	       vol->vol_no, vol->type, vol->name,
363 	       sizetostr(vol->size));
364 
365 	/*
366 	 * Reserve space for (future) header junk, setup our poor-man's
367 	 * bigblock allocator.
368 	 */
369 	vol->vol_alloc = HAMMER_BUFSIZE * 16;
370 }
371 
372 /*
373  * Format a HAMMER volume.  Cluster 0 will be initially placed in volume 0.
374  */
375 static
376 void
377 format_volume(struct volume_info *vol, int nvols, const char *label,
378 	      off_t total_size __unused)
379 {
380 	struct volume_info *root_vol;
381 	struct hammer_volume_ondisk *ondisk;
382 	int64_t freeblks;
383 	int i;
384 
385 	/*
386 	 * Initialize basic information in the on-disk volume structure.
387 	 */
388 	ondisk = vol->ondisk;
389 
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;
396 
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;
401 
402 	/*
403 	 * The remaining area is the zone 2 buffer allocation area.  These
404 	 * buffers
405 	 */
406 	ondisk->vol_buf_beg = vol->vol_alloc;
407 	ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK;
408 
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);
412 	}
413 
414 	ondisk->vol_nblocks = (ondisk->vol_buf_end - ondisk->vol_buf_beg) /
415 			      HAMMER_BUFSIZE;
416 	ondisk->vol_blocksize = HAMMER_BUFSIZE;
417 
418 	ondisk->vol_rootvol = RootVolNo;
419 	ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
420 
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);
423 
424 	/*
425 	 * Format the root volume.
426 	 */
427 	if (vol->vol_no == RootVolNo) {
428 		/*
429 		 * Starting TID
430 		 */
431 		ondisk->vol0_next_tid = createtid();
432 
433 		format_freemap(vol,
434 			&ondisk->vol0_blockmap[HAMMER_ZONE_FREEMAP_INDEX]);
435 
436 		freeblks = initialize_freemap(vol);
437 		ondisk->vol0_stat_freebigblocks = freeblks;
438 
439 		for (i = 8; i < HAMMER_MAX_ZONES; ++i) {
440 			format_blockmap(&ondisk->vol0_blockmap[i],
441 					HAMMER_ZONE_ENCODE(i, 0));
442 		}
443 		format_undomap(ondisk);
444 
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);
454 	}
455 }
456 
457 /*
458  * Format the root directory.
459  */
460 static
461 hammer_off_t
462 format_root(const char *label)
463 {
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;
475 
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();
481 
482 	/*
483 	 * Populate the inode data and inode record for the root directory.
484 	 */
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;
493 
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);
502 
503 	/*
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.
506 	 */
507 	bnode->signature = HAMMER_BTREE_SIGNATURE_GOOD;
508 	bnode->count = 2;
509 	bnode->type = HAMMER_BTREE_TYPE_LEAF;
510 
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);
521 
522 	elm->leaf.data_offset = data_off;
523 	elm->leaf.data_len = sizeof(*idata);
524 	elm->leaf.data_crc = crc32(idata, HAMMER_INODE_CRCSIZE);
525 
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 = HAMMER_FIXKEY_PSEUDOFS;
531 	elm->leaf.base.create_tid = create_tid;
532 	elm->leaf.base.delete_tid = 0;
533 	elm->leaf.base.rec_type = HAMMER_RECTYPE_FIX;
534 	elm->leaf.base.obj_type = 0;
535 	elm->leaf.create_ts = (u_int32_t)time(NULL);
536 
537 	elm->leaf.data_offset = pfsd_off;
538 	elm->leaf.data_len = sizeof(*pfsd);
539 	elm->leaf.data_crc = crc32(pfsd, sizeof(*pfsd));
540 
541 	bnode->crc = crc32(&bnode->crc + 1, HAMMER_BTREE_CRCSIZE);
542 
543 	return(btree_off);
544 }
545 
546