xref: /dragonfly/usr.sbin/makefs/hammer2.c (revision a765cedf)
1 /*
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 2022 Tomohiro Kusumi <tkusumi@netbsd.org>
5  * Copyright (c) 2011-2022 The DragonFly Project.  All rights reserved.
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 #if HAVE_NBTOOL_CONFIG_H
36 #include "nbtool_config.h"
37 #endif
38 
39 #include <sys/param.h>
40 #include <sys/stat.h>
41 #include <sys/sysctl.h>
42 #include <sys/mman.h>
43 
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <stdbool.h>
47 #include <string.h>
48 #include <unistd.h>
49 #include <fcntl.h>
50 #include <time.h>
51 #include <err.h>
52 #include <assert.h>
53 #include <util.h>
54 
55 #include "makefs.h"
56 #include "hammer2.h"
57 
58 #define APRINTF(X, ...)	\
59     printf("%s: " X, __func__, ## __VA_ARGS__)
60 
61 static void hammer2_dump_fsinfo(fsinfo_t *);
62 static int hammer2_create_image(const char *, fsinfo_t *);
63 static int hammer2_populate_dir(struct m_vnode *, const char *, fsnode *,
64     fsnode *, fsinfo_t *, int);
65 static void hammer2_validate(const char *, fsnode *, fsinfo_t *);
66 static void hammer2_size_dir(fsnode *, fsinfo_t *);
67 static int hammer2_write_file(struct m_vnode *, const char *, fsnode *);
68 
69 void
70 hammer2_prep_opts(fsinfo_t *fsopts)
71 {
72 	hammer2_makefs_options_t *h2_opt = ecalloc(1, sizeof(*h2_opt));
73 	hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
74 
75 	const option_t hammer2_options[] = {
76 		/* newfs_hammer2(8) compatible options */
77 		{ 'b', "BootAreaSize", NULL, OPT_STRBUF, 0, 0, "boot area size" },
78 		{ 'r', "AuxAreaSize", NULL, OPT_STRBUF, 0, 0, "aux area size" },
79 		{ 'V', "Hammer2Version", NULL, OPT_STRBUF, 0, 0, "file system version" },
80 		{ 'L', "Label", NULL, OPT_STRBUF, 0, 0, "PFS label" },
81 		/* makefs(8) specific options */
82 		{ 'm', "MountLabel", NULL, OPT_STRBUF, 0, 0, "destination PFS label" },
83 		{ 'v', "NumVolhdr", &h2_opt->num_volhdr, OPT_INT32,
84 		    1, HAMMER2_NUM_VOLHDRS, "number of volume headers" },
85 		{ 'd', "Hammer2Debug", NULL, OPT_STRBUF, 0, 0, "debug tunable" },
86 		{ .name = NULL },
87 	};
88 
89 	hammer2_mkfs_init(opt);
90 
91 	/* make this tunable ? */
92 	assert(opt->CompType == HAMMER2_COMP_NEWFS_DEFAULT);
93 	assert(opt->CheckType == HAMMER2_CHECK_XXHASH64);
94 
95 	/* force debug mode for mkfs */
96 	opt->DebugOpt = 1;
97 
98 	fsopts->fs_specific = h2_opt;
99 	fsopts->fs_options = copy_opts(hammer2_options);
100 	fsopts->sectorsize = DEV_BSIZE;
101 }
102 
103 void
104 hammer2_cleanup_opts(fsinfo_t *fsopts)
105 {
106 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
107 	hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
108 
109 	hammer2_mkfs_cleanup(opt);
110 
111 	free(h2_opt);
112 	free(fsopts->fs_options);
113 }
114 
115 int
116 hammer2_parse_opts(const char *option, fsinfo_t *fsopts)
117 {
118 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
119 	hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
120 
121 	option_t *hammer2_options = fsopts->fs_options;
122 	char buf[1024]; /* > HAMMER2_INODE_MAXNAME */
123 	int i;
124 
125 	assert(option != NULL);
126 	assert(fsopts != NULL);
127 
128 	if (debug & DEBUG_FS_PARSE_OPTS)
129 		APRINTF("got `%s'\n", option);
130 
131 	i = set_option(hammer2_options, option, buf, sizeof(buf));
132 	if (i == -1)
133 		return 0;
134 
135 	if (hammer2_options[i].name == NULL)
136 		abort();
137 
138 	switch (hammer2_options[i].letter) {
139 	case 'b':
140 		opt->BootAreaSize = getsize(buf, HAMMER2_NEWFS_ALIGN,
141 		    HAMMER2_BOOT_MAX_BYTES, 2);
142 		break;
143 	case 'r':
144 		opt->AuxAreaSize = getsize(buf, HAMMER2_NEWFS_ALIGN,
145 		    HAMMER2_AUX_MAX_BYTES, 2);
146 		break;
147 	case 'V':
148 		opt->Hammer2Version = strtol(buf, NULL, 0);
149 		if (opt->Hammer2Version < HAMMER2_VOL_VERSION_MIN ||
150 		    opt->Hammer2Version >= HAMMER2_VOL_VERSION_WIP)
151 			errx(1, "I don't understand how to format "
152 			    "HAMMER2 version %d",
153 			    opt->Hammer2Version);
154 		break;
155 	case 'L':
156 		h2_opt->label_specified = 1;
157 		if (strcasecmp(buf, "none") == 0)
158 			break;
159 		if (opt->NLabels >= MAXLABELS)
160 			errx(1, "Limit of %d local labels", MAXLABELS - 1);
161 		if (strlen(buf) == 0)
162 			errx(1, "Volume label '%s' cannot be 0-length", buf);
163 		if (strlen(buf) >= HAMMER2_INODE_MAXNAME)
164 			errx(1, "Volume label '%s' is too long (%d chars max)",
165 			    buf, HAMMER2_INODE_MAXNAME - 1);
166 		opt->Label[opt->NLabels++] = strdup(buf);
167 		break;
168 	case 'm':
169 		if (strlen(buf) == 0)
170 			errx(1, "Volume label '%s' cannot be 0-length", buf);
171 		if (strlen(buf) >= HAMMER2_INODE_MAXNAME)
172 			errx(1, "Volume label '%s' is too long (%d chars max)",
173 			    buf, HAMMER2_INODE_MAXNAME - 1);
174 		strlcpy(h2_opt->mount_label, buf, sizeof(h2_opt->mount_label));
175 		break;
176 	case 'd':
177 		hammer2_debug = strtoll(buf, NULL, 0);
178 		break;
179 	default:
180 		break;
181 	}
182 
183 	return 1;
184 }
185 
186 void
187 hammer2_makefs(const char *image, const char *dir, fsnode *root,
188     fsinfo_t *fsopts)
189 {
190 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
191 	struct mount mp;
192 	struct hammer2_mount_info info;
193 	struct m_vnode devvp, *vroot;
194 	hammer2_inode_t *iroot;
195 	struct timeval start;
196 	int error;
197 
198 	assert(image != NULL);
199 	assert(dir != NULL);
200 	assert(root != NULL);
201 	assert(fsopts != NULL);
202 
203 	if (debug & DEBUG_FS_MAKEFS)
204 		APRINTF("image \"%s\" directory \"%s\" root %p\n",
205 		    image, dir, root);
206 
207 	/* validate tree and options */
208 	TIMER_START(start);
209 	hammer2_validate(dir, root, fsopts);
210 	TIMER_RESULTS(start, "hammer2_validate");
211 
212 	/* create image */
213 	TIMER_START(start);
214 	if (hammer2_create_image(image, fsopts) == -1)
215 		errx(1, "image file `%s' not created", image);
216 	TIMER_RESULTS(start, "hammer2_create_image");
217 
218 	if (debug & DEBUG_FS_MAKEFS)
219 		putchar('\n');
220 
221 	/* vfs init */
222 	error = hammer2_vfs_init();
223 	if (error)
224 		errx(1, "failed to vfs init, error %d", error);
225 
226 	/* mount image */
227 	memset(&devvp, 0, sizeof(devvp));
228 	devvp.fs = fsopts;
229 	memset(&mp, 0, sizeof(mp));
230 	memset(&info, 0, sizeof(info));
231 	error = hammer2_vfs_mount(&devvp, &mp, h2_opt->mount_label, &info);
232 	if (error)
233 		errx(1, "failed to mount, error %d", error);
234 	assert(mp.mnt_data);
235 
236 	/* get root vnode */
237 	vroot = NULL;
238 	error = hammer2_vfs_root(&mp, &vroot);
239 	if (error)
240 		errx(1, "failed to get root vnode, error %d", error);
241 	assert(vroot);
242 
243 	iroot = VTOI(vroot);
244 	assert(iroot);
245 	printf("root inode inum %ld, mode 0%o, refs %d\n",
246 	    iroot->meta.inum, iroot->meta.mode, iroot->refs);
247 
248 	/* populate image */
249 	printf("populating `%s'\n", image);
250 	TIMER_START(start);
251 	if (hammer2_populate_dir(vroot, dir, root, root, fsopts, 0))
252 		errx(1, "image file `%s' not populated", image);
253 	TIMER_RESULTS(start, "hammer2_populate_dir");
254 
255 	/* unmount image */
256 	error = hammer2_vfs_unmount(&mp, 0);
257 	if (error)
258 		errx(1, "failed to unmount, error %d", error);
259 
260 	/* check leaked resource */
261 	if (vnode_count)
262 		printf("XXX %ld vnode left\n", vnode_count);
263 	if (hammer2_chain_allocs)
264 		printf("XXX %ld chain left\n", hammer2_chain_allocs);
265 	bcleanup();
266 
267 	/* vfs uninit */
268 	error = hammer2_vfs_uninit();
269 	if (error)
270 		errx(1, "failed to vfs uninit, error %d", error);
271 
272 	if (close(fsopts->fd) == -1)
273 		err(1, "closing `%s'", image);
274 	fsopts->fd = -1;
275 
276 	printf("image `%s' complete\n", image);
277 }
278 
279 /* end of public functions */
280 
281 static hammer2_off_t
282 hammer2_image_size(fsinfo_t *fsopts)
283 {
284 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
285 	hammer2_off_t image_size, used_size = 0;
286 	int num_level1, delta_num_level1;
287 
288 	/* use 4 volume headers by default */
289 	num_level1 = h2_opt->num_volhdr * 2; /* default 4 x 2 */
290 	assert(num_level1 != 0);
291 	assert(num_level1 <= 8);
292 
293 	/* add 4MiB segment for each level1 */
294 	used_size += HAMMER2_ZONE_SEG64 * num_level1;
295 
296 	/* add boot/aux area, but exact size unknown at this point */
297 	used_size += HAMMER2_BOOT_NOM_BYTES + HAMMER2_AUX_NOM_BYTES;
298 
299 	/* add data size */
300 	used_size += fsopts->size;
301 
302 	/* XXX add extra level1 for meta data and indirect blocks */
303 	used_size += HAMMER2_FREEMAP_LEVEL1_SIZE;
304 
305 	/* XXX add extra level1 for safety */
306 	if (used_size > HAMMER2_FREEMAP_LEVEL1_SIZE * 10)
307 		used_size += HAMMER2_FREEMAP_LEVEL1_SIZE;
308 
309 	/* use 8GiB image size by default */
310 	image_size = HAMMER2_FREEMAP_LEVEL1_SIZE * num_level1;
311 	printf("trying default image size %s\n", sizetostr(image_size));
312 
313 	/* adjust if image size isn't large enough */
314 	if (used_size > image_size) {
315 		/* determine extra level1 needed */
316 		delta_num_level1 = howmany(used_size - image_size,
317 		    HAMMER2_FREEMAP_LEVEL1_SIZE);
318 
319 		/* adjust used size with 4MiB segment for each extra level1 */
320 		used_size += HAMMER2_ZONE_SEG64 * delta_num_level1;
321 
322 		/* adjust image size with extra level1 */
323 		image_size += HAMMER2_FREEMAP_LEVEL1_SIZE * delta_num_level1;
324 		printf("trying adjusted image size %s\n",
325 		    sizetostr(image_size));
326 
327 		if (used_size > image_size)
328 			errx(1, "invalid used_size %ld > image_size %ld",
329 			    used_size, image_size);
330 	}
331 
332 	return image_size;
333 }
334 
335 static const char *
336 hammer2_label_name(int label_type)
337 {
338 	switch (label_type) {
339 	case HAMMER2_LABEL_NONE:
340 		return "NONE";
341 	case HAMMER2_LABEL_BOOT:
342 		return "BOOT";
343 	case HAMMER2_LABEL_ROOT:
344 		return "ROOT";
345 	case HAMMER2_LABEL_DATA:
346 		return "DATA";
347 	default:
348 		assert(0);
349 	}
350 	return NULL;
351 }
352 
353 static void
354 hammer2_validate(const char *dir, fsnode *root, fsinfo_t *fsopts)
355 {
356 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
357 	hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
358 	hammer2_off_t image_size, minsize, maxsize;
359 	const char *s;
360 
361 	assert(dir != NULL);
362 	assert(root != NULL);
363 	assert(fsopts != NULL);
364 
365 	if (debug & DEBUG_FS_VALIDATE) {
366 		APRINTF("before defaults set:\n");
367 		hammer2_dump_fsinfo(fsopts);
368 	}
369 
370 	/* makefs only supports "DATA" for default PFS label */
371 	if (!h2_opt->label_specified) {
372 		opt->DefaultLabelType = HAMMER2_LABEL_DATA;
373 		s = hammer2_label_name(opt->DefaultLabelType);
374 		printf("using default label \"%s\"\n", s);
375 	}
376 
377 	/* set default mount PFS label */
378 	if (!strcmp(h2_opt->mount_label, "")) {
379 		s = hammer2_label_name(HAMMER2_LABEL_DATA);
380 		strlcpy(h2_opt->mount_label, s, sizeof(h2_opt->mount_label));
381 		printf("using default mount label \"%s\"\n", s);
382 	}
383 
384 	/* set default number of volume headers */
385 	if (!h2_opt->num_volhdr) {
386 		h2_opt->num_volhdr = HAMMER2_NUM_VOLHDRS;
387 		printf("using default %d volume headers\n", h2_opt->num_volhdr);
388 	}
389 
390 	/* calculate data size */
391 	if (fsopts->size != 0)
392 		fsopts->size = 0; /* shouldn't reach here to begin with */
393 	hammer2_size_dir(root, fsopts);
394 	printf("estimated data size %s from %lld inode\n",
395 	    sizetostr(fsopts->size), (long long)fsopts->inodes);
396 
397 	/* determine image size from data size */
398 	image_size = hammer2_image_size(fsopts);
399 	assert((image_size & HAMMER2_FREEMAP_LEVEL1_MASK) == 0);
400 
401 	minsize = roundup(fsopts->minsize, HAMMER2_FREEMAP_LEVEL1_SIZE);
402 	maxsize = roundup(fsopts->maxsize, HAMMER2_FREEMAP_LEVEL1_SIZE);
403 	if (image_size < minsize)
404 		image_size = minsize;
405 	else if (maxsize > 0 && image_size > maxsize)
406 		errx(1, "`%s' size of %ld is larger than the maxsize of %ld",
407 		    dir, image_size, maxsize);
408 
409 	assert((image_size & HAMMER2_FREEMAP_LEVEL1_MASK) == 0);
410 	h2_opt->image_size = image_size;
411 	printf("using %s image size\n", sizetostr(h2_opt->image_size));
412 
413 	if (debug & DEBUG_FS_VALIDATE) {
414 		APRINTF("after defaults set:\n");
415 		hammer2_dump_fsinfo(fsopts);
416 	}
417 }
418 
419 static void
420 hammer2_dump_fsinfo(fsinfo_t *fsopts)
421 {
422 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
423 	hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
424 	int i;
425 	char *s;
426 
427 	assert(fsopts != NULL);
428 
429 	APRINTF("fsinfo_t at %p\n", fsopts);
430 
431 	printf("\tinodes %lld\n", (long long)fsopts->inodes);
432 	printf("\tsize %lld, minsize %lld, maxsize %lld\n",
433 	    (long long)fsopts->size,
434 	    (long long)fsopts->minsize,
435 	    (long long)fsopts->maxsize);
436 
437 	printf("\thammer2_debug 0x%x\n", hammer2_debug);
438 
439 	printf("\tlabel_specified %d\n", h2_opt->label_specified);
440 	printf("\tmount_label \"%s\"\n", h2_opt->mount_label);
441 	printf("\tnum_volhdr %d\n", h2_opt->num_volhdr);
442 	printf("\timage_size 0x%lx\n", h2_opt->image_size);
443 
444 	printf("\tHammer2Version %d\n", opt->Hammer2Version);
445 	printf("\tBootAreaSize 0x%jx\n", opt->BootAreaSize);
446 	printf("\tAuxAreaSize 0x%jx\n", opt->AuxAreaSize);
447 	printf("\tNLabels %d\n", opt->NLabels);
448 	printf("\tCompType %d\n", opt->CompType);
449 	printf("\tCheckType %d\n", opt->CheckType);
450 	printf("\tDefaultLabelType %d\n", opt->DefaultLabelType);
451 	printf("\tDebugOpt %d\n", opt->DebugOpt);
452 
453 	s = NULL;
454 	hammer2_uuid_to_str(&opt->Hammer2_FSType, &s);
455 	printf("\tHammer2_FSType \"%s\"\n", s);
456 	s = NULL;
457 	hammer2_uuid_to_str(&opt->Hammer2_VolFSID, &s);
458 	printf("\tHammer2_VolFSID \"%s\"\n", s);
459 	s = NULL;
460 	hammer2_uuid_to_str(&opt->Hammer2_SupCLID, &s);
461 	printf("\tHammer2_SupCLID \"%s\"\n", s);
462 	s = NULL;
463 	hammer2_uuid_to_str(&opt->Hammer2_SupFSID, &s);
464 	printf("\tHammer2_SupFSID \"%s\"\n", s);
465 
466 	for (i = 0; i < opt->NLabels; i++) {
467 		printf("\tLabel[%d] \"%s\"\n", i, opt->Label[i]);
468 		s = NULL;
469 		hammer2_uuid_to_str(&opt->Hammer2_PfsCLID[i], &s);
470 		printf("\t Hammer2_PfsCLID[%d] \"%s\"\n", i, s);
471 		s = NULL;
472 		hammer2_uuid_to_str(&opt->Hammer2_PfsFSID[i], &s);
473 		printf("\t Hammer2_PfsFSID[%d] \"%s\"\n", i, s);
474 	}
475 
476 	free(s);
477 }
478 
479 static int
480 hammer2_create_image(const char *image, fsinfo_t *fsopts)
481 {
482 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
483 	hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
484 	char *av[] = { (char *)image, }; /* XXX support multi-volumes */
485 	char *buf;
486 	int i, bufsize, oflags;
487 	off_t bufrem;
488 
489 	assert(image != NULL);
490 	assert(fsopts != NULL);
491 
492 	/* create image */
493 	oflags = O_RDWR | O_CREAT;
494 	if (fsopts->offset == 0)
495 		oflags |= O_TRUNC;
496 	if ((fsopts->fd = open(image, oflags, 0666)) == -1) {
497 		warn("can't open `%s' for writing", image);
498 		return -1;
499 	}
500 
501 	/* zero image */
502 	bufsize = HAMMER2_PBUFSIZE;
503 	bufrem = h2_opt->image_size;
504 	if (fsopts->sparse) {
505 		if (ftruncate(fsopts->fd, bufrem) == -1) {
506 			warn("sparse option disabled");
507 			fsopts->sparse = 0;
508 		}
509 	}
510 	if (fsopts->sparse) {
511 		/* File truncated at bufrem. Remaining is 0 */
512 		bufrem = 0;
513 		buf = NULL;
514 	} else {
515 		if (debug & DEBUG_FS_CREATE_IMAGE)
516 			APRINTF("zero-ing image `%s', %lld sectors, "
517 			    "using %d byte chunks\n",
518 			    image, (long long)bufrem, bufsize);
519 		buf = ecalloc(1, bufsize);
520 	}
521 
522 	if (fsopts->offset != 0) {
523 		if (lseek(fsopts->fd, fsopts->offset, SEEK_SET) == -1) {
524 			warn("can't seek");
525 			free(buf);
526 			return -1;
527 		}
528 	}
529 
530 	while (bufrem > 0) {
531 		i = write(fsopts->fd, buf, MIN(bufsize, bufrem));
532 		if (i == -1) {
533 			warn("zeroing image, %lld bytes to go",
534 			    (long long)bufrem);
535 			free(buf);
536 			return -1;
537 		}
538 		bufrem -= i;
539 	}
540 	if (buf)
541 		free(buf);
542 
543 	/* make the file system */
544 	if (debug & DEBUG_FS_CREATE_IMAGE)
545 		APRINTF("calling mkfs(\"%s\", ...)\n", image);
546 	hammer2_mkfs(1, av, opt); /* success if returned */
547 
548 	return fsopts->fd;
549 }
550 
551 static off_t
552 hammer2_phys_size(off_t size)
553 {
554 	off_t radix_size, phys_size = 0;
555 	int i;
556 
557 	if (size > HAMMER2_PBUFSIZE) {
558 		phys_size += rounddown(size, HAMMER2_PBUFSIZE);
559 		size = size % HAMMER2_PBUFSIZE;
560 	}
561 
562 	for (i = HAMMER2_RADIX_MIN; i <= HAMMER2_RADIX_MAX; i++) {
563 		radix_size = 1UL << i;
564 		if (radix_size >= size) {
565 			phys_size += radix_size;
566 			break;
567 		}
568 	}
569 
570 	return phys_size;
571 }
572 
573 /* calculate data size */
574 static void
575 hammer2_size_dir(fsnode *root, fsinfo_t *fsopts)
576 {
577 	fsnode *node;
578 
579 	assert(fsopts != NULL);
580 
581 	if (debug & DEBUG_FS_SIZE_DIR)
582 		APRINTF("entry: bytes %lld inodes %lld\n",
583 		    (long long)fsopts->size, (long long)fsopts->inodes);
584 
585 	for (node = root; node != NULL; node = node->next) {
586 		if (node == root) { /* we're at "." */
587 			assert(strcmp(node->name, ".") == 0);
588 		} else if ((node->inode->flags & FI_SIZED) == 0) {
589 			/* don't count duplicate names */
590 			node->inode->flags |= FI_SIZED;
591 			if (debug & DEBUG_FS_SIZE_DIR_NODE)
592 				APRINTF("`%s' size %lld\n",
593 				    node->name,
594 				    (long long)node->inode->st.st_size);
595 			fsopts->inodes++;
596 			fsopts->size += sizeof(hammer2_inode_data_t);
597 			if (node->type == S_IFREG) {
598 				size_t st_size = node->inode->st.st_size;
599 				if (st_size > HAMMER2_EMBEDDED_BYTES)
600 					fsopts->size += hammer2_phys_size(st_size);
601 			} else if (node->type == S_IFLNK) {
602 				size_t nlen = strlen(node->symlink);
603 				if (nlen > HAMMER2_EMBEDDED_BYTES)
604 					fsopts->size += hammer2_phys_size(nlen);
605 			}
606 		}
607 		if (node->type == S_IFDIR)
608 			hammer2_size_dir(node->child, fsopts);
609 	}
610 
611 	if (debug & DEBUG_FS_SIZE_DIR)
612 		APRINTF("exit: size %lld inodes %lld\n",
613 		    (long long)fsopts->size, (long long)fsopts->inodes);
614 }
615 
616 static void
617 hammer2_print(const struct m_vnode *dvp, const struct m_vnode *vp,
618     const fsnode *node, int depth, const char *msg)
619 {
620 	if (debug & DEBUG_FS_POPULATE) {
621 		if (1) {
622 			int indent = depth * 2;
623 			char *type;
624 			if (S_ISDIR(node->type))
625 				type = "dir";
626 			else if (S_ISREG(node->type))
627 				type = "reg";
628 			else if (S_ISLNK(node->type))
629 				type = "lnk";
630 			else if (S_ISFIFO(node->type))
631 				type = "fifo";
632 			else
633 				type = "???";
634 			printf("%*.*s", indent, indent, "");
635 			printf("dvp=%p/%d vp=%p/%d \"%s\" %s %s\n",
636 			    dvp, dvp ? VTOI(dvp)->refs : 0,
637 			    vp, vp ? VTOI(vp)->refs : 0,
638 			    node->name, type, msg);
639 		} else {
640 			char type;
641 			if (S_ISDIR(node->type))
642 				type = 'd';
643 			else if (S_ISREG(node->type))
644 				type = 'r';
645 			else if (S_ISLNK(node->type))
646 				type = 'l';
647 			else if (S_ISFIFO(node->type))
648 				type = 'f';
649 			else
650 				type = '?';
651 			printf("%c", type);
652 			fflush(stdout);
653 		}
654 	}
655 }
656 
657 static int
658 hammer2_populate_dir(struct m_vnode *dvp, const char *dir, fsnode *root,
659     fsnode *parent, fsinfo_t *fsopts, int depth)
660 {
661 	fsnode *cur;
662 	struct m_vnode *vp;
663 	struct stat st;
664 	char f[MAXPATHLEN];
665 	const char *path;
666 	int hardlink;
667 	int error;
668 
669 	assert(dvp != NULL);
670 	assert(dir != NULL);
671 	assert(root != NULL);
672 	assert(parent != NULL);
673 	assert(fsopts != NULL);
674 
675 	/* assert root directory */
676 	assert(S_ISDIR(root->type));
677 	assert(!strcmp(root->name, "."));
678 	assert(!root->child);
679 	assert(!root->parent || root->parent->child == root);
680 
681 	hammer2_print(dvp, NULL, root, depth, "enter");
682 	if (stat(dir, &st) == -1)
683 		err(1, "no such path %s", dir);
684 	if (!S_ISDIR(st.st_mode))
685 		errx(1, "no such dir %s", dir);
686 
687 	for (cur = root->next; cur != NULL; cur = cur->next) {
688 		/* construct source path */
689 		if (cur->contents) {
690 			path = cur->contents;
691 		} else {
692 			if (S_ISDIR(cur->type)) {
693 				/* this should be same as root/path/name */
694 				if (snprintf(f, sizeof(f), "%s/%s",
695 				    dir, cur->name) >= (int)sizeof(f))
696 					errx(1, "path %s too long", f);
697 			} else {
698 				if (snprintf(f, sizeof(f), "%s/%s/%s",
699 				    cur->root, cur->path, cur->name) >= (int)sizeof(f))
700 					errx(1, "path %s too long", f);
701 			}
702 			path = f;
703 		}
704 		if (stat(path, &st) == -1)
705 			err(1, "no such file %s", f);
706 
707 		/* update node state */
708 		if ((cur->inode->flags & FI_ALLOCATED) == 0) {
709 			cur->inode->flags |= FI_ALLOCATED;
710 			if (cur != root)
711 				cur->parent = parent;
712 		}
713 
714 		/* detect hardlink */
715 		if (cur->inode->flags & FI_WRITTEN) {
716 			assert(!S_ISDIR(cur->type));
717 			hardlink = 1;
718 		} else {
719 			hardlink = 0;
720 		}
721 		cur->inode->flags |= FI_WRITTEN;
722 
723 		/* make sure it doesn't exist yet */
724 		vp = NULL;
725 		error = hammer2_nresolve(dvp, &vp, cur->name,
726 		    strlen(cur->name));
727 		if (!error)
728 			errx(1, "hammer2_nresolve(\"%s\") already exists",
729 			    cur->name);
730 		hammer2_print(dvp, vp, cur, depth, "nresolve");
731 
732 		/* if directory, mkdir and recurse */
733 		if (S_ISDIR(cur->type)) {
734 			assert(cur->child);
735 
736 			vp = NULL;
737 			error = hammer2_nmkdir(dvp, &vp, cur->name,
738 			    strlen(cur->name), cur->inode->st.st_mode);
739 			if (error)
740 				errx(1, "hammer2_nmkdir(\"%s\") failed: %s",
741 				    cur->name, strerror(error));
742 			assert(vp);
743 			hammer2_print(dvp, vp, cur, depth, "nmkdir");
744 
745 			error = hammer2_populate_dir(vp, path, cur->child, cur,
746 			    fsopts, depth + 1);
747 			if (error)
748 				errx(1, "failed to populate %s: %s",
749 				    path, strerror(error));
750 			cur->inode->priv = vp;
751 			continue;
752 		}
753 
754 		/* if regular file, creat and write its data */
755 		if (S_ISREG(cur->type) && !hardlink) {
756 			assert(cur->child == NULL);
757 
758 			vp = NULL;
759 			error = hammer2_ncreate(dvp, &vp, cur->name,
760 			    strlen(cur->name), cur->inode->st.st_mode);
761 			if (error)
762 				errx(1, "hammer2_ncreate(\"%s\") failed: %s",
763 				    cur->name, strerror(error));
764 			assert(vp);
765 			hammer2_print(dvp, vp, cur, depth, "ncreate");
766 
767 			error = hammer2_write_file(vp, path, cur);
768 			if (error)
769 				errx(1, "hammer2_write_file(\"%s\") failed: %s",
770 				    path, strerror(error));
771 			cur->inode->priv = vp;
772 			continue;
773 		}
774 
775 		/* if symlink, create a symlink against target */
776 		if (S_ISLNK(cur->type)) {
777 			assert(cur->child == NULL);
778 
779 			vp = NULL;
780 			error = hammer2_nsymlink(dvp, &vp, cur->name,
781 			    strlen(cur->name), cur->symlink,
782 			    cur->inode->st.st_mode);
783 			if (error)
784 				errx(1, "hammer2_nsymlink(\"%s\") failed: %s",
785 				    cur->name, strerror(error));
786 			assert(vp);
787 			hammer2_print(dvp, vp, cur, depth, "nsymlink");
788 			cur->inode->priv = vp;
789 			continue;
790 		}
791 
792 		/* if fifo, create a fifo */
793 		if (S_ISFIFO(cur->type) && !hardlink) {
794 			assert(cur->child == NULL);
795 
796 			vp = NULL;
797 			error = hammer2_nmknod(dvp, &vp, cur->name,
798 			    strlen(cur->name), VFIFO, cur->inode->st.st_mode);
799 			if (error)
800 				errx(1, "hammer2_nmknod(\"%s\") failed: %s",
801 				    cur->name, strerror(error));
802 			assert(vp);
803 			hammer2_print(dvp, vp, cur, depth, "nmknod");
804 			cur->inode->priv = vp;
805 			continue;
806 		}
807 
808 		/* if hardlink, creat a hardlink */
809 		if ((S_ISREG(cur->type) || S_ISFIFO(cur->type)) && hardlink) {
810 			char buf[64];
811 			assert(cur->child == NULL);
812 
813 			/* source vnode must not be NULL */
814 			vp = cur->inode->priv;
815 			assert(vp);
816 			/* currently these conditions must be true */
817 			assert(vp->v_data);
818 			assert(vp->v_type == VREG || vp->v_type == VFIFO);
819 			assert(vp->v_logical);
820 			assert(!vp->v_vflushed);
821 			assert(vp->v_malloced);
822 			assert(VTOI(vp)->refs > 0);
823 
824 			error = hammer2_nlink(dvp, vp, cur->name,
825 			    strlen(cur->name));
826 			if (error)
827 				errx(1, "hammer2_nlink(\"%s\") failed: %s",
828 				    cur->name, strerror(error));
829 			snprintf(buf, sizeof(buf), "nlink=%ld",
830 			    VTOI(vp)->meta.nlinks);
831 			hammer2_print(dvp, vp, cur, depth, buf);
832 			continue;
833 		}
834 
835 		/* other types are unsupported */
836 		printf("ignore %s 0%o\n", path, cur->type);
837 	}
838 
839 	return 0;
840 }
841 
842 static int
843 hammer2_write_file(struct m_vnode *vp, const char *path, fsnode *node)
844 {
845 	struct stat *st = &node->inode->st;
846 	size_t nsize, bufsize;
847 	off_t offset;
848 	int fd, error;
849 	char *p;
850 
851 	nsize = st->st_size;
852 	if (nsize == 0)
853 		return 0;
854 	/* check nsize vs maximum file size */
855 
856 	fd = open(path, O_RDONLY);
857 	if (fd < 0)
858 		err(1, "failed to open %s", path);
859 
860 	p = mmap(0, nsize, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0);
861 	if (p == MAP_FAILED)
862 		err(1, "failed to mmap %s", path);
863 	close(fd);
864 
865 	for (offset = 0; offset < nsize; ) {
866 		bufsize = MIN(nsize - offset, HAMMER2_PBUFSIZE);
867 		assert(bufsize <= HAMMER2_PBUFSIZE);
868 		error = hammer2_write(vp, p + offset, bufsize, offset);
869 		if (error)
870 			errx(1, "failed to write to %s vnode: %s",
871 			    path, strerror(error));
872 		offset += bufsize;
873 		if (bufsize == HAMMER2_PBUFSIZE)
874 			assert((offset & (HAMMER2_PBUFSIZE - 1)) == 0);
875 	}
876 	munmap(p, nsize);
877 
878 	return 0;
879 }
880