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