xref: /dragonfly/usr.sbin/makefs/hammer2.c (revision f0e61bb7)
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 #include <sys/time.h>
44 #include <sys/dirent.h>
45 
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <stdbool.h>
49 #include <string.h>
50 #include <ctype.h>
51 #include <unistd.h>
52 #include <fcntl.h>
53 #include <time.h>
54 #include <err.h>
55 #include <assert.h>
56 #include <util.h>
57 
58 #include "makefs.h"
59 #include "hammer2.h"
60 
61 #define APRINTF(X, ...)	\
62     printf("%s: " X, __func__, ## __VA_ARGS__)
63 
64 static void hammer2_parse_pfs_opts(const char *, fsinfo_t *);
65 static void hammer2_parse_inode_opts(const char *, fsinfo_t *);
66 static void hammer2_dump_fsinfo(fsinfo_t *);
67 static int hammer2_create_image(const char *, fsinfo_t *);
68 static int hammer2_populate_dir(struct m_vnode *, const char *, fsnode *,
69     fsnode *, fsinfo_t *, int);
70 static void hammer2_validate(const char *, fsnode *, fsinfo_t *);
71 static void hammer2_size_dir(fsnode *, fsinfo_t *);
72 static int hammer2_write_file(struct m_vnode *, const char *, fsnode *);
73 static int hammer2_version_get(struct m_vnode *);
74 static int hammer2_pfs_get(struct m_vnode *);
75 static int hammer2_pfs_lookup(struct m_vnode *, const char *);
76 static int hammer2_pfs_create(struct m_vnode *, const char *);
77 static int hammer2_pfs_delete(struct m_vnode *, const char *);
78 static int hammer2_pfs_snapshot(struct m_vnode *, const char *, const char *);
79 static int hammer2_inode_getx(struct m_vnode *, const char *);
80 static int hammer2_inode_setcheck(struct m_vnode *, const char *);
81 static int hammer2_inode_setcomp(struct m_vnode *, const char *);
82 static int hammer2_bulkfree(struct m_vnode *);
83 static int hammer2_destroy_path(struct m_vnode *, const char *);
84 static int hammer2_destroy_inum(struct m_vnode *, hammer2_tid_t);
85 static int hammer2_growfs(struct m_vnode *, hammer2_off_t);
86 struct hammer2_linkq;
87 static int hammer2_readx_handle(struct m_vnode *, const char *, const char *,
88     struct hammer2_linkq *);
89 static int hammer2_readx(struct m_vnode *, const char *, const char *);
90 static void unittest_trim_slash(void);
91 
92 fsnode *hammer2_curnode;
93 
94 void
95 hammer2_prep_opts(fsinfo_t *fsopts)
96 {
97 	hammer2_makefs_options_t *h2_opt = ecalloc(1, sizeof(*h2_opt));
98 	hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
99 
100 	const option_t hammer2_options[] = {
101 		/* newfs_hammer2(8) compatible options */
102 		{ 'b', "BootAreaSize", NULL, OPT_STRBUF, 0, 0, "boot area size" },
103 		{ 'r', "AuxAreaSize", NULL, OPT_STRBUF, 0, 0, "aux area size" },
104 		{ 'V', "Hammer2Version", NULL, OPT_STRBUF, 0, 0, "file system version" },
105 		{ 'L', "Label", NULL, OPT_STRBUF, 0, 0, "PFS label" },
106 		/* makefs(8) specific options */
107 		{ 'm', "MountLabel", NULL, OPT_STRBUF, 0, 0, "destination PFS label" },
108 		{ 'v', "NumVolhdr", &h2_opt->num_volhdr, OPT_INT32,
109 		    1, HAMMER2_NUM_VOLHDRS, "number of volume headers" },
110 		{ 'd', "Hammer2Debug", NULL, OPT_STRBUF, 0, 0, "debug tunable" },
111 		{ 'E', "EmergencyMode", &h2_opt->emergency_mode, OPT_BOOL, 0, 0,
112 		    "emergency mode" },
113 		{ 'P', "PFS", NULL, OPT_STRBUF, 0, 0, "offline PFS" },
114 		{ 'I', "Inode", NULL, OPT_STRBUF, 0, 0, "offline inode" },
115 		{ 'B', "Bulkfree", NULL, OPT_STRBUF, 0, 0, "offline bulkfree" },
116 		{ 'D', "Destroy", NULL, OPT_STRBUF, 0, 0, "offline destroy" },
117 		{ 'G', "Growfs", NULL, OPT_STRBUF, 0, 0, "offline growfs" },
118 		{ 'R', "Read", NULL, OPT_STRBUF, 0, 0, "offline read" },
119 		{ .name = NULL },
120 	};
121 
122 	hammer2_mkfs_init(opt);
123 
124 	/* make this tunable ? */
125 	assert(opt->CompType == HAMMER2_COMP_NEWFS_DEFAULT);
126 	assert(opt->CheckType == HAMMER2_CHECK_XXHASH64);
127 
128 	/* force debug mode for mkfs */
129 	opt->DebugOpt = 1;
130 
131 	fsopts->fs_specific = h2_opt;
132 	fsopts->fs_options = copy_opts(hammer2_options);
133 	fsopts->sectorsize = DEV_BSIZE;
134 }
135 
136 void
137 hammer2_cleanup_opts(fsinfo_t *fsopts)
138 {
139 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
140 	hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
141 
142 	hammer2_mkfs_cleanup(opt);
143 
144 	free(h2_opt);
145 	free(fsopts->fs_options);
146 }
147 
148 int
149 hammer2_parse_opts(const char *option, fsinfo_t *fsopts)
150 {
151 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
152 	hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
153 
154 	option_t *hammer2_options = fsopts->fs_options;
155 	char buf[1024]; /* > HAMMER2_INODE_MAXNAME */
156 	int i;
157 
158 	assert(option != NULL);
159 	assert(fsopts != NULL);
160 
161 	if (debug & DEBUG_FS_PARSE_OPTS)
162 		APRINTF("got `%s'\n", option);
163 
164 	i = set_option(hammer2_options, option, buf, sizeof(buf));
165 	if (i == -1)
166 		return 0;
167 
168 	if (hammer2_options[i].name == NULL)
169 		abort();
170 
171 	switch (hammer2_options[i].letter) {
172 	case 'b':
173 		opt->BootAreaSize = getsize(buf, HAMMER2_NEWFS_ALIGN,
174 		    HAMMER2_BOOT_MAX_BYTES, 2);
175 		break;
176 	case 'r':
177 		opt->AuxAreaSize = getsize(buf, HAMMER2_NEWFS_ALIGN,
178 		    HAMMER2_AUX_MAX_BYTES, 2);
179 		break;
180 	case 'V':
181 		if (strlen(buf) == 0) {
182 			h2_opt->ioctl_cmd = HAMMER2IOC_VERSION_GET;
183 		} else {
184 			opt->Hammer2Version = strtol(buf, NULL, 0);
185 			if (opt->Hammer2Version < HAMMER2_VOL_VERSION_MIN ||
186 			    opt->Hammer2Version >= HAMMER2_VOL_VERSION_WIP)
187 				errx(1, "I don't understand how to format "
188 				    "HAMMER2 version %d",
189 				    opt->Hammer2Version);
190 		}
191 		break;
192 	case 'L':
193 		h2_opt->label_specified = 1;
194 		if (strcasecmp(buf, "none") == 0)
195 			break;
196 		if (opt->NLabels >= MAXLABELS)
197 			errx(1, "Limit of %d local labels", MAXLABELS - 1);
198 		if (strlen(buf) == 0)
199 			errx(1, "Volume label '%s' cannot be 0-length", buf);
200 		if (strlen(buf) >= HAMMER2_INODE_MAXNAME)
201 			errx(1, "Volume label '%s' is too long (%d chars max)",
202 			    buf, HAMMER2_INODE_MAXNAME - 1);
203 		opt->Label[opt->NLabels++] = strdup(buf);
204 		break;
205 	case 'm':
206 		if (strlen(buf) == 0)
207 			errx(1, "Volume label '%s' cannot be 0-length", buf);
208 		if (strlen(buf) >= HAMMER2_INODE_MAXNAME)
209 			errx(1, "Volume label '%s' is too long (%d chars max)",
210 			    buf, HAMMER2_INODE_MAXNAME - 1);
211 		strlcpy(h2_opt->mount_label, buf, sizeof(h2_opt->mount_label));
212 		break;
213 	case 'd':
214 		hammer2_debug = strtoll(buf, NULL, 0);
215 		break;
216 	case 'P':
217 		if (strlen(buf) == 0)
218 			errx(1, "PFS argument '%s' cannot be 0-length", buf);
219 		hammer2_parse_pfs_opts(buf, fsopts);
220 		break;
221 	case 'I':
222 		if (strlen(buf) == 0)
223 			errx(1, "Inode argument '%s' cannot be 0-length", buf);
224 		hammer2_parse_inode_opts(buf, fsopts);
225 		break;
226 	case 'B':
227 		h2_opt->ioctl_cmd = HAMMER2IOC_BULKFREE_SCAN;
228 		break;
229 	case 'D':
230 		h2_opt->ioctl_cmd = HAMMER2IOC_DESTROY;
231 		if (strlen(buf) == 0)
232 			errx(1, "Destroy argument '%s' cannot be 0-length", buf);
233 		if (buf[0] == '/') {
234 			strlcpy(h2_opt->destroy_path, buf,
235 			    sizeof(h2_opt->destroy_path));
236 		} else if (strncmp(buf, "0x", 2) == 0 ||
237 		    (buf[0] >= '0' && buf[0] <= '9')) {
238 			h2_opt->destroy_inum = strtoull(buf, NULL, 0);
239 			if (errno)
240 				err(1, "strtoull");
241 		} else {
242 			errx(1, "Invalid destroy argument %s", buf);
243 		}
244 		break;
245 	case 'G':
246 		h2_opt->ioctl_cmd = HAMMER2IOC_GROWFS;
247 		break;
248 	case 'R':
249 		h2_opt->ioctl_cmd = HAMMER2IOC_READ;
250 		if (strlen(buf) == 0)
251 			errx(1, "Read argument '%s' cannot be 0-length", buf);
252 		strlcpy(h2_opt->read_path, buf, sizeof(h2_opt->read_path));
253 		break;
254 	default:
255 		break;
256 	}
257 
258 	if (hammer2_debug && h2_opt->ioctl_cmd)
259 		unittest_trim_slash();
260 
261 	return 1;
262 }
263 
264 void
265 hammer2_makefs(const char *image, const char *dir, fsnode *root,
266     fsinfo_t *fsopts)
267 {
268 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
269 	struct mount mp;
270 	struct hammer2_mount_info info;
271 	struct m_vnode devvp, *vroot;
272 	hammer2_inode_t *iroot;
273 	struct timeval start;
274 	int error;
275 
276 	/* ioctl commands could have NULL dir / root */
277 	assert(image != NULL);
278 	assert(fsopts != NULL);
279 
280 	if (debug & DEBUG_FS_MAKEFS)
281 		APRINTF("image \"%s\" directory \"%s\" root %p\n",
282 		    image, dir, root);
283 
284 	/* validate tree and options */
285 	TIMER_START(start);
286 	hammer2_validate(dir, root, fsopts);
287 	TIMER_RESULTS(start, "hammer2_validate");
288 
289 	if (h2_opt->ioctl_cmd) {
290 		/* open existing image */
291 		fsopts->fd = open(image, O_RDWR);
292 		if (fsopts->fd < 0)
293 			err(1, "failed to open `%s'", image);
294 	} else {
295 		/* create image */
296 		TIMER_START(start);
297 		if (hammer2_create_image(image, fsopts) == -1)
298 			errx(1, "image file `%s' not created", image);
299 		TIMER_RESULTS(start, "hammer2_create_image");
300 	}
301 	assert(fsopts->fd > 0);
302 
303 	if (debug & DEBUG_FS_MAKEFS)
304 		putchar('\n');
305 
306 	/* vfs init */
307 	error = hammer2_vfs_init();
308 	if (error)
309 		errx(1, "failed to vfs init, error %d", error);
310 
311 	/* mount image */
312 	memset(&devvp, 0, sizeof(devvp));
313 	devvp.fs = fsopts;
314 	memset(&mp, 0, sizeof(mp));
315 	memset(&info, 0, sizeof(info));
316 	error = hammer2_vfs_mount(&devvp, &mp, h2_opt->mount_label, &info);
317 	if (error)
318 		errx(1, "failed to mount, error %d", error);
319 	assert(mp.mnt_data);
320 
321 	/* get root vnode */
322 	vroot = NULL;
323 	error = hammer2_vfs_root(&mp, &vroot);
324 	if (error)
325 		errx(1, "failed to get root vnode, error %d", error);
326 	assert(vroot);
327 
328 	iroot = VTOI(vroot);
329 	assert(iroot);
330 	printf("root inode inum %lld, mode 0%o, refs %d\n",
331 	    (long long)iroot->meta.inum, iroot->meta.mode, iroot->refs);
332 
333 	if (h2_opt->emergency_mode)
334 		hammer2_ioctl_emerg_mode(iroot, 1);
335 
336 	switch (h2_opt->ioctl_cmd) {
337 	case HAMMER2IOC_VERSION_GET:
338 		printf("version get `%s'\n", image);
339 		TIMER_START(start);
340 		error = hammer2_version_get(vroot);
341 		if (error)
342 			errx(1, "version get `%s' failed '%s'", image,
343 			    strerror(error));
344 		TIMER_RESULTS(start, "hammer2_version_get");
345 		break;
346 	case HAMMER2IOC_PFS_GET:
347 		printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image);
348 		TIMER_START(start);
349 		error = hammer2_pfs_get(vroot);
350 		if (error)
351 			errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name,
352 			    image, strerror(error));
353 		TIMER_RESULTS(start, "hammer2_pfs_get");
354 		break;
355 	case HAMMER2IOC_PFS_LOOKUP:
356 		printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image);
357 		TIMER_START(start);
358 		error = hammer2_pfs_lookup(vroot, h2_opt->pfs_name);
359 		if (error)
360 			errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name,
361 			    image, strerror(error));
362 		TIMER_RESULTS(start, "hammer2_pfs_lookup");
363 		break;
364 	case HAMMER2IOC_PFS_CREATE:
365 		printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image);
366 		TIMER_START(start);
367 		error = hammer2_pfs_create(vroot, h2_opt->pfs_name);
368 		if (error)
369 			errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name,
370 			    image, strerror(error));
371 		TIMER_RESULTS(start, "hammer2_pfs_create");
372 		break;
373 	case HAMMER2IOC_PFS_DELETE:
374 		printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image);
375 		TIMER_START(start);
376 		error = hammer2_pfs_delete(vroot, h2_opt->pfs_name);
377 		if (error)
378 			errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name,
379 			    image, strerror(error));
380 		TIMER_RESULTS(start, "hammer2_pfs_delete");
381 		break;
382 	case HAMMER2IOC_PFS_SNAPSHOT:
383 		printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image);
384 		TIMER_START(start);
385 		error = hammer2_pfs_snapshot(vroot, h2_opt->pfs_name,
386 		    h2_opt->mount_label);
387 		if (error)
388 			errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name,
389 			    image, strerror(error));
390 		TIMER_RESULTS(start, "hammer2_pfs_snapshot");
391 		break;
392 	case HAMMER2IOC_INODE_GET:
393 		printf("inode %s `%s'\n", h2_opt->inode_cmd_name, image);
394 		TIMER_START(start);
395 		error = hammer2_inode_getx(vroot, h2_opt->inode_path);
396 		if (error)
397 			errx(1, "inode %s `%s' failed '%s'",
398 			    h2_opt->inode_cmd_name, image, strerror(error));
399 		TIMER_RESULTS(start, "hammer2_inode_getx");
400 		break;
401 	case HAMMER2IOC_INODE_SET:
402 		printf("inode %s `%s'\n", h2_opt->inode_cmd_name, image);
403 		TIMER_START(start);
404 		if (!strcmp(h2_opt->inode_cmd_name, "setcheck")) {
405 			error = hammer2_inode_setcheck(vroot,
406 			    h2_opt->inode_path);
407 			if (error)
408 				errx(1, "inode %s `%s' failed '%s'",
409 				    h2_opt->inode_cmd_name, image,
410 				    strerror(error));
411 		} else if (!strcmp(h2_opt->inode_cmd_name, "setcomp")) {
412 			error = hammer2_inode_setcomp(vroot,
413 			    h2_opt->inode_path);
414 			if (error)
415 				errx(1, "inode %s `%s' failed '%s'",
416 				    h2_opt->inode_cmd_name, image,
417 				    strerror(error));
418 		} else {
419 			assert(0);
420 		}
421 		TIMER_RESULTS(start, "hammer2_inode_setx");
422 		break;
423 	case HAMMER2IOC_BULKFREE_SCAN:
424 		printf("bulkfree `%s'\n", image);
425 		TIMER_START(start);
426 		error = hammer2_bulkfree(vroot);
427 		if (error)
428 			errx(1, "bulkfree `%s' failed '%s'", image,
429 			    strerror(error));
430 		TIMER_RESULTS(start, "hammer2_bulkfree");
431 		break;
432 	case HAMMER2IOC_DESTROY:
433 		TIMER_START(start);
434 		if (strlen(h2_opt->destroy_path)) {
435 			printf("destroy `%s' in `%s'\n",
436 			    h2_opt->destroy_path, image);
437 			error = hammer2_destroy_path(vroot,
438 			    h2_opt->destroy_path);
439 			if (error)
440 				errx(1, "destroy `%s' in `%s' failed '%s'",
441 				    h2_opt->destroy_path, image,
442 				    strerror(error));
443 		} else {
444 			printf("destroy %lld in `%s'\n",
445 			    (long long)h2_opt->destroy_inum, image);
446 			error = hammer2_destroy_inum(vroot,
447 			    h2_opt->destroy_inum);
448 			if (error)
449 				errx(1, "destroy %lld in `%s' failed '%s'",
450 				    (long long)h2_opt->destroy_inum, image,
451 				    strerror(error));
452 		}
453 		TIMER_RESULTS(start, "hammer2_destroy");
454 		break;
455 	case HAMMER2IOC_GROWFS:
456 		printf("growfs `%s'\n", image);
457 		TIMER_START(start);
458 		error = hammer2_growfs(vroot, h2_opt->image_size);
459 		if (error)
460 			errx(1, "growfs `%s' failed '%s'", image,
461 			    strerror(error));
462 		TIMER_RESULTS(start, "hammer2_growfs");
463 		break;
464 	case HAMMER2IOC_READ:
465 		printf("read `%s'\n", image);
466 		TIMER_START(start);
467 		error = hammer2_readx(vroot, dir, h2_opt->read_path);
468 		if (error)
469 			errx(1, "read `%s' failed '%s'", image,
470 			    strerror(error));
471 		TIMER_RESULTS(start, "hammer2_readx");
472 		break;
473 	default:
474 		printf("populating `%s'\n", image);
475 		TIMER_START(start);
476 		if (hammer2_populate_dir(vroot, dir, root, root, fsopts, 0))
477 			errx(1, "image file `%s' not populated", image);
478 		TIMER_RESULTS(start, "hammer2_populate_dir");
479 		break;
480 	}
481 
482 	/* unmount image */
483 	error = hammer2_vfs_unmount(&mp, 0);
484 	if (error)
485 		errx(1, "failed to unmount, error %d", error);
486 
487 	/* check leaked resource */
488 	if (vnode_count)
489 		printf("XXX %lld vnode left\n", (long long)vnode_count);
490 	if (hammer2_chain_allocs)
491 		printf("XXX %ld chain left\n", hammer2_chain_allocs);
492 	bcleanup();
493 
494 	/* vfs uninit */
495 	error = hammer2_vfs_uninit();
496 	if (error)
497 		errx(1, "failed to vfs uninit, error %d", error);
498 
499 	if (close(fsopts->fd) == -1)
500 		err(1, "closing `%s'", image);
501 	fsopts->fd = -1;
502 
503 	printf("image `%s' complete\n", image);
504 }
505 
506 /* end of public functions */
507 
508 static void
509 hammer2_parse_pfs_opts(const char *buf, fsinfo_t *fsopts)
510 {
511 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
512 	char *o, *p;
513 	size_t n;
514 
515 	o = p = strdup(buf);
516 	p = strchr(p, ':');
517 	if (p != NULL) {
518 		*p++ = 0;
519 		n = strlen(p);
520 	} else {
521 		n = 0;
522 	}
523 
524 	if (!strcmp(o, "get") || !strcmp(o, "list")) {
525 		h2_opt->ioctl_cmd = HAMMER2IOC_PFS_GET;
526 	} else if (!strcmp(o, "lookup")) {
527 		if (n == 0 || n > NAME_MAX)
528 			errx(1, "invalid PFS name \"%s\"", p);
529 		h2_opt->ioctl_cmd = HAMMER2IOC_PFS_LOOKUP;
530 	} else if (!strcmp(o, "create")) {
531 		if (n == 0 || n > NAME_MAX)
532 			errx(1, "invalid PFS name \"%s\"", p);
533 		h2_opt->ioctl_cmd = HAMMER2IOC_PFS_CREATE;
534 	} else if (!strcmp(o, "delete")) {
535 		if (n == 0 || n > NAME_MAX)
536 			errx(1, "invalid PFS name \"%s\"", p);
537 		h2_opt->ioctl_cmd = HAMMER2IOC_PFS_DELETE;
538 	} else if (!strcmp(o, "snapshot")) {
539 		if (n > NAME_MAX)
540 			errx(1, "invalid PFS name \"%s\"", p);
541 		h2_opt->ioctl_cmd = HAMMER2IOC_PFS_SNAPSHOT;
542 	} else {
543 		errx(1, "invalid PFS command \"%s\"", o);
544 	}
545 
546 	strlcpy(h2_opt->pfs_cmd_name, o, sizeof(h2_opt->pfs_cmd_name));
547 	if (n > 0)
548 		strlcpy(h2_opt->pfs_name, p, sizeof(h2_opt->pfs_name));
549 
550 	free(o);
551 }
552 
553 static void
554 hammer2_parse_inode_opts(const char *buf, fsinfo_t *fsopts)
555 {
556 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
557 	char *o, *p;
558 	size_t n;
559 
560 	o = p = strdup(buf);
561 	p = strchr(p, ':');
562 	if (p != NULL) {
563 		*p++ = 0;
564 		n = strlen(p);
565 	} else {
566 		n = 0;
567 	}
568 
569 	if (!strcmp(o, "get")) {
570 		if (n == 0 || n > PATH_MAX)
571 			errx(1, "invalid file path \"%s\"", p);
572 		h2_opt->ioctl_cmd = HAMMER2IOC_INODE_GET;
573 	} else if (!strcmp(o, "setcheck")) {
574 		if (n == 0 || n > PATH_MAX - 10)
575 			errx(1, "invalid argument \"%s\"", p);
576 		h2_opt->ioctl_cmd = HAMMER2IOC_INODE_SET;
577 	} else if (!strcmp(o, "setcomp")) {
578 		if (n == 0 || n > PATH_MAX - 10)
579 			errx(1, "invalid argument \"%s\"", p);
580 		h2_opt->ioctl_cmd = HAMMER2IOC_INODE_SET;
581 	} else {
582 		errx(1, "invalid inode command \"%s\"", o);
583 	}
584 
585 	strlcpy(h2_opt->inode_cmd_name, o, sizeof(h2_opt->inode_cmd_name));
586 	if (n > 0)
587 		strlcpy(h2_opt->inode_path, p, sizeof(h2_opt->inode_path));
588 
589 	free(o);
590 }
591 
592 static hammer2_off_t
593 hammer2_image_size(fsinfo_t *fsopts)
594 {
595 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
596 	hammer2_off_t image_size, used_size = 0;
597 	int num_level1, delta_num_level1;
598 
599 	/* use 4 volume headers by default */
600 	num_level1 = h2_opt->num_volhdr * 2; /* default 4 x 2 */
601 	assert(num_level1 != 0);
602 	assert(num_level1 <= 8);
603 
604 	/* add 4MiB segment for each level1 */
605 	used_size += HAMMER2_ZONE_SEG64 * num_level1;
606 
607 	/* add boot/aux area, but exact size unknown at this point */
608 	used_size += HAMMER2_BOOT_NOM_BYTES + HAMMER2_AUX_NOM_BYTES;
609 
610 	/* add data size */
611 	used_size += fsopts->size;
612 
613 	/* XXX add extra level1 for meta data and indirect blocks */
614 	used_size += HAMMER2_FREEMAP_LEVEL1_SIZE;
615 
616 	/* XXX add extra level1 for safety */
617 	if (used_size > HAMMER2_FREEMAP_LEVEL1_SIZE * 10)
618 		used_size += HAMMER2_FREEMAP_LEVEL1_SIZE;
619 
620 	/* use 8GiB image size by default */
621 	image_size = HAMMER2_FREEMAP_LEVEL1_SIZE * num_level1;
622 	printf("trying default image size %s\n", sizetostr(image_size));
623 
624 	/* adjust if image size isn't large enough */
625 	if (used_size > image_size) {
626 		/* determine extra level1 needed */
627 		delta_num_level1 = howmany(used_size - image_size,
628 		    HAMMER2_FREEMAP_LEVEL1_SIZE);
629 
630 		/* adjust used size with 4MiB segment for each extra level1 */
631 		used_size += HAMMER2_ZONE_SEG64 * delta_num_level1;
632 
633 		/* adjust image size with extra level1 */
634 		image_size += HAMMER2_FREEMAP_LEVEL1_SIZE * delta_num_level1;
635 		printf("trying adjusted image size %s\n",
636 		    sizetostr(image_size));
637 
638 		if (used_size > image_size)
639 			errx(1, "invalid used_size %lld > image_size %lld",
640 			    (long long)used_size, (long long)image_size);
641 	}
642 
643 	return image_size;
644 }
645 
646 static const char *
647 hammer2_label_name(int label_type)
648 {
649 	switch (label_type) {
650 	case HAMMER2_LABEL_NONE:
651 		return "NONE";
652 	case HAMMER2_LABEL_BOOT:
653 		return "BOOT";
654 	case HAMMER2_LABEL_ROOT:
655 		return "ROOT";
656 	case HAMMER2_LABEL_DATA:
657 		return "DATA";
658 	default:
659 		assert(0);
660 	}
661 	return NULL;
662 }
663 
664 static void
665 hammer2_validate(const char *dir, fsnode *root, fsinfo_t *fsopts)
666 {
667 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
668 	hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
669 	hammer2_off_t image_size = 0, minsize, maxsize;
670 	const char *s;
671 
672 	/* ioctl commands could have NULL dir / root */
673 	assert(fsopts != NULL);
674 
675 	if (debug & DEBUG_FS_VALIDATE) {
676 		APRINTF("before defaults set:\n");
677 		hammer2_dump_fsinfo(fsopts);
678 	}
679 
680 	/* makefs only supports "DATA" for default PFS label */
681 	if (!h2_opt->label_specified) {
682 		opt->DefaultLabelType = HAMMER2_LABEL_DATA;
683 		s = hammer2_label_name(opt->DefaultLabelType);
684 		printf("using default label \"%s\"\n", s);
685 	}
686 
687 	/* set default mount PFS label */
688 	if (!strcmp(h2_opt->mount_label, "")) {
689 		s = hammer2_label_name(HAMMER2_LABEL_DATA);
690 		strlcpy(h2_opt->mount_label, s, sizeof(h2_opt->mount_label));
691 		printf("using default mount label \"%s\"\n", s);
692 	}
693 
694 	/* set default number of volume headers */
695 	if (!h2_opt->num_volhdr) {
696 		h2_opt->num_volhdr = HAMMER2_NUM_VOLHDRS;
697 		printf("using default %d volume headers\n", h2_opt->num_volhdr);
698 	}
699 
700 	/* done if ioctl commands */
701 	if (h2_opt->ioctl_cmd) {
702 		if (h2_opt->ioctl_cmd == HAMMER2IOC_GROWFS)
703 			goto ignore_size_dir;
704 		else
705 			goto done;
706 	}
707 
708 	/* calculate data size */
709 	if (fsopts->size != 0)
710 		fsopts->size = 0; /* shouldn't reach here to begin with */
711 	if (root == NULL)
712 		errx(1, "fsnode tree not constructed");
713 	hammer2_size_dir(root, fsopts);
714 	printf("estimated data size %s from %lld inode\n",
715 	    sizetostr(fsopts->size), (long long)fsopts->inodes);
716 
717 	/* determine image size from data size */
718 	image_size = hammer2_image_size(fsopts);
719 	assert((image_size & HAMMER2_FREEMAP_LEVEL1_MASK) == 0);
720 ignore_size_dir:
721 	minsize = roundup(fsopts->minsize, HAMMER2_FREEMAP_LEVEL1_SIZE);
722 	maxsize = roundup(fsopts->maxsize, HAMMER2_FREEMAP_LEVEL1_SIZE);
723 	if (image_size < minsize)
724 		image_size = minsize;
725 	else if (maxsize > 0 && image_size > maxsize)
726 		errx(1, "`%s' size of %lld is larger than the maxsize of %lld",
727 		    dir, (long long)image_size, (long long)maxsize);
728 
729 	assert((image_size & HAMMER2_FREEMAP_LEVEL1_MASK) == 0);
730 	h2_opt->image_size = image_size;
731 	printf("using %s image size\n", sizetostr(h2_opt->image_size));
732 done:
733 	if (debug & DEBUG_FS_VALIDATE) {
734 		APRINTF("after defaults set:\n");
735 		hammer2_dump_fsinfo(fsopts);
736 	}
737 }
738 
739 static void
740 hammer2_dump_fsinfo(fsinfo_t *fsopts)
741 {
742 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
743 	hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
744 	int i;
745 	char *s;
746 
747 	assert(fsopts != NULL);
748 
749 	APRINTF("fsinfo_t at %p\n", fsopts);
750 
751 	printf("\tinodes %lld\n", (long long)fsopts->inodes);
752 	printf("\tsize %lld, minsize %lld, maxsize %lld\n",
753 	    (long long)fsopts->size,
754 	    (long long)fsopts->minsize,
755 	    (long long)fsopts->maxsize);
756 
757 	printf("\thammer2_debug 0x%x\n", hammer2_debug);
758 
759 	printf("\tlabel_specified %d\n", h2_opt->label_specified);
760 	printf("\tmount_label \"%s\"\n", h2_opt->mount_label);
761 	printf("\tnum_volhdr %d\n", h2_opt->num_volhdr);
762 	printf("\tioctl_cmd %ld\n", h2_opt->ioctl_cmd);
763 	printf("\temergency_mode %d\n", h2_opt->emergency_mode);
764 	printf("\tpfs_cmd_name \"%s\"\n", h2_opt->pfs_cmd_name);
765 	printf("\tpfs_name \"%s\"\n", h2_opt->pfs_name);
766 	printf("\tinode_cmd_name \"%s\"\n", h2_opt->inode_cmd_name);
767 	printf("\tinode_path \"%s\"\n", h2_opt->inode_path);
768 	printf("\tdestroy_path \"%s\"\n", h2_opt->destroy_path);
769 	printf("\tdestroy_inum %lld\n", (long long)h2_opt->destroy_inum);
770 	printf("\tread_path \"%s\"\n", h2_opt->read_path);
771 	printf("\timage_size 0x%llx\n", (long long)h2_opt->image_size);
772 
773 	printf("\tHammer2Version %d\n", opt->Hammer2Version);
774 	printf("\tBootAreaSize 0x%jx\n", opt->BootAreaSize);
775 	printf("\tAuxAreaSize 0x%jx\n", opt->AuxAreaSize);
776 	printf("\tNLabels %d\n", opt->NLabels);
777 	printf("\tCompType %d\n", opt->CompType);
778 	printf("\tCheckType %d\n", opt->CheckType);
779 	printf("\tDefaultLabelType %d\n", opt->DefaultLabelType);
780 	printf("\tDebugOpt %d\n", opt->DebugOpt);
781 
782 	s = NULL;
783 	hammer2_uuid_to_str(&opt->Hammer2_FSType, &s);
784 	printf("\tHammer2_FSType \"%s\"\n", s);
785 	s = NULL;
786 	hammer2_uuid_to_str(&opt->Hammer2_VolFSID, &s);
787 	printf("\tHammer2_VolFSID \"%s\"\n", s);
788 	s = NULL;
789 	hammer2_uuid_to_str(&opt->Hammer2_SupCLID, &s);
790 	printf("\tHammer2_SupCLID \"%s\"\n", s);
791 	s = NULL;
792 	hammer2_uuid_to_str(&opt->Hammer2_SupFSID, &s);
793 	printf("\tHammer2_SupFSID \"%s\"\n", s);
794 
795 	for (i = 0; i < opt->NLabels; i++) {
796 		printf("\tLabel[%d] \"%s\"\n", i, opt->Label[i]);
797 		s = NULL;
798 		hammer2_uuid_to_str(&opt->Hammer2_PfsCLID[i], &s);
799 		printf("\t Hammer2_PfsCLID[%d] \"%s\"\n", i, s);
800 		s = NULL;
801 		hammer2_uuid_to_str(&opt->Hammer2_PfsFSID[i], &s);
802 		printf("\t Hammer2_PfsFSID[%d] \"%s\"\n", i, s);
803 	}
804 
805 	free(s);
806 }
807 
808 static int
809 hammer2_create_image(const char *image, fsinfo_t *fsopts)
810 {
811 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
812 	hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
813 	char *av[] = { (char *)image, }; /* XXX support multi-volumes */
814 	char *buf;
815 	int i, bufsize, oflags;
816 	off_t bufrem;
817 
818 	assert(image != NULL);
819 	assert(fsopts != NULL);
820 
821 	/* create image */
822 	oflags = O_RDWR | O_CREAT;
823 	if (fsopts->offset == 0)
824 		oflags |= O_TRUNC;
825 	if ((fsopts->fd = open(image, oflags, 0666)) == -1) {
826 		warn("can't open `%s' for writing", image);
827 		return -1;
828 	}
829 
830 	/* zero image */
831 	bufsize = HAMMER2_PBUFSIZE;
832 	bufrem = h2_opt->image_size;
833 	if (fsopts->sparse) {
834 		if (ftruncate(fsopts->fd, bufrem) == -1) {
835 			warn("sparse option disabled");
836 			fsopts->sparse = 0;
837 		}
838 	}
839 	if (fsopts->sparse) {
840 		/* File truncated at bufrem. Remaining is 0 */
841 		bufrem = 0;
842 		buf = NULL;
843 	} else {
844 		if (debug & DEBUG_FS_CREATE_IMAGE)
845 			APRINTF("zero-ing image `%s', %lld sectors, "
846 			    "using %d byte chunks\n",
847 			    image, (long long)bufrem, bufsize);
848 		buf = ecalloc(1, bufsize);
849 	}
850 
851 	if (fsopts->offset != 0) {
852 		if (lseek(fsopts->fd, fsopts->offset, SEEK_SET) == -1) {
853 			warn("can't seek");
854 			free(buf);
855 			return -1;
856 		}
857 	}
858 
859 	while (bufrem > 0) {
860 		i = write(fsopts->fd, buf, MIN(bufsize, bufrem));
861 		if (i == -1) {
862 			warn("zeroing image, %lld bytes to go",
863 			    (long long)bufrem);
864 			free(buf);
865 			return -1;
866 		}
867 		bufrem -= i;
868 	}
869 	if (buf)
870 		free(buf);
871 
872 	/* make the file system */
873 	if (debug & DEBUG_FS_CREATE_IMAGE)
874 		APRINTF("calling mkfs(\"%s\", ...)\n", image);
875 	hammer2_mkfs(1, av, opt); /* success if returned */
876 
877 	return fsopts->fd;
878 }
879 
880 static off_t
881 hammer2_phys_size(off_t size)
882 {
883 	off_t radix_size, phys_size = 0;
884 	int i;
885 
886 	if (size > HAMMER2_PBUFSIZE) {
887 		phys_size += rounddown(size, HAMMER2_PBUFSIZE);
888 		size = size % HAMMER2_PBUFSIZE;
889 	}
890 
891 	for (i = HAMMER2_RADIX_MIN; i <= HAMMER2_RADIX_MAX; i++) {
892 		radix_size = 1UL << i;
893 		if (radix_size >= size) {
894 			phys_size += radix_size;
895 			break;
896 		}
897 	}
898 
899 	return phys_size;
900 }
901 
902 /* calculate data size */
903 static void
904 hammer2_size_dir(fsnode *root, fsinfo_t *fsopts)
905 {
906 	fsnode *node;
907 
908 	assert(fsopts != NULL);
909 
910 	if (debug & DEBUG_FS_SIZE_DIR)
911 		APRINTF("entry: bytes %lld inodes %lld\n",
912 		    (long long)fsopts->size, (long long)fsopts->inodes);
913 
914 	for (node = root; node != NULL; node = node->next) {
915 		if (node == root) { /* we're at "." */
916 			assert(strcmp(node->name, ".") == 0);
917 		} else if ((node->inode->flags & FI_SIZED) == 0) {
918 			/* don't count duplicate names */
919 			node->inode->flags |= FI_SIZED;
920 			if (debug & DEBUG_FS_SIZE_DIR_NODE)
921 				APRINTF("`%s' size %lld\n",
922 				    node->name,
923 				    (long long)node->inode->st.st_size);
924 			fsopts->inodes++;
925 			fsopts->size += sizeof(hammer2_inode_data_t);
926 			if (node->type == S_IFREG) {
927 				size_t st_size = node->inode->st.st_size;
928 				if (st_size > HAMMER2_EMBEDDED_BYTES)
929 					fsopts->size += hammer2_phys_size(st_size);
930 			} else if (node->type == S_IFLNK) {
931 				size_t nlen = strlen(node->symlink);
932 				if (nlen > HAMMER2_EMBEDDED_BYTES)
933 					fsopts->size += hammer2_phys_size(nlen);
934 			}
935 		}
936 		if (node->type == S_IFDIR)
937 			hammer2_size_dir(node->child, fsopts);
938 	}
939 
940 	if (debug & DEBUG_FS_SIZE_DIR)
941 		APRINTF("exit: size %lld inodes %lld\n",
942 		    (long long)fsopts->size, (long long)fsopts->inodes);
943 }
944 
945 static void
946 hammer2_print(const struct m_vnode *dvp, const struct m_vnode *vp,
947     const fsnode *node, int depth, const char *msg)
948 {
949 	if (debug & DEBUG_FS_POPULATE) {
950 		if (1) {
951 			int indent = depth * 2;
952 			char *type;
953 			if (S_ISDIR(node->type))
954 				type = "dir";
955 			else if (S_ISREG(node->type))
956 				type = "reg";
957 			else if (S_ISLNK(node->type))
958 				type = "lnk";
959 			else if (S_ISFIFO(node->type))
960 				type = "fifo";
961 			else
962 				type = "???";
963 			printf("%*.*s", indent, indent, "");
964 			printf("dvp=%p/%d vp=%p/%d \"%s\" %s %s\n",
965 			    dvp, dvp ? VTOI(dvp)->refs : 0,
966 			    vp, vp ? VTOI(vp)->refs : 0,
967 			    node->name, type, msg);
968 		} else {
969 			char type;
970 			if (S_ISDIR(node->type))
971 				type = 'd';
972 			else if (S_ISREG(node->type))
973 				type = 'r';
974 			else if (S_ISLNK(node->type))
975 				type = 'l';
976 			else if (S_ISFIFO(node->type))
977 				type = 'f';
978 			else
979 				type = '?';
980 			printf("%c", type);
981 			fflush(stdout);
982 		}
983 	}
984 }
985 
986 static int
987 hammer2_populate_dir(struct m_vnode *dvp, const char *dir, fsnode *root,
988     fsnode *parent, fsinfo_t *fsopts, int depth)
989 {
990 	fsnode *cur;
991 	struct m_vnode *vp;
992 	struct stat st;
993 	char f[MAXPATHLEN];
994 	const char *path;
995 	int hardlink;
996 	int error;
997 
998 	assert(dvp != NULL);
999 	assert(dir != NULL);
1000 	assert(root != NULL);
1001 	assert(parent != NULL);
1002 	assert(fsopts != NULL);
1003 
1004 	/* assert root directory */
1005 	assert(S_ISDIR(root->type));
1006 	assert(!strcmp(root->name, "."));
1007 	assert(!root->child);
1008 	assert(!root->parent || root->parent->child == root);
1009 
1010 	hammer2_print(dvp, NULL, root, depth, "enter");
1011 	if (stat(dir, &st) == -1)
1012 		err(1, "no such path %s", dir);
1013 	if (!S_ISDIR(st.st_mode))
1014 		errx(1, "no such dir %s", dir);
1015 
1016 	for (cur = root->next; cur != NULL; cur = cur->next) {
1017 		/* global variable for HAMMER2 vnops */
1018 		hammer2_curnode = cur;
1019 
1020 		/* construct source path */
1021 		if (cur->contents) {
1022 			path = cur->contents;
1023 		} else {
1024 			if (snprintf(f, sizeof(f), "%s/%s/%s",
1025 			    cur->root, cur->path, cur->name) >= (int)sizeof(f))
1026 				errx(1, "path %s too long", f);
1027 			path = f;
1028 		}
1029 		if (S_ISLNK(cur->type)) {
1030 			if (lstat(path, &st) == -1)
1031 				err(1, "no such symlink %s", path);
1032 		} else {
1033 			if (stat(path, &st) == -1)
1034 				err(1, "no such path %s", path);
1035 		}
1036 
1037 		/* update node state */
1038 		if ((cur->inode->flags & FI_ALLOCATED) == 0) {
1039 			cur->inode->flags |= FI_ALLOCATED;
1040 			if (cur != root)
1041 				cur->parent = parent;
1042 		}
1043 
1044 		/* detect hardlink */
1045 		if (cur->inode->flags & FI_WRITTEN) {
1046 			assert(!S_ISDIR(cur->type));
1047 			hardlink = 1;
1048 		} else {
1049 			hardlink = 0;
1050 		}
1051 		cur->inode->flags |= FI_WRITTEN;
1052 
1053 		/* make sure it doesn't exist yet */
1054 		vp = NULL;
1055 		error = hammer2_nresolve(dvp, &vp, cur->name,
1056 		    strlen(cur->name));
1057 		if (!error)
1058 			errx(1, "hammer2_nresolve(\"%s\") already exists",
1059 			    cur->name);
1060 		hammer2_print(dvp, vp, cur, depth, "nresolve");
1061 
1062 		/* if directory, mkdir and recurse */
1063 		if (S_ISDIR(cur->type)) {
1064 			assert(cur->child);
1065 
1066 			vp = NULL;
1067 			error = hammer2_nmkdir(dvp, &vp, cur->name,
1068 			    strlen(cur->name), cur->inode->st.st_mode);
1069 			if (error)
1070 				errx(1, "hammer2_nmkdir(\"%s\") failed: %s",
1071 				    cur->name, strerror(error));
1072 			assert(vp);
1073 			hammer2_print(dvp, vp, cur, depth, "nmkdir");
1074 
1075 			error = hammer2_populate_dir(vp, path, cur->child, cur,
1076 			    fsopts, depth + 1);
1077 			if (error)
1078 				errx(1, "failed to populate %s: %s",
1079 				    path, strerror(error));
1080 			cur->inode->param = vp;
1081 			continue;
1082 		}
1083 
1084 		/* if regular file, creat and write its data */
1085 		if (S_ISREG(cur->type) && !hardlink) {
1086 			assert(cur->child == NULL);
1087 
1088 			vp = NULL;
1089 			error = hammer2_ncreate(dvp, &vp, cur->name,
1090 			    strlen(cur->name), cur->inode->st.st_mode);
1091 			if (error)
1092 				errx(1, "hammer2_ncreate(\"%s\") failed: %s",
1093 				    cur->name, strerror(error));
1094 			assert(vp);
1095 			hammer2_print(dvp, vp, cur, depth, "ncreate");
1096 
1097 			error = hammer2_write_file(vp, path, cur);
1098 			if (error)
1099 				errx(1, "hammer2_write_file(\"%s\") failed: %s",
1100 				    path, strerror(error));
1101 			cur->inode->param = vp;
1102 			continue;
1103 		}
1104 
1105 		/* if symlink, create a symlink against target */
1106 		if (S_ISLNK(cur->type)) {
1107 			assert(cur->child == NULL);
1108 
1109 			vp = NULL;
1110 			error = hammer2_nsymlink(dvp, &vp, cur->name,
1111 			    strlen(cur->name), cur->symlink,
1112 			    cur->inode->st.st_mode);
1113 			if (error)
1114 				errx(1, "hammer2_nsymlink(\"%s\") failed: %s",
1115 				    cur->name, strerror(error));
1116 			assert(vp);
1117 			hammer2_print(dvp, vp, cur, depth, "nsymlink");
1118 			cur->inode->param = vp;
1119 			continue;
1120 		}
1121 
1122 		/* if fifo, create a fifo */
1123 		if (S_ISFIFO(cur->type) && !hardlink) {
1124 			assert(cur->child == NULL);
1125 
1126 			vp = NULL;
1127 			error = hammer2_nmknod(dvp, &vp, cur->name,
1128 			    strlen(cur->name), VFIFO, cur->inode->st.st_mode);
1129 			if (error)
1130 				errx(1, "hammer2_nmknod(\"%s\") failed: %s",
1131 				    cur->name, strerror(error));
1132 			assert(vp);
1133 			hammer2_print(dvp, vp, cur, depth, "nmknod");
1134 			cur->inode->param = vp;
1135 			continue;
1136 		}
1137 
1138 		/* if hardlink, creat a hardlink */
1139 		if ((S_ISREG(cur->type) || S_ISFIFO(cur->type)) && hardlink) {
1140 			char buf[64];
1141 			assert(cur->child == NULL);
1142 
1143 			/* source vnode must not be NULL */
1144 			vp = cur->inode->param;
1145 			assert(vp);
1146 			/* currently these conditions must be true */
1147 			assert(vp->v_data);
1148 			assert(vp->v_type == VREG || vp->v_type == VFIFO);
1149 			assert(vp->v_logical);
1150 			assert(!vp->v_vflushed);
1151 			assert(vp->v_malloced);
1152 			assert(VTOI(vp)->refs > 0);
1153 
1154 			error = hammer2_nlink(dvp, vp, cur->name,
1155 			    strlen(cur->name));
1156 			if (error)
1157 				errx(1, "hammer2_nlink(\"%s\") failed: %s",
1158 				    cur->name, strerror(error));
1159 			snprintf(buf, sizeof(buf), "nlink=%lld",
1160 			    (long long)VTOI(vp)->meta.nlinks);
1161 			hammer2_print(dvp, vp, cur, depth, buf);
1162 			continue;
1163 		}
1164 
1165 		/* other types are unsupported */
1166 		printf("ignore %s 0%o\n", path, cur->type);
1167 	}
1168 
1169 	return 0;
1170 }
1171 
1172 static int
1173 hammer2_write_file(struct m_vnode *vp, const char *path, fsnode *node)
1174 {
1175 	struct stat *st = &node->inode->st;
1176 	size_t nsize, bufsize;
1177 	off_t offset;
1178 	int fd, error;
1179 	char *p;
1180 
1181 	nsize = st->st_size;
1182 	if (nsize == 0)
1183 		return 0;
1184 	/* check nsize vs maximum file size */
1185 
1186 	fd = open(path, O_RDONLY);
1187 	if (fd < 0)
1188 		err(1, "failed to open %s", path);
1189 
1190 	p = mmap(0, nsize, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0);
1191 	if (p == MAP_FAILED)
1192 		err(1, "failed to mmap %s", path);
1193 	close(fd);
1194 
1195 	for (offset = 0; offset < nsize; ) {
1196 		bufsize = MIN(nsize - offset, HAMMER2_PBUFSIZE);
1197 		assert(bufsize <= HAMMER2_PBUFSIZE);
1198 		error = hammer2_write(vp, p + offset, bufsize, offset);
1199 		if (error)
1200 			errx(1, "failed to write to %s vnode: %s",
1201 			    path, strerror(error));
1202 		offset += bufsize;
1203 		if (bufsize == HAMMER2_PBUFSIZE)
1204 			assert((offset & (HAMMER2_PBUFSIZE - 1)) == 0);
1205 	}
1206 	munmap(p, nsize);
1207 
1208 	return 0;
1209 }
1210 
1211 static int
1212 trim_char(char *p, char c)
1213 {
1214 	char *o, tmp[PATH_MAX];
1215 	bool prev_was_c;
1216 	size_t n;
1217 	int i;
1218 
1219 	assert(p);
1220 	/* nothing to do */
1221 	if (strlen(p) == 0)
1222 		return 0;
1223 
1224 	strlcpy(tmp, p, sizeof(tmp));
1225 	if (strncmp(tmp, p, sizeof(tmp)))
1226 		return ENOSPC;
1227 
1228 	/* trim consecutive */
1229 	prev_was_c = false;
1230 	o = p;
1231 	n = strlen(p);
1232 
1233 	for (i = 0; i < n; i++) {
1234 		if (tmp[i] == c) {
1235 			if (!prev_was_c)
1236 				*p++ = tmp[i];
1237 			prev_was_c = true;
1238 		} else {
1239 			*p++ = tmp[i];
1240 			prev_was_c = false;
1241 		}
1242 	}
1243 	*p = 0;
1244 	assert(strlen(p) <= strlen(tmp));
1245 
1246 	/* assert no consecutive */
1247 	prev_was_c = false;
1248 	p = o;
1249 	n = strlen(p);
1250 
1251 	for (i = 0; i < n; i++) {
1252 		if (p[i] == c) {
1253 			assert(!prev_was_c);
1254 			prev_was_c = true;
1255 		} else {
1256 			prev_was_c = false;
1257 		}
1258 	}
1259 
1260 	/* trim leading */
1261 	if (*p == c)
1262 		memmove(p, p + 1, strlen(p + 1) + 1);
1263 	assert(*p != '/');
1264 
1265 	/* trim trailing */
1266 	p += strlen(p);
1267 	p--;
1268 	if (*p == c)
1269 		*p = 0;
1270 	assert(p[strlen(p) - 1] != '/');
1271 
1272 	return 0;
1273 }
1274 
1275 static int
1276 trim_slash(char *p)
1277 {
1278 	return trim_char(p, '/');
1279 }
1280 
1281 static bool
1282 is_supported_link(const char *s)
1283 {
1284 	/* absolute path can't be supported */
1285 	if (strlen(s) >= 1 && strncmp(s, "/", 1) == 0)
1286 		return false;
1287 
1288 	/* XXX ".." is currently unsupported */
1289 	if (strlen(s) >= 3 && strncmp(s, "../", 3) == 0)
1290 		return false;
1291 
1292 	return true;
1293 }
1294 
1295 static int
1296 hammer2_version_get(struct m_vnode *vp)
1297 {
1298 	hammer2_dev_t *hmp;
1299 
1300 	hmp = VTOI(vp)->pmp->pfs_hmps[0];
1301 	if (hmp == NULL)
1302 		return EINVAL;
1303 
1304 	printf("version: %d\n", hmp->voldata.version);
1305 
1306 	return 0;
1307 }
1308 
1309 struct pfs_entry {
1310 	TAILQ_ENTRY(pfs_entry) entry;
1311 	char name[NAME_MAX+1];
1312 	char s[NAME_MAX+1];
1313 };
1314 
1315 static int
1316 hammer2_pfs_get(struct m_vnode *vp)
1317 {
1318 	hammer2_ioc_pfs_t pfs;
1319 	TAILQ_HEAD(, pfs_entry) head;
1320 	struct pfs_entry *p, *e;
1321 	char *pfs_id_str;
1322 	const char *type_str;
1323 	int error;
1324 
1325 	bzero(&pfs, sizeof(pfs));
1326 	TAILQ_INIT(&head);
1327 
1328 	while ((pfs.name_key = pfs.name_next) != (hammer2_key_t)-1) {
1329 		error = hammer2_ioctl_pfs_get(VTOI(vp), &pfs);
1330 		if (error)
1331 			return error;
1332 
1333 		pfs_id_str = NULL;
1334 		hammer2_uuid_to_str(&pfs.pfs_clid, &pfs_id_str);
1335 
1336 		if (pfs.pfs_type == HAMMER2_PFSTYPE_MASTER) {
1337 			if (pfs.pfs_subtype == HAMMER2_PFSSUBTYPE_NONE)
1338 				type_str = "MASTER";
1339 			else
1340 				type_str = hammer2_pfssubtype_to_str(
1341 				    pfs.pfs_subtype);
1342 		} else {
1343 			type_str = hammer2_pfstype_to_str(pfs.pfs_type);
1344 		}
1345 		e = ecalloc(1, sizeof(*e));
1346 		snprintf(e->name, sizeof(e->name), "%s", pfs.name);
1347 		snprintf(e->s, sizeof(e->s), "%-11s %s", type_str, pfs_id_str);
1348 		free(pfs_id_str);
1349 
1350 		p = TAILQ_FIRST(&head);
1351 		while (p) {
1352 			if (strcmp(e->name, p->name) <= 0) {
1353 				TAILQ_INSERT_BEFORE(p, e, entry);
1354 				break;
1355 			}
1356 			p = TAILQ_NEXT(p, entry);
1357 		}
1358 		if (!p)
1359 			TAILQ_INSERT_TAIL(&head, e, entry);
1360 	}
1361 
1362 	printf("Type        "
1363 	    "ClusterId (pfs_clid)                 "
1364 	    "Label\n");
1365 	while ((p = TAILQ_FIRST(&head)) != NULL) {
1366 		printf("%s %s\n", p->s, p->name);
1367 		TAILQ_REMOVE(&head, p, entry);
1368 		free(p);
1369 	}
1370 
1371 	return 0;
1372 }
1373 
1374 static int
1375 hammer2_pfs_lookup(struct m_vnode *vp, const char *pfs_name)
1376 {
1377 	hammer2_ioc_pfs_t pfs;
1378 	char *pfs_id_str;
1379 	int error;
1380 
1381 	bzero(&pfs, sizeof(pfs));
1382 	strlcpy(pfs.name, pfs_name, sizeof(pfs.name));
1383 
1384 	error = hammer2_ioctl_pfs_lookup(VTOI(vp), &pfs);
1385 	if (error == 0) {
1386 		printf("name: %s\n", pfs.name);
1387 		printf("type: %s\n", hammer2_pfstype_to_str(pfs.pfs_type));
1388 		printf("subtype: %s\n",
1389 		    hammer2_pfssubtype_to_str(pfs.pfs_subtype));
1390 
1391 		pfs_id_str = NULL;
1392 		hammer2_uuid_to_str(&pfs.pfs_fsid, &pfs_id_str);
1393 		printf("fsid: %s\n", pfs_id_str);
1394 		free(pfs_id_str);
1395 
1396 		pfs_id_str = NULL;
1397 		hammer2_uuid_to_str(&pfs.pfs_clid, &pfs_id_str);
1398 		printf("clid: %s\n", pfs_id_str);
1399 		free(pfs_id_str);
1400 	}
1401 
1402 	return error;
1403 }
1404 
1405 static int
1406 hammer2_pfs_create(struct m_vnode *vp, const char *pfs_name)
1407 {
1408 	hammer2_ioc_pfs_t pfs;
1409 	int error;
1410 
1411 	bzero(&pfs, sizeof(pfs));
1412 	strlcpy(pfs.name, pfs_name, sizeof(pfs.name));
1413 	pfs.pfs_type = HAMMER2_PFSTYPE_MASTER;
1414 	uuid_create(&pfs.pfs_clid, NULL);
1415 	uuid_create(&pfs.pfs_fsid, NULL);
1416 
1417 	error = hammer2_ioctl_pfs_create(VTOI(vp), &pfs);
1418 	if (error == EEXIST)
1419 		fprintf(stderr,
1420 		    "NOTE: Typically the same name is "
1421 		    "used for cluster elements on "
1422 		    "different mounts,\n"
1423 		    "      but cluster elements on the "
1424 		    "same mount require unique names.\n"
1425 		    "hammer2: pfs_create(%s): already present\n",
1426 		    pfs_name);
1427 
1428 	return error;
1429 }
1430 
1431 static int
1432 hammer2_pfs_delete(struct m_vnode *vp, const char *pfs_name)
1433 {
1434 	hammer2_ioc_pfs_t pfs;
1435 
1436 	bzero(&pfs, sizeof(pfs));
1437 	strlcpy(pfs.name, pfs_name, sizeof(pfs.name));
1438 
1439 	return hammer2_ioctl_pfs_delete(VTOI(vp), &pfs);
1440 }
1441 
1442 static int
1443 hammer2_pfs_snapshot(struct m_vnode *vp, const char *pfs_name,
1444     const char *mount_label)
1445 {
1446 	hammer2_ioc_pfs_t pfs;
1447 	struct tm *tp;
1448 	time_t t;
1449 
1450 	bzero(&pfs, sizeof(pfs));
1451 	strlcpy(pfs.name, pfs_name, sizeof(pfs.name));
1452 
1453 	if (strlen(pfs.name) == 0) {
1454 		time(&t);
1455 		tp = localtime(&t);
1456 		snprintf(pfs.name, sizeof(pfs.name),
1457 		    "%s.%04d%02d%02d.%02d%02d%02d",
1458 		    mount_label,
1459 		    tp->tm_year + 1900,
1460 		    tp->tm_mon + 1,
1461 		    tp->tm_mday,
1462 		    tp->tm_hour,
1463 		    tp->tm_min,
1464 		    tp->tm_sec);
1465 	}
1466 
1467 	return hammer2_ioctl_pfs_snapshot(VTOI(vp), &pfs);
1468 }
1469 
1470 static int
1471 hammer2_inode_getx(struct m_vnode *dvp, const char *f)
1472 {
1473 	hammer2_ioc_inode_t inode;
1474 	hammer2_inode_t *ip;
1475 	hammer2_inode_meta_t *meta;
1476 	struct m_vnode *vp;
1477 	char *o, *p, *name, *str = NULL;
1478 	char tmp[PATH_MAX];
1479 	int error;
1480 	uuid_t uuid;
1481 
1482 	assert(strlen(f) > 0);
1483 	o = p = name = strdup(f);
1484 
1485 	error = trim_slash(p);
1486 	if (error)
1487 		return error;
1488 	if (strlen(p) == 0) {
1489 		vp = dvp;
1490 		goto start_ioctl;
1491 	}
1492 
1493 	while ((p = strchr(p, '/')) != NULL) {
1494 		*p++ = 0; /* NULL terminate name */
1495 		if (!strcmp(name, ".")) {
1496 			name = p;
1497 			continue;
1498 		}
1499 		vp = NULL;
1500 		error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1501 		if (error)
1502 			return error;
1503 
1504 		ip = VTOI(vp);
1505 		switch (ip->meta.type) {
1506 		case HAMMER2_OBJTYPE_DIRECTORY:
1507 			break;
1508 		case HAMMER2_OBJTYPE_SOFTLINK:
1509 			bzero(tmp, sizeof(tmp));
1510 			error = hammer2_readlink(vp, tmp, sizeof(tmp));
1511 			if (error)
1512 				return error;
1513 			if (!is_supported_link(tmp))
1514 				return EINVAL;
1515 			strlcat(tmp, "/", sizeof(tmp));
1516 			strlcat(tmp, p, sizeof(tmp));
1517 			error = trim_slash(tmp);
1518 			if (error)
1519 				return error;
1520 			p = name = tmp;
1521 			continue;
1522 		default:
1523 			return EINVAL;
1524 		}
1525 
1526 		dvp = vp;
1527 		name = p;
1528 	}
1529 
1530 	error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1531 	if (error)
1532 		return error;
1533 start_ioctl:
1534 	bzero(&inode, sizeof(inode));
1535 	error = hammer2_ioctl_inode_get(VTOI(vp), &inode);
1536 	if (error)
1537 		return error;
1538 
1539 	meta = &inode.ip_data.meta;
1540 	printf("--------------------\n");
1541 	printf("flags = 0x%x\n", inode.flags);
1542 	printf("data_count = %ju\n", (uintmax_t)inode.data_count);
1543 	printf("inode_count = %ju\n", (uintmax_t)inode.inode_count);
1544 	printf("--------------------\n");
1545 	printf("version = %u\n", meta->version);
1546 	printf("pfs_subtype = %u (%s)\n", meta->pfs_subtype,
1547 	    hammer2_pfssubtype_to_str(meta->pfs_subtype));
1548 	printf("uflags = 0x%x\n", (unsigned int)meta->uflags);
1549 	printf("rmajor = %u\n", meta->rmajor);
1550 	printf("rminor = %u\n", meta->rminor);
1551 	printf("ctime = %s\n", hammer2_time64_to_str(meta->ctime, &str));
1552 	printf("mtime = %s\n", hammer2_time64_to_str(meta->mtime, &str));
1553 	printf("atime = %s\n", hammer2_time64_to_str(meta->atime, &str));
1554 	printf("btime = %s\n", hammer2_time64_to_str(meta->btime, &str));
1555 	uuid = meta->uid;
1556 	printf("uid = %s\n", hammer2_uuid_to_str(&uuid, &str));
1557 	uuid = meta->gid;
1558 	printf("gid = %s\n", hammer2_uuid_to_str(&uuid, &str));
1559 	printf("type = %u (%s)\n", meta->type,
1560 	    hammer2_iptype_to_str(meta->type));
1561 	printf("op_flags = 0x%x\n", meta->op_flags);
1562 	printf("cap_flags = 0x%x\n", meta->cap_flags);
1563 	printf("mode = 0%o\n", meta->mode);
1564 	printf("inum = 0x%jx\n", (uintmax_t)meta->inum);
1565 	printf("size = %ju\n", (uintmax_t)meta->size);
1566 	printf("nlinks = %ju\n", (uintmax_t)meta->nlinks);
1567 	printf("iparent = 0x%jx\n", (uintmax_t)meta->iparent);
1568 	printf("name_key = 0x%jx\n", (uintmax_t)meta->name_key);
1569 	printf("name_len = %u\n", meta->name_len);
1570 	printf("ncopies = %u\n", meta->ncopies);
1571 	printf("comp_algo = 0x%jx\n", (uintmax_t)meta->comp_algo);
1572 	printf("target_type = %u\n", meta->target_type);
1573 	printf("check_algo = %u\n", meta->check_algo);
1574 	printf("pfs_nmasters = %u\n", meta->pfs_nmasters);
1575 	printf("pfs_type = %u (%s)\n", meta->pfs_type,
1576 	    hammer2_pfstype_to_str(meta->pfs_type));
1577 	printf("pfs_inum = 0x%jx\n", (uintmax_t)meta->pfs_inum);
1578 	uuid = meta->pfs_clid;
1579 	printf("pfs_clid = %s\n", hammer2_uuid_to_str(&uuid, &str));
1580 	uuid = meta->pfs_fsid;
1581 	printf("pfs_fsid = %s\n", hammer2_uuid_to_str(&uuid, &str));
1582 	printf("data_quota = 0x%jx\n", (uintmax_t)meta->data_quota);
1583 	printf("inode_quota = 0x%jx\n", (uintmax_t)meta->inode_quota);
1584 	printf("pfs_lsnap_tid = 0x%jx\n", (uintmax_t)meta->pfs_lsnap_tid);
1585 	printf("decrypt_check = 0x%jx\n", (uintmax_t)meta->decrypt_check);
1586 	printf("--------------------\n");
1587 
1588 	free(o);
1589 
1590 	return error;
1591 }
1592 
1593 static int
1594 hammer2_inode_setcheck(struct m_vnode *dvp, const char *f)
1595 {
1596 	hammer2_ioc_inode_t inode;
1597 	hammer2_inode_t *ip;
1598 	struct m_vnode *vp;
1599 	char *o, *p, *name, *check_algo_str;
1600 	char tmp[PATH_MAX];
1601 	const char *checks[] = { "none", "disabled", "crc32", "xxhash64",
1602 	    "sha192", };
1603 	int check_algo_idx, error;
1604 	uint8_t check_algo;
1605 
1606 	assert(strlen(f) > 0);
1607 	o = p = strdup(f);
1608 
1609 	p = strrchr(p, ':');
1610 	if (p == NULL)
1611 		return EINVAL;
1612 
1613 	*p++ = 0; /* NULL terminate path */
1614 	check_algo_str = p;
1615 	name = p = o;
1616 
1617 	/* fail if already empty before trim */
1618 	if (strlen(p) == 0)
1619 		return EINVAL;
1620 
1621 	error = trim_slash(p);
1622 	if (error)
1623 		return error;
1624 	if (strlen(check_algo_str) == 0)
1625 		return EINVAL;
1626 
1627 	/* convert check_algo_str to check_algo_idx */
1628 	check_algo_idx = nitems(checks);
1629 	while (--check_algo_idx >= 0)
1630 		if (strcasecmp(check_algo_str, checks[check_algo_idx]) == 0)
1631 			break;
1632 	if (check_algo_idx < 0) {
1633 		if (strcasecmp(check_algo_str, "default") == 0) {
1634 			check_algo_str = "xxhash64";
1635 			check_algo_idx = HAMMER2_CHECK_XXHASH64;
1636 		} else if (strcasecmp(check_algo_str, "disabled") == 0) {
1637 			check_algo_str = "disabled";
1638 			check_algo_idx = HAMMER2_CHECK_DISABLED;
1639 		} else {
1640 			printf("invalid check_algo_str: %s\n", check_algo_str);
1641 			return EINVAL;
1642 		}
1643 	}
1644 	check_algo = HAMMER2_ENC_ALGO(check_algo_idx);
1645 	printf("change %s to algo %d (%s)\n", p, check_algo, check_algo_str);
1646 
1647 	if (strlen(p) == 0) {
1648 		vp = dvp;
1649 		goto start_ioctl;
1650 	}
1651 
1652 	while ((p = strchr(p, '/')) != NULL) {
1653 		*p++ = 0; /* NULL terminate name */
1654 		if (!strcmp(name, ".")) {
1655 			name = p;
1656 			continue;
1657 		}
1658 		vp = NULL;
1659 		error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1660 		if (error)
1661 			return error;
1662 
1663 		ip = VTOI(vp);
1664 		switch (ip->meta.type) {
1665 		case HAMMER2_OBJTYPE_DIRECTORY:
1666 			break;
1667 		case HAMMER2_OBJTYPE_SOFTLINK:
1668 			bzero(tmp, sizeof(tmp));
1669 			error = hammer2_readlink(vp, tmp, sizeof(tmp));
1670 			if (error)
1671 				return error;
1672 			if (!is_supported_link(tmp))
1673 				return EINVAL;
1674 			strlcat(tmp, "/", sizeof(tmp));
1675 			strlcat(tmp, p, sizeof(tmp));
1676 			error = trim_slash(tmp);
1677 			if (error)
1678 				return error;
1679 			p = name = tmp;
1680 			continue;
1681 		default:
1682 			return EINVAL;
1683 		}
1684 
1685 		dvp = vp;
1686 		name = p;
1687 	}
1688 
1689 	error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1690 	if (error)
1691 		return error;
1692 start_ioctl:
1693 	ip = VTOI(vp);
1694 
1695 	bzero(&inode, sizeof(inode));
1696 	error = hammer2_ioctl_inode_get(ip, &inode);
1697 	if (error)
1698 		return error;
1699 
1700 	inode.flags |= HAMMER2IOC_INODE_FLAG_CHECK;
1701 	inode.ip_data.meta.check_algo = check_algo;
1702 	error = hammer2_ioctl_inode_set(ip, &inode);
1703 	if (error)
1704 		return error;
1705 
1706 	free(o);
1707 
1708 	return error;
1709 }
1710 
1711 static int
1712 hammer2_inode_setcomp(struct m_vnode *dvp, const char *f)
1713 {
1714 	hammer2_ioc_inode_t inode;
1715 	hammer2_inode_t *ip;
1716 	struct m_vnode *vp;
1717 	char *o, *p, *name, *comp_algo_str, *comp_level_str;
1718 	char tmp[PATH_MAX];
1719 	const char *comps[] = { "none", "autozero", "lz4", "zlib", };
1720 	int comp_algo_idx, comp_level_idx, error;
1721 	uint8_t comp_algo, comp_level;
1722 
1723 	assert(strlen(f) > 0);
1724 	o = p = strdup(f);
1725 
1726 	p = strrchr(p, ':');
1727 	if (p == NULL)
1728 		return EINVAL;
1729 
1730 	*p++ = 0; /* NULL terminate comp_algo_str */
1731 	comp_level_str = p;
1732 	p = o;
1733 
1734 	p = strrchr(p, ':');
1735 	if (p == NULL) {
1736 		/* comp_level_str not specified */
1737 		comp_algo_str = comp_level_str;
1738 		comp_level_str = NULL;
1739 	} else {
1740 		*p++ = 0; /* NULL terminate path */
1741 		comp_algo_str = p;
1742 	}
1743 	name = p = o;
1744 
1745 	/* fail if already empty before trim */
1746 	if (strlen(p) == 0)
1747 		return EINVAL;
1748 
1749 	error = trim_slash(p);
1750 	if (error)
1751 		return error;
1752 	if (strlen(comp_algo_str) == 0)
1753 		return EINVAL;
1754 
1755 	/* convert comp_algo_str to comp_algo_idx */
1756 	comp_algo_idx = nitems(comps);
1757 	while (--comp_algo_idx >= 0)
1758 		if (strcasecmp(comp_algo_str, comps[comp_algo_idx]) == 0)
1759 			break;
1760 	if (comp_algo_idx < 0) {
1761 		if (strcasecmp(comp_algo_str, "default") == 0) {
1762 			comp_algo_str = "lz4";
1763 			comp_algo_idx = HAMMER2_COMP_LZ4;
1764 		} else if (strcasecmp(comp_algo_str, "disabled") == 0) {
1765 			comp_algo_str = "autozero";
1766 			comp_algo_idx = HAMMER2_COMP_AUTOZERO;
1767 		} else {
1768 			printf("invalid comp_algo_str: %s\n", comp_algo_str);
1769 			return EINVAL;
1770 		}
1771 	}
1772 	comp_algo = HAMMER2_ENC_ALGO(comp_algo_idx);
1773 
1774 	/* convert comp_level_str to comp_level_idx */
1775 	if (comp_level_str == NULL) {
1776 		comp_level_idx = 0;
1777 	} else if (isdigit((int)comp_level_str[0])) {
1778 		comp_level_idx = strtol(comp_level_str, NULL, 0);
1779 	} else if (strcasecmp(comp_level_str, "default") == 0) {
1780 		comp_level_idx = 0;
1781 	} else {
1782 		printf("invalid comp_level_str: %s\n", comp_level_str);
1783 		return EINVAL;
1784 	}
1785 	if (comp_level_idx) {
1786 		switch (comp_algo) {
1787 		case HAMMER2_COMP_ZLIB:
1788 			if (comp_level_idx < 6 || comp_level_idx > 9) {
1789 				printf("unsupported comp_level %d for %s\n",
1790 				    comp_level_idx, comp_algo_str);
1791 				return EINVAL;
1792 			}
1793 			break;
1794 		default:
1795 			printf("unsupported comp_level %d for %s\n",
1796 			    comp_level_idx, comp_algo_str);
1797 			return EINVAL;
1798 		}
1799 	}
1800 	comp_level = HAMMER2_ENC_LEVEL(comp_level_idx);
1801 	printf("change %s to algo %d (%s) level %d\n",
1802 	    p, comp_algo, comp_algo_str, comp_level_idx);
1803 
1804 	if (strlen(p) == 0) {
1805 		vp = dvp;
1806 		goto start_ioctl;
1807 	}
1808 
1809 	while ((p = strchr(p, '/')) != NULL) {
1810 		*p++ = 0; /* NULL terminate name */
1811 		if (!strcmp(name, ".")) {
1812 			name = p;
1813 			continue;
1814 		}
1815 		vp = NULL;
1816 		error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1817 		if (error)
1818 			return error;
1819 
1820 		ip = VTOI(vp);
1821 		switch (ip->meta.type) {
1822 		case HAMMER2_OBJTYPE_DIRECTORY:
1823 			break;
1824 		case HAMMER2_OBJTYPE_SOFTLINK:
1825 			bzero(tmp, sizeof(tmp));
1826 			error = hammer2_readlink(vp, tmp, sizeof(tmp));
1827 			if (error)
1828 				return error;
1829 			if (!is_supported_link(tmp))
1830 				return EINVAL;
1831 			strlcat(tmp, "/", sizeof(tmp));
1832 			strlcat(tmp, p, sizeof(tmp));
1833 			error = trim_slash(tmp);
1834 			if (error)
1835 				return error;
1836 			p = name = tmp;
1837 			continue;
1838 		default:
1839 			return EINVAL;
1840 		}
1841 
1842 		dvp = vp;
1843 		name = p;
1844 	}
1845 
1846 	error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1847 	if (error)
1848 		return error;
1849 start_ioctl:
1850 	ip = VTOI(vp);
1851 
1852 	bzero(&inode, sizeof(inode));
1853 	error = hammer2_ioctl_inode_get(ip, &inode);
1854 	if (error)
1855 		return error;
1856 
1857 	inode.flags |= HAMMER2IOC_INODE_FLAG_COMP;
1858 	inode.ip_data.meta.comp_algo = comp_algo | comp_level;
1859 	error = hammer2_ioctl_inode_set(ip, &inode);
1860 	if (error)
1861 		return error;
1862 
1863 	free(o);
1864 
1865 	return error;
1866 }
1867 
1868 static int
1869 hammer2_bulkfree(struct m_vnode *vp)
1870 {
1871 	hammer2_ioc_bulkfree_t bfi;
1872 	size_t usermem;
1873 	size_t usermem_size = sizeof(usermem);
1874 
1875 	bzero(&bfi, sizeof(bfi));
1876 	usermem = 0;
1877 	if (sysctlbyname("hw.usermem", &usermem, &usermem_size, NULL, 0) == 0)
1878 		bfi.size = usermem / 16;
1879 	else
1880 		bfi.size = 0;
1881 	if (bfi.size < 8192 * 1024)
1882 		bfi.size = 8192 * 1024;
1883 
1884 	return hammer2_ioctl_bulkfree_scan(VTOI(vp), &bfi);
1885 }
1886 
1887 static int
1888 hammer2_destroy_path(struct m_vnode *dvp, const char *f)
1889 {
1890 	hammer2_ioc_destroy_t destroy;
1891 	hammer2_inode_t *ip;
1892 	struct m_vnode *vp;
1893 	char *o, *p, *name;
1894 	char tmp[PATH_MAX];
1895 	int error;
1896 
1897 	assert(strlen(f) > 0);
1898 	o = p = name = strdup(f);
1899 
1900 	error = trim_slash(p);
1901 	if (error)
1902 		return error;
1903 	if (strlen(p) == 0)
1904 		return EINVAL;
1905 
1906 	while ((p = strchr(p, '/')) != NULL) {
1907 		*p++ = 0; /* NULL terminate name */
1908 		if (!strcmp(name, ".")) {
1909 			name = p;
1910 			continue;
1911 		}
1912 		vp = NULL;
1913 		error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1914 		if (error)
1915 			return error;
1916 
1917 		ip = VTOI(vp);
1918 		switch (ip->meta.type) {
1919 		case HAMMER2_OBJTYPE_DIRECTORY:
1920 			break;
1921 		case HAMMER2_OBJTYPE_SOFTLINK:
1922 			bzero(tmp, sizeof(tmp));
1923 			error = hammer2_readlink(vp, tmp, sizeof(tmp));
1924 			if (error)
1925 				return error;
1926 			if (!is_supported_link(tmp))
1927 				return EINVAL;
1928 			strlcat(tmp, "/", sizeof(tmp));
1929 			strlcat(tmp, p, sizeof(tmp));
1930 			error = trim_slash(tmp);
1931 			if (error)
1932 				return error;
1933 			p = name = tmp;
1934 			continue;
1935 		default:
1936 			return EINVAL;
1937 		}
1938 
1939 		dvp = vp;
1940 		name = p;
1941 	}
1942 
1943 	/* XXX When does (or why does not) ioctl modify this inode ? */
1944 	hammer2_inode_modify(VTOI(dvp));
1945 
1946 	bzero(&destroy, sizeof(destroy));
1947 	destroy.cmd = HAMMER2_DELETE_FILE;
1948 	snprintf(destroy.path, sizeof(destroy.path), "%s", name);
1949 
1950 	printf("%s\t", f);
1951 	fflush(stdout);
1952 
1953 	error = hammer2_ioctl_destroy(VTOI(dvp), &destroy);
1954 	if (error)
1955 		printf("%s\n", strerror(error));
1956 	else
1957 		printf("ok\n");
1958 	free(o);
1959 
1960 	return error;
1961 }
1962 
1963 static int
1964 hammer2_destroy_inum(struct m_vnode *vp, hammer2_tid_t inum)
1965 {
1966 	hammer2_ioc_destroy_t destroy;
1967 	int error;
1968 
1969 	bzero(&destroy, sizeof(destroy));
1970 	destroy.cmd = HAMMER2_DELETE_INUM;
1971 	destroy.inum = inum;
1972 
1973 	printf("%jd\t", (intmax_t)destroy.inum);
1974 	fflush(stdout);
1975 
1976 	error = hammer2_ioctl_destroy(VTOI(vp), &destroy);
1977 	if (error)
1978 		printf("%s\n", strerror(error));
1979 	else
1980 		printf("ok\n");
1981 
1982 	return error;
1983 }
1984 
1985 static int
1986 hammer2_growfs(struct m_vnode *vp, hammer2_off_t size)
1987 {
1988 	hammer2_ioc_growfs_t growfs;
1989 	int error;
1990 
1991 	bzero(&growfs, sizeof(growfs));
1992 	growfs.size = size;
1993 
1994 	error = hammer2_ioctl_growfs(VTOI(vp), &growfs, NULL);
1995 	if (!error) {
1996 		if (growfs.modified)
1997 			printf("grown to %016jx\n", (intmax_t)growfs.size);
1998 		else
1999 			printf("no size change - %016jx\n",
2000 			    (intmax_t)growfs.size);
2001 	}
2002 
2003 	return error;
2004 }
2005 
2006 struct hammer2_link {
2007 	TAILQ_ENTRY(hammer2_link) entry;
2008 	hammer2_tid_t inum;
2009 	uint64_t nlinks;
2010 	char path[PATH_MAX];
2011 };
2012 
2013 TAILQ_HEAD(hammer2_linkq, hammer2_link);
2014 
2015 static void
2016 hammer2_linkq_init(struct hammer2_linkq *linkq)
2017 {
2018 	TAILQ_INIT(linkq);
2019 }
2020 
2021 static void
2022 hammer2_linkq_cleanup(struct hammer2_linkq *linkq, bool is_root)
2023 {
2024 	struct hammer2_link *e;
2025 	int count = 0;
2026 
2027 	/*
2028 	 * If is_root is true, linkq must be empty, or link count is broken.
2029 	 * Note that if an image was made by makefs, hardlinks in the source
2030 	 * directory became hardlinks in the image only if >1 links existed under
2031 	 * that directory, as makefs doesn't determine hardlink via link count.
2032 	 */
2033 	while ((e = TAILQ_FIRST(linkq)) != NULL) {
2034 		count++;
2035 		TAILQ_REMOVE(linkq, e, entry);
2036 		free(e);
2037 	}
2038 	assert(TAILQ_EMPTY(linkq));
2039 
2040 	if (count && is_root)
2041 		errx(1, "%d link entries remained", count);
2042 }
2043 
2044 static void
2045 hammer2_linkq_add(struct hammer2_linkq *linkq, hammer2_tid_t inum,
2046     uint64_t nlinks, const char *path)
2047 {
2048 	struct hammer2_link *e;
2049 	int count = 0;
2050 
2051 	e = ecalloc(1, sizeof(*e));
2052 	e->inum = inum;
2053 	e->nlinks = nlinks;
2054 	strlcpy(e->path, path, sizeof(e->path));
2055 	TAILQ_INSERT_TAIL(linkq, e, entry);
2056 
2057 	TAILQ_FOREACH(e, linkq, entry)
2058 		if (e->inum == inum)
2059 			count++;
2060 	if (count > 1)
2061 		errx(1, "%d link entries exist for inum %jd",
2062 		    count, (intmax_t)inum);
2063 }
2064 
2065 static void
2066 hammer2_linkq_del(struct hammer2_linkq *linkq, hammer2_tid_t inum)
2067 {
2068 	struct hammer2_link *e, *next;
2069 
2070 	TAILQ_FOREACH_MUTABLE(e, linkq, entry, next)
2071 		if (e->inum == inum) {
2072 			e->nlinks--;
2073 			if (e->nlinks == 1) {
2074 				TAILQ_REMOVE(linkq, e, entry);
2075 				free(e);
2076 			}
2077 		}
2078 }
2079 
2080 static void
2081 hammer2_utimes(struct m_vnode *vp, const char *f)
2082 {
2083 	hammer2_inode_t *ip = VTOI(vp);
2084 	struct timeval tv[2];
2085 
2086 	hammer2_time_to_timeval(ip->meta.atime, &tv[0]);
2087 	hammer2_time_to_timeval(ip->meta.mtime, &tv[1]);
2088 
2089 	utimes(f, tv); /* ignore failure */
2090 }
2091 
2092 static int
2093 hammer2_readx_directory(struct m_vnode *dvp, const char *dir, const char *name,
2094     struct hammer2_linkq *linkq)
2095 {
2096 	struct m_vnode *vp;
2097 	struct dirent *dp;
2098 	struct stat st;
2099 	char *buf, tmp[PATH_MAX];
2100 	off_t offset = 0;
2101 	int ndirent = 0;
2102 	int eofflag = 0;
2103 	int i, error;
2104 
2105 	snprintf(tmp, sizeof(tmp), "%s/%s", dir, name);
2106 	if (stat(tmp, &st) == -1 && mkdir(tmp, 0666) == -1)
2107 		err(1, "failed to mkdir %s", tmp);
2108 
2109 	buf = ecalloc(1, HAMMER2_PBUFSIZE);
2110 
2111 	while (!eofflag) {
2112 		error = hammer2_readdir(dvp, buf, HAMMER2_PBUFSIZE, &offset,
2113 		    &ndirent, &eofflag);
2114 		if (error)
2115 			errx(1, "failed to readdir");
2116 		dp = (void *)buf;
2117 
2118 		for (i = 0; i < ndirent; i++) {
2119 			if (strcmp(dp->d_name, ".") &&
2120 			    strcmp(dp->d_name, "..")) {
2121 				error = hammer2_nresolve(dvp, &vp, dp->d_name,
2122 				    strlen(dp->d_name));
2123 				if (error)
2124 					return error;
2125 				error = hammer2_readx_handle(vp, tmp,
2126 				    dp->d_name, linkq);
2127 				if (error)
2128 					return error;
2129 			}
2130 			dp = (void *)((char *)dp +
2131 			    _DIRENT_RECLEN(dp->d_namlen));
2132 		}
2133 	}
2134 
2135 	free(buf);
2136 	hammer2_utimes(dvp, tmp);
2137 
2138 	return 0;
2139 }
2140 
2141 static int
2142 hammer2_readx_link(struct m_vnode *vp, const char *src, const char *lnk,
2143     struct hammer2_linkq *linkq)
2144 {
2145 	hammer2_inode_t *ip = VTOI(vp);
2146 	struct stat st;
2147 	int error;
2148 
2149 	if (!stat(lnk, &st)) {
2150 		error = unlink(lnk);
2151 		if (error)
2152 			return error;
2153 	}
2154 
2155 	error = link(src, lnk);
2156 	if (error)
2157 		return error;
2158 
2159 	hammer2_linkq_del(linkq, ip->meta.inum);
2160 
2161 	return 0;
2162 }
2163 
2164 static int
2165 hammer2_readx_regfile(struct m_vnode *vp, const char *dir, const char *name,
2166     struct hammer2_linkq *linkq)
2167 {
2168 	hammer2_inode_t *ip = VTOI(vp);
2169 	struct hammer2_link *e;
2170 	char *buf, out[PATH_MAX];
2171 	size_t resid, n;
2172 	off_t offset;
2173 	int fd, error;
2174 	bool found = false;
2175 
2176 	snprintf(out, sizeof(out), "%s/%s", dir, name);
2177 
2178 	if (ip->meta.nlinks > 1) {
2179 		TAILQ_FOREACH(e, linkq, entry)
2180 			if (e->inum == ip->meta.inum) {
2181 				found = true;
2182 				error = hammer2_readx_link(vp, e->path, out,
2183 				    linkq);
2184 				if (error == 0)
2185 					return 0;
2186 				/* ignore failure */
2187 			}
2188 		if (!found)
2189 			hammer2_linkq_add(linkq, ip->meta.inum, ip->meta.nlinks,
2190 			    out);
2191 	}
2192 
2193 	fd = open(out, O_WRONLY | O_CREAT | O_TRUNC, 0666);
2194 	if (fd == -1)
2195 		err(1, "failed to create %s", out);
2196 
2197 	buf = ecalloc(1, HAMMER2_PBUFSIZE);
2198 	resid = ip->meta.size;
2199 	offset = 0;
2200 
2201 	while (resid > 0) {
2202 		bzero(buf, HAMMER2_PBUFSIZE);
2203 		error = hammer2_read(vp, buf, HAMMER2_PBUFSIZE, offset);
2204 		if (error)
2205 			errx(1, "failed to read from %s", name);
2206 
2207 		n = resid >= HAMMER2_PBUFSIZE ? HAMMER2_PBUFSIZE : resid;
2208 		error = write(fd, buf, n);
2209 		if (error == -1)
2210 			err(1, "failed to write to %s", out);
2211 		else if (error != n)
2212 			return EINVAL;
2213 
2214 		resid -= n;
2215 		offset += HAMMER2_PBUFSIZE;
2216 	}
2217 	fsync(fd);
2218 	close(fd);
2219 
2220 	free(buf);
2221 	hammer2_utimes(vp, out);
2222 
2223 	return 0;
2224 }
2225 
2226 static int
2227 hammer2_readx_handle(struct m_vnode *vp, const char *dir, const char *name,
2228     struct hammer2_linkq *linkq)
2229 {
2230 	hammer2_inode_t *ip = VTOI(vp);
2231 
2232 	switch (ip->meta.type) {
2233 	case HAMMER2_OBJTYPE_DIRECTORY:
2234 		return hammer2_readx_directory(vp, dir, name, linkq);
2235 	case HAMMER2_OBJTYPE_REGFILE:
2236 		return hammer2_readx_regfile(vp, dir, name, linkq);
2237 	default:
2238 		/* XXX */
2239 		printf("ignore inode %jd %s \"%s\"\n",
2240 		    (intmax_t)ip->meta.inum,
2241 		    hammer2_iptype_to_str(ip->meta.type),
2242 		    name);
2243 		return 0;
2244 	}
2245 	return EINVAL;
2246 }
2247 
2248 static int
2249 hammer2_readx(struct m_vnode *dvp, const char *dir, const char *f)
2250 {
2251 	hammer2_inode_t *ip;
2252 	struct hammer2_linkq linkq;
2253 	struct m_vnode *vp, *ovp = dvp;
2254 	char *o, *p, *name;
2255 	char tmp[PATH_MAX];
2256 	int error;
2257 
2258 	if (dir == NULL)
2259 		return EINVAL;
2260 
2261 	assert(strlen(f) > 0);
2262 	o = p = name = strdup(f);
2263 
2264 	error = trim_slash(p);
2265 	if (error)
2266 		return error;
2267 	if (strlen(p) == 0) {
2268 		vp = dvp;
2269 		goto start_read;
2270 	}
2271 
2272 	while ((p = strchr(p, '/')) != NULL) {
2273 		*p++ = 0; /* NULL terminate name */
2274 		if (!strcmp(name, ".")) {
2275 			name = p;
2276 			continue;
2277 		}
2278 		vp = NULL;
2279 		error = hammer2_nresolve(dvp, &vp, name, strlen(name));
2280 		if (error)
2281 			return error;
2282 
2283 		ip = VTOI(vp);
2284 		switch (ip->meta.type) {
2285 		case HAMMER2_OBJTYPE_DIRECTORY:
2286 			break;
2287 		case HAMMER2_OBJTYPE_SOFTLINK:
2288 			bzero(tmp, sizeof(tmp));
2289 			error = hammer2_readlink(vp, tmp, sizeof(tmp));
2290 			if (error)
2291 				return error;
2292 			if (!is_supported_link(tmp))
2293 				return EINVAL;
2294 			strlcat(tmp, "/", sizeof(tmp));
2295 			strlcat(tmp, p, sizeof(tmp));
2296 			error = trim_slash(tmp);
2297 			if (error)
2298 				return error;
2299 			p = name = tmp;
2300 			continue;
2301 		default:
2302 			return EINVAL;
2303 		}
2304 
2305 		dvp = vp;
2306 		name = p;
2307 	}
2308 
2309 	error = hammer2_nresolve(dvp, &vp, name, strlen(name));
2310 	if (error)
2311 		return error;
2312 start_read:
2313 	hammer2_linkq_init(&linkq);
2314 	error = hammer2_readx_handle(vp, dir, name, &linkq);
2315 	hammer2_linkq_cleanup(&linkq, vp == ovp);
2316 	if (error)
2317 		return error;
2318 
2319 	free(o);
2320 
2321 	return 0;
2322 }
2323 
2324 static void
2325 assert_trim_slash(const char *input, const char *expected)
2326 {
2327 	char tmp[PATH_MAX];
2328 	int error;
2329 
2330 	strlcpy(tmp, input, sizeof(tmp));
2331 	error = trim_slash(tmp);
2332 	if (error)
2333 		errx(1, "input \"%s\" error %d", input, error);
2334 
2335 	if (strncmp(tmp, expected, sizeof(tmp)))
2336 		errx(1, "input \"%s\" result \"%s\" vs expected \"%s\"",
2337 		    input, tmp, expected);
2338 }
2339 
2340 static void
2341 unittest_trim_slash(void)
2342 {
2343 	assert_trim_slash("", "");
2344 	assert_trim_slash("/", "");
2345 	assert_trim_slash("//", "");
2346 	assert_trim_slash("///", "");
2347 
2348 	assert_trim_slash("makefs", "makefs");
2349 	assert_trim_slash("/makefs", "makefs");
2350 	assert_trim_slash("//makefs", "makefs");
2351 	assert_trim_slash("makefs/", "makefs");
2352 	assert_trim_slash("makefs//", "makefs");
2353 	assert_trim_slash("/makefs/", "makefs");
2354 	assert_trim_slash("//makefs//", "makefs");
2355 
2356 	assert_trim_slash("sys/vfs/hammer2", "sys/vfs/hammer2");
2357 	assert_trim_slash("/sys/vfs/hammer2", "sys/vfs/hammer2");
2358 	assert_trim_slash("//sys/vfs/hammer2", "sys/vfs/hammer2");
2359 	assert_trim_slash("///sys/vfs/hammer2", "sys/vfs/hammer2");
2360 	assert_trim_slash("sys/vfs/hammer2/", "sys/vfs/hammer2");
2361 	assert_trim_slash("sys/vfs/hammer2//", "sys/vfs/hammer2");
2362 	assert_trim_slash("sys/vfs/hammer2///", "sys/vfs/hammer2");
2363 	assert_trim_slash("/sys/vfs/hammer2/", "sys/vfs/hammer2");
2364 	assert_trim_slash("//sys//vfs//hammer2//", "sys/vfs/hammer2");
2365 	assert_trim_slash("///sys///vfs///hammer2///", "sys/vfs/hammer2");
2366 
2367 	APRINTF("success\n");
2368 }
2369