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