xref: /dragonfly/usr.sbin/makefs/hammer2.c (revision 2de31a7d)
12d60b848STomohiro Kusumi /*
22d60b848STomohiro Kusumi  * SPDX-License-Identifier: BSD-3-Clause
32d60b848STomohiro Kusumi  *
42d60b848STomohiro Kusumi  * Copyright (c) 2022 Tomohiro Kusumi <tkusumi@netbsd.org>
52d60b848STomohiro Kusumi  * Copyright (c) 2011-2022 The DragonFly Project.  All rights reserved.
62d60b848STomohiro Kusumi  *
72d60b848STomohiro Kusumi  * Redistribution and use in source and binary forms, with or without
82d60b848STomohiro Kusumi  * modification, are permitted provided that the following conditions
92d60b848STomohiro Kusumi  * are met:
102d60b848STomohiro Kusumi  *
112d60b848STomohiro Kusumi  * 1. Redistributions of source code must retain the above copyright
122d60b848STomohiro Kusumi  *    notice, this list of conditions and the following disclaimer.
132d60b848STomohiro Kusumi  * 2. Redistributions in binary form must reproduce the above copyright
142d60b848STomohiro Kusumi  *    notice, this list of conditions and the following disclaimer in
152d60b848STomohiro Kusumi  *    the documentation and/or other materials provided with the
162d60b848STomohiro Kusumi  *    distribution.
172d60b848STomohiro Kusumi  * 3. Neither the name of The DragonFly Project nor the names of its
182d60b848STomohiro Kusumi  *    contributors may be used to endorse or promote products derived
192d60b848STomohiro Kusumi  *    from this software without specific, prior written permission.
202d60b848STomohiro Kusumi  *
212d60b848STomohiro Kusumi  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
222d60b848STomohiro Kusumi  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
232d60b848STomohiro Kusumi  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
242d60b848STomohiro Kusumi  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
252d60b848STomohiro Kusumi  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
262d60b848STomohiro Kusumi  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
272d60b848STomohiro Kusumi  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
282d60b848STomohiro Kusumi  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
292d60b848STomohiro Kusumi  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
302d60b848STomohiro Kusumi  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
312d60b848STomohiro Kusumi  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
322d60b848STomohiro Kusumi  * SUCH DAMAGE.
332d60b848STomohiro Kusumi  */
342d60b848STomohiro Kusumi 
352d60b848STomohiro Kusumi #if HAVE_NBTOOL_CONFIG_H
362d60b848STomohiro Kusumi #include "nbtool_config.h"
372d60b848STomohiro Kusumi #endif
382d60b848STomohiro Kusumi 
392d60b848STomohiro Kusumi #include <sys/param.h>
402d60b848STomohiro Kusumi #include <sys/stat.h>
412d60b848STomohiro Kusumi #include <sys/sysctl.h>
422d60b848STomohiro Kusumi #include <sys/mman.h>
432d60b848STomohiro Kusumi 
442d60b848STomohiro Kusumi #include <stdio.h>
452d60b848STomohiro Kusumi #include <stdlib.h>
462d60b848STomohiro Kusumi #include <stdbool.h>
472d60b848STomohiro Kusumi #include <string.h>
48465e1141STomohiro Kusumi #include <ctype.h>
492d60b848STomohiro Kusumi #include <unistd.h>
502d60b848STomohiro Kusumi #include <fcntl.h>
512d60b848STomohiro Kusumi #include <time.h>
522d60b848STomohiro Kusumi #include <err.h>
532d60b848STomohiro Kusumi #include <assert.h>
542d60b848STomohiro Kusumi #include <util.h>
552d60b848STomohiro Kusumi 
562d60b848STomohiro Kusumi #include "makefs.h"
572d60b848STomohiro Kusumi #include "hammer2.h"
582d60b848STomohiro Kusumi 
592d60b848STomohiro Kusumi #define APRINTF(X, ...)	\
602d60b848STomohiro Kusumi     printf("%s: " X, __func__, ## __VA_ARGS__)
612d60b848STomohiro Kusumi 
623999233bSTomohiro Kusumi static void hammer2_parse_pfs_opts(const char *, fsinfo_t *);
63f804c425STomohiro Kusumi static void hammer2_parse_inode_opts(const char *, fsinfo_t *);
642d60b848STomohiro Kusumi static void hammer2_dump_fsinfo(fsinfo_t *);
652d60b848STomohiro Kusumi static int hammer2_create_image(const char *, fsinfo_t *);
666bcbb706STomohiro Kusumi static int hammer2_populate_dir(struct m_vnode *, const char *, fsnode *,
672d60b848STomohiro Kusumi     fsnode *, fsinfo_t *, int);
682d60b848STomohiro Kusumi static void hammer2_validate(const char *, fsnode *, fsinfo_t *);
692d60b848STomohiro Kusumi static void hammer2_size_dir(fsnode *, fsinfo_t *);
706bcbb706STomohiro Kusumi static int hammer2_write_file(struct m_vnode *, const char *, fsnode *);
71f72350acSTomohiro Kusumi static int hammer2_version_get(struct m_vnode *);
729a149651STomohiro Kusumi static int hammer2_pfs_get(struct m_vnode *);
739a149651STomohiro Kusumi static int hammer2_pfs_lookup(struct m_vnode *, const char *);
749a149651STomohiro Kusumi static int hammer2_pfs_create(struct m_vnode *, const char *);
759a149651STomohiro Kusumi static int hammer2_pfs_delete(struct m_vnode *, const char *);
769a149651STomohiro Kusumi static int hammer2_pfs_snapshot(struct m_vnode *, const char *, const char *);
77465e1141STomohiro Kusumi static int hammer2_inode_getx(struct m_vnode *, const char *);
78465e1141STomohiro Kusumi static int hammer2_inode_setcheck(struct m_vnode *, const char *);
79465e1141STomohiro Kusumi static int hammer2_inode_setcomp(struct m_vnode *, const char *);
803999233bSTomohiro Kusumi static int hammer2_bulkfree(struct m_vnode *);
813999233bSTomohiro Kusumi static int hammer2_destroy_path(struct m_vnode *, const char *);
823999233bSTomohiro Kusumi static int hammer2_destroy_inum(struct m_vnode *, hammer2_tid_t);
833999233bSTomohiro Kusumi static int hammer2_growfs(struct m_vnode *, hammer2_off_t);
846857f034STomohiro Kusumi static void unittest_trim_slash(void);
852d60b848STomohiro Kusumi 
86ddd1d3d1STomohiro Kusumi fsnode *hammer2_curnode;
87ddd1d3d1STomohiro Kusumi 
882d60b848STomohiro Kusumi void
892d60b848STomohiro Kusumi hammer2_prep_opts(fsinfo_t *fsopts)
902d60b848STomohiro Kusumi {
912d60b848STomohiro Kusumi 	hammer2_makefs_options_t *h2_opt = ecalloc(1, sizeof(*h2_opt));
922d60b848STomohiro Kusumi 	hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
932d60b848STomohiro Kusumi 
942d60b848STomohiro Kusumi 	const option_t hammer2_options[] = {
952d60b848STomohiro Kusumi 		/* newfs_hammer2(8) compatible options */
962d60b848STomohiro Kusumi 		{ 'b', "BootAreaSize", NULL, OPT_STRBUF, 0, 0, "boot area size" },
972d60b848STomohiro Kusumi 		{ 'r', "AuxAreaSize", NULL, OPT_STRBUF, 0, 0, "aux area size" },
982d60b848STomohiro Kusumi 		{ 'V', "Hammer2Version", NULL, OPT_STRBUF, 0, 0, "file system version" },
992d60b848STomohiro Kusumi 		{ 'L', "Label", NULL, OPT_STRBUF, 0, 0, "PFS label" },
1002d60b848STomohiro Kusumi 		/* makefs(8) specific options */
1012d60b848STomohiro Kusumi 		{ 'm', "MountLabel", NULL, OPT_STRBUF, 0, 0, "destination PFS label" },
1022d60b848STomohiro Kusumi 		{ 'v', "NumVolhdr", &h2_opt->num_volhdr, OPT_INT32,
1032d60b848STomohiro Kusumi 		    1, HAMMER2_NUM_VOLHDRS, "number of volume headers" },
1042d60b848STomohiro Kusumi 		{ 'd', "Hammer2Debug", NULL, OPT_STRBUF, 0, 0, "debug tunable" },
10548ed4577STomohiro Kusumi 		{ 'E', "EmergencyMode", &h2_opt->emergency_mode, OPT_BOOL, 0, 0,
10648ed4577STomohiro Kusumi 		    "emergency mode" },
1073999233bSTomohiro Kusumi 		{ 'P', "PFS", NULL, OPT_STRBUF, 0, 0, "offline PFS" },
108f804c425STomohiro Kusumi 		{ 'I', "Inode", NULL, OPT_STRBUF, 0, 0, "offline inode" },
1099a149651STomohiro Kusumi 		{ 'B', "Bulkfree", NULL, OPT_STRBUF, 0, 0, "offline bulkfree" },
110917508cdSTomohiro Kusumi 		{ 'D', "Destroy", NULL, OPT_STRBUF, 0, 0, "offline destroy" },
1119a149651STomohiro Kusumi 		{ 'G', "Growfs", NULL, OPT_STRBUF, 0, 0, "offline growfs" },
1122d60b848STomohiro Kusumi 		{ .name = NULL },
1132d60b848STomohiro Kusumi 	};
1142d60b848STomohiro Kusumi 
1152d60b848STomohiro Kusumi 	hammer2_mkfs_init(opt);
1162d60b848STomohiro Kusumi 
1172d60b848STomohiro Kusumi 	/* make this tunable ? */
1182d60b848STomohiro Kusumi 	assert(opt->CompType == HAMMER2_COMP_NEWFS_DEFAULT);
1192d60b848STomohiro Kusumi 	assert(opt->CheckType == HAMMER2_CHECK_XXHASH64);
1202d60b848STomohiro Kusumi 
1212d60b848STomohiro Kusumi 	/* force debug mode for mkfs */
1222d60b848STomohiro Kusumi 	opt->DebugOpt = 1;
1232d60b848STomohiro Kusumi 
1242d60b848STomohiro Kusumi 	fsopts->fs_specific = h2_opt;
1252d60b848STomohiro Kusumi 	fsopts->fs_options = copy_opts(hammer2_options);
1262d60b848STomohiro Kusumi 	fsopts->sectorsize = DEV_BSIZE;
1272d60b848STomohiro Kusumi }
1282d60b848STomohiro Kusumi 
1292d60b848STomohiro Kusumi void
1302d60b848STomohiro Kusumi hammer2_cleanup_opts(fsinfo_t *fsopts)
1312d60b848STomohiro Kusumi {
1322d60b848STomohiro Kusumi 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
1332d60b848STomohiro Kusumi 	hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
1342d60b848STomohiro Kusumi 
1352d60b848STomohiro Kusumi 	hammer2_mkfs_cleanup(opt);
1362d60b848STomohiro Kusumi 
1372d60b848STomohiro Kusumi 	free(h2_opt);
1382d60b848STomohiro Kusumi 	free(fsopts->fs_options);
1392d60b848STomohiro Kusumi }
1402d60b848STomohiro Kusumi 
1412d60b848STomohiro Kusumi int
1422d60b848STomohiro Kusumi hammer2_parse_opts(const char *option, fsinfo_t *fsopts)
1432d60b848STomohiro Kusumi {
1442d60b848STomohiro Kusumi 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
1452d60b848STomohiro Kusumi 	hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
1462d60b848STomohiro Kusumi 
1472d60b848STomohiro Kusumi 	option_t *hammer2_options = fsopts->fs_options;
1482d60b848STomohiro Kusumi 	char buf[1024]; /* > HAMMER2_INODE_MAXNAME */
1492d60b848STomohiro Kusumi 	int i;
1502d60b848STomohiro Kusumi 
1512d60b848STomohiro Kusumi 	assert(option != NULL);
1522d60b848STomohiro Kusumi 	assert(fsopts != NULL);
1532d60b848STomohiro Kusumi 
1542d60b848STomohiro Kusumi 	if (debug & DEBUG_FS_PARSE_OPTS)
1552d60b848STomohiro Kusumi 		APRINTF("got `%s'\n", option);
1562d60b848STomohiro Kusumi 
1572d60b848STomohiro Kusumi 	i = set_option(hammer2_options, option, buf, sizeof(buf));
1582d60b848STomohiro Kusumi 	if (i == -1)
1592d60b848STomohiro Kusumi 		return 0;
1602d60b848STomohiro Kusumi 
1612d60b848STomohiro Kusumi 	if (hammer2_options[i].name == NULL)
1622d60b848STomohiro Kusumi 		abort();
1632d60b848STomohiro Kusumi 
1642d60b848STomohiro Kusumi 	switch (hammer2_options[i].letter) {
1652d60b848STomohiro Kusumi 	case 'b':
1662d60b848STomohiro Kusumi 		opt->BootAreaSize = getsize(buf, HAMMER2_NEWFS_ALIGN,
1672d60b848STomohiro Kusumi 		    HAMMER2_BOOT_MAX_BYTES, 2);
1682d60b848STomohiro Kusumi 		break;
1692d60b848STomohiro Kusumi 	case 'r':
1702d60b848STomohiro Kusumi 		opt->AuxAreaSize = getsize(buf, HAMMER2_NEWFS_ALIGN,
1712d60b848STomohiro Kusumi 		    HAMMER2_AUX_MAX_BYTES, 2);
1722d60b848STomohiro Kusumi 		break;
1732d60b848STomohiro Kusumi 	case 'V':
174f72350acSTomohiro Kusumi 		if (strlen(buf) == 0) {
175f72350acSTomohiro Kusumi 			h2_opt->ioctl_cmd = HAMMER2IOC_VERSION_GET;
176f72350acSTomohiro Kusumi 		} else {
1772d60b848STomohiro Kusumi 			opt->Hammer2Version = strtol(buf, NULL, 0);
1782d60b848STomohiro Kusumi 			if (opt->Hammer2Version < HAMMER2_VOL_VERSION_MIN ||
1792d60b848STomohiro Kusumi 			    opt->Hammer2Version >= HAMMER2_VOL_VERSION_WIP)
1802d60b848STomohiro Kusumi 				errx(1, "I don't understand how to format "
1812d60b848STomohiro Kusumi 				    "HAMMER2 version %d",
1822d60b848STomohiro Kusumi 				    opt->Hammer2Version);
183f72350acSTomohiro Kusumi 		}
1842d60b848STomohiro Kusumi 		break;
1852d60b848STomohiro Kusumi 	case 'L':
1862d60b848STomohiro Kusumi 		h2_opt->label_specified = 1;
1872d60b848STomohiro Kusumi 		if (strcasecmp(buf, "none") == 0)
1882d60b848STomohiro Kusumi 			break;
1892d60b848STomohiro Kusumi 		if (opt->NLabels >= MAXLABELS)
1902d60b848STomohiro Kusumi 			errx(1, "Limit of %d local labels", MAXLABELS - 1);
1912d60b848STomohiro Kusumi 		if (strlen(buf) == 0)
1922d60b848STomohiro Kusumi 			errx(1, "Volume label '%s' cannot be 0-length", buf);
1932d60b848STomohiro Kusumi 		if (strlen(buf) >= HAMMER2_INODE_MAXNAME)
1942d60b848STomohiro Kusumi 			errx(1, "Volume label '%s' is too long (%d chars max)",
1952d60b848STomohiro Kusumi 			    buf, HAMMER2_INODE_MAXNAME - 1);
1962d60b848STomohiro Kusumi 		opt->Label[opt->NLabels++] = strdup(buf);
1972d60b848STomohiro Kusumi 		break;
1982d60b848STomohiro Kusumi 	case 'm':
1992d60b848STomohiro Kusumi 		if (strlen(buf) == 0)
2002d60b848STomohiro Kusumi 			errx(1, "Volume label '%s' cannot be 0-length", buf);
2012d60b848STomohiro Kusumi 		if (strlen(buf) >= HAMMER2_INODE_MAXNAME)
2022d60b848STomohiro Kusumi 			errx(1, "Volume label '%s' is too long (%d chars max)",
2032d60b848STomohiro Kusumi 			    buf, HAMMER2_INODE_MAXNAME - 1);
2042d60b848STomohiro Kusumi 		strlcpy(h2_opt->mount_label, buf, sizeof(h2_opt->mount_label));
2052d60b848STomohiro Kusumi 		break;
2062d60b848STomohiro Kusumi 	case 'd':
2072d60b848STomohiro Kusumi 		hammer2_debug = strtoll(buf, NULL, 0);
2082d60b848STomohiro Kusumi 		break;
2093999233bSTomohiro Kusumi 	case 'P':
2103999233bSTomohiro Kusumi 		if (strlen(buf) == 0)
2113999233bSTomohiro Kusumi 			errx(1, "PFS argument '%s' cannot be 0-length", buf);
2123999233bSTomohiro Kusumi 		hammer2_parse_pfs_opts(buf, fsopts);
2133999233bSTomohiro Kusumi 		break;
214f804c425STomohiro Kusumi 	case 'I':
215f804c425STomohiro Kusumi 		if (strlen(buf) == 0)
216f804c425STomohiro Kusumi 			errx(1, "Inode argument '%s' cannot be 0-length", buf);
217f804c425STomohiro Kusumi 		hammer2_parse_inode_opts(buf, fsopts);
218f804c425STomohiro Kusumi 		break;
2199a149651STomohiro Kusumi 	case 'B':
2209a149651STomohiro Kusumi 		h2_opt->ioctl_cmd = HAMMER2IOC_BULKFREE_SCAN;
2219a149651STomohiro Kusumi 		break;
222917508cdSTomohiro Kusumi 	case 'D':
2239a149651STomohiro Kusumi 		h2_opt->ioctl_cmd = HAMMER2IOC_DESTROY;
224b4807901STomohiro Kusumi 		if (strlen(buf) == 0)
225b4807901STomohiro Kusumi 			errx(1, "Destroy argument '%s' cannot be 0-length", buf);
226917508cdSTomohiro Kusumi 		if (buf[0] == '/') {
2276857f034STomohiro Kusumi 			strlcpy(h2_opt->destroy_path, buf,
228917508cdSTomohiro Kusumi 			    sizeof(h2_opt->destroy_path));
229b4807901STomohiro Kusumi 		} else if (strncmp(buf, "0x", 2) == 0 ||
230917508cdSTomohiro Kusumi 		    (buf[0] >= '0' && buf[0] <= '9')) {
231917508cdSTomohiro Kusumi 			h2_opt->destroy_inum = strtoull(buf, NULL, 0);
232917508cdSTomohiro Kusumi 			if (errno)
233917508cdSTomohiro Kusumi 				err(1, "strtoull");
234917508cdSTomohiro Kusumi 		} else {
235917508cdSTomohiro Kusumi 			errx(1, "Invalid destroy argument %s", buf);
236917508cdSTomohiro Kusumi 		}
237917508cdSTomohiro Kusumi 		break;
2389a149651STomohiro Kusumi 	case 'G':
2399a149651STomohiro Kusumi 		h2_opt->ioctl_cmd = HAMMER2IOC_GROWFS;
2409a149651STomohiro Kusumi 		break;
2412d60b848STomohiro Kusumi 	default:
2422d60b848STomohiro Kusumi 		break;
2432d60b848STomohiro Kusumi 	}
2442d60b848STomohiro Kusumi 
2456857f034STomohiro Kusumi 	if (hammer2_debug && h2_opt->ioctl_cmd)
2466857f034STomohiro Kusumi 		unittest_trim_slash();
2476857f034STomohiro Kusumi 
2482d60b848STomohiro Kusumi 	return 1;
2492d60b848STomohiro Kusumi }
2502d60b848STomohiro Kusumi 
2512d60b848STomohiro Kusumi void
2522d60b848STomohiro Kusumi hammer2_makefs(const char *image, const char *dir, fsnode *root,
2532d60b848STomohiro Kusumi     fsinfo_t *fsopts)
2542d60b848STomohiro Kusumi {
2552d60b848STomohiro Kusumi 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
2562d60b848STomohiro Kusumi 	struct mount mp;
2572d60b848STomohiro Kusumi 	struct hammer2_mount_info info;
2586bcbb706STomohiro Kusumi 	struct m_vnode devvp, *vroot;
2592d60b848STomohiro Kusumi 	hammer2_inode_t *iroot;
2602d60b848STomohiro Kusumi 	struct timeval start;
2612d60b848STomohiro Kusumi 	int error;
2622d60b848STomohiro Kusumi 
26370e962f7STomohiro Kusumi 	/* ioctl commands could have NULL dir / root */
2642d60b848STomohiro Kusumi 	assert(image != NULL);
2652d60b848STomohiro Kusumi 	assert(fsopts != NULL);
2662d60b848STomohiro Kusumi 
2672d60b848STomohiro Kusumi 	if (debug & DEBUG_FS_MAKEFS)
2682d60b848STomohiro Kusumi 		APRINTF("image \"%s\" directory \"%s\" root %p\n",
2692d60b848STomohiro Kusumi 		    image, dir, root);
2702d60b848STomohiro Kusumi 
2712d60b848STomohiro Kusumi 	/* validate tree and options */
2722d60b848STomohiro Kusumi 	TIMER_START(start);
2732d60b848STomohiro Kusumi 	hammer2_validate(dir, root, fsopts);
2742d60b848STomohiro Kusumi 	TIMER_RESULTS(start, "hammer2_validate");
2752d60b848STomohiro Kusumi 
2769a149651STomohiro Kusumi 	if (h2_opt->ioctl_cmd) {
277b4807901STomohiro Kusumi 		/* open existing image */
278b4807901STomohiro Kusumi 		fsopts->fd = open(image, O_RDWR);
279b4807901STomohiro Kusumi 		if (fsopts->fd < 0)
280b4807901STomohiro Kusumi 			err(1, "failed to open `%s'", image);
281b4807901STomohiro Kusumi 	} else {
2822d60b848STomohiro Kusumi 		/* create image */
2832d60b848STomohiro Kusumi 		TIMER_START(start);
2842d60b848STomohiro Kusumi 		if (hammer2_create_image(image, fsopts) == -1)
2852d60b848STomohiro Kusumi 			errx(1, "image file `%s' not created", image);
2862d60b848STomohiro Kusumi 		TIMER_RESULTS(start, "hammer2_create_image");
287a63188c8STomohiro Kusumi 	}
288a63188c8STomohiro Kusumi 	assert(fsopts->fd > 0);
2892d60b848STomohiro Kusumi 
2902d60b848STomohiro Kusumi 	if (debug & DEBUG_FS_MAKEFS)
2912d60b848STomohiro Kusumi 		putchar('\n');
2922d60b848STomohiro Kusumi 
2932d60b848STomohiro Kusumi 	/* vfs init */
2942d60b848STomohiro Kusumi 	error = hammer2_vfs_init();
2952d60b848STomohiro Kusumi 	if (error)
2962d60b848STomohiro Kusumi 		errx(1, "failed to vfs init, error %d", error);
2972d60b848STomohiro Kusumi 
2982d60b848STomohiro Kusumi 	/* mount image */
2992d60b848STomohiro Kusumi 	memset(&devvp, 0, sizeof(devvp));
3002d60b848STomohiro Kusumi 	devvp.fs = fsopts;
3012d60b848STomohiro Kusumi 	memset(&mp, 0, sizeof(mp));
3022d60b848STomohiro Kusumi 	memset(&info, 0, sizeof(info));
3032d60b848STomohiro Kusumi 	error = hammer2_vfs_mount(&devvp, &mp, h2_opt->mount_label, &info);
3042d60b848STomohiro Kusumi 	if (error)
3052d60b848STomohiro Kusumi 		errx(1, "failed to mount, error %d", error);
3062d60b848STomohiro Kusumi 	assert(mp.mnt_data);
3072d60b848STomohiro Kusumi 
3082d60b848STomohiro Kusumi 	/* get root vnode */
3092d60b848STomohiro Kusumi 	vroot = NULL;
3102d60b848STomohiro Kusumi 	error = hammer2_vfs_root(&mp, &vroot);
3112d60b848STomohiro Kusumi 	if (error)
3122d60b848STomohiro Kusumi 		errx(1, "failed to get root vnode, error %d", error);
3132d60b848STomohiro Kusumi 	assert(vroot);
3142d60b848STomohiro Kusumi 
3152d60b848STomohiro Kusumi 	iroot = VTOI(vroot);
3162d60b848STomohiro Kusumi 	assert(iroot);
317f8a1147cSTomohiro Kusumi 	printf("root inode inum %lld, mode 0%o, refs %d\n",
318f8a1147cSTomohiro Kusumi 	    (long long)iroot->meta.inum, iroot->meta.mode, iroot->refs);
3192d60b848STomohiro Kusumi 
32048ed4577STomohiro Kusumi 	if (h2_opt->emergency_mode)
32148ed4577STomohiro Kusumi 		hammer2_ioctl_emerg_mode(iroot, 1);
32248ed4577STomohiro Kusumi 
3239a149651STomohiro Kusumi 	switch (h2_opt->ioctl_cmd) {
324f72350acSTomohiro Kusumi 	case HAMMER2IOC_VERSION_GET:
325f72350acSTomohiro Kusumi 		printf("version get `%s'\n", image);
326f72350acSTomohiro Kusumi 		TIMER_START(start);
327f72350acSTomohiro Kusumi 		error = hammer2_version_get(vroot);
328f72350acSTomohiro Kusumi 		if (error)
329f72350acSTomohiro Kusumi 			errx(1, "version get `%s' failed '%s'", image,
330f72350acSTomohiro Kusumi 			    strerror(error));
331f72350acSTomohiro Kusumi 		TIMER_RESULTS(start, "hammer2_version_get");
332f72350acSTomohiro Kusumi 		break;
3339a149651STomohiro Kusumi 	case HAMMER2IOC_PFS_GET:
3343999233bSTomohiro Kusumi 		printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image);
3353999233bSTomohiro Kusumi 		TIMER_START(start);
3369a149651STomohiro Kusumi 		error = hammer2_pfs_get(vroot);
3379a149651STomohiro Kusumi 		if (error)
3389a149651STomohiro Kusumi 			errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name,
3399a149651STomohiro Kusumi 			    image, strerror(error));
3409a149651STomohiro Kusumi 		TIMER_RESULTS(start, "hammer2_pfs_get");
3419a149651STomohiro Kusumi 		break;
3429a149651STomohiro Kusumi 	case HAMMER2IOC_PFS_LOOKUP:
3439a149651STomohiro Kusumi 		printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image);
3449a149651STomohiro Kusumi 		TIMER_START(start);
3459a149651STomohiro Kusumi 		error = hammer2_pfs_lookup(vroot, h2_opt->pfs_name);
3469a149651STomohiro Kusumi 		if (error)
3479a149651STomohiro Kusumi 			errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name,
3489a149651STomohiro Kusumi 			    image, strerror(error));
3499a149651STomohiro Kusumi 		TIMER_RESULTS(start, "hammer2_pfs_lookup");
3509a149651STomohiro Kusumi 		break;
3519a149651STomohiro Kusumi 	case HAMMER2IOC_PFS_CREATE:
3529a149651STomohiro Kusumi 		printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image);
3539a149651STomohiro Kusumi 		TIMER_START(start);
3549a149651STomohiro Kusumi 		error = hammer2_pfs_create(vroot, h2_opt->pfs_name);
3559a149651STomohiro Kusumi 		if (error)
3569a149651STomohiro Kusumi 			errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name,
3579a149651STomohiro Kusumi 			    image, strerror(error));
3589a149651STomohiro Kusumi 		TIMER_RESULTS(start, "hammer2_pfs_create");
3599a149651STomohiro Kusumi 		break;
3609a149651STomohiro Kusumi 	case HAMMER2IOC_PFS_DELETE:
3619a149651STomohiro Kusumi 		printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image);
3629a149651STomohiro Kusumi 		TIMER_START(start);
3639a149651STomohiro Kusumi 		error = hammer2_pfs_delete(vroot, h2_opt->pfs_name);
3649a149651STomohiro Kusumi 		if (error)
3659a149651STomohiro Kusumi 			errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name,
3669a149651STomohiro Kusumi 			    image, strerror(error));
3679a149651STomohiro Kusumi 		TIMER_RESULTS(start, "hammer2_pfs_delete");
3689a149651STomohiro Kusumi 		break;
3699a149651STomohiro Kusumi 	case HAMMER2IOC_PFS_SNAPSHOT:
3709a149651STomohiro Kusumi 		printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image);
3719a149651STomohiro Kusumi 		TIMER_START(start);
3729a149651STomohiro Kusumi 		error = hammer2_pfs_snapshot(vroot, h2_opt->pfs_name,
37306d1ac82STomohiro Kusumi 		    h2_opt->mount_label);
37406d1ac82STomohiro Kusumi 		if (error)
37506d1ac82STomohiro Kusumi 			errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name,
37606d1ac82STomohiro Kusumi 			    image, strerror(error));
3779a149651STomohiro Kusumi 		TIMER_RESULTS(start, "hammer2_pfs_snapshot");
3789a149651STomohiro Kusumi 		break;
379f804c425STomohiro Kusumi 	case HAMMER2IOC_INODE_GET:
380465e1141STomohiro Kusumi 		printf("inode %s `%s'\n", h2_opt->inode_cmd_name, image);
381f804c425STomohiro Kusumi 		TIMER_START(start);
382f804c425STomohiro Kusumi 		error = hammer2_inode_getx(vroot, h2_opt->inode_path);
383f804c425STomohiro Kusumi 		if (error)
384465e1141STomohiro Kusumi 			errx(1, "inode %s `%s' failed '%s'",
385465e1141STomohiro Kusumi 			    h2_opt->inode_cmd_name, image, strerror(error));
386f804c425STomohiro Kusumi 		TIMER_RESULTS(start, "hammer2_inode_getx");
387f804c425STomohiro Kusumi 		break;
388465e1141STomohiro Kusumi 	case HAMMER2IOC_INODE_SET:
389465e1141STomohiro Kusumi 		printf("inode %s `%s'\n", h2_opt->inode_cmd_name, image);
390465e1141STomohiro Kusumi 		TIMER_START(start);
391465e1141STomohiro Kusumi 		if (!strcmp(h2_opt->inode_cmd_name, "setcheck")) {
392465e1141STomohiro Kusumi 			error = hammer2_inode_setcheck(vroot,
393465e1141STomohiro Kusumi 			    h2_opt->inode_path);
394465e1141STomohiro Kusumi 			if (error)
395465e1141STomohiro Kusumi 				errx(1, "inode %s `%s' failed '%s'",
396465e1141STomohiro Kusumi 				    h2_opt->inode_cmd_name, image,
397465e1141STomohiro Kusumi 				    strerror(error));
398465e1141STomohiro Kusumi 		} else if (!strcmp(h2_opt->inode_cmd_name, "setcomp")) {
399465e1141STomohiro Kusumi 			error = hammer2_inode_setcomp(vroot,
400465e1141STomohiro Kusumi 			    h2_opt->inode_path);
401465e1141STomohiro Kusumi 			if (error)
402465e1141STomohiro Kusumi 				errx(1, "inode %s `%s' failed '%s'",
403465e1141STomohiro Kusumi 				    h2_opt->inode_cmd_name, image,
404465e1141STomohiro Kusumi 				    strerror(error));
405465e1141STomohiro Kusumi 		} else {
406465e1141STomohiro Kusumi 			assert(0);
407465e1141STomohiro Kusumi 		}
408465e1141STomohiro Kusumi 		TIMER_RESULTS(start, "hammer2_inode_setx");
409465e1141STomohiro Kusumi 		break;
4109a149651STomohiro Kusumi 	case HAMMER2IOC_BULKFREE_SCAN:
411a63188c8STomohiro Kusumi 		printf("bulkfree `%s'\n", image);
412a63188c8STomohiro Kusumi 		TIMER_START(start);
41306d1ac82STomohiro Kusumi 		error = hammer2_bulkfree(vroot);
41406d1ac82STomohiro Kusumi 		if (error)
41506d1ac82STomohiro Kusumi 			errx(1, "bulkfree `%s' failed '%s'", image,
41606d1ac82STomohiro Kusumi 			    strerror(error));
417a63188c8STomohiro Kusumi 		TIMER_RESULTS(start, "hammer2_bulkfree");
4189a149651STomohiro Kusumi 		break;
4199a149651STomohiro Kusumi 	case HAMMER2IOC_DESTROY:
420917508cdSTomohiro Kusumi 		TIMER_START(start);
421917508cdSTomohiro Kusumi 		if (strlen(h2_opt->destroy_path)) {
422917508cdSTomohiro Kusumi 			printf("destroy `%s' in `%s'\n",
423917508cdSTomohiro Kusumi 			    h2_opt->destroy_path, image);
42406d1ac82STomohiro Kusumi 			error = hammer2_destroy_path(vroot,
42506d1ac82STomohiro Kusumi 			    h2_opt->destroy_path);
42606d1ac82STomohiro Kusumi 			if (error)
42706d1ac82STomohiro Kusumi 				errx(1, "destroy `%s' in `%s' failed '%s'",
42806d1ac82STomohiro Kusumi 				    h2_opt->destroy_path, image,
42906d1ac82STomohiro Kusumi 				    strerror(error));
430917508cdSTomohiro Kusumi 		} else {
431917508cdSTomohiro Kusumi 			printf("destroy %lld in `%s'\n",
432917508cdSTomohiro Kusumi 			    (long long)h2_opt->destroy_inum, image);
43306d1ac82STomohiro Kusumi 			error = hammer2_destroy_inum(vroot,
43406d1ac82STomohiro Kusumi 			    h2_opt->destroy_inum);
43506d1ac82STomohiro Kusumi 			if (error)
43606d1ac82STomohiro Kusumi 				errx(1, "destroy %lld in `%s' failed '%s'",
43706d1ac82STomohiro Kusumi 				    (long long)h2_opt->destroy_inum, image,
43806d1ac82STomohiro Kusumi 				    strerror(error));
439917508cdSTomohiro Kusumi 		}
440917508cdSTomohiro Kusumi 		TIMER_RESULTS(start, "hammer2_destroy");
4419a149651STomohiro Kusumi 		break;
4429a149651STomohiro Kusumi 	case HAMMER2IOC_GROWFS:
443afa5234bSTomohiro Kusumi 		printf("growfs `%s'\n", image);
444afa5234bSTomohiro Kusumi 		TIMER_START(start);
44506d1ac82STomohiro Kusumi 		error = hammer2_growfs(vroot, h2_opt->image_size);
44606d1ac82STomohiro Kusumi 		if (error)
44706d1ac82STomohiro Kusumi 			errx(1, "growfs `%s' failed '%s'", image,
44806d1ac82STomohiro Kusumi 			    strerror(error));
449afa5234bSTomohiro Kusumi 		TIMER_RESULTS(start, "hammer2_growfs");
4509a149651STomohiro Kusumi 		break;
4519a149651STomohiro Kusumi 	default:
452b4807901STomohiro Kusumi 		printf("populating `%s'\n", image);
453b4807901STomohiro Kusumi 		TIMER_START(start);
454b4807901STomohiro Kusumi 		if (hammer2_populate_dir(vroot, dir, root, root, fsopts, 0))
455b4807901STomohiro Kusumi 			errx(1, "image file `%s' not populated", image);
456b4807901STomohiro Kusumi 		TIMER_RESULTS(start, "hammer2_populate_dir");
4579a149651STomohiro Kusumi 		break;
458a63188c8STomohiro Kusumi 	}
4592d60b848STomohiro Kusumi 
4602d60b848STomohiro Kusumi 	/* unmount image */
4612d60b848STomohiro Kusumi 	error = hammer2_vfs_unmount(&mp, 0);
4622d60b848STomohiro Kusumi 	if (error)
4632d60b848STomohiro Kusumi 		errx(1, "failed to unmount, error %d", error);
4642d60b848STomohiro Kusumi 
4652d60b848STomohiro Kusumi 	/* check leaked resource */
4662d60b848STomohiro Kusumi 	if (vnode_count)
467f8a1147cSTomohiro Kusumi 		printf("XXX %lld vnode left\n", (long long)vnode_count);
4682d60b848STomohiro Kusumi 	if (hammer2_chain_allocs)
4692d60b848STomohiro Kusumi 		printf("XXX %ld chain left\n", hammer2_chain_allocs);
4702d60b848STomohiro Kusumi 	bcleanup();
4712d60b848STomohiro Kusumi 
4722d60b848STomohiro Kusumi 	/* vfs uninit */
4732d60b848STomohiro Kusumi 	error = hammer2_vfs_uninit();
4742d60b848STomohiro Kusumi 	if (error)
4752d60b848STomohiro Kusumi 		errx(1, "failed to vfs uninit, error %d", error);
4762d60b848STomohiro Kusumi 
4772d60b848STomohiro Kusumi 	if (close(fsopts->fd) == -1)
4782d60b848STomohiro Kusumi 		err(1, "closing `%s'", image);
4792d60b848STomohiro Kusumi 	fsopts->fd = -1;
4802d60b848STomohiro Kusumi 
4812d60b848STomohiro Kusumi 	printf("image `%s' complete\n", image);
4822d60b848STomohiro Kusumi }
4832d60b848STomohiro Kusumi 
4842d60b848STomohiro Kusumi /* end of public functions */
4852d60b848STomohiro Kusumi 
4863999233bSTomohiro Kusumi static void
4873999233bSTomohiro Kusumi hammer2_parse_pfs_opts(const char *buf, fsinfo_t *fsopts)
4883999233bSTomohiro Kusumi {
4893999233bSTomohiro Kusumi 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
4903999233bSTomohiro Kusumi 	char *o, *p;
4913999233bSTomohiro Kusumi 	size_t n;
4923999233bSTomohiro Kusumi 
4933999233bSTomohiro Kusumi 	o = p = strdup(buf);
4943999233bSTomohiro Kusumi 	p = strchr(p, ':');
4953999233bSTomohiro Kusumi 	if (p != NULL) {
4963999233bSTomohiro Kusumi 		*p++ = 0;
4973999233bSTomohiro Kusumi 		n = strlen(p);
4983999233bSTomohiro Kusumi 	} else {
4993999233bSTomohiro Kusumi 		n = 0;
5003999233bSTomohiro Kusumi 	}
5013999233bSTomohiro Kusumi 
5023999233bSTomohiro Kusumi 	if (!strcmp(o, "get") || !strcmp(o, "list")) {
5039a149651STomohiro Kusumi 		h2_opt->ioctl_cmd = HAMMER2IOC_PFS_GET;
5043999233bSTomohiro Kusumi 	} else if (!strcmp(o, "lookup")) {
5053999233bSTomohiro Kusumi 		if (n == 0 || n > NAME_MAX)
5063999233bSTomohiro Kusumi 			errx(1, "invalid PFS name \"%s\"", p);
5079a149651STomohiro Kusumi 		h2_opt->ioctl_cmd = HAMMER2IOC_PFS_LOOKUP;
5083999233bSTomohiro Kusumi 	} else if (!strcmp(o, "create")) {
5093999233bSTomohiro Kusumi 		if (n == 0 || n > NAME_MAX)
5103999233bSTomohiro Kusumi 			errx(1, "invalid PFS name \"%s\"", p);
5119a149651STomohiro Kusumi 		h2_opt->ioctl_cmd = HAMMER2IOC_PFS_CREATE;
5123999233bSTomohiro Kusumi 	} else if (!strcmp(o, "delete")) {
5133999233bSTomohiro Kusumi 		if (n == 0 || n > NAME_MAX)
5143999233bSTomohiro Kusumi 			errx(1, "invalid PFS name \"%s\"", p);
5159a149651STomohiro Kusumi 		h2_opt->ioctl_cmd = HAMMER2IOC_PFS_DELETE;
5163999233bSTomohiro Kusumi 	} else if (!strcmp(o, "snapshot")) {
5173999233bSTomohiro Kusumi 		if (n > NAME_MAX)
5183999233bSTomohiro Kusumi 			errx(1, "invalid PFS name \"%s\"", p);
5199a149651STomohiro Kusumi 		h2_opt->ioctl_cmd = HAMMER2IOC_PFS_SNAPSHOT;
5203999233bSTomohiro Kusumi 	} else {
5213999233bSTomohiro Kusumi 		errx(1, "invalid PFS command \"%s\"", o);
5223999233bSTomohiro Kusumi 	}
5233999233bSTomohiro Kusumi 
5243999233bSTomohiro Kusumi 	strlcpy(h2_opt->pfs_cmd_name, o, sizeof(h2_opt->pfs_cmd_name));
5253999233bSTomohiro Kusumi 	if (n > 0)
5263999233bSTomohiro Kusumi 		strlcpy(h2_opt->pfs_name, p, sizeof(h2_opt->pfs_name));
5273999233bSTomohiro Kusumi 
5283999233bSTomohiro Kusumi 	free(o);
5293999233bSTomohiro Kusumi }
5303999233bSTomohiro Kusumi 
531f804c425STomohiro Kusumi static void
532f804c425STomohiro Kusumi hammer2_parse_inode_opts(const char *buf, fsinfo_t *fsopts)
533f804c425STomohiro Kusumi {
534f804c425STomohiro Kusumi 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
535f804c425STomohiro Kusumi 	char *o, *p;
536f804c425STomohiro Kusumi 	size_t n;
537f804c425STomohiro Kusumi 
538f804c425STomohiro Kusumi 	o = p = strdup(buf);
539f804c425STomohiro Kusumi 	p = strchr(p, ':');
540f804c425STomohiro Kusumi 	if (p != NULL) {
541f804c425STomohiro Kusumi 		*p++ = 0;
542f804c425STomohiro Kusumi 		n = strlen(p);
543f804c425STomohiro Kusumi 	} else {
544f804c425STomohiro Kusumi 		n = 0;
545f804c425STomohiro Kusumi 	}
546f804c425STomohiro Kusumi 
547f804c425STomohiro Kusumi 	if (!strcmp(o, "get")) {
548f804c425STomohiro Kusumi 		if (n == 0 || n > PATH_MAX)
549f804c425STomohiro Kusumi 			errx(1, "invalid file path \"%s\"", p);
550f804c425STomohiro Kusumi 		h2_opt->ioctl_cmd = HAMMER2IOC_INODE_GET;
551465e1141STomohiro Kusumi 	} else if (!strcmp(o, "setcheck")) {
552465e1141STomohiro Kusumi 		if (n == 0 || n > PATH_MAX - 10)
553465e1141STomohiro Kusumi 			errx(1, "invalid argument \"%s\"", p);
554465e1141STomohiro Kusumi 		h2_opt->ioctl_cmd = HAMMER2IOC_INODE_SET;
555465e1141STomohiro Kusumi 	} else if (!strcmp(o, "setcomp")) {
556465e1141STomohiro Kusumi 		if (n == 0 || n > PATH_MAX - 10)
557465e1141STomohiro Kusumi 			errx(1, "invalid argument \"%s\"", p);
558465e1141STomohiro Kusumi 		h2_opt->ioctl_cmd = HAMMER2IOC_INODE_SET;
559f804c425STomohiro Kusumi 	} else {
560f804c425STomohiro Kusumi 		errx(1, "invalid inode command \"%s\"", o);
561f804c425STomohiro Kusumi 	}
562f804c425STomohiro Kusumi 
563465e1141STomohiro Kusumi 	strlcpy(h2_opt->inode_cmd_name, o, sizeof(h2_opt->inode_cmd_name));
5646857f034STomohiro Kusumi 	if (n > 0)
565f804c425STomohiro Kusumi 		strlcpy(h2_opt->inode_path, p, sizeof(h2_opt->inode_path));
566f804c425STomohiro Kusumi 
567f804c425STomohiro Kusumi 	free(o);
568f804c425STomohiro Kusumi }
569f804c425STomohiro Kusumi 
5702d60b848STomohiro Kusumi static hammer2_off_t
5712d60b848STomohiro Kusumi hammer2_image_size(fsinfo_t *fsopts)
5722d60b848STomohiro Kusumi {
5732d60b848STomohiro Kusumi 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
5742d60b848STomohiro Kusumi 	hammer2_off_t image_size, used_size = 0;
5752d60b848STomohiro Kusumi 	int num_level1, delta_num_level1;
5762d60b848STomohiro Kusumi 
5772d60b848STomohiro Kusumi 	/* use 4 volume headers by default */
5782d60b848STomohiro Kusumi 	num_level1 = h2_opt->num_volhdr * 2; /* default 4 x 2 */
5792d60b848STomohiro Kusumi 	assert(num_level1 != 0);
5802d60b848STomohiro Kusumi 	assert(num_level1 <= 8);
5812d60b848STomohiro Kusumi 
5822d60b848STomohiro Kusumi 	/* add 4MiB segment for each level1 */
5832d60b848STomohiro Kusumi 	used_size += HAMMER2_ZONE_SEG64 * num_level1;
5842d60b848STomohiro Kusumi 
5852d60b848STomohiro Kusumi 	/* add boot/aux area, but exact size unknown at this point */
5862d60b848STomohiro Kusumi 	used_size += HAMMER2_BOOT_NOM_BYTES + HAMMER2_AUX_NOM_BYTES;
5872d60b848STomohiro Kusumi 
5882d60b848STomohiro Kusumi 	/* add data size */
5892d60b848STomohiro Kusumi 	used_size += fsopts->size;
5902d60b848STomohiro Kusumi 
5912d60b848STomohiro Kusumi 	/* XXX add extra level1 for meta data and indirect blocks */
5922d60b848STomohiro Kusumi 	used_size += HAMMER2_FREEMAP_LEVEL1_SIZE;
5932d60b848STomohiro Kusumi 
5942d60b848STomohiro Kusumi 	/* XXX add extra level1 for safety */
5952d60b848STomohiro Kusumi 	if (used_size > HAMMER2_FREEMAP_LEVEL1_SIZE * 10)
5962d60b848STomohiro Kusumi 		used_size += HAMMER2_FREEMAP_LEVEL1_SIZE;
5972d60b848STomohiro Kusumi 
5982d60b848STomohiro Kusumi 	/* use 8GiB image size by default */
5992d60b848STomohiro Kusumi 	image_size = HAMMER2_FREEMAP_LEVEL1_SIZE * num_level1;
6002d60b848STomohiro Kusumi 	printf("trying default image size %s\n", sizetostr(image_size));
6012d60b848STomohiro Kusumi 
6022d60b848STomohiro Kusumi 	/* adjust if image size isn't large enough */
6032d60b848STomohiro Kusumi 	if (used_size > image_size) {
6042d60b848STomohiro Kusumi 		/* determine extra level1 needed */
6052d60b848STomohiro Kusumi 		delta_num_level1 = howmany(used_size - image_size,
6062d60b848STomohiro Kusumi 		    HAMMER2_FREEMAP_LEVEL1_SIZE);
6072d60b848STomohiro Kusumi 
6082d60b848STomohiro Kusumi 		/* adjust used size with 4MiB segment for each extra level1 */
6092d60b848STomohiro Kusumi 		used_size += HAMMER2_ZONE_SEG64 * delta_num_level1;
6102d60b848STomohiro Kusumi 
6112d60b848STomohiro Kusumi 		/* adjust image size with extra level1 */
6122d60b848STomohiro Kusumi 		image_size += HAMMER2_FREEMAP_LEVEL1_SIZE * delta_num_level1;
6132d60b848STomohiro Kusumi 		printf("trying adjusted image size %s\n",
6142d60b848STomohiro Kusumi 		    sizetostr(image_size));
6152d60b848STomohiro Kusumi 
6162d60b848STomohiro Kusumi 		if (used_size > image_size)
617f8a1147cSTomohiro Kusumi 			errx(1, "invalid used_size %lld > image_size %lld",
618f8a1147cSTomohiro Kusumi 			    (long long)used_size, (long long)image_size);
6192d60b848STomohiro Kusumi 	}
6202d60b848STomohiro Kusumi 
6212d60b848STomohiro Kusumi 	return image_size;
6222d60b848STomohiro Kusumi }
6232d60b848STomohiro Kusumi 
6242d60b848STomohiro Kusumi static const char *
6252d60b848STomohiro Kusumi hammer2_label_name(int label_type)
6262d60b848STomohiro Kusumi {
6272d60b848STomohiro Kusumi 	switch (label_type) {
6282d60b848STomohiro Kusumi 	case HAMMER2_LABEL_NONE:
6292d60b848STomohiro Kusumi 		return "NONE";
6302d60b848STomohiro Kusumi 	case HAMMER2_LABEL_BOOT:
6312d60b848STomohiro Kusumi 		return "BOOT";
6322d60b848STomohiro Kusumi 	case HAMMER2_LABEL_ROOT:
6332d60b848STomohiro Kusumi 		return "ROOT";
6342d60b848STomohiro Kusumi 	case HAMMER2_LABEL_DATA:
6352d60b848STomohiro Kusumi 		return "DATA";
6362d60b848STomohiro Kusumi 	default:
6372d60b848STomohiro Kusumi 		assert(0);
6382d60b848STomohiro Kusumi 	}
6392d60b848STomohiro Kusumi 	return NULL;
6402d60b848STomohiro Kusumi }
6412d60b848STomohiro Kusumi 
6422d60b848STomohiro Kusumi static void
6432d60b848STomohiro Kusumi hammer2_validate(const char *dir, fsnode *root, fsinfo_t *fsopts)
6442d60b848STomohiro Kusumi {
6452d60b848STomohiro Kusumi 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
6462d60b848STomohiro Kusumi 	hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
647afa5234bSTomohiro Kusumi 	hammer2_off_t image_size = 0, minsize, maxsize;
6482d60b848STomohiro Kusumi 	const char *s;
6492d60b848STomohiro Kusumi 
65070e962f7STomohiro Kusumi 	/* ioctl commands could have NULL dir / root */
6512d60b848STomohiro Kusumi 	assert(fsopts != NULL);
6522d60b848STomohiro Kusumi 
6532d60b848STomohiro Kusumi 	if (debug & DEBUG_FS_VALIDATE) {
6542d60b848STomohiro Kusumi 		APRINTF("before defaults set:\n");
6552d60b848STomohiro Kusumi 		hammer2_dump_fsinfo(fsopts);
6562d60b848STomohiro Kusumi 	}
6572d60b848STomohiro Kusumi 
6582d60b848STomohiro Kusumi 	/* makefs only supports "DATA" for default PFS label */
6592d60b848STomohiro Kusumi 	if (!h2_opt->label_specified) {
6602d60b848STomohiro Kusumi 		opt->DefaultLabelType = HAMMER2_LABEL_DATA;
6612d60b848STomohiro Kusumi 		s = hammer2_label_name(opt->DefaultLabelType);
6622d60b848STomohiro Kusumi 		printf("using default label \"%s\"\n", s);
6632d60b848STomohiro Kusumi 	}
6642d60b848STomohiro Kusumi 
6652d60b848STomohiro Kusumi 	/* set default mount PFS label */
6662d60b848STomohiro Kusumi 	if (!strcmp(h2_opt->mount_label, "")) {
6672d60b848STomohiro Kusumi 		s = hammer2_label_name(HAMMER2_LABEL_DATA);
6682d60b848STomohiro Kusumi 		strlcpy(h2_opt->mount_label, s, sizeof(h2_opt->mount_label));
6692d60b848STomohiro Kusumi 		printf("using default mount label \"%s\"\n", s);
6702d60b848STomohiro Kusumi 	}
6712d60b848STomohiro Kusumi 
6722d60b848STomohiro Kusumi 	/* set default number of volume headers */
6732d60b848STomohiro Kusumi 	if (!h2_opt->num_volhdr) {
6742d60b848STomohiro Kusumi 		h2_opt->num_volhdr = HAMMER2_NUM_VOLHDRS;
6752d60b848STomohiro Kusumi 		printf("using default %d volume headers\n", h2_opt->num_volhdr);
6762d60b848STomohiro Kusumi 	}
6772d60b848STomohiro Kusumi 
678917508cdSTomohiro Kusumi 	/* done if ioctl commands */
6799a149651STomohiro Kusumi 	if (h2_opt->ioctl_cmd) {
6809a149651STomohiro Kusumi 		if (h2_opt->ioctl_cmd == HAMMER2IOC_GROWFS)
681afa5234bSTomohiro Kusumi 			goto ignore_size_dir;
6829a149651STomohiro Kusumi 		else
6839a149651STomohiro Kusumi 			goto done;
6849a149651STomohiro Kusumi 	}
685a63188c8STomohiro Kusumi 
6862d60b848STomohiro Kusumi 	/* calculate data size */
6872d60b848STomohiro Kusumi 	if (fsopts->size != 0)
6882d60b848STomohiro Kusumi 		fsopts->size = 0; /* shouldn't reach here to begin with */
689a63188c8STomohiro Kusumi 	if (root == NULL)
690a63188c8STomohiro Kusumi 		errx(1, "fsnode tree not constructed");
6912d60b848STomohiro Kusumi 	hammer2_size_dir(root, fsopts);
6922d60b848STomohiro Kusumi 	printf("estimated data size %s from %lld inode\n",
6932d60b848STomohiro Kusumi 	    sizetostr(fsopts->size), (long long)fsopts->inodes);
6942d60b848STomohiro Kusumi 
6952d60b848STomohiro Kusumi 	/* determine image size from data size */
6962d60b848STomohiro Kusumi 	image_size = hammer2_image_size(fsopts);
6972d60b848STomohiro Kusumi 	assert((image_size & HAMMER2_FREEMAP_LEVEL1_MASK) == 0);
698afa5234bSTomohiro Kusumi ignore_size_dir:
6992d60b848STomohiro Kusumi 	minsize = roundup(fsopts->minsize, HAMMER2_FREEMAP_LEVEL1_SIZE);
7002d60b848STomohiro Kusumi 	maxsize = roundup(fsopts->maxsize, HAMMER2_FREEMAP_LEVEL1_SIZE);
7012d60b848STomohiro Kusumi 	if (image_size < minsize)
7022d60b848STomohiro Kusumi 		image_size = minsize;
7032d60b848STomohiro Kusumi 	else if (maxsize > 0 && image_size > maxsize)
704f8a1147cSTomohiro Kusumi 		errx(1, "`%s' size of %lld is larger than the maxsize of %lld",
705f8a1147cSTomohiro Kusumi 		    dir, (long long)image_size, (long long)maxsize);
7062d60b848STomohiro Kusumi 
7072d60b848STomohiro Kusumi 	assert((image_size & HAMMER2_FREEMAP_LEVEL1_MASK) == 0);
7082d60b848STomohiro Kusumi 	h2_opt->image_size = image_size;
7092d60b848STomohiro Kusumi 	printf("using %s image size\n", sizetostr(h2_opt->image_size));
710a63188c8STomohiro Kusumi done:
7112d60b848STomohiro Kusumi 	if (debug & DEBUG_FS_VALIDATE) {
7122d60b848STomohiro Kusumi 		APRINTF("after defaults set:\n");
7132d60b848STomohiro Kusumi 		hammer2_dump_fsinfo(fsopts);
7142d60b848STomohiro Kusumi 	}
7152d60b848STomohiro Kusumi }
7162d60b848STomohiro Kusumi 
7172d60b848STomohiro Kusumi static void
7182d60b848STomohiro Kusumi hammer2_dump_fsinfo(fsinfo_t *fsopts)
7192d60b848STomohiro Kusumi {
7202d60b848STomohiro Kusumi 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
7212d60b848STomohiro Kusumi 	hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
7222d60b848STomohiro Kusumi 	int i;
7232d60b848STomohiro Kusumi 	char *s;
7242d60b848STomohiro Kusumi 
7252d60b848STomohiro Kusumi 	assert(fsopts != NULL);
7262d60b848STomohiro Kusumi 
7272d60b848STomohiro Kusumi 	APRINTF("fsinfo_t at %p\n", fsopts);
7282d60b848STomohiro Kusumi 
7292d60b848STomohiro Kusumi 	printf("\tinodes %lld\n", (long long)fsopts->inodes);
7302d60b848STomohiro Kusumi 	printf("\tsize %lld, minsize %lld, maxsize %lld\n",
7312d60b848STomohiro Kusumi 	    (long long)fsopts->size,
7322d60b848STomohiro Kusumi 	    (long long)fsopts->minsize,
7332d60b848STomohiro Kusumi 	    (long long)fsopts->maxsize);
7342d60b848STomohiro Kusumi 
7352d60b848STomohiro Kusumi 	printf("\thammer2_debug 0x%x\n", hammer2_debug);
7362d60b848STomohiro Kusumi 
7372d60b848STomohiro Kusumi 	printf("\tlabel_specified %d\n", h2_opt->label_specified);
7382d60b848STomohiro Kusumi 	printf("\tmount_label \"%s\"\n", h2_opt->mount_label);
7392d60b848STomohiro Kusumi 	printf("\tnum_volhdr %d\n", h2_opt->num_volhdr);
7409a149651STomohiro Kusumi 	printf("\tioctl_cmd %ld\n", h2_opt->ioctl_cmd);
74148ed4577STomohiro Kusumi 	printf("\temergency_mode %d\n", h2_opt->emergency_mode);
7423999233bSTomohiro Kusumi 	printf("\tpfs_cmd_name \"%s\"\n", h2_opt->pfs_cmd_name);
7433999233bSTomohiro Kusumi 	printf("\tpfs_name \"%s\"\n", h2_opt->pfs_name);
744465e1141STomohiro Kusumi 	printf("\tinode_cmd_name \"%s\"\n", h2_opt->inode_cmd_name);
745f804c425STomohiro Kusumi 	printf("\tinode_path \"%s\"\n", h2_opt->inode_path);
746917508cdSTomohiro Kusumi 	printf("\tdestroy_path \"%s\"\n", h2_opt->destroy_path);
747917508cdSTomohiro Kusumi 	printf("\tdestroy_inum %lld\n", (long long)h2_opt->destroy_inum);
748f8a1147cSTomohiro Kusumi 	printf("\timage_size 0x%llx\n", (long long)h2_opt->image_size);
7492d60b848STomohiro Kusumi 
7502d60b848STomohiro Kusumi 	printf("\tHammer2Version %d\n", opt->Hammer2Version);
7512d60b848STomohiro Kusumi 	printf("\tBootAreaSize 0x%jx\n", opt->BootAreaSize);
7522d60b848STomohiro Kusumi 	printf("\tAuxAreaSize 0x%jx\n", opt->AuxAreaSize);
7532d60b848STomohiro Kusumi 	printf("\tNLabels %d\n", opt->NLabels);
7542d60b848STomohiro Kusumi 	printf("\tCompType %d\n", opt->CompType);
7552d60b848STomohiro Kusumi 	printf("\tCheckType %d\n", opt->CheckType);
7562d60b848STomohiro Kusumi 	printf("\tDefaultLabelType %d\n", opt->DefaultLabelType);
7572d60b848STomohiro Kusumi 	printf("\tDebugOpt %d\n", opt->DebugOpt);
7582d60b848STomohiro Kusumi 
7592d60b848STomohiro Kusumi 	s = NULL;
7602d60b848STomohiro Kusumi 	hammer2_uuid_to_str(&opt->Hammer2_FSType, &s);
7612d60b848STomohiro Kusumi 	printf("\tHammer2_FSType \"%s\"\n", s);
7622d60b848STomohiro Kusumi 	s = NULL;
7632d60b848STomohiro Kusumi 	hammer2_uuid_to_str(&opt->Hammer2_VolFSID, &s);
7642d60b848STomohiro Kusumi 	printf("\tHammer2_VolFSID \"%s\"\n", s);
7652d60b848STomohiro Kusumi 	s = NULL;
7662d60b848STomohiro Kusumi 	hammer2_uuid_to_str(&opt->Hammer2_SupCLID, &s);
7672d60b848STomohiro Kusumi 	printf("\tHammer2_SupCLID \"%s\"\n", s);
7682d60b848STomohiro Kusumi 	s = NULL;
7692d60b848STomohiro Kusumi 	hammer2_uuid_to_str(&opt->Hammer2_SupFSID, &s);
7702d60b848STomohiro Kusumi 	printf("\tHammer2_SupFSID \"%s\"\n", s);
7712d60b848STomohiro Kusumi 
7722d60b848STomohiro Kusumi 	for (i = 0; i < opt->NLabels; i++) {
7732d60b848STomohiro Kusumi 		printf("\tLabel[%d] \"%s\"\n", i, opt->Label[i]);
7742d60b848STomohiro Kusumi 		s = NULL;
7752d60b848STomohiro Kusumi 		hammer2_uuid_to_str(&opt->Hammer2_PfsCLID[i], &s);
7762d60b848STomohiro Kusumi 		printf("\t Hammer2_PfsCLID[%d] \"%s\"\n", i, s);
7772d60b848STomohiro Kusumi 		s = NULL;
7782d60b848STomohiro Kusumi 		hammer2_uuid_to_str(&opt->Hammer2_PfsFSID[i], &s);
7792d60b848STomohiro Kusumi 		printf("\t Hammer2_PfsFSID[%d] \"%s\"\n", i, s);
7802d60b848STomohiro Kusumi 	}
7812d60b848STomohiro Kusumi 
7822d60b848STomohiro Kusumi 	free(s);
7832d60b848STomohiro Kusumi }
7842d60b848STomohiro Kusumi 
7852d60b848STomohiro Kusumi static int
7862d60b848STomohiro Kusumi hammer2_create_image(const char *image, fsinfo_t *fsopts)
7872d60b848STomohiro Kusumi {
7882d60b848STomohiro Kusumi 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
7892d60b848STomohiro Kusumi 	hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
7902d60b848STomohiro Kusumi 	char *av[] = { (char *)image, }; /* XXX support multi-volumes */
7912d60b848STomohiro Kusumi 	char *buf;
7922d60b848STomohiro Kusumi 	int i, bufsize, oflags;
7932d60b848STomohiro Kusumi 	off_t bufrem;
7942d60b848STomohiro Kusumi 
7952d60b848STomohiro Kusumi 	assert(image != NULL);
7962d60b848STomohiro Kusumi 	assert(fsopts != NULL);
7972d60b848STomohiro Kusumi 
7982d60b848STomohiro Kusumi 	/* create image */
7992d60b848STomohiro Kusumi 	oflags = O_RDWR | O_CREAT;
8002d60b848STomohiro Kusumi 	if (fsopts->offset == 0)
8012d60b848STomohiro Kusumi 		oflags |= O_TRUNC;
8022d60b848STomohiro Kusumi 	if ((fsopts->fd = open(image, oflags, 0666)) == -1) {
8032d60b848STomohiro Kusumi 		warn("can't open `%s' for writing", image);
8042d60b848STomohiro Kusumi 		return -1;
8052d60b848STomohiro Kusumi 	}
8062d60b848STomohiro Kusumi 
8072d60b848STomohiro Kusumi 	/* zero image */
8082d60b848STomohiro Kusumi 	bufsize = HAMMER2_PBUFSIZE;
8092d60b848STomohiro Kusumi 	bufrem = h2_opt->image_size;
8102d60b848STomohiro Kusumi 	if (fsopts->sparse) {
8112d60b848STomohiro Kusumi 		if (ftruncate(fsopts->fd, bufrem) == -1) {
8122d60b848STomohiro Kusumi 			warn("sparse option disabled");
8132d60b848STomohiro Kusumi 			fsopts->sparse = 0;
8142d60b848STomohiro Kusumi 		}
8152d60b848STomohiro Kusumi 	}
8162d60b848STomohiro Kusumi 	if (fsopts->sparse) {
8172d60b848STomohiro Kusumi 		/* File truncated at bufrem. Remaining is 0 */
8182d60b848STomohiro Kusumi 		bufrem = 0;
8192d60b848STomohiro Kusumi 		buf = NULL;
8202d60b848STomohiro Kusumi 	} else {
8212d60b848STomohiro Kusumi 		if (debug & DEBUG_FS_CREATE_IMAGE)
8222d60b848STomohiro Kusumi 			APRINTF("zero-ing image `%s', %lld sectors, "
8232d60b848STomohiro Kusumi 			    "using %d byte chunks\n",
8242d60b848STomohiro Kusumi 			    image, (long long)bufrem, bufsize);
8252d60b848STomohiro Kusumi 		buf = ecalloc(1, bufsize);
8262d60b848STomohiro Kusumi 	}
8272d60b848STomohiro Kusumi 
8282d60b848STomohiro Kusumi 	if (fsopts->offset != 0) {
8292d60b848STomohiro Kusumi 		if (lseek(fsopts->fd, fsopts->offset, SEEK_SET) == -1) {
8302d60b848STomohiro Kusumi 			warn("can't seek");
8312d60b848STomohiro Kusumi 			free(buf);
8322d60b848STomohiro Kusumi 			return -1;
8332d60b848STomohiro Kusumi 		}
8342d60b848STomohiro Kusumi 	}
8352d60b848STomohiro Kusumi 
8362d60b848STomohiro Kusumi 	while (bufrem > 0) {
8372d60b848STomohiro Kusumi 		i = write(fsopts->fd, buf, MIN(bufsize, bufrem));
8382d60b848STomohiro Kusumi 		if (i == -1) {
8392d60b848STomohiro Kusumi 			warn("zeroing image, %lld bytes to go",
8402d60b848STomohiro Kusumi 			    (long long)bufrem);
8412d60b848STomohiro Kusumi 			free(buf);
8422d60b848STomohiro Kusumi 			return -1;
8432d60b848STomohiro Kusumi 		}
8442d60b848STomohiro Kusumi 		bufrem -= i;
8452d60b848STomohiro Kusumi 	}
8462d60b848STomohiro Kusumi 	if (buf)
8472d60b848STomohiro Kusumi 		free(buf);
8482d60b848STomohiro Kusumi 
8492d60b848STomohiro Kusumi 	/* make the file system */
8502d60b848STomohiro Kusumi 	if (debug & DEBUG_FS_CREATE_IMAGE)
8512d60b848STomohiro Kusumi 		APRINTF("calling mkfs(\"%s\", ...)\n", image);
8522d60b848STomohiro Kusumi 	hammer2_mkfs(1, av, opt); /* success if returned */
8532d60b848STomohiro Kusumi 
8542d60b848STomohiro Kusumi 	return fsopts->fd;
8552d60b848STomohiro Kusumi }
8562d60b848STomohiro Kusumi 
8572d60b848STomohiro Kusumi static off_t
8582d60b848STomohiro Kusumi hammer2_phys_size(off_t size)
8592d60b848STomohiro Kusumi {
8602d60b848STomohiro Kusumi 	off_t radix_size, phys_size = 0;
8612d60b848STomohiro Kusumi 	int i;
8622d60b848STomohiro Kusumi 
8632d60b848STomohiro Kusumi 	if (size > HAMMER2_PBUFSIZE) {
8642d60b848STomohiro Kusumi 		phys_size += rounddown(size, HAMMER2_PBUFSIZE);
8652d60b848STomohiro Kusumi 		size = size % HAMMER2_PBUFSIZE;
8662d60b848STomohiro Kusumi 	}
8672d60b848STomohiro Kusumi 
8682d60b848STomohiro Kusumi 	for (i = HAMMER2_RADIX_MIN; i <= HAMMER2_RADIX_MAX; i++) {
8692d60b848STomohiro Kusumi 		radix_size = 1UL << i;
8702d60b848STomohiro Kusumi 		if (radix_size >= size) {
8712d60b848STomohiro Kusumi 			phys_size += radix_size;
8722d60b848STomohiro Kusumi 			break;
8732d60b848STomohiro Kusumi 		}
8742d60b848STomohiro Kusumi 	}
8752d60b848STomohiro Kusumi 
8762d60b848STomohiro Kusumi 	return phys_size;
8772d60b848STomohiro Kusumi }
8782d60b848STomohiro Kusumi 
8792d60b848STomohiro Kusumi /* calculate data size */
8802d60b848STomohiro Kusumi static void
8812d60b848STomohiro Kusumi hammer2_size_dir(fsnode *root, fsinfo_t *fsopts)
8822d60b848STomohiro Kusumi {
8832d60b848STomohiro Kusumi 	fsnode *node;
8842d60b848STomohiro Kusumi 
8852d60b848STomohiro Kusumi 	assert(fsopts != NULL);
8862d60b848STomohiro Kusumi 
8872d60b848STomohiro Kusumi 	if (debug & DEBUG_FS_SIZE_DIR)
8882d60b848STomohiro Kusumi 		APRINTF("entry: bytes %lld inodes %lld\n",
8892d60b848STomohiro Kusumi 		    (long long)fsopts->size, (long long)fsopts->inodes);
8902d60b848STomohiro Kusumi 
8912d60b848STomohiro Kusumi 	for (node = root; node != NULL; node = node->next) {
8922d60b848STomohiro Kusumi 		if (node == root) { /* we're at "." */
8932d60b848STomohiro Kusumi 			assert(strcmp(node->name, ".") == 0);
8942d60b848STomohiro Kusumi 		} else if ((node->inode->flags & FI_SIZED) == 0) {
8952d60b848STomohiro Kusumi 			/* don't count duplicate names */
8962d60b848STomohiro Kusumi 			node->inode->flags |= FI_SIZED;
8972d60b848STomohiro Kusumi 			if (debug & DEBUG_FS_SIZE_DIR_NODE)
8982d60b848STomohiro Kusumi 				APRINTF("`%s' size %lld\n",
8992d60b848STomohiro Kusumi 				    node->name,
9002d60b848STomohiro Kusumi 				    (long long)node->inode->st.st_size);
9012d60b848STomohiro Kusumi 			fsopts->inodes++;
9022d60b848STomohiro Kusumi 			fsopts->size += sizeof(hammer2_inode_data_t);
9032d60b848STomohiro Kusumi 			if (node->type == S_IFREG) {
9042d60b848STomohiro Kusumi 				size_t st_size = node->inode->st.st_size;
9052d60b848STomohiro Kusumi 				if (st_size > HAMMER2_EMBEDDED_BYTES)
9062d60b848STomohiro Kusumi 					fsopts->size += hammer2_phys_size(st_size);
9072d60b848STomohiro Kusumi 			} else if (node->type == S_IFLNK) {
9082d60b848STomohiro Kusumi 				size_t nlen = strlen(node->symlink);
9092d60b848STomohiro Kusumi 				if (nlen > HAMMER2_EMBEDDED_BYTES)
9102d60b848STomohiro Kusumi 					fsopts->size += hammer2_phys_size(nlen);
9112d60b848STomohiro Kusumi 			}
9122d60b848STomohiro Kusumi 		}
9132d60b848STomohiro Kusumi 		if (node->type == S_IFDIR)
9142d60b848STomohiro Kusumi 			hammer2_size_dir(node->child, fsopts);
9152d60b848STomohiro Kusumi 	}
9162d60b848STomohiro Kusumi 
9172d60b848STomohiro Kusumi 	if (debug & DEBUG_FS_SIZE_DIR)
9182d60b848STomohiro Kusumi 		APRINTF("exit: size %lld inodes %lld\n",
9192d60b848STomohiro Kusumi 		    (long long)fsopts->size, (long long)fsopts->inodes);
9202d60b848STomohiro Kusumi }
9212d60b848STomohiro Kusumi 
9222d60b848STomohiro Kusumi static void
9236bcbb706STomohiro Kusumi hammer2_print(const struct m_vnode *dvp, const struct m_vnode *vp,
9242d60b848STomohiro Kusumi     const fsnode *node, int depth, const char *msg)
9252d60b848STomohiro Kusumi {
9262d60b848STomohiro Kusumi 	if (debug & DEBUG_FS_POPULATE) {
9272d60b848STomohiro Kusumi 		if (1) {
9282d60b848STomohiro Kusumi 			int indent = depth * 2;
9292d60b848STomohiro Kusumi 			char *type;
9302d60b848STomohiro Kusumi 			if (S_ISDIR(node->type))
9312d60b848STomohiro Kusumi 				type = "dir";
9322d60b848STomohiro Kusumi 			else if (S_ISREG(node->type))
9332d60b848STomohiro Kusumi 				type = "reg";
9342d60b848STomohiro Kusumi 			else if (S_ISLNK(node->type))
9352d60b848STomohiro Kusumi 				type = "lnk";
9364d8112c5STomohiro Kusumi 			else if (S_ISFIFO(node->type))
9374d8112c5STomohiro Kusumi 				type = "fifo";
9382d60b848STomohiro Kusumi 			else
9392d60b848STomohiro Kusumi 				type = "???";
9402d60b848STomohiro Kusumi 			printf("%*.*s", indent, indent, "");
9412d60b848STomohiro Kusumi 			printf("dvp=%p/%d vp=%p/%d \"%s\" %s %s\n",
9422d60b848STomohiro Kusumi 			    dvp, dvp ? VTOI(dvp)->refs : 0,
9432d60b848STomohiro Kusumi 			    vp, vp ? VTOI(vp)->refs : 0,
9442d60b848STomohiro Kusumi 			    node->name, type, msg);
9452d60b848STomohiro Kusumi 		} else {
9462d60b848STomohiro Kusumi 			char type;
9472d60b848STomohiro Kusumi 			if (S_ISDIR(node->type))
9482d60b848STomohiro Kusumi 				type = 'd';
9492d60b848STomohiro Kusumi 			else if (S_ISREG(node->type))
9502d60b848STomohiro Kusumi 				type = 'r';
9512d60b848STomohiro Kusumi 			else if (S_ISLNK(node->type))
9522d60b848STomohiro Kusumi 				type = 'l';
9534d8112c5STomohiro Kusumi 			else if (S_ISFIFO(node->type))
9544d8112c5STomohiro Kusumi 				type = 'f';
9552d60b848STomohiro Kusumi 			else
9562d60b848STomohiro Kusumi 				type = '?';
9572d60b848STomohiro Kusumi 			printf("%c", type);
9582d60b848STomohiro Kusumi 			fflush(stdout);
9592d60b848STomohiro Kusumi 		}
9602d60b848STomohiro Kusumi 	}
9612d60b848STomohiro Kusumi }
9622d60b848STomohiro Kusumi 
9632d60b848STomohiro Kusumi static int
9646bcbb706STomohiro Kusumi hammer2_populate_dir(struct m_vnode *dvp, const char *dir, fsnode *root,
9652d60b848STomohiro Kusumi     fsnode *parent, fsinfo_t *fsopts, int depth)
9662d60b848STomohiro Kusumi {
9672d60b848STomohiro Kusumi 	fsnode *cur;
9686bcbb706STomohiro Kusumi 	struct m_vnode *vp;
9692d60b848STomohiro Kusumi 	struct stat st;
9702d60b848STomohiro Kusumi 	char f[MAXPATHLEN];
9712d60b848STomohiro Kusumi 	const char *path;
972213ebac1STomohiro Kusumi 	int hardlink;
9732d60b848STomohiro Kusumi 	int error;
9742d60b848STomohiro Kusumi 
9752d60b848STomohiro Kusumi 	assert(dvp != NULL);
9762d60b848STomohiro Kusumi 	assert(dir != NULL);
9772d60b848STomohiro Kusumi 	assert(root != NULL);
9782d60b848STomohiro Kusumi 	assert(parent != NULL);
9792d60b848STomohiro Kusumi 	assert(fsopts != NULL);
9802d60b848STomohiro Kusumi 
9812d60b848STomohiro Kusumi 	/* assert root directory */
9822d60b848STomohiro Kusumi 	assert(S_ISDIR(root->type));
9832d60b848STomohiro Kusumi 	assert(!strcmp(root->name, "."));
9842d60b848STomohiro Kusumi 	assert(!root->child);
9852d60b848STomohiro Kusumi 	assert(!root->parent || root->parent->child == root);
9862d60b848STomohiro Kusumi 
9872d60b848STomohiro Kusumi 	hammer2_print(dvp, NULL, root, depth, "enter");
9882d60b848STomohiro Kusumi 	if (stat(dir, &st) == -1)
9892d60b848STomohiro Kusumi 		err(1, "no such path %s", dir);
9902d60b848STomohiro Kusumi 	if (!S_ISDIR(st.st_mode))
9912d60b848STomohiro Kusumi 		errx(1, "no such dir %s", dir);
9922d60b848STomohiro Kusumi 
9932d60b848STomohiro Kusumi 	for (cur = root->next; cur != NULL; cur = cur->next) {
994ddd1d3d1STomohiro Kusumi 		/* global variable for HAMMER2 vnops */
995ddd1d3d1STomohiro Kusumi 		hammer2_curnode = cur;
996ddd1d3d1STomohiro Kusumi 
9972d60b848STomohiro Kusumi 		/* construct source path */
9982d60b848STomohiro Kusumi 		if (cur->contents) {
9992d60b848STomohiro Kusumi 			path = cur->contents;
10002d60b848STomohiro Kusumi 		} else {
10012d60b848STomohiro Kusumi 			if (S_ISDIR(cur->type)) {
10022d60b848STomohiro Kusumi 				/* this should be same as root/path/name */
10032d60b848STomohiro Kusumi 				if (snprintf(f, sizeof(f), "%s/%s",
10042d60b848STomohiro Kusumi 				    dir, cur->name) >= (int)sizeof(f))
1005d5e693f1STomohiro Kusumi 					errx(1, "path %s too long", f);
10062d60b848STomohiro Kusumi 			} else {
10072d60b848STomohiro Kusumi 				if (snprintf(f, sizeof(f), "%s/%s/%s",
10082d60b848STomohiro Kusumi 				    cur->root, cur->path, cur->name) >= (int)sizeof(f))
1009d5e693f1STomohiro Kusumi 					errx(1, "path %s too long", f);
10102d60b848STomohiro Kusumi 			}
10112d60b848STomohiro Kusumi 			path = f;
10122d60b848STomohiro Kusumi 		}
1013*2de31a7dSTomohiro Kusumi 		if (S_ISLNK(cur->type)) {
1014*2de31a7dSTomohiro Kusumi 			if (lstat(path, &st) == -1)
1015*2de31a7dSTomohiro Kusumi 				err(1, "no such symlink %s", path);
1016*2de31a7dSTomohiro Kusumi 		} else {
10172d60b848STomohiro Kusumi 			if (stat(path, &st) == -1)
1018*2de31a7dSTomohiro Kusumi 				err(1, "no such path %s", path);
1019*2de31a7dSTomohiro Kusumi 		}
10202d60b848STomohiro Kusumi 
10212d60b848STomohiro Kusumi 		/* update node state */
10222d60b848STomohiro Kusumi 		if ((cur->inode->flags & FI_ALLOCATED) == 0) {
10232d60b848STomohiro Kusumi 			cur->inode->flags |= FI_ALLOCATED;
10242d60b848STomohiro Kusumi 			if (cur != root)
10252d60b848STomohiro Kusumi 				cur->parent = parent;
10262d60b848STomohiro Kusumi 		}
10272d60b848STomohiro Kusumi 
1028213ebac1STomohiro Kusumi 		/* detect hardlink */
1029213ebac1STomohiro Kusumi 		if (cur->inode->flags & FI_WRITTEN) {
1030213ebac1STomohiro Kusumi 			assert(!S_ISDIR(cur->type));
1031213ebac1STomohiro Kusumi 			hardlink = 1;
1032213ebac1STomohiro Kusumi 		} else {
1033213ebac1STomohiro Kusumi 			hardlink = 0;
1034213ebac1STomohiro Kusumi 		}
10352d60b848STomohiro Kusumi 		cur->inode->flags |= FI_WRITTEN;
10362d60b848STomohiro Kusumi 
10372d60b848STomohiro Kusumi 		/* make sure it doesn't exist yet */
10382d60b848STomohiro Kusumi 		vp = NULL;
10392d60b848STomohiro Kusumi 		error = hammer2_nresolve(dvp, &vp, cur->name,
10402d60b848STomohiro Kusumi 		    strlen(cur->name));
10412d60b848STomohiro Kusumi 		if (!error)
10422d60b848STomohiro Kusumi 			errx(1, "hammer2_nresolve(\"%s\") already exists",
10432d60b848STomohiro Kusumi 			    cur->name);
10442d60b848STomohiro Kusumi 		hammer2_print(dvp, vp, cur, depth, "nresolve");
10452d60b848STomohiro Kusumi 
10462d60b848STomohiro Kusumi 		/* if directory, mkdir and recurse */
10472d60b848STomohiro Kusumi 		if (S_ISDIR(cur->type)) {
10482d60b848STomohiro Kusumi 			assert(cur->child);
10492d60b848STomohiro Kusumi 
10502d60b848STomohiro Kusumi 			vp = NULL;
10512d60b848STomohiro Kusumi 			error = hammer2_nmkdir(dvp, &vp, cur->name,
10528f9d1da1STomohiro Kusumi 			    strlen(cur->name), cur->inode->st.st_mode);
10532d60b848STomohiro Kusumi 			if (error)
10542d60b848STomohiro Kusumi 				errx(1, "hammer2_nmkdir(\"%s\") failed: %s",
10552d60b848STomohiro Kusumi 				    cur->name, strerror(error));
10562d60b848STomohiro Kusumi 			assert(vp);
10572d60b848STomohiro Kusumi 			hammer2_print(dvp, vp, cur, depth, "nmkdir");
10582d60b848STomohiro Kusumi 
10592d60b848STomohiro Kusumi 			error = hammer2_populate_dir(vp, path, cur->child, cur,
10602d60b848STomohiro Kusumi 			    fsopts, depth + 1);
10612d60b848STomohiro Kusumi 			if (error)
10622d60b848STomohiro Kusumi 				errx(1, "failed to populate %s: %s",
10632d60b848STomohiro Kusumi 				    path, strerror(error));
10643bffb051STomohiro Kusumi 			cur->inode->param = vp;
10652d60b848STomohiro Kusumi 			continue;
10662d60b848STomohiro Kusumi 		}
10672d60b848STomohiro Kusumi 
10682d60b848STomohiro Kusumi 		/* if regular file, creat and write its data */
1069213ebac1STomohiro Kusumi 		if (S_ISREG(cur->type) && !hardlink) {
10702d60b848STomohiro Kusumi 			assert(cur->child == NULL);
10712d60b848STomohiro Kusumi 
10722d60b848STomohiro Kusumi 			vp = NULL;
10732d60b848STomohiro Kusumi 			error = hammer2_ncreate(dvp, &vp, cur->name,
10748f9d1da1STomohiro Kusumi 			    strlen(cur->name), cur->inode->st.st_mode);
10752d60b848STomohiro Kusumi 			if (error)
10762d60b848STomohiro Kusumi 				errx(1, "hammer2_ncreate(\"%s\") failed: %s",
10772d60b848STomohiro Kusumi 				    cur->name, strerror(error));
10782d60b848STomohiro Kusumi 			assert(vp);
10792d60b848STomohiro Kusumi 			hammer2_print(dvp, vp, cur, depth, "ncreate");
10802d60b848STomohiro Kusumi 
10812d60b848STomohiro Kusumi 			error = hammer2_write_file(vp, path, cur);
10822d60b848STomohiro Kusumi 			if (error)
10832d60b848STomohiro Kusumi 				errx(1, "hammer2_write_file(\"%s\") failed: %s",
10842d60b848STomohiro Kusumi 				    path, strerror(error));
10853bffb051STomohiro Kusumi 			cur->inode->param = vp;
1086213ebac1STomohiro Kusumi 			continue;
1087213ebac1STomohiro Kusumi 		}
1088213ebac1STomohiro Kusumi 
10892d60b848STomohiro Kusumi 		/* if symlink, create a symlink against target */
10902d60b848STomohiro Kusumi 		if (S_ISLNK(cur->type)) {
10912d60b848STomohiro Kusumi 			assert(cur->child == NULL);
10922d60b848STomohiro Kusumi 
10932d60b848STomohiro Kusumi 			vp = NULL;
10942d60b848STomohiro Kusumi 			error = hammer2_nsymlink(dvp, &vp, cur->name,
10958f9d1da1STomohiro Kusumi 			    strlen(cur->name), cur->symlink,
10968f9d1da1STomohiro Kusumi 			    cur->inode->st.st_mode);
10972d60b848STomohiro Kusumi 			if (error)
10982d60b848STomohiro Kusumi 				errx(1, "hammer2_nsymlink(\"%s\") failed: %s",
10992d60b848STomohiro Kusumi 				    cur->name, strerror(error));
11002d60b848STomohiro Kusumi 			assert(vp);
11012d60b848STomohiro Kusumi 			hammer2_print(dvp, vp, cur, depth, "nsymlink");
11023bffb051STomohiro Kusumi 			cur->inode->param = vp;
11032d60b848STomohiro Kusumi 			continue;
11042d60b848STomohiro Kusumi 		}
11052d60b848STomohiro Kusumi 
11064d8112c5STomohiro Kusumi 		/* if fifo, create a fifo */
110748a07aadSTomohiro Kusumi 		if (S_ISFIFO(cur->type) && !hardlink) {
11084d8112c5STomohiro Kusumi 			assert(cur->child == NULL);
11094d8112c5STomohiro Kusumi 
11104d8112c5STomohiro Kusumi 			vp = NULL;
11114d8112c5STomohiro Kusumi 			error = hammer2_nmknod(dvp, &vp, cur->name,
11128f9d1da1STomohiro Kusumi 			    strlen(cur->name), VFIFO, cur->inode->st.st_mode);
11134d8112c5STomohiro Kusumi 			if (error)
11144d8112c5STomohiro Kusumi 				errx(1, "hammer2_nmknod(\"%s\") failed: %s",
11154d8112c5STomohiro Kusumi 				    cur->name, strerror(error));
11164d8112c5STomohiro Kusumi 			assert(vp);
11174d8112c5STomohiro Kusumi 			hammer2_print(dvp, vp, cur, depth, "nmknod");
11183bffb051STomohiro Kusumi 			cur->inode->param = vp;
11194d8112c5STomohiro Kusumi 			continue;
11204d8112c5STomohiro Kusumi 		}
11214d8112c5STomohiro Kusumi 
112248a07aadSTomohiro Kusumi 		/* if hardlink, creat a hardlink */
112348a07aadSTomohiro Kusumi 		if ((S_ISREG(cur->type) || S_ISFIFO(cur->type)) && hardlink) {
112448a07aadSTomohiro Kusumi 			char buf[64];
112548a07aadSTomohiro Kusumi 			assert(cur->child == NULL);
112648a07aadSTomohiro Kusumi 
112748a07aadSTomohiro Kusumi 			/* source vnode must not be NULL */
11283bffb051STomohiro Kusumi 			vp = cur->inode->param;
112948a07aadSTomohiro Kusumi 			assert(vp);
113048a07aadSTomohiro Kusumi 			/* currently these conditions must be true */
113148a07aadSTomohiro Kusumi 			assert(vp->v_data);
113248a07aadSTomohiro Kusumi 			assert(vp->v_type == VREG || vp->v_type == VFIFO);
113348a07aadSTomohiro Kusumi 			assert(vp->v_logical);
113448a07aadSTomohiro Kusumi 			assert(!vp->v_vflushed);
113548a07aadSTomohiro Kusumi 			assert(vp->v_malloced);
113648a07aadSTomohiro Kusumi 			assert(VTOI(vp)->refs > 0);
113748a07aadSTomohiro Kusumi 
113848a07aadSTomohiro Kusumi 			error = hammer2_nlink(dvp, vp, cur->name,
113948a07aadSTomohiro Kusumi 			    strlen(cur->name));
114048a07aadSTomohiro Kusumi 			if (error)
114148a07aadSTomohiro Kusumi 				errx(1, "hammer2_nlink(\"%s\") failed: %s",
114248a07aadSTomohiro Kusumi 				    cur->name, strerror(error));
1143f8a1147cSTomohiro Kusumi 			snprintf(buf, sizeof(buf), "nlink=%lld",
1144f8a1147cSTomohiro Kusumi 			    (long long)VTOI(vp)->meta.nlinks);
114548a07aadSTomohiro Kusumi 			hammer2_print(dvp, vp, cur, depth, buf);
114648a07aadSTomohiro Kusumi 			continue;
114748a07aadSTomohiro Kusumi 		}
114848a07aadSTomohiro Kusumi 
11492d60b848STomohiro Kusumi 		/* other types are unsupported */
11502d60b848STomohiro Kusumi 		printf("ignore %s 0%o\n", path, cur->type);
11512d60b848STomohiro Kusumi 	}
11522d60b848STomohiro Kusumi 
11532d60b848STomohiro Kusumi 	return 0;
11542d60b848STomohiro Kusumi }
11552d60b848STomohiro Kusumi 
11562d60b848STomohiro Kusumi static int
11576bcbb706STomohiro Kusumi hammer2_write_file(struct m_vnode *vp, const char *path, fsnode *node)
11582d60b848STomohiro Kusumi {
11592d60b848STomohiro Kusumi 	struct stat *st = &node->inode->st;
11602d60b848STomohiro Kusumi 	size_t nsize, bufsize;
11612d60b848STomohiro Kusumi 	off_t offset;
11622d60b848STomohiro Kusumi 	int fd, error;
11632d60b848STomohiro Kusumi 	char *p;
11642d60b848STomohiro Kusumi 
11652d60b848STomohiro Kusumi 	nsize = st->st_size;
11662d60b848STomohiro Kusumi 	if (nsize == 0)
11672d60b848STomohiro Kusumi 		return 0;
11682d60b848STomohiro Kusumi 	/* check nsize vs maximum file size */
11692d60b848STomohiro Kusumi 
11702d60b848STomohiro Kusumi 	fd = open(path, O_RDONLY);
11712d60b848STomohiro Kusumi 	if (fd < 0)
11722d60b848STomohiro Kusumi 		err(1, "failed to open %s", path);
11732d60b848STomohiro Kusumi 
11742d60b848STomohiro Kusumi 	p = mmap(0, nsize, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0);
11752d60b848STomohiro Kusumi 	if (p == MAP_FAILED)
11762d60b848STomohiro Kusumi 		err(1, "failed to mmap %s", path);
11772d60b848STomohiro Kusumi 	close(fd);
11782d60b848STomohiro Kusumi 
11792d60b848STomohiro Kusumi 	for (offset = 0; offset < nsize; ) {
11802d60b848STomohiro Kusumi 		bufsize = MIN(nsize - offset, HAMMER2_PBUFSIZE);
11812d60b848STomohiro Kusumi 		assert(bufsize <= HAMMER2_PBUFSIZE);
11822d60b848STomohiro Kusumi 		error = hammer2_write(vp, p + offset, bufsize, offset);
11832d60b848STomohiro Kusumi 		if (error)
11842d60b848STomohiro Kusumi 			errx(1, "failed to write to %s vnode: %s",
11852d60b848STomohiro Kusumi 			    path, strerror(error));
11862d60b848STomohiro Kusumi 		offset += bufsize;
11872d60b848STomohiro Kusumi 		if (bufsize == HAMMER2_PBUFSIZE)
11882d60b848STomohiro Kusumi 			assert((offset & (HAMMER2_PBUFSIZE - 1)) == 0);
11892d60b848STomohiro Kusumi 	}
11902d60b848STomohiro Kusumi 	munmap(p, nsize);
11912d60b848STomohiro Kusumi 
11922d60b848STomohiro Kusumi 	return 0;
11932d60b848STomohiro Kusumi }
1194a63188c8STomohiro Kusumi 
1195f72350acSTomohiro Kusumi static int
11966857f034STomohiro Kusumi trim_char(char *p, char c)
11976857f034STomohiro Kusumi {
11986857f034STomohiro Kusumi 	char *o, tmp[PATH_MAX];
1199bca6a9a0STomohiro Kusumi 	bool prev_was_c;
12006857f034STomohiro Kusumi 	size_t n;
12016857f034STomohiro Kusumi 	int i;
12026857f034STomohiro Kusumi 
12036857f034STomohiro Kusumi 	strlcpy(tmp, p, sizeof(tmp));
12046857f034STomohiro Kusumi 	if (strncmp(tmp, p, sizeof(tmp)))
12056857f034STomohiro Kusumi 		return ENOSPC;
12066857f034STomohiro Kusumi 
12076857f034STomohiro Kusumi 	/* trim consecutive */
1208bca6a9a0STomohiro Kusumi 	prev_was_c = false;
12096857f034STomohiro Kusumi 	o = p;
12106857f034STomohiro Kusumi 	n = strlen(p);
12116857f034STomohiro Kusumi 
12126857f034STomohiro Kusumi 	for (i = 0; i < n; i++) {
12136857f034STomohiro Kusumi 		if (tmp[i] == c) {
1214bca6a9a0STomohiro Kusumi 			if (!prev_was_c)
12156857f034STomohiro Kusumi 				*p++ = tmp[i];
1216bca6a9a0STomohiro Kusumi 			prev_was_c = true;
12176857f034STomohiro Kusumi 		} else {
12186857f034STomohiro Kusumi 			*p++ = tmp[i];
1219bca6a9a0STomohiro Kusumi 			prev_was_c = false;
12206857f034STomohiro Kusumi 		}
12216857f034STomohiro Kusumi 	}
12226857f034STomohiro Kusumi 	*p = 0;
12236857f034STomohiro Kusumi 	assert(strlen(p) <= strlen(tmp));
12246857f034STomohiro Kusumi 
12256857f034STomohiro Kusumi 	/* assert no consecutive */
1226bca6a9a0STomohiro Kusumi 	prev_was_c = false;
12276857f034STomohiro Kusumi 	p = o;
12286857f034STomohiro Kusumi 	n = strlen(p);
12296857f034STomohiro Kusumi 
12306857f034STomohiro Kusumi 	for (i = 0; i < n; i++) {
12316857f034STomohiro Kusumi 		if (p[i] == c) {
1232bca6a9a0STomohiro Kusumi 			assert(!prev_was_c);
1233bca6a9a0STomohiro Kusumi 			prev_was_c = true;
12346857f034STomohiro Kusumi 		} else {
1235bca6a9a0STomohiro Kusumi 			prev_was_c = false;
12366857f034STomohiro Kusumi 		}
12376857f034STomohiro Kusumi 	}
12386857f034STomohiro Kusumi 
12396857f034STomohiro Kusumi 	/* trim leading */
12406857f034STomohiro Kusumi 	if (*p == c)
12416857f034STomohiro Kusumi 		memmove(p, p + 1, strlen(p + 1) + 1);
12426857f034STomohiro Kusumi 	assert(*p != '/');
12436857f034STomohiro Kusumi 
12446857f034STomohiro Kusumi 	/* trim trailing */
12456857f034STomohiro Kusumi 	p += strlen(p);
12466857f034STomohiro Kusumi 	p--;
12476857f034STomohiro Kusumi 	if (*p == c)
12486857f034STomohiro Kusumi 		*p = 0;
12496857f034STomohiro Kusumi 	assert(p[strlen(p) - 1] != '/');
12506857f034STomohiro Kusumi 
12516857f034STomohiro Kusumi 	return 0;
12526857f034STomohiro Kusumi }
12536857f034STomohiro Kusumi 
12546857f034STomohiro Kusumi static int
12556857f034STomohiro Kusumi trim_slash(char *p)
12566857f034STomohiro Kusumi {
12576857f034STomohiro Kusumi 	return trim_char(p, '/');
12586857f034STomohiro Kusumi }
12596857f034STomohiro Kusumi 
12606857f034STomohiro Kusumi static int
1261f72350acSTomohiro Kusumi hammer2_version_get(struct m_vnode *vp)
1262f72350acSTomohiro Kusumi {
1263f72350acSTomohiro Kusumi 	hammer2_dev_t *hmp;
1264f72350acSTomohiro Kusumi 
1265f72350acSTomohiro Kusumi 	hmp = VTOI(vp)->pmp->pfs_hmps[0];
1266f72350acSTomohiro Kusumi 	if (hmp == NULL)
1267f72350acSTomohiro Kusumi 		return EINVAL;
1268f72350acSTomohiro Kusumi 
1269f72350acSTomohiro Kusumi 	printf("version: %d\n", hmp->voldata.version);
1270f72350acSTomohiro Kusumi 
1271f72350acSTomohiro Kusumi 	return 0;
1272f72350acSTomohiro Kusumi }
1273f72350acSTomohiro Kusumi 
12743999233bSTomohiro Kusumi struct pfs_entry {
12753999233bSTomohiro Kusumi 	TAILQ_ENTRY(pfs_entry) entry;
12763999233bSTomohiro Kusumi 	char name[NAME_MAX+1];
12773999233bSTomohiro Kusumi 	char s[NAME_MAX+1];
12783999233bSTomohiro Kusumi };
12793999233bSTomohiro Kusumi 
12803999233bSTomohiro Kusumi static int
12813999233bSTomohiro Kusumi hammer2_pfs_get(struct m_vnode *vp)
12823999233bSTomohiro Kusumi {
12833999233bSTomohiro Kusumi 	hammer2_ioc_pfs_t pfs;
12843999233bSTomohiro Kusumi 	TAILQ_HEAD(, pfs_entry) head;
12853999233bSTomohiro Kusumi 	struct pfs_entry *p, *e;
12863999233bSTomohiro Kusumi 	char *pfs_id_str;
12873999233bSTomohiro Kusumi 	const char *type_str;
12883999233bSTomohiro Kusumi 	int error;
12893999233bSTomohiro Kusumi 
12903999233bSTomohiro Kusumi 	bzero(&pfs, sizeof(pfs));
12913999233bSTomohiro Kusumi 	TAILQ_INIT(&head);
12923999233bSTomohiro Kusumi 
12933999233bSTomohiro Kusumi 	while ((pfs.name_key = pfs.name_next) != (hammer2_key_t)-1) {
12943999233bSTomohiro Kusumi 		error = hammer2_ioctl_pfs_get(VTOI(vp), &pfs);
12953999233bSTomohiro Kusumi 		if (error)
12963999233bSTomohiro Kusumi 			return error;
12973999233bSTomohiro Kusumi 
12983999233bSTomohiro Kusumi 		pfs_id_str = NULL;
12993999233bSTomohiro Kusumi 		hammer2_uuid_to_str(&pfs.pfs_clid, &pfs_id_str);
13003999233bSTomohiro Kusumi 
13013999233bSTomohiro Kusumi 		if (pfs.pfs_type == HAMMER2_PFSTYPE_MASTER) {
13023999233bSTomohiro Kusumi 			if (pfs.pfs_subtype == HAMMER2_PFSSUBTYPE_NONE)
13033999233bSTomohiro Kusumi 				type_str = "MASTER";
13043999233bSTomohiro Kusumi 			else
13053999233bSTomohiro Kusumi 				type_str = hammer2_pfssubtype_to_str(
13063999233bSTomohiro Kusumi 				    pfs.pfs_subtype);
13073999233bSTomohiro Kusumi 		} else {
13083999233bSTomohiro Kusumi 			type_str = hammer2_pfstype_to_str(pfs.pfs_type);
13093999233bSTomohiro Kusumi 		}
131006d1ac82STomohiro Kusumi 		e = ecalloc(1, sizeof(*e));
13113999233bSTomohiro Kusumi 		snprintf(e->name, sizeof(e->name), "%s", pfs.name);
13123999233bSTomohiro Kusumi 		snprintf(e->s, sizeof(e->s), "%-11s %s", type_str, pfs_id_str);
13133999233bSTomohiro Kusumi 		free(pfs_id_str);
13143999233bSTomohiro Kusumi 
13153999233bSTomohiro Kusumi 		p = TAILQ_FIRST(&head);
13163999233bSTomohiro Kusumi 		while (p) {
13173999233bSTomohiro Kusumi 			if (strcmp(e->name, p->name) <= 0) {
13183999233bSTomohiro Kusumi 				TAILQ_INSERT_BEFORE(p, e, entry);
13193999233bSTomohiro Kusumi 				break;
13203999233bSTomohiro Kusumi 			}
13213999233bSTomohiro Kusumi 			p = TAILQ_NEXT(p, entry);
13223999233bSTomohiro Kusumi 		}
13233999233bSTomohiro Kusumi 		if (!p)
13243999233bSTomohiro Kusumi 			TAILQ_INSERT_TAIL(&head, e, entry);
13253999233bSTomohiro Kusumi 	}
13263999233bSTomohiro Kusumi 
13273999233bSTomohiro Kusumi 	printf("Type        "
13283999233bSTomohiro Kusumi 	    "ClusterId (pfs_clid)                 "
13293999233bSTomohiro Kusumi 	    "Label\n");
13303999233bSTomohiro Kusumi 	while ((p = TAILQ_FIRST(&head)) != NULL) {
13313999233bSTomohiro Kusumi 		printf("%s %s\n", p->s, p->name);
13323999233bSTomohiro Kusumi 		TAILQ_REMOVE(&head, p, entry);
13333999233bSTomohiro Kusumi 		free(p);
13343999233bSTomohiro Kusumi 	}
13353999233bSTomohiro Kusumi 
13363999233bSTomohiro Kusumi 	return 0;
13373999233bSTomohiro Kusumi }
13383999233bSTomohiro Kusumi 
13393999233bSTomohiro Kusumi static int
13403999233bSTomohiro Kusumi hammer2_pfs_lookup(struct m_vnode *vp, const char *pfs_name)
13413999233bSTomohiro Kusumi {
13423999233bSTomohiro Kusumi 	hammer2_ioc_pfs_t pfs;
13433999233bSTomohiro Kusumi 	char *pfs_id_str;
13443999233bSTomohiro Kusumi 	int error;
13453999233bSTomohiro Kusumi 
13463999233bSTomohiro Kusumi 	bzero(&pfs, sizeof(pfs));
13473999233bSTomohiro Kusumi 	strlcpy(pfs.name, pfs_name, sizeof(pfs.name));
13483999233bSTomohiro Kusumi 
13493999233bSTomohiro Kusumi 	error = hammer2_ioctl_pfs_lookup(VTOI(vp), &pfs);
13503999233bSTomohiro Kusumi 	if (error == 0) {
13513999233bSTomohiro Kusumi 		printf("name: %s\n", pfs.name);
13523999233bSTomohiro Kusumi 		printf("type: %s\n", hammer2_pfstype_to_str(pfs.pfs_type));
13533999233bSTomohiro Kusumi 		printf("subtype: %s\n",
13543999233bSTomohiro Kusumi 		    hammer2_pfssubtype_to_str(pfs.pfs_subtype));
13553999233bSTomohiro Kusumi 
13563999233bSTomohiro Kusumi 		pfs_id_str = NULL;
13573999233bSTomohiro Kusumi 		hammer2_uuid_to_str(&pfs.pfs_fsid, &pfs_id_str);
13583999233bSTomohiro Kusumi 		printf("fsid: %s\n", pfs_id_str);
13593999233bSTomohiro Kusumi 		free(pfs_id_str);
13603999233bSTomohiro Kusumi 
13613999233bSTomohiro Kusumi 		pfs_id_str = NULL;
13623999233bSTomohiro Kusumi 		hammer2_uuid_to_str(&pfs.pfs_clid, &pfs_id_str);
13633999233bSTomohiro Kusumi 		printf("clid: %s\n", pfs_id_str);
13643999233bSTomohiro Kusumi 		free(pfs_id_str);
13653999233bSTomohiro Kusumi 	}
13663999233bSTomohiro Kusumi 
13673999233bSTomohiro Kusumi 	return error;
13683999233bSTomohiro Kusumi }
13693999233bSTomohiro Kusumi 
13703999233bSTomohiro Kusumi static int
13713999233bSTomohiro Kusumi hammer2_pfs_create(struct m_vnode *vp, const char *pfs_name)
13723999233bSTomohiro Kusumi {
13733999233bSTomohiro Kusumi 	hammer2_ioc_pfs_t pfs;
13743999233bSTomohiro Kusumi 	int error;
13753999233bSTomohiro Kusumi 
13763999233bSTomohiro Kusumi 	bzero(&pfs, sizeof(pfs));
13773999233bSTomohiro Kusumi 	strlcpy(pfs.name, pfs_name, sizeof(pfs.name));
13783999233bSTomohiro Kusumi 	pfs.pfs_type = HAMMER2_PFSTYPE_MASTER;
13793999233bSTomohiro Kusumi 	uuid_create(&pfs.pfs_clid, NULL);
13803999233bSTomohiro Kusumi 	uuid_create(&pfs.pfs_fsid, NULL);
13813999233bSTomohiro Kusumi 
13823999233bSTomohiro Kusumi 	error = hammer2_ioctl_pfs_create(VTOI(vp), &pfs);
13833999233bSTomohiro Kusumi 	if (error == EEXIST)
13843999233bSTomohiro Kusumi 		fprintf(stderr,
13853999233bSTomohiro Kusumi 		    "NOTE: Typically the same name is "
13863999233bSTomohiro Kusumi 		    "used for cluster elements on "
13873999233bSTomohiro Kusumi 		    "different mounts,\n"
13883999233bSTomohiro Kusumi 		    "      but cluster elements on the "
13893999233bSTomohiro Kusumi 		    "same mount require unique names.\n"
13903999233bSTomohiro Kusumi 		    "hammer2: pfs_create(%s): already present\n",
13913999233bSTomohiro Kusumi 		    pfs_name);
13923999233bSTomohiro Kusumi 
13933999233bSTomohiro Kusumi 	return error;
13943999233bSTomohiro Kusumi }
13953999233bSTomohiro Kusumi 
13963999233bSTomohiro Kusumi static int
13973999233bSTomohiro Kusumi hammer2_pfs_delete(struct m_vnode *vp, const char *pfs_name)
13983999233bSTomohiro Kusumi {
13993999233bSTomohiro Kusumi 	hammer2_ioc_pfs_t pfs;
14003999233bSTomohiro Kusumi 
14013999233bSTomohiro Kusumi 	bzero(&pfs, sizeof(pfs));
14023999233bSTomohiro Kusumi 	strlcpy(pfs.name, pfs_name, sizeof(pfs.name));
14033999233bSTomohiro Kusumi 
14043999233bSTomohiro Kusumi 	return hammer2_ioctl_pfs_delete(VTOI(vp), &pfs);
14053999233bSTomohiro Kusumi }
14063999233bSTomohiro Kusumi 
14073999233bSTomohiro Kusumi static int
14083999233bSTomohiro Kusumi hammer2_pfs_snapshot(struct m_vnode *vp, const char *pfs_name,
14093999233bSTomohiro Kusumi     const char *mount_label)
14103999233bSTomohiro Kusumi {
14113999233bSTomohiro Kusumi 	hammer2_ioc_pfs_t pfs;
14123999233bSTomohiro Kusumi 	struct tm *tp;
14133999233bSTomohiro Kusumi 	time_t t;
14143999233bSTomohiro Kusumi 
14153999233bSTomohiro Kusumi 	bzero(&pfs, sizeof(pfs));
14163999233bSTomohiro Kusumi 	strlcpy(pfs.name, pfs_name, sizeof(pfs.name));
14173999233bSTomohiro Kusumi 
14183999233bSTomohiro Kusumi 	if (strlen(pfs.name) == 0) {
14193999233bSTomohiro Kusumi 		time(&t);
14203999233bSTomohiro Kusumi 		tp = localtime(&t);
14213999233bSTomohiro Kusumi 		snprintf(pfs.name, sizeof(pfs.name),
14223999233bSTomohiro Kusumi 		    "%s.%04d%02d%02d.%02d%02d%02d",
14233999233bSTomohiro Kusumi 		    mount_label,
14243999233bSTomohiro Kusumi 		    tp->tm_year + 1900,
14253999233bSTomohiro Kusumi 		    tp->tm_mon + 1,
14263999233bSTomohiro Kusumi 		    tp->tm_mday,
14273999233bSTomohiro Kusumi 		    tp->tm_hour,
14283999233bSTomohiro Kusumi 		    tp->tm_min,
14293999233bSTomohiro Kusumi 		    tp->tm_sec);
14303999233bSTomohiro Kusumi 	}
14313999233bSTomohiro Kusumi 
14323999233bSTomohiro Kusumi 	return hammer2_ioctl_pfs_snapshot(VTOI(vp), &pfs);
14333999233bSTomohiro Kusumi }
14343999233bSTomohiro Kusumi 
14353999233bSTomohiro Kusumi static int
1436f804c425STomohiro Kusumi hammer2_inode_getx(struct m_vnode *dvp, const char *f)
1437f804c425STomohiro Kusumi {
1438f804c425STomohiro Kusumi 	hammer2_ioc_inode_t inode;
1439f804c425STomohiro Kusumi 	hammer2_inode_t *ip;
1440f804c425STomohiro Kusumi 	hammer2_inode_meta_t *meta;
1441f804c425STomohiro Kusumi 	struct m_vnode *vp;
1442f804c425STomohiro Kusumi 	char *o, *p, *name, *str = NULL;
1443f804c425STomohiro Kusumi 	int error;
1444f804c425STomohiro Kusumi 	uuid_t uuid;
1445f804c425STomohiro Kusumi 
1446f804c425STomohiro Kusumi 	assert(strlen(f) > 0);
14476857f034STomohiro Kusumi 	o = p = name = strdup(f);
1448f804c425STomohiro Kusumi 
14496857f034STomohiro Kusumi 	error = trim_slash(p);
14506857f034STomohiro Kusumi 	if (error)
14516857f034STomohiro Kusumi 		return error;
1452f804c425STomohiro Kusumi 	if (strlen(p) == 0)
1453f804c425STomohiro Kusumi 		return EINVAL;
1454f804c425STomohiro Kusumi 
1455f804c425STomohiro Kusumi 	while ((p = strchr(p, '/')) != NULL) {
1456f804c425STomohiro Kusumi 		*p++ = 0; /* NULL terminate name */
1457f804c425STomohiro Kusumi 		vp = NULL;
1458f804c425STomohiro Kusumi 		error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1459f804c425STomohiro Kusumi 		if (error)
1460f804c425STomohiro Kusumi 			return error;
1461f804c425STomohiro Kusumi 
1462f804c425STomohiro Kusumi 		ip = VTOI(vp);
1463f804c425STomohiro Kusumi 		assert(ip->meta.type == HAMMER2_OBJTYPE_DIRECTORY);
1464f804c425STomohiro Kusumi 
1465f804c425STomohiro Kusumi 		dvp = vp;
1466f804c425STomohiro Kusumi 		name = p;
1467f804c425STomohiro Kusumi 	}
1468f804c425STomohiro Kusumi 
1469f804c425STomohiro Kusumi 	error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1470f804c425STomohiro Kusumi 	if (error)
1471f804c425STomohiro Kusumi 		return error;
1472f804c425STomohiro Kusumi 
1473f804c425STomohiro Kusumi 	bzero(&inode, sizeof(inode));
1474f804c425STomohiro Kusumi 	error = hammer2_ioctl_inode_get(VTOI(vp), &inode);
1475f804c425STomohiro Kusumi 	if (error)
1476f804c425STomohiro Kusumi 		return error;
1477f804c425STomohiro Kusumi 
1478f804c425STomohiro Kusumi 	meta = &inode.ip_data.meta;
1479f804c425STomohiro Kusumi 	printf("--------------------\n");
1480f804c425STomohiro Kusumi 	printf("flags = 0x%x\n", inode.flags);
1481f804c425STomohiro Kusumi 	printf("data_count = %ju\n", (uintmax_t)inode.data_count);
1482f804c425STomohiro Kusumi 	printf("inode_count = %ju\n", (uintmax_t)inode.inode_count);
1483f804c425STomohiro Kusumi 	printf("--------------------\n");
1484f804c425STomohiro Kusumi 	printf("version = %u\n", meta->version);
1485f804c425STomohiro Kusumi 	printf("pfs_subtype = %u (%s)\n", meta->pfs_subtype,
1486f804c425STomohiro Kusumi 	    hammer2_pfssubtype_to_str(meta->pfs_subtype));
1487f804c425STomohiro Kusumi 	printf("uflags = 0x%x\n", (unsigned int)meta->uflags);
1488f804c425STomohiro Kusumi 	printf("rmajor = %u\n", meta->rmajor);
1489f804c425STomohiro Kusumi 	printf("rminor = %u\n", meta->rminor);
1490f804c425STomohiro Kusumi 	printf("ctime = %s\n", hammer2_time64_to_str(meta->ctime, &str));
1491f804c425STomohiro Kusumi 	printf("mtime = %s\n", hammer2_time64_to_str(meta->mtime, &str));
1492f804c425STomohiro Kusumi 	printf("atime = %s\n", hammer2_time64_to_str(meta->atime, &str));
1493f804c425STomohiro Kusumi 	printf("btime = %s\n", hammer2_time64_to_str(meta->btime, &str));
1494f804c425STomohiro Kusumi 	uuid = meta->uid;
1495f804c425STomohiro Kusumi 	printf("uid = %s\n", hammer2_uuid_to_str(&uuid, &str));
1496f804c425STomohiro Kusumi 	uuid = meta->gid;
1497f804c425STomohiro Kusumi 	printf("gid = %s\n", hammer2_uuid_to_str(&uuid, &str));
1498f804c425STomohiro Kusumi 	printf("type = %u (%s)\n", meta->type,
1499f804c425STomohiro Kusumi 	    hammer2_iptype_to_str(meta->type));
1500f804c425STomohiro Kusumi 	printf("op_flags = 0x%x\n", meta->op_flags);
1501f804c425STomohiro Kusumi 	printf("cap_flags = 0x%x\n", meta->cap_flags);
1502f804c425STomohiro Kusumi 	printf("mode = 0%o\n", meta->mode);
1503f804c425STomohiro Kusumi 	printf("inum = 0x%jx\n", (uintmax_t)meta->inum);
1504f804c425STomohiro Kusumi 	printf("size = %ju\n", (uintmax_t)meta->size);
1505f804c425STomohiro Kusumi 	printf("nlinks = %ju\n", (uintmax_t)meta->nlinks);
1506f804c425STomohiro Kusumi 	printf("iparent = 0x%jx\n", (uintmax_t)meta->iparent);
1507f804c425STomohiro Kusumi 	printf("name_key = 0x%jx\n", (uintmax_t)meta->name_key);
1508f804c425STomohiro Kusumi 	printf("name_len = %u\n", meta->name_len);
1509f804c425STomohiro Kusumi 	printf("ncopies = %u\n", meta->ncopies);
1510f804c425STomohiro Kusumi 	printf("comp_algo = %u\n", meta->comp_algo);
1511f804c425STomohiro Kusumi 	printf("target_type = %u\n", meta->target_type);
1512f804c425STomohiro Kusumi 	printf("check_algo = %u\n", meta->check_algo);
1513f804c425STomohiro Kusumi 	printf("pfs_nmasters = %u\n", meta->pfs_nmasters);
1514f804c425STomohiro Kusumi 	printf("pfs_type = %u (%s)\n", meta->pfs_type,
1515f804c425STomohiro Kusumi 	    hammer2_pfstype_to_str(meta->pfs_type));
1516f804c425STomohiro Kusumi 	printf("pfs_inum = 0x%jx\n", (uintmax_t)meta->pfs_inum);
1517f804c425STomohiro Kusumi 	uuid = meta->pfs_clid;
1518f804c425STomohiro Kusumi 	printf("pfs_clid = %s\n", hammer2_uuid_to_str(&uuid, &str));
1519f804c425STomohiro Kusumi 	uuid = meta->pfs_fsid;
1520f804c425STomohiro Kusumi 	printf("pfs_fsid = %s\n", hammer2_uuid_to_str(&uuid, &str));
1521f804c425STomohiro Kusumi 	printf("data_quota = 0x%jx\n", (uintmax_t)meta->data_quota);
1522f804c425STomohiro Kusumi 	printf("inode_quota = 0x%jx\n", (uintmax_t)meta->inode_quota);
1523f804c425STomohiro Kusumi 	printf("pfs_lsnap_tid = 0x%jx\n", (uintmax_t)meta->pfs_lsnap_tid);
1524f804c425STomohiro Kusumi 	printf("decrypt_check = 0x%jx\n", (uintmax_t)meta->decrypt_check);
1525f804c425STomohiro Kusumi 	printf("--------------------\n");
1526f804c425STomohiro Kusumi 
1527f804c425STomohiro Kusumi 	free(o);
1528f804c425STomohiro Kusumi 
1529f804c425STomohiro Kusumi 	return error;
1530f804c425STomohiro Kusumi }
1531f804c425STomohiro Kusumi 
1532f804c425STomohiro Kusumi static int
1533465e1141STomohiro Kusumi hammer2_inode_setcheck(struct m_vnode *dvp, const char *f)
1534465e1141STomohiro Kusumi {
1535465e1141STomohiro Kusumi 	hammer2_ioc_inode_t inode;
1536465e1141STomohiro Kusumi 	hammer2_inode_t *ip;
1537465e1141STomohiro Kusumi 	struct m_vnode *vp;
1538465e1141STomohiro Kusumi 	char *o, *p, *name, *check_algo_str;
1539465e1141STomohiro Kusumi 	const char *checks[] = { "none", "disabled", "crc32", "xxhash64",
1540465e1141STomohiro Kusumi 	    "sha192", };
1541465e1141STomohiro Kusumi 	int check_algo_idx, error;
1542465e1141STomohiro Kusumi 	uint8_t check_algo;
1543465e1141STomohiro Kusumi 
1544465e1141STomohiro Kusumi 	assert(strlen(f) > 0);
1545465e1141STomohiro Kusumi 	o = p = strdup(f);
1546465e1141STomohiro Kusumi 
1547465e1141STomohiro Kusumi 	p = strrchr(p, ':');
1548465e1141STomohiro Kusumi 	if (p == NULL)
1549465e1141STomohiro Kusumi 		return EINVAL;
1550465e1141STomohiro Kusumi 
1551465e1141STomohiro Kusumi 	*p++ = 0; /* NULL terminate path */
1552465e1141STomohiro Kusumi 	check_algo_str = p;
1553465e1141STomohiro Kusumi 	name = p = o;
1554465e1141STomohiro Kusumi 
15556857f034STomohiro Kusumi 	error = trim_slash(p);
15566857f034STomohiro Kusumi 	if (error)
15576857f034STomohiro Kusumi 		return error;
1558465e1141STomohiro Kusumi 	if (strlen(p) == 0 || strlen(check_algo_str) == 0)
1559465e1141STomohiro Kusumi 		return EINVAL;
1560465e1141STomohiro Kusumi 
1561465e1141STomohiro Kusumi 	/* convert check_algo_str to check_algo_idx */
15626857f034STomohiro Kusumi 	check_algo_idx = nitems(checks);
1563465e1141STomohiro Kusumi 	while (--check_algo_idx >= 0)
1564465e1141STomohiro Kusumi 		if (strcasecmp(check_algo_str, checks[check_algo_idx]) == 0)
1565465e1141STomohiro Kusumi 			break;
1566465e1141STomohiro Kusumi 	if (check_algo_idx < 0) {
1567465e1141STomohiro Kusumi 		if (strcasecmp(check_algo_str, "default") == 0) {
1568465e1141STomohiro Kusumi 			check_algo_str = "xxhash64";
1569465e1141STomohiro Kusumi 			check_algo_idx = HAMMER2_CHECK_XXHASH64;
1570465e1141STomohiro Kusumi 		} else if (strcasecmp(check_algo_str, "disabled") == 0) {
1571465e1141STomohiro Kusumi 			check_algo_str = "disabled";
1572465e1141STomohiro Kusumi 			check_algo_idx = HAMMER2_CHECK_DISABLED;
1573465e1141STomohiro Kusumi 		} else {
1574465e1141STomohiro Kusumi 			printf("invalid check_algo_str: %s\n", check_algo_str);
1575465e1141STomohiro Kusumi 			return EINVAL;
1576465e1141STomohiro Kusumi 		}
1577465e1141STomohiro Kusumi 	}
1578465e1141STomohiro Kusumi 	check_algo = HAMMER2_ENC_ALGO(check_algo_idx);
1579465e1141STomohiro Kusumi 	printf("change %s to algo %d (%s)\n", p, check_algo, check_algo_str);
1580465e1141STomohiro Kusumi 
1581465e1141STomohiro Kusumi 	while ((p = strchr(p, '/')) != NULL) {
1582465e1141STomohiro Kusumi 		*p++ = 0; /* NULL terminate name */
1583465e1141STomohiro Kusumi 		vp = NULL;
1584465e1141STomohiro Kusumi 		error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1585465e1141STomohiro Kusumi 		if (error)
1586465e1141STomohiro Kusumi 			return error;
1587465e1141STomohiro Kusumi 
1588465e1141STomohiro Kusumi 		ip = VTOI(vp);
1589465e1141STomohiro Kusumi 		assert(ip->meta.type == HAMMER2_OBJTYPE_DIRECTORY);
1590465e1141STomohiro Kusumi 
1591465e1141STomohiro Kusumi 		dvp = vp;
1592465e1141STomohiro Kusumi 		name = p;
1593465e1141STomohiro Kusumi 	}
1594465e1141STomohiro Kusumi 
1595465e1141STomohiro Kusumi 	error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1596465e1141STomohiro Kusumi 	if (error)
1597465e1141STomohiro Kusumi 		return error;
1598465e1141STomohiro Kusumi 	ip = VTOI(vp);
1599465e1141STomohiro Kusumi 
1600465e1141STomohiro Kusumi 	bzero(&inode, sizeof(inode));
1601465e1141STomohiro Kusumi 	error = hammer2_ioctl_inode_get(ip, &inode);
1602465e1141STomohiro Kusumi 	if (error)
1603465e1141STomohiro Kusumi 		return error;
1604465e1141STomohiro Kusumi 
1605465e1141STomohiro Kusumi 	inode.flags |= HAMMER2IOC_INODE_FLAG_CHECK;
1606465e1141STomohiro Kusumi 	inode.ip_data.meta.check_algo = check_algo;
1607465e1141STomohiro Kusumi 	error = hammer2_ioctl_inode_set(ip, &inode);
1608465e1141STomohiro Kusumi 	if (error)
1609465e1141STomohiro Kusumi 		return error;
1610465e1141STomohiro Kusumi 
1611465e1141STomohiro Kusumi 	free(o);
1612465e1141STomohiro Kusumi 
1613465e1141STomohiro Kusumi 	return error;
1614465e1141STomohiro Kusumi }
1615465e1141STomohiro Kusumi 
1616465e1141STomohiro Kusumi static int
1617465e1141STomohiro Kusumi hammer2_inode_setcomp(struct m_vnode *dvp, const char *f)
1618465e1141STomohiro Kusumi {
1619465e1141STomohiro Kusumi 	hammer2_ioc_inode_t inode;
1620465e1141STomohiro Kusumi 	hammer2_inode_t *ip;
1621465e1141STomohiro Kusumi 	struct m_vnode *vp;
1622465e1141STomohiro Kusumi 	char *o, *p, *name, *comp_algo_str, *comp_level_str;
1623465e1141STomohiro Kusumi 	const char *comps[] = { "none", "autozero", "lz4", "zlib", };
1624465e1141STomohiro Kusumi 	int comp_algo_idx, comp_level_idx, error;
1625465e1141STomohiro Kusumi 	uint8_t comp_algo, comp_level;
1626465e1141STomohiro Kusumi 
1627465e1141STomohiro Kusumi 	assert(strlen(f) > 0);
1628465e1141STomohiro Kusumi 	o = p = strdup(f);
1629465e1141STomohiro Kusumi 
1630465e1141STomohiro Kusumi 	p = strrchr(p, ':');
1631465e1141STomohiro Kusumi 	if (p == NULL)
1632465e1141STomohiro Kusumi 		return EINVAL;
1633465e1141STomohiro Kusumi 
1634465e1141STomohiro Kusumi 	*p++ = 0; /* NULL terminate comp_algo_str */
1635465e1141STomohiro Kusumi 	comp_level_str = p;
1636465e1141STomohiro Kusumi 	p = o;
1637465e1141STomohiro Kusumi 
1638465e1141STomohiro Kusumi 	p = strrchr(p, ':');
1639465e1141STomohiro Kusumi 	if (p == NULL) {
1640465e1141STomohiro Kusumi 		/* comp_level_str not specified */
1641465e1141STomohiro Kusumi 		comp_algo_str = comp_level_str;
1642465e1141STomohiro Kusumi 		comp_level_str = NULL;
1643465e1141STomohiro Kusumi 	} else {
1644465e1141STomohiro Kusumi 		*p++ = 0; /* NULL terminate path */
1645465e1141STomohiro Kusumi 		comp_algo_str = p;
1646465e1141STomohiro Kusumi 	}
1647465e1141STomohiro Kusumi 	name = p = o;
1648465e1141STomohiro Kusumi 
16496857f034STomohiro Kusumi 	error = trim_slash(p);
16506857f034STomohiro Kusumi 	if (error)
16516857f034STomohiro Kusumi 		return error;
1652465e1141STomohiro Kusumi 	if (strlen(p) == 0 || strlen(comp_algo_str) == 0)
1653465e1141STomohiro Kusumi 		return EINVAL;
1654465e1141STomohiro Kusumi 
1655465e1141STomohiro Kusumi 	/* convert comp_algo_str to comp_algo_idx */
16566857f034STomohiro Kusumi 	comp_algo_idx = nitems(comps);
1657465e1141STomohiro Kusumi 	while (--comp_algo_idx >= 0)
1658465e1141STomohiro Kusumi 		if (strcasecmp(comp_algo_str, comps[comp_algo_idx]) == 0)
1659465e1141STomohiro Kusumi 			break;
1660465e1141STomohiro Kusumi 	if (comp_algo_idx < 0) {
1661465e1141STomohiro Kusumi 		if (strcasecmp(comp_algo_str, "default") == 0) {
1662465e1141STomohiro Kusumi 			comp_algo_str = "lz4";
1663465e1141STomohiro Kusumi 			comp_algo_idx = HAMMER2_COMP_LZ4;
1664465e1141STomohiro Kusumi 		} else if (strcasecmp(comp_algo_str, "disabled") == 0) {
1665465e1141STomohiro Kusumi 			comp_algo_str = "autozero";
1666465e1141STomohiro Kusumi 			comp_algo_idx = HAMMER2_COMP_AUTOZERO;
1667465e1141STomohiro Kusumi 		} else {
1668465e1141STomohiro Kusumi 			printf("invalid comp_algo_str: %s\n", comp_algo_str);
1669465e1141STomohiro Kusumi 			return EINVAL;
1670465e1141STomohiro Kusumi 		}
1671465e1141STomohiro Kusumi 	}
1672465e1141STomohiro Kusumi 	comp_algo = HAMMER2_ENC_ALGO(comp_algo_idx);
1673465e1141STomohiro Kusumi 
1674465e1141STomohiro Kusumi 	/* convert comp_level_str to comp_level_idx */
1675465e1141STomohiro Kusumi 	if (comp_level_str == NULL) {
1676465e1141STomohiro Kusumi 		comp_level_idx = 0;
1677bca6a9a0STomohiro Kusumi 	} else if (isdigit((int)comp_level_str[0])) {
1678465e1141STomohiro Kusumi 		comp_level_idx = strtol(comp_level_str, NULL, 0);
1679465e1141STomohiro Kusumi 	} else if (strcasecmp(comp_level_str, "default") == 0) {
1680465e1141STomohiro Kusumi 		comp_level_idx = 0;
1681465e1141STomohiro Kusumi 	} else {
1682465e1141STomohiro Kusumi 		printf("invalid comp_level_str: %s\n", comp_level_str);
1683465e1141STomohiro Kusumi 		return EINVAL;
1684465e1141STomohiro Kusumi 	}
1685465e1141STomohiro Kusumi 	if (comp_level_idx) {
1686465e1141STomohiro Kusumi 		switch (comp_algo) {
1687465e1141STomohiro Kusumi 		case HAMMER2_COMP_ZLIB:
1688465e1141STomohiro Kusumi 			if (comp_level_idx < 6 || comp_level_idx > 9) {
1689465e1141STomohiro Kusumi 				printf("unsupported comp_level %d for %s\n",
1690465e1141STomohiro Kusumi 				    comp_level_idx, comp_algo_str);
1691465e1141STomohiro Kusumi 				return EINVAL;
1692465e1141STomohiro Kusumi 			}
1693465e1141STomohiro Kusumi 			break;
1694465e1141STomohiro Kusumi 		default:
1695465e1141STomohiro Kusumi 			printf("unsupported comp_level %d for %s\n",
1696465e1141STomohiro Kusumi 			    comp_level_idx, comp_algo_str);
1697465e1141STomohiro Kusumi 			return EINVAL;
1698465e1141STomohiro Kusumi 		}
1699465e1141STomohiro Kusumi 	}
1700465e1141STomohiro Kusumi 	comp_level = HAMMER2_ENC_LEVEL(comp_level_idx);
1701465e1141STomohiro Kusumi 	printf("change %s to algo %d (%s) level %d\n",
1702465e1141STomohiro Kusumi 	    p, comp_algo, comp_algo_str, comp_level_idx);
1703465e1141STomohiro Kusumi 
1704465e1141STomohiro Kusumi 	while ((p = strchr(p, '/')) != NULL) {
1705465e1141STomohiro Kusumi 		*p++ = 0; /* NULL terminate name */
1706465e1141STomohiro Kusumi 		vp = NULL;
1707465e1141STomohiro Kusumi 		error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1708465e1141STomohiro Kusumi 		if (error)
1709465e1141STomohiro Kusumi 			return error;
1710465e1141STomohiro Kusumi 
1711465e1141STomohiro Kusumi 		ip = VTOI(vp);
1712465e1141STomohiro Kusumi 		assert(ip->meta.type == HAMMER2_OBJTYPE_DIRECTORY);
1713465e1141STomohiro Kusumi 
1714465e1141STomohiro Kusumi 		dvp = vp;
1715465e1141STomohiro Kusumi 		name = p;
1716465e1141STomohiro Kusumi 	}
1717465e1141STomohiro Kusumi 
1718465e1141STomohiro Kusumi 	error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1719465e1141STomohiro Kusumi 	if (error)
1720465e1141STomohiro Kusumi 		return error;
1721465e1141STomohiro Kusumi 	ip = VTOI(vp);
1722465e1141STomohiro Kusumi 
1723465e1141STomohiro Kusumi 	bzero(&inode, sizeof(inode));
1724465e1141STomohiro Kusumi 	error = hammer2_ioctl_inode_get(ip, &inode);
1725465e1141STomohiro Kusumi 	if (error)
1726465e1141STomohiro Kusumi 		return error;
1727465e1141STomohiro Kusumi 
1728465e1141STomohiro Kusumi 	inode.flags |= HAMMER2IOC_INODE_FLAG_COMP;
1729465e1141STomohiro Kusumi 	inode.ip_data.meta.comp_algo = comp_algo | comp_level;
1730465e1141STomohiro Kusumi 	error = hammer2_ioctl_inode_set(ip, &inode);
1731465e1141STomohiro Kusumi 	if (error)
1732465e1141STomohiro Kusumi 		return error;
1733465e1141STomohiro Kusumi 
1734465e1141STomohiro Kusumi 	free(o);
1735465e1141STomohiro Kusumi 
1736465e1141STomohiro Kusumi 	return error;
1737465e1141STomohiro Kusumi }
1738465e1141STomohiro Kusumi 
1739465e1141STomohiro Kusumi static int
1740a63188c8STomohiro Kusumi hammer2_bulkfree(struct m_vnode *vp)
1741a63188c8STomohiro Kusumi {
1742a63188c8STomohiro Kusumi 	hammer2_ioc_bulkfree_t bfi;
1743a63188c8STomohiro Kusumi 	size_t usermem;
1744a63188c8STomohiro Kusumi 	size_t usermem_size = sizeof(usermem);
1745a63188c8STomohiro Kusumi 
1746a63188c8STomohiro Kusumi 	bzero(&bfi, sizeof(bfi));
1747a63188c8STomohiro Kusumi 	usermem = 0;
1748a63188c8STomohiro Kusumi 	if (sysctlbyname("hw.usermem", &usermem, &usermem_size, NULL, 0) == 0)
1749a63188c8STomohiro Kusumi 		bfi.size = usermem / 16;
1750a63188c8STomohiro Kusumi 	else
1751a63188c8STomohiro Kusumi 		bfi.size = 0;
1752a63188c8STomohiro Kusumi 	if (bfi.size < 8192 * 1024)
1753a63188c8STomohiro Kusumi 		bfi.size = 8192 * 1024;
1754a63188c8STomohiro Kusumi 
1755a63188c8STomohiro Kusumi 	return hammer2_ioctl_bulkfree_scan(VTOI(vp), &bfi);
1756a63188c8STomohiro Kusumi }
1757afa5234bSTomohiro Kusumi 
1758afa5234bSTomohiro Kusumi static int
1759ac97ce60STomohiro Kusumi hammer2_destroy_path(struct m_vnode *dvp, const char *f)
1760917508cdSTomohiro Kusumi {
1761917508cdSTomohiro Kusumi 	hammer2_ioc_destroy_t destroy;
1762ac97ce60STomohiro Kusumi 	hammer2_inode_t *ip;
1763ac97ce60STomohiro Kusumi 	struct m_vnode *vp;
1764ac97ce60STomohiro Kusumi 	char *o, *p, *name;
1765b4807901STomohiro Kusumi 	int error;
1766917508cdSTomohiro Kusumi 
176706d1ac82STomohiro Kusumi 	assert(strlen(f) > 0);
17686857f034STomohiro Kusumi 	o = p = name = strdup(f);
1769ac97ce60STomohiro Kusumi 
17706857f034STomohiro Kusumi 	error = trim_slash(p);
17716857f034STomohiro Kusumi 	if (error)
17726857f034STomohiro Kusumi 		return error;
1773ac97ce60STomohiro Kusumi 	if (strlen(p) == 0)
1774ac97ce60STomohiro Kusumi 		return EINVAL;
1775ac97ce60STomohiro Kusumi 
1776ac97ce60STomohiro Kusumi 	while ((p = strchr(p, '/')) != NULL) {
1777ac97ce60STomohiro Kusumi 		*p++ = 0; /* NULL terminate name */
1778ac97ce60STomohiro Kusumi 		vp = NULL;
1779ac97ce60STomohiro Kusumi 		error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1780ac97ce60STomohiro Kusumi 		if (error)
1781ac97ce60STomohiro Kusumi 			return error;
1782ac97ce60STomohiro Kusumi 
1783ac97ce60STomohiro Kusumi 		ip = VTOI(vp);
1784ac97ce60STomohiro Kusumi 		assert(ip->meta.type == HAMMER2_OBJTYPE_DIRECTORY);
1785ac97ce60STomohiro Kusumi 
1786ac97ce60STomohiro Kusumi 		dvp = vp;
1787ac97ce60STomohiro Kusumi 		name = p;
1788ac97ce60STomohiro Kusumi 	}
1789ac97ce60STomohiro Kusumi 
1790ba024da8STomohiro Kusumi 	/* XXX When does (or why does not) ioctl modify this inode ? */
1791ba024da8STomohiro Kusumi 	hammer2_inode_modify(VTOI(dvp));
1792ba024da8STomohiro Kusumi 
1793917508cdSTomohiro Kusumi 	bzero(&destroy, sizeof(destroy));
1794917508cdSTomohiro Kusumi 	destroy.cmd = HAMMER2_DELETE_FILE;
1795ac97ce60STomohiro Kusumi 	snprintf(destroy.path, sizeof(destroy.path), "%s", name);
1796917508cdSTomohiro Kusumi 
1797917508cdSTomohiro Kusumi 	printf("%s\t", f);
1798917508cdSTomohiro Kusumi 	fflush(stdout);
1799917508cdSTomohiro Kusumi 
1800ac97ce60STomohiro Kusumi 	error = hammer2_ioctl_destroy(VTOI(dvp), &destroy);
1801917508cdSTomohiro Kusumi 	if (error)
1802917508cdSTomohiro Kusumi 		printf("%s\n", strerror(error));
1803917508cdSTomohiro Kusumi 	else
1804917508cdSTomohiro Kusumi 		printf("ok\n");
1805ac97ce60STomohiro Kusumi 	free(o);
1806917508cdSTomohiro Kusumi 
1807917508cdSTomohiro Kusumi 	return error;
1808917508cdSTomohiro Kusumi }
1809917508cdSTomohiro Kusumi 
1810917508cdSTomohiro Kusumi static int
1811917508cdSTomohiro Kusumi hammer2_destroy_inum(struct m_vnode *vp, hammer2_tid_t inum)
1812917508cdSTomohiro Kusumi {
1813917508cdSTomohiro Kusumi 	hammer2_ioc_destroy_t destroy;
1814b4807901STomohiro Kusumi 	int error;
1815917508cdSTomohiro Kusumi 
1816917508cdSTomohiro Kusumi 	bzero(&destroy, sizeof(destroy));
1817917508cdSTomohiro Kusumi 	destroy.cmd = HAMMER2_DELETE_INUM;
1818917508cdSTomohiro Kusumi 	destroy.inum = inum;
1819917508cdSTomohiro Kusumi 
1820917508cdSTomohiro Kusumi 	printf("%jd\t", (intmax_t)destroy.inum);
1821917508cdSTomohiro Kusumi 	fflush(stdout);
1822917508cdSTomohiro Kusumi 
1823917508cdSTomohiro Kusumi 	error = hammer2_ioctl_destroy(VTOI(vp), &destroy);
1824917508cdSTomohiro Kusumi 	if (error)
1825917508cdSTomohiro Kusumi 		printf("%s\n", strerror(error));
1826917508cdSTomohiro Kusumi 	else
1827917508cdSTomohiro Kusumi 		printf("ok\n");
1828917508cdSTomohiro Kusumi 
1829917508cdSTomohiro Kusumi 	return error;
1830917508cdSTomohiro Kusumi }
1831917508cdSTomohiro Kusumi 
1832917508cdSTomohiro Kusumi static int
1833afa5234bSTomohiro Kusumi hammer2_growfs(struct m_vnode *vp, hammer2_off_t size)
1834afa5234bSTomohiro Kusumi {
1835afa5234bSTomohiro Kusumi 	hammer2_ioc_growfs_t growfs;
1836afa5234bSTomohiro Kusumi 	int error;
1837afa5234bSTomohiro Kusumi 
1838afa5234bSTomohiro Kusumi 	bzero(&growfs, sizeof(growfs));
1839afa5234bSTomohiro Kusumi 	growfs.size = size;
1840afa5234bSTomohiro Kusumi 
1841afa5234bSTomohiro Kusumi 	error = hammer2_ioctl_growfs(VTOI(vp), &growfs, NULL);
1842afa5234bSTomohiro Kusumi 	if (!error) {
1843afa5234bSTomohiro Kusumi 		if (growfs.modified)
1844afa5234bSTomohiro Kusumi 			printf("grown to %016jx\n", (intmax_t)growfs.size);
1845afa5234bSTomohiro Kusumi 		else
1846afa5234bSTomohiro Kusumi 			printf("no size change - %016jx\n",
1847afa5234bSTomohiro Kusumi 			    (intmax_t)growfs.size);
1848afa5234bSTomohiro Kusumi 	}
1849afa5234bSTomohiro Kusumi 
1850afa5234bSTomohiro Kusumi 	return error;
1851afa5234bSTomohiro Kusumi }
18526857f034STomohiro Kusumi 
18536857f034STomohiro Kusumi static void
18546857f034STomohiro Kusumi assert_trim_slash(const char *input, const char *expected)
18556857f034STomohiro Kusumi {
18566857f034STomohiro Kusumi 	char tmp[PATH_MAX];
18576857f034STomohiro Kusumi 	int error;
18586857f034STomohiro Kusumi 
18596857f034STomohiro Kusumi 	strlcpy(tmp, input, sizeof(tmp));
18606857f034STomohiro Kusumi 	error = trim_slash(tmp);
18616857f034STomohiro Kusumi 	if (error)
18626857f034STomohiro Kusumi 		errx(1, "input \"%s\" error %d", input, error);
18636857f034STomohiro Kusumi 
18646857f034STomohiro Kusumi 	if (strncmp(tmp, expected, sizeof(tmp)))
18656857f034STomohiro Kusumi 		errx(1, "input \"%s\" result \"%s\" vs expected \"%s\"",
18666857f034STomohiro Kusumi 		    input, tmp, expected);
18676857f034STomohiro Kusumi }
18686857f034STomohiro Kusumi 
18696857f034STomohiro Kusumi static void
18706857f034STomohiro Kusumi unittest_trim_slash(void)
18716857f034STomohiro Kusumi {
18726857f034STomohiro Kusumi 	assert_trim_slash("", "");
18736857f034STomohiro Kusumi 	assert_trim_slash("/", "");
18746857f034STomohiro Kusumi 	assert_trim_slash("//", "");
18756857f034STomohiro Kusumi 	assert_trim_slash("///", "");
18766857f034STomohiro Kusumi 
18776857f034STomohiro Kusumi 	assert_trim_slash("makefs", "makefs");
18786857f034STomohiro Kusumi 	assert_trim_slash("/makefs", "makefs");
18796857f034STomohiro Kusumi 	assert_trim_slash("//makefs", "makefs");
18806857f034STomohiro Kusumi 	assert_trim_slash("makefs/", "makefs");
18816857f034STomohiro Kusumi 	assert_trim_slash("makefs//", "makefs");
18826857f034STomohiro Kusumi 	assert_trim_slash("/makefs/", "makefs");
18836857f034STomohiro Kusumi 	assert_trim_slash("//makefs//", "makefs");
18846857f034STomohiro Kusumi 
18856857f034STomohiro Kusumi 	assert_trim_slash("sys/vfs/hammer2", "sys/vfs/hammer2");
18866857f034STomohiro Kusumi 	assert_trim_slash("/sys/vfs/hammer2", "sys/vfs/hammer2");
18876857f034STomohiro Kusumi 	assert_trim_slash("//sys/vfs/hammer2", "sys/vfs/hammer2");
18886857f034STomohiro Kusumi 	assert_trim_slash("///sys/vfs/hammer2", "sys/vfs/hammer2");
18896857f034STomohiro Kusumi 	assert_trim_slash("sys/vfs/hammer2/", "sys/vfs/hammer2");
18906857f034STomohiro Kusumi 	assert_trim_slash("sys/vfs/hammer2//", "sys/vfs/hammer2");
18916857f034STomohiro Kusumi 	assert_trim_slash("sys/vfs/hammer2///", "sys/vfs/hammer2");
18926857f034STomohiro Kusumi 	assert_trim_slash("/sys/vfs/hammer2/", "sys/vfs/hammer2");
18936857f034STomohiro Kusumi 	assert_trim_slash("//sys//vfs//hammer2//", "sys/vfs/hammer2");
18946857f034STomohiro Kusumi 	assert_trim_slash("///sys///vfs///hammer2///", "sys/vfs/hammer2");
18956857f034STomohiro Kusumi 
1896bca6a9a0STomohiro Kusumi 	APRINTF("success\n");
18976857f034STomohiro Kusumi }
1898