xref: /dragonfly/usr.sbin/makefs/hammer2.c (revision 764bf12e)
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>
436da97f51STomohiro Kusumi #include <sys/time.h>
445e8b0eb7STomohiro Kusumi #include <sys/dirent.h>
452d60b848STomohiro Kusumi 
462d60b848STomohiro Kusumi #include <stdio.h>
472d60b848STomohiro Kusumi #include <stdlib.h>
482d60b848STomohiro Kusumi #include <stdbool.h>
492d60b848STomohiro Kusumi #include <string.h>
50465e1141STomohiro Kusumi #include <ctype.h>
512d60b848STomohiro Kusumi #include <unistd.h>
522d60b848STomohiro Kusumi #include <fcntl.h>
532d60b848STomohiro Kusumi #include <time.h>
542d60b848STomohiro Kusumi #include <err.h>
552d60b848STomohiro Kusumi #include <assert.h>
562d60b848STomohiro Kusumi #include <util.h>
572d60b848STomohiro Kusumi 
582d60b848STomohiro Kusumi #include "makefs.h"
592d60b848STomohiro Kusumi #include "hammer2.h"
602d60b848STomohiro Kusumi 
612d60b848STomohiro Kusumi #define APRINTF(X, ...)	\
622d60b848STomohiro Kusumi     printf("%s: " X, __func__, ## __VA_ARGS__)
632d60b848STomohiro Kusumi 
643999233bSTomohiro Kusumi static void hammer2_parse_pfs_opts(const char *, fsinfo_t *);
65f804c425STomohiro Kusumi static void hammer2_parse_inode_opts(const char *, fsinfo_t *);
662d60b848STomohiro Kusumi static void hammer2_dump_fsinfo(fsinfo_t *);
672d60b848STomohiro Kusumi static int hammer2_create_image(const char *, fsinfo_t *);
686bcbb706STomohiro Kusumi static int hammer2_populate_dir(struct m_vnode *, const char *, fsnode *,
692d60b848STomohiro Kusumi     fsnode *, fsinfo_t *, int);
702d60b848STomohiro Kusumi static void hammer2_validate(const char *, fsnode *, fsinfo_t *);
712d60b848STomohiro Kusumi static void hammer2_size_dir(fsnode *, fsinfo_t *);
726bcbb706STomohiro Kusumi static int hammer2_write_file(struct m_vnode *, const char *, fsnode *);
73f72350acSTomohiro Kusumi static int hammer2_version_get(struct m_vnode *);
749a149651STomohiro Kusumi static int hammer2_pfs_get(struct m_vnode *);
759a149651STomohiro Kusumi static int hammer2_pfs_lookup(struct m_vnode *, const char *);
769a149651STomohiro Kusumi static int hammer2_pfs_create(struct m_vnode *, const char *);
779a149651STomohiro Kusumi static int hammer2_pfs_delete(struct m_vnode *, const char *);
789a149651STomohiro Kusumi static int hammer2_pfs_snapshot(struct m_vnode *, const char *, const char *);
79465e1141STomohiro Kusumi static int hammer2_inode_getx(struct m_vnode *, const char *);
80465e1141STomohiro Kusumi static int hammer2_inode_setcheck(struct m_vnode *, const char *);
81465e1141STomohiro Kusumi static int hammer2_inode_setcomp(struct m_vnode *, const char *);
823999233bSTomohiro Kusumi static int hammer2_bulkfree(struct m_vnode *);
833999233bSTomohiro Kusumi static int hammer2_destroy_path(struct m_vnode *, const char *);
843999233bSTomohiro Kusumi static int hammer2_destroy_inum(struct m_vnode *, hammer2_tid_t);
853999233bSTomohiro Kusumi static int hammer2_growfs(struct m_vnode *, hammer2_off_t);
862d0322dbSTomohiro Kusumi struct hammer2_linkq;
872d0322dbSTomohiro Kusumi static int hammer2_readx_handle(struct m_vnode *, const char *, const char *,
882d0322dbSTomohiro Kusumi     struct hammer2_linkq *);
89fc4148feSTomohiro Kusumi static int hammer2_readx(struct m_vnode *, const char *, const char *);
906857f034STomohiro Kusumi static void unittest_trim_slash(void);
912d60b848STomohiro Kusumi 
92ddd1d3d1STomohiro Kusumi fsnode *hammer2_curnode;
93ddd1d3d1STomohiro Kusumi 
942d60b848STomohiro Kusumi void
hammer2_prep_opts(fsinfo_t * fsopts)952d60b848STomohiro Kusumi hammer2_prep_opts(fsinfo_t *fsopts)
962d60b848STomohiro Kusumi {
972d60b848STomohiro Kusumi 	hammer2_makefs_options_t *h2_opt = ecalloc(1, sizeof(*h2_opt));
982d60b848STomohiro Kusumi 	hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
992d60b848STomohiro Kusumi 
1002d60b848STomohiro Kusumi 	const option_t hammer2_options[] = {
1012d60b848STomohiro Kusumi 		/* newfs_hammer2(8) compatible options */
1022d60b848STomohiro Kusumi 		{ 'b', "BootAreaSize", NULL, OPT_STRBUF, 0, 0, "boot area size" },
1032d60b848STomohiro Kusumi 		{ 'r', "AuxAreaSize", NULL, OPT_STRBUF, 0, 0, "aux area size" },
1042d60b848STomohiro Kusumi 		{ 'V', "Hammer2Version", NULL, OPT_STRBUF, 0, 0, "file system version" },
1052d60b848STomohiro Kusumi 		{ 'L', "Label", NULL, OPT_STRBUF, 0, 0, "PFS label" },
1062d60b848STomohiro Kusumi 		/* makefs(8) specific options */
1072d60b848STomohiro Kusumi 		{ 'm', "MountLabel", NULL, OPT_STRBUF, 0, 0, "destination PFS label" },
1082d60b848STomohiro Kusumi 		{ 'v', "NumVolhdr", &h2_opt->num_volhdr, OPT_INT32,
1092d60b848STomohiro Kusumi 		    1, HAMMER2_NUM_VOLHDRS, "number of volume headers" },
11060e242c5STomohiro Kusumi 		{ 'c', "CompressionType", NULL, OPT_STRBUF, 0, 0, "compression type" },
11160e242c5STomohiro Kusumi 		{ 'C', "CheckType", NULL, OPT_STRBUF, 0, 0, "check type" },
1122d60b848STomohiro Kusumi 		{ 'd', "Hammer2Debug", NULL, OPT_STRBUF, 0, 0, "debug tunable" },
11348ed4577STomohiro Kusumi 		{ 'E', "EmergencyMode", &h2_opt->emergency_mode, OPT_BOOL, 0, 0,
11448ed4577STomohiro Kusumi 		    "emergency mode" },
1153999233bSTomohiro Kusumi 		{ 'P', "PFS", NULL, OPT_STRBUF, 0, 0, "offline PFS" },
116f804c425STomohiro Kusumi 		{ 'I', "Inode", NULL, OPT_STRBUF, 0, 0, "offline inode" },
1179a149651STomohiro Kusumi 		{ 'B', "Bulkfree", NULL, OPT_STRBUF, 0, 0, "offline bulkfree" },
118917508cdSTomohiro Kusumi 		{ 'D', "Destroy", NULL, OPT_STRBUF, 0, 0, "offline destroy" },
1199a149651STomohiro Kusumi 		{ 'G', "Growfs", NULL, OPT_STRBUF, 0, 0, "offline growfs" },
120fc4148feSTomohiro Kusumi 		{ 'R', "Read", NULL, OPT_STRBUF, 0, 0, "offline read" },
1212d60b848STomohiro Kusumi 		{ .name = NULL },
1222d60b848STomohiro Kusumi 	};
1232d60b848STomohiro Kusumi 
1242d60b848STomohiro Kusumi 	hammer2_mkfs_init(opt);
1252d60b848STomohiro Kusumi 
126a071436bSTomohiro Kusumi 	assert(opt->CompType == HAMMER2_COMP_DEFAULT);
1279046282cSTomohiro Kusumi 	assert(opt->CheckType == HAMMER2_CHECK_DEFAULT);
1282d60b848STomohiro Kusumi 
1292d60b848STomohiro Kusumi 	/* force debug mode for mkfs */
1302d60b848STomohiro Kusumi 	opt->DebugOpt = 1;
1312d60b848STomohiro Kusumi 
1322d60b848STomohiro Kusumi 	fsopts->fs_specific = h2_opt;
1332d60b848STomohiro Kusumi 	fsopts->fs_options = copy_opts(hammer2_options);
1342d60b848STomohiro Kusumi 	fsopts->sectorsize = DEV_BSIZE;
1352d60b848STomohiro Kusumi }
1362d60b848STomohiro Kusumi 
1372d60b848STomohiro Kusumi void
hammer2_cleanup_opts(fsinfo_t * fsopts)1382d60b848STomohiro Kusumi hammer2_cleanup_opts(fsinfo_t *fsopts)
1392d60b848STomohiro Kusumi {
1402d60b848STomohiro Kusumi 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
1412d60b848STomohiro Kusumi 	hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
1422d60b848STomohiro Kusumi 
1432d60b848STomohiro Kusumi 	hammer2_mkfs_cleanup(opt);
1442d60b848STomohiro Kusumi 
1452d60b848STomohiro Kusumi 	free(h2_opt);
1462d60b848STomohiro Kusumi 	free(fsopts->fs_options);
1472d60b848STomohiro Kusumi }
1482d60b848STomohiro Kusumi 
1492d60b848STomohiro Kusumi int
hammer2_parse_opts(const char * option,fsinfo_t * fsopts)1502d60b848STomohiro Kusumi hammer2_parse_opts(const char *option, fsinfo_t *fsopts)
1512d60b848STomohiro Kusumi {
1522d60b848STomohiro Kusumi 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
1532d60b848STomohiro Kusumi 	hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
1542d60b848STomohiro Kusumi 
1552d60b848STomohiro Kusumi 	option_t *hammer2_options = fsopts->fs_options;
1562d60b848STomohiro Kusumi 	char buf[1024]; /* > HAMMER2_INODE_MAXNAME */
1572d60b848STomohiro Kusumi 	int i;
1582d60b848STomohiro Kusumi 
1592d60b848STomohiro Kusumi 	assert(option != NULL);
1602d60b848STomohiro Kusumi 	assert(fsopts != NULL);
1612d60b848STomohiro Kusumi 
1622d60b848STomohiro Kusumi 	if (debug & DEBUG_FS_PARSE_OPTS)
1632d60b848STomohiro Kusumi 		APRINTF("got `%s'\n", option);
1642d60b848STomohiro Kusumi 
1652d60b848STomohiro Kusumi 	i = set_option(hammer2_options, option, buf, sizeof(buf));
1662d60b848STomohiro Kusumi 	if (i == -1)
1672d60b848STomohiro Kusumi 		return 0;
1682d60b848STomohiro Kusumi 
1692d60b848STomohiro Kusumi 	if (hammer2_options[i].name == NULL)
1702d60b848STomohiro Kusumi 		abort();
1712d60b848STomohiro Kusumi 
1722d60b848STomohiro Kusumi 	switch (hammer2_options[i].letter) {
1732d60b848STomohiro Kusumi 	case 'b':
1742d60b848STomohiro Kusumi 		opt->BootAreaSize = getsize(buf, HAMMER2_NEWFS_ALIGN,
1752d60b848STomohiro Kusumi 		    HAMMER2_BOOT_MAX_BYTES, 2);
1762d60b848STomohiro Kusumi 		break;
1772d60b848STomohiro Kusumi 	case 'r':
1782d60b848STomohiro Kusumi 		opt->AuxAreaSize = getsize(buf, HAMMER2_NEWFS_ALIGN,
1792d60b848STomohiro Kusumi 		    HAMMER2_AUX_MAX_BYTES, 2);
1802d60b848STomohiro Kusumi 		break;
1812d60b848STomohiro Kusumi 	case 'V':
182f72350acSTomohiro Kusumi 		if (strlen(buf) == 0) {
183f72350acSTomohiro Kusumi 			h2_opt->ioctl_cmd = HAMMER2IOC_VERSION_GET;
184f72350acSTomohiro Kusumi 		} else {
1852d60b848STomohiro Kusumi 			opt->Hammer2Version = strtol(buf, NULL, 0);
1862d60b848STomohiro Kusumi 			if (opt->Hammer2Version < HAMMER2_VOL_VERSION_MIN ||
1872d60b848STomohiro Kusumi 			    opt->Hammer2Version >= HAMMER2_VOL_VERSION_WIP)
1882d60b848STomohiro Kusumi 				errx(1, "I don't understand how to format "
1892d60b848STomohiro Kusumi 				    "HAMMER2 version %d",
1902d60b848STomohiro Kusumi 				    opt->Hammer2Version);
191f72350acSTomohiro Kusumi 		}
1922d60b848STomohiro Kusumi 		break;
1932d60b848STomohiro Kusumi 	case 'L':
1942d60b848STomohiro Kusumi 		h2_opt->label_specified = 1;
1952d60b848STomohiro Kusumi 		if (strcasecmp(buf, "none") == 0)
1962d60b848STomohiro Kusumi 			break;
1972d60b848STomohiro Kusumi 		if (opt->NLabels >= MAXLABELS)
1982d60b848STomohiro Kusumi 			errx(1, "Limit of %d local labels", MAXLABELS - 1);
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 		opt->Label[opt->NLabels++] = strdup(buf);
2052d60b848STomohiro Kusumi 		break;
2062d60b848STomohiro Kusumi 	case 'm':
2072d60b848STomohiro Kusumi 		if (strlen(buf) == 0)
2082d60b848STomohiro Kusumi 			errx(1, "Volume label '%s' cannot be 0-length", buf);
2092d60b848STomohiro Kusumi 		if (strlen(buf) >= HAMMER2_INODE_MAXNAME)
2102d60b848STomohiro Kusumi 			errx(1, "Volume label '%s' is too long (%d chars max)",
2112d60b848STomohiro Kusumi 			    buf, HAMMER2_INODE_MAXNAME - 1);
2122d60b848STomohiro Kusumi 		strlcpy(h2_opt->mount_label, buf, sizeof(h2_opt->mount_label));
2132d60b848STomohiro Kusumi 		break;
21460e242c5STomohiro Kusumi 	case 'c':
21560e242c5STomohiro Kusumi 		if (strlen(buf) == 0)
21660e242c5STomohiro Kusumi 			errx(1, "Compression type '%s' cannot be 0-length", buf);
21760e242c5STomohiro Kusumi 		if (strcasecmp(buf, "none") == 0)
21860e242c5STomohiro Kusumi 			opt->CompType = HAMMER2_COMP_NONE;
21960e242c5STomohiro Kusumi 		else if (strcasecmp(buf, "autozero") == 0)
22060e242c5STomohiro Kusumi 			opt->CompType = HAMMER2_COMP_AUTOZERO;
22160e242c5STomohiro Kusumi 		else if (strcasecmp(buf, "lz4") == 0)
22260e242c5STomohiro Kusumi 			opt->CompType = HAMMER2_COMP_LZ4;
22360e242c5STomohiro Kusumi 		else if (strcasecmp(buf, "zlib") == 0)
22460e242c5STomohiro Kusumi 			opt->CompType = HAMMER2_COMP_ZLIB;
22560e242c5STomohiro Kusumi 		else
22660e242c5STomohiro Kusumi 			errx(1, "Invalid compression type '%s'", buf);
22760e242c5STomohiro Kusumi 		break;
22860e242c5STomohiro Kusumi 	case 'C':
22960e242c5STomohiro Kusumi 		if (strlen(buf) == 0)
23060e242c5STomohiro Kusumi 			errx(1, "Check type '%s' cannot be 0-length", buf);
23160e242c5STomohiro Kusumi 		if (strcasecmp(buf, "none") == 0)
23260e242c5STomohiro Kusumi 			opt->CheckType = HAMMER2_CHECK_NONE;
23360e242c5STomohiro Kusumi 		else if (strcasecmp(buf, "disabled") == 0)
23460e242c5STomohiro Kusumi 			opt->CheckType = HAMMER2_CHECK_DISABLED;
23560e242c5STomohiro Kusumi 		else if (strcasecmp(buf, "iscsi32") == 0)
23660e242c5STomohiro Kusumi 			opt->CheckType = HAMMER2_CHECK_ISCSI32;
23760e242c5STomohiro Kusumi 		else if (strcasecmp(buf, "xxhash64") == 0)
23860e242c5STomohiro Kusumi 			opt->CheckType = HAMMER2_CHECK_XXHASH64;
23960e242c5STomohiro Kusumi 		else if (strcasecmp(buf, "sha192") == 0)
24060e242c5STomohiro Kusumi 			opt->CheckType = HAMMER2_CHECK_SHA192;
24160e242c5STomohiro Kusumi 		else if (strcasecmp(buf, "freemap") == 0)
24260e242c5STomohiro Kusumi 			opt->CheckType = HAMMER2_CHECK_FREEMAP;
24360e242c5STomohiro Kusumi 		else
24460e242c5STomohiro Kusumi 			errx(1, "Invalid check type '%s'", buf);
24560e242c5STomohiro Kusumi 		break;
2462d60b848STomohiro Kusumi 	case 'd':
2472d60b848STomohiro Kusumi 		hammer2_debug = strtoll(buf, NULL, 0);
2482d60b848STomohiro Kusumi 		break;
2493999233bSTomohiro Kusumi 	case 'P':
2503999233bSTomohiro Kusumi 		if (strlen(buf) == 0)
2513999233bSTomohiro Kusumi 			errx(1, "PFS argument '%s' cannot be 0-length", buf);
2523999233bSTomohiro Kusumi 		hammer2_parse_pfs_opts(buf, fsopts);
2533999233bSTomohiro Kusumi 		break;
254f804c425STomohiro Kusumi 	case 'I':
255f804c425STomohiro Kusumi 		if (strlen(buf) == 0)
256f804c425STomohiro Kusumi 			errx(1, "Inode argument '%s' cannot be 0-length", buf);
257f804c425STomohiro Kusumi 		hammer2_parse_inode_opts(buf, fsopts);
258f804c425STomohiro Kusumi 		break;
2599a149651STomohiro Kusumi 	case 'B':
2609a149651STomohiro Kusumi 		h2_opt->ioctl_cmd = HAMMER2IOC_BULKFREE_SCAN;
2619a149651STomohiro Kusumi 		break;
262917508cdSTomohiro Kusumi 	case 'D':
2639a149651STomohiro Kusumi 		h2_opt->ioctl_cmd = HAMMER2IOC_DESTROY;
264b4807901STomohiro Kusumi 		if (strlen(buf) == 0)
265b4807901STomohiro Kusumi 			errx(1, "Destroy argument '%s' cannot be 0-length", buf);
266917508cdSTomohiro Kusumi 		if (buf[0] == '/') {
2676857f034STomohiro Kusumi 			strlcpy(h2_opt->destroy_path, buf,
268917508cdSTomohiro Kusumi 			    sizeof(h2_opt->destroy_path));
269b4807901STomohiro Kusumi 		} else if (strncmp(buf, "0x", 2) == 0 ||
270917508cdSTomohiro Kusumi 		    (buf[0] >= '0' && buf[0] <= '9')) {
271917508cdSTomohiro Kusumi 			h2_opt->destroy_inum = strtoull(buf, NULL, 0);
272917508cdSTomohiro Kusumi 			if (errno)
273917508cdSTomohiro Kusumi 				err(1, "strtoull");
274917508cdSTomohiro Kusumi 		} else {
275917508cdSTomohiro Kusumi 			errx(1, "Invalid destroy argument %s", buf);
276917508cdSTomohiro Kusumi 		}
277917508cdSTomohiro Kusumi 		break;
2789a149651STomohiro Kusumi 	case 'G':
2799a149651STomohiro Kusumi 		h2_opt->ioctl_cmd = HAMMER2IOC_GROWFS;
2809a149651STomohiro Kusumi 		break;
281fc4148feSTomohiro Kusumi 	case 'R':
282fc4148feSTomohiro Kusumi 		h2_opt->ioctl_cmd = HAMMER2IOC_READ;
283fc4148feSTomohiro Kusumi 		if (strlen(buf) == 0)
284fc4148feSTomohiro Kusumi 			errx(1, "Read argument '%s' cannot be 0-length", buf);
285fc4148feSTomohiro Kusumi 		strlcpy(h2_opt->read_path, buf, sizeof(h2_opt->read_path));
286fc4148feSTomohiro Kusumi 		break;
2872d60b848STomohiro Kusumi 	default:
2882d60b848STomohiro Kusumi 		break;
2892d60b848STomohiro Kusumi 	}
2902d60b848STomohiro Kusumi 
2916857f034STomohiro Kusumi 	if (hammer2_debug && h2_opt->ioctl_cmd)
2926857f034STomohiro Kusumi 		unittest_trim_slash();
2936857f034STomohiro Kusumi 
2942d60b848STomohiro Kusumi 	return 1;
2952d60b848STomohiro Kusumi }
2962d60b848STomohiro Kusumi 
2972d60b848STomohiro Kusumi void
hammer2_makefs(const char * image,const char * dir,fsnode * root,fsinfo_t * fsopts)2982d60b848STomohiro Kusumi hammer2_makefs(const char *image, const char *dir, fsnode *root,
2992d60b848STomohiro Kusumi     fsinfo_t *fsopts)
3002d60b848STomohiro Kusumi {
3012d60b848STomohiro Kusumi 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
3022d60b848STomohiro Kusumi 	struct mount mp;
3032d60b848STomohiro Kusumi 	struct hammer2_mount_info info;
3046bcbb706STomohiro Kusumi 	struct m_vnode devvp, *vroot;
3052d60b848STomohiro Kusumi 	hammer2_inode_t *iroot;
3062d60b848STomohiro Kusumi 	struct timeval start;
3072d60b848STomohiro Kusumi 	int error;
3082d60b848STomohiro Kusumi 
30970e962f7STomohiro Kusumi 	/* ioctl commands could have NULL dir / root */
3102d60b848STomohiro Kusumi 	assert(image != NULL);
3112d60b848STomohiro Kusumi 	assert(fsopts != NULL);
3122d60b848STomohiro Kusumi 
3132d60b848STomohiro Kusumi 	if (debug & DEBUG_FS_MAKEFS)
3142d60b848STomohiro Kusumi 		APRINTF("image \"%s\" directory \"%s\" root %p\n",
3152d60b848STomohiro Kusumi 		    image, dir, root);
3162d60b848STomohiro Kusumi 
3172d60b848STomohiro Kusumi 	/* validate tree and options */
3182d60b848STomohiro Kusumi 	TIMER_START(start);
3192d60b848STomohiro Kusumi 	hammer2_validate(dir, root, fsopts);
3202d60b848STomohiro Kusumi 	TIMER_RESULTS(start, "hammer2_validate");
3212d60b848STomohiro Kusumi 
3229a149651STomohiro Kusumi 	if (h2_opt->ioctl_cmd) {
323b4807901STomohiro Kusumi 		/* open existing image */
324b4807901STomohiro Kusumi 		fsopts->fd = open(image, O_RDWR);
325b4807901STomohiro Kusumi 		if (fsopts->fd < 0)
326b4807901STomohiro Kusumi 			err(1, "failed to open `%s'", image);
327b4807901STomohiro Kusumi 	} else {
3282d60b848STomohiro Kusumi 		/* create image */
3292d60b848STomohiro Kusumi 		TIMER_START(start);
3302d60b848STomohiro Kusumi 		if (hammer2_create_image(image, fsopts) == -1)
3312d60b848STomohiro Kusumi 			errx(1, "image file `%s' not created", image);
3322d60b848STomohiro Kusumi 		TIMER_RESULTS(start, "hammer2_create_image");
333a63188c8STomohiro Kusumi 	}
334a63188c8STomohiro Kusumi 	assert(fsopts->fd > 0);
3352d60b848STomohiro Kusumi 
3362d60b848STomohiro Kusumi 	if (debug & DEBUG_FS_MAKEFS)
3372d60b848STomohiro Kusumi 		putchar('\n');
3382d60b848STomohiro Kusumi 
3392d60b848STomohiro Kusumi 	/* vfs init */
3402d60b848STomohiro Kusumi 	error = hammer2_vfs_init();
3412d60b848STomohiro Kusumi 	if (error)
3422d60b848STomohiro Kusumi 		errx(1, "failed to vfs init, error %d", error);
3432d60b848STomohiro Kusumi 
3442d60b848STomohiro Kusumi 	/* mount image */
3452d60b848STomohiro Kusumi 	memset(&devvp, 0, sizeof(devvp));
3462d60b848STomohiro Kusumi 	devvp.fs = fsopts;
3472d60b848STomohiro Kusumi 	memset(&mp, 0, sizeof(mp));
3482d60b848STomohiro Kusumi 	memset(&info, 0, sizeof(info));
3492d60b848STomohiro Kusumi 	error = hammer2_vfs_mount(&devvp, &mp, h2_opt->mount_label, &info);
3502d60b848STomohiro Kusumi 	if (error)
3512d60b848STomohiro Kusumi 		errx(1, "failed to mount, error %d", error);
3522d60b848STomohiro Kusumi 	assert(mp.mnt_data);
3532d60b848STomohiro Kusumi 
3542d60b848STomohiro Kusumi 	/* get root vnode */
3552d60b848STomohiro Kusumi 	vroot = NULL;
3562d60b848STomohiro Kusumi 	error = hammer2_vfs_root(&mp, &vroot);
3572d60b848STomohiro Kusumi 	if (error)
3582d60b848STomohiro Kusumi 		errx(1, "failed to get root vnode, error %d", error);
3592d60b848STomohiro Kusumi 	assert(vroot);
3602d60b848STomohiro Kusumi 
3612d60b848STomohiro Kusumi 	iroot = VTOI(vroot);
3622d60b848STomohiro Kusumi 	assert(iroot);
363f8a1147cSTomohiro Kusumi 	printf("root inode inum %lld, mode 0%o, refs %d\n",
364f8a1147cSTomohiro Kusumi 	    (long long)iroot->meta.inum, iroot->meta.mode, iroot->refs);
3652d60b848STomohiro Kusumi 
36648ed4577STomohiro Kusumi 	if (h2_opt->emergency_mode)
36748ed4577STomohiro Kusumi 		hammer2_ioctl_emerg_mode(iroot, 1);
36848ed4577STomohiro Kusumi 
3699a149651STomohiro Kusumi 	switch (h2_opt->ioctl_cmd) {
370f72350acSTomohiro Kusumi 	case HAMMER2IOC_VERSION_GET:
371f72350acSTomohiro Kusumi 		printf("version get `%s'\n", image);
372f72350acSTomohiro Kusumi 		TIMER_START(start);
373f72350acSTomohiro Kusumi 		error = hammer2_version_get(vroot);
374f72350acSTomohiro Kusumi 		if (error)
375f72350acSTomohiro Kusumi 			errx(1, "version get `%s' failed '%s'", image,
376f72350acSTomohiro Kusumi 			    strerror(error));
377f72350acSTomohiro Kusumi 		TIMER_RESULTS(start, "hammer2_version_get");
378f72350acSTomohiro Kusumi 		break;
3799a149651STomohiro Kusumi 	case HAMMER2IOC_PFS_GET:
3803999233bSTomohiro Kusumi 		printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image);
3813999233bSTomohiro Kusumi 		TIMER_START(start);
3829a149651STomohiro Kusumi 		error = hammer2_pfs_get(vroot);
3839a149651STomohiro Kusumi 		if (error)
3849a149651STomohiro Kusumi 			errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name,
3859a149651STomohiro Kusumi 			    image, strerror(error));
3869a149651STomohiro Kusumi 		TIMER_RESULTS(start, "hammer2_pfs_get");
3879a149651STomohiro Kusumi 		break;
3889a149651STomohiro Kusumi 	case HAMMER2IOC_PFS_LOOKUP:
3899a149651STomohiro Kusumi 		printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image);
3909a149651STomohiro Kusumi 		TIMER_START(start);
3919a149651STomohiro Kusumi 		error = hammer2_pfs_lookup(vroot, h2_opt->pfs_name);
3929a149651STomohiro Kusumi 		if (error)
3939a149651STomohiro Kusumi 			errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name,
3949a149651STomohiro Kusumi 			    image, strerror(error));
3959a149651STomohiro Kusumi 		TIMER_RESULTS(start, "hammer2_pfs_lookup");
3969a149651STomohiro Kusumi 		break;
3979a149651STomohiro Kusumi 	case HAMMER2IOC_PFS_CREATE:
3989a149651STomohiro Kusumi 		printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image);
3999a149651STomohiro Kusumi 		TIMER_START(start);
4009a149651STomohiro Kusumi 		error = hammer2_pfs_create(vroot, h2_opt->pfs_name);
4019a149651STomohiro Kusumi 		if (error)
4029a149651STomohiro Kusumi 			errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name,
4039a149651STomohiro Kusumi 			    image, strerror(error));
4049a149651STomohiro Kusumi 		TIMER_RESULTS(start, "hammer2_pfs_create");
4059a149651STomohiro Kusumi 		break;
4069a149651STomohiro Kusumi 	case HAMMER2IOC_PFS_DELETE:
4079a149651STomohiro Kusumi 		printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image);
4089a149651STomohiro Kusumi 		TIMER_START(start);
4099a149651STomohiro Kusumi 		error = hammer2_pfs_delete(vroot, h2_opt->pfs_name);
4109a149651STomohiro Kusumi 		if (error)
4119a149651STomohiro Kusumi 			errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name,
4129a149651STomohiro Kusumi 			    image, strerror(error));
4139a149651STomohiro Kusumi 		TIMER_RESULTS(start, "hammer2_pfs_delete");
4149a149651STomohiro Kusumi 		break;
4159a149651STomohiro Kusumi 	case HAMMER2IOC_PFS_SNAPSHOT:
4169a149651STomohiro Kusumi 		printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image);
4179a149651STomohiro Kusumi 		TIMER_START(start);
4189a149651STomohiro Kusumi 		error = hammer2_pfs_snapshot(vroot, h2_opt->pfs_name,
41906d1ac82STomohiro Kusumi 		    h2_opt->mount_label);
42006d1ac82STomohiro Kusumi 		if (error)
42106d1ac82STomohiro Kusumi 			errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name,
42206d1ac82STomohiro Kusumi 			    image, strerror(error));
4239a149651STomohiro Kusumi 		TIMER_RESULTS(start, "hammer2_pfs_snapshot");
4249a149651STomohiro Kusumi 		break;
425f804c425STomohiro Kusumi 	case HAMMER2IOC_INODE_GET:
426465e1141STomohiro Kusumi 		printf("inode %s `%s'\n", h2_opt->inode_cmd_name, image);
427f804c425STomohiro Kusumi 		TIMER_START(start);
428f804c425STomohiro Kusumi 		error = hammer2_inode_getx(vroot, h2_opt->inode_path);
429f804c425STomohiro Kusumi 		if (error)
430465e1141STomohiro Kusumi 			errx(1, "inode %s `%s' failed '%s'",
431465e1141STomohiro Kusumi 			    h2_opt->inode_cmd_name, image, strerror(error));
432f804c425STomohiro Kusumi 		TIMER_RESULTS(start, "hammer2_inode_getx");
433f804c425STomohiro Kusumi 		break;
434465e1141STomohiro Kusumi 	case HAMMER2IOC_INODE_SET:
435465e1141STomohiro Kusumi 		printf("inode %s `%s'\n", h2_opt->inode_cmd_name, image);
436465e1141STomohiro Kusumi 		TIMER_START(start);
437465e1141STomohiro Kusumi 		if (!strcmp(h2_opt->inode_cmd_name, "setcheck")) {
438465e1141STomohiro Kusumi 			error = hammer2_inode_setcheck(vroot,
439465e1141STomohiro Kusumi 			    h2_opt->inode_path);
440465e1141STomohiro Kusumi 			if (error)
441465e1141STomohiro Kusumi 				errx(1, "inode %s `%s' failed '%s'",
442465e1141STomohiro Kusumi 				    h2_opt->inode_cmd_name, image,
443465e1141STomohiro Kusumi 				    strerror(error));
444465e1141STomohiro Kusumi 		} else if (!strcmp(h2_opt->inode_cmd_name, "setcomp")) {
445465e1141STomohiro Kusumi 			error = hammer2_inode_setcomp(vroot,
446465e1141STomohiro Kusumi 			    h2_opt->inode_path);
447465e1141STomohiro Kusumi 			if (error)
448465e1141STomohiro Kusumi 				errx(1, "inode %s `%s' failed '%s'",
449465e1141STomohiro Kusumi 				    h2_opt->inode_cmd_name, image,
450465e1141STomohiro Kusumi 				    strerror(error));
451465e1141STomohiro Kusumi 		} else {
452465e1141STomohiro Kusumi 			assert(0);
453465e1141STomohiro Kusumi 		}
454465e1141STomohiro Kusumi 		TIMER_RESULTS(start, "hammer2_inode_setx");
455465e1141STomohiro Kusumi 		break;
4569a149651STomohiro Kusumi 	case HAMMER2IOC_BULKFREE_SCAN:
457a63188c8STomohiro Kusumi 		printf("bulkfree `%s'\n", image);
458a63188c8STomohiro Kusumi 		TIMER_START(start);
45906d1ac82STomohiro Kusumi 		error = hammer2_bulkfree(vroot);
46006d1ac82STomohiro Kusumi 		if (error)
46106d1ac82STomohiro Kusumi 			errx(1, "bulkfree `%s' failed '%s'", image,
46206d1ac82STomohiro Kusumi 			    strerror(error));
463a63188c8STomohiro Kusumi 		TIMER_RESULTS(start, "hammer2_bulkfree");
4649a149651STomohiro Kusumi 		break;
4659a149651STomohiro Kusumi 	case HAMMER2IOC_DESTROY:
466917508cdSTomohiro Kusumi 		TIMER_START(start);
467917508cdSTomohiro Kusumi 		if (strlen(h2_opt->destroy_path)) {
468917508cdSTomohiro Kusumi 			printf("destroy `%s' in `%s'\n",
469917508cdSTomohiro Kusumi 			    h2_opt->destroy_path, image);
47006d1ac82STomohiro Kusumi 			error = hammer2_destroy_path(vroot,
47106d1ac82STomohiro Kusumi 			    h2_opt->destroy_path);
47206d1ac82STomohiro Kusumi 			if (error)
47306d1ac82STomohiro Kusumi 				errx(1, "destroy `%s' in `%s' failed '%s'",
47406d1ac82STomohiro Kusumi 				    h2_opt->destroy_path, image,
47506d1ac82STomohiro Kusumi 				    strerror(error));
476917508cdSTomohiro Kusumi 		} else {
477917508cdSTomohiro Kusumi 			printf("destroy %lld in `%s'\n",
478917508cdSTomohiro Kusumi 			    (long long)h2_opt->destroy_inum, image);
47906d1ac82STomohiro Kusumi 			error = hammer2_destroy_inum(vroot,
48006d1ac82STomohiro Kusumi 			    h2_opt->destroy_inum);
48106d1ac82STomohiro Kusumi 			if (error)
48206d1ac82STomohiro Kusumi 				errx(1, "destroy %lld in `%s' failed '%s'",
48306d1ac82STomohiro Kusumi 				    (long long)h2_opt->destroy_inum, image,
48406d1ac82STomohiro Kusumi 				    strerror(error));
485917508cdSTomohiro Kusumi 		}
486917508cdSTomohiro Kusumi 		TIMER_RESULTS(start, "hammer2_destroy");
4879a149651STomohiro Kusumi 		break;
4889a149651STomohiro Kusumi 	case HAMMER2IOC_GROWFS:
489afa5234bSTomohiro Kusumi 		printf("growfs `%s'\n", image);
490afa5234bSTomohiro Kusumi 		TIMER_START(start);
49106d1ac82STomohiro Kusumi 		error = hammer2_growfs(vroot, h2_opt->image_size);
49206d1ac82STomohiro Kusumi 		if (error)
49306d1ac82STomohiro Kusumi 			errx(1, "growfs `%s' failed '%s'", image,
49406d1ac82STomohiro Kusumi 			    strerror(error));
495afa5234bSTomohiro Kusumi 		TIMER_RESULTS(start, "hammer2_growfs");
4969a149651STomohiro Kusumi 		break;
497fc4148feSTomohiro Kusumi 	case HAMMER2IOC_READ:
498fc4148feSTomohiro Kusumi 		printf("read `%s'\n", image);
499fc4148feSTomohiro Kusumi 		TIMER_START(start);
500fc4148feSTomohiro Kusumi 		error = hammer2_readx(vroot, dir, h2_opt->read_path);
501fc4148feSTomohiro Kusumi 		if (error)
502fc4148feSTomohiro Kusumi 			errx(1, "read `%s' failed '%s'", image,
503fc4148feSTomohiro Kusumi 			    strerror(error));
504fc4148feSTomohiro Kusumi 		TIMER_RESULTS(start, "hammer2_readx");
505fc4148feSTomohiro Kusumi 		break;
5069a149651STomohiro Kusumi 	default:
507b4807901STomohiro Kusumi 		printf("populating `%s'\n", image);
508b4807901STomohiro Kusumi 		TIMER_START(start);
509b4807901STomohiro Kusumi 		if (hammer2_populate_dir(vroot, dir, root, root, fsopts, 0))
510b4807901STomohiro Kusumi 			errx(1, "image file `%s' not populated", image);
511b4807901STomohiro Kusumi 		TIMER_RESULTS(start, "hammer2_populate_dir");
5129a149651STomohiro Kusumi 		break;
513a63188c8STomohiro Kusumi 	}
5142d60b848STomohiro Kusumi 
5152d60b848STomohiro Kusumi 	/* unmount image */
5162d60b848STomohiro Kusumi 	error = hammer2_vfs_unmount(&mp, 0);
5172d60b848STomohiro Kusumi 	if (error)
5182d60b848STomohiro Kusumi 		errx(1, "failed to unmount, error %d", error);
5192d60b848STomohiro Kusumi 
5202d60b848STomohiro Kusumi 	/* check leaked resource */
5212d60b848STomohiro Kusumi 	if (vnode_count)
522f8a1147cSTomohiro Kusumi 		printf("XXX %lld vnode left\n", (long long)vnode_count);
5232d60b848STomohiro Kusumi 	if (hammer2_chain_allocs)
5242d60b848STomohiro Kusumi 		printf("XXX %ld chain left\n", hammer2_chain_allocs);
5252d60b848STomohiro Kusumi 	bcleanup();
5262d60b848STomohiro Kusumi 
5272d60b848STomohiro Kusumi 	/* vfs uninit */
5282d60b848STomohiro Kusumi 	error = hammer2_vfs_uninit();
5292d60b848STomohiro Kusumi 	if (error)
5302d60b848STomohiro Kusumi 		errx(1, "failed to vfs uninit, error %d", error);
5312d60b848STomohiro Kusumi 
5322d60b848STomohiro Kusumi 	if (close(fsopts->fd) == -1)
5332d60b848STomohiro Kusumi 		err(1, "closing `%s'", image);
5342d60b848STomohiro Kusumi 	fsopts->fd = -1;
5352d60b848STomohiro Kusumi 
5362d60b848STomohiro Kusumi 	printf("image `%s' complete\n", image);
5372d60b848STomohiro Kusumi }
5382d60b848STomohiro Kusumi 
5392d60b848STomohiro Kusumi /* end of public functions */
5402d60b848STomohiro Kusumi 
5413999233bSTomohiro Kusumi static void
hammer2_parse_pfs_opts(const char * buf,fsinfo_t * fsopts)5423999233bSTomohiro Kusumi hammer2_parse_pfs_opts(const char *buf, fsinfo_t *fsopts)
5433999233bSTomohiro Kusumi {
5443999233bSTomohiro Kusumi 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
5453999233bSTomohiro Kusumi 	char *o, *p;
5463999233bSTomohiro Kusumi 	size_t n;
5473999233bSTomohiro Kusumi 
5483999233bSTomohiro Kusumi 	o = p = strdup(buf);
5493999233bSTomohiro Kusumi 	p = strchr(p, ':');
5503999233bSTomohiro Kusumi 	if (p != NULL) {
5513999233bSTomohiro Kusumi 		*p++ = 0;
5523999233bSTomohiro Kusumi 		n = strlen(p);
5533999233bSTomohiro Kusumi 	} else {
5543999233bSTomohiro Kusumi 		n = 0;
5553999233bSTomohiro Kusumi 	}
5563999233bSTomohiro Kusumi 
5573999233bSTomohiro Kusumi 	if (!strcmp(o, "get") || !strcmp(o, "list")) {
5589a149651STomohiro Kusumi 		h2_opt->ioctl_cmd = HAMMER2IOC_PFS_GET;
5593999233bSTomohiro Kusumi 	} else if (!strcmp(o, "lookup")) {
5603999233bSTomohiro Kusumi 		if (n == 0 || n > NAME_MAX)
5613999233bSTomohiro Kusumi 			errx(1, "invalid PFS name \"%s\"", p);
5629a149651STomohiro Kusumi 		h2_opt->ioctl_cmd = HAMMER2IOC_PFS_LOOKUP;
5633999233bSTomohiro Kusumi 	} else if (!strcmp(o, "create")) {
5643999233bSTomohiro Kusumi 		if (n == 0 || n > NAME_MAX)
5653999233bSTomohiro Kusumi 			errx(1, "invalid PFS name \"%s\"", p);
5669a149651STomohiro Kusumi 		h2_opt->ioctl_cmd = HAMMER2IOC_PFS_CREATE;
5673999233bSTomohiro Kusumi 	} else if (!strcmp(o, "delete")) {
5683999233bSTomohiro Kusumi 		if (n == 0 || n > NAME_MAX)
5693999233bSTomohiro Kusumi 			errx(1, "invalid PFS name \"%s\"", p);
5709a149651STomohiro Kusumi 		h2_opt->ioctl_cmd = HAMMER2IOC_PFS_DELETE;
5713999233bSTomohiro Kusumi 	} else if (!strcmp(o, "snapshot")) {
5723999233bSTomohiro Kusumi 		if (n > NAME_MAX)
5733999233bSTomohiro Kusumi 			errx(1, "invalid PFS name \"%s\"", p);
5749a149651STomohiro Kusumi 		h2_opt->ioctl_cmd = HAMMER2IOC_PFS_SNAPSHOT;
5753999233bSTomohiro Kusumi 	} else {
5763999233bSTomohiro Kusumi 		errx(1, "invalid PFS command \"%s\"", o);
5773999233bSTomohiro Kusumi 	}
5783999233bSTomohiro Kusumi 
5793999233bSTomohiro Kusumi 	strlcpy(h2_opt->pfs_cmd_name, o, sizeof(h2_opt->pfs_cmd_name));
5803999233bSTomohiro Kusumi 	if (n > 0)
5813999233bSTomohiro Kusumi 		strlcpy(h2_opt->pfs_name, p, sizeof(h2_opt->pfs_name));
5823999233bSTomohiro Kusumi 
5833999233bSTomohiro Kusumi 	free(o);
5843999233bSTomohiro Kusumi }
5853999233bSTomohiro Kusumi 
586f804c425STomohiro Kusumi static void
hammer2_parse_inode_opts(const char * buf,fsinfo_t * fsopts)587f804c425STomohiro Kusumi hammer2_parse_inode_opts(const char *buf, fsinfo_t *fsopts)
588f804c425STomohiro Kusumi {
589f804c425STomohiro Kusumi 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
590f804c425STomohiro Kusumi 	char *o, *p;
591f804c425STomohiro Kusumi 	size_t n;
592f804c425STomohiro Kusumi 
593f804c425STomohiro Kusumi 	o = p = strdup(buf);
594f804c425STomohiro Kusumi 	p = strchr(p, ':');
595f804c425STomohiro Kusumi 	if (p != NULL) {
596f804c425STomohiro Kusumi 		*p++ = 0;
597f804c425STomohiro Kusumi 		n = strlen(p);
598f804c425STomohiro Kusumi 	} else {
599f804c425STomohiro Kusumi 		n = 0;
600f804c425STomohiro Kusumi 	}
601f804c425STomohiro Kusumi 
602f804c425STomohiro Kusumi 	if (!strcmp(o, "get")) {
603f804c425STomohiro Kusumi 		if (n == 0 || n > PATH_MAX)
604f804c425STomohiro Kusumi 			errx(1, "invalid file path \"%s\"", p);
605f804c425STomohiro Kusumi 		h2_opt->ioctl_cmd = HAMMER2IOC_INODE_GET;
606465e1141STomohiro Kusumi 	} else if (!strcmp(o, "setcheck")) {
607465e1141STomohiro Kusumi 		if (n == 0 || n > PATH_MAX - 10)
608465e1141STomohiro Kusumi 			errx(1, "invalid argument \"%s\"", p);
609465e1141STomohiro Kusumi 		h2_opt->ioctl_cmd = HAMMER2IOC_INODE_SET;
610465e1141STomohiro Kusumi 	} else if (!strcmp(o, "setcomp")) {
611465e1141STomohiro Kusumi 		if (n == 0 || n > PATH_MAX - 10)
612465e1141STomohiro Kusumi 			errx(1, "invalid argument \"%s\"", p);
613465e1141STomohiro Kusumi 		h2_opt->ioctl_cmd = HAMMER2IOC_INODE_SET;
614f804c425STomohiro Kusumi 	} else {
615f804c425STomohiro Kusumi 		errx(1, "invalid inode command \"%s\"", o);
616f804c425STomohiro Kusumi 	}
617f804c425STomohiro Kusumi 
618465e1141STomohiro Kusumi 	strlcpy(h2_opt->inode_cmd_name, o, sizeof(h2_opt->inode_cmd_name));
6196857f034STomohiro Kusumi 	if (n > 0)
620f804c425STomohiro Kusumi 		strlcpy(h2_opt->inode_path, p, sizeof(h2_opt->inode_path));
621f804c425STomohiro Kusumi 
622f804c425STomohiro Kusumi 	free(o);
623f804c425STomohiro Kusumi }
624f804c425STomohiro Kusumi 
6252d60b848STomohiro Kusumi static hammer2_off_t
hammer2_image_size(fsinfo_t * fsopts)6262d60b848STomohiro Kusumi hammer2_image_size(fsinfo_t *fsopts)
6272d60b848STomohiro Kusumi {
6282d60b848STomohiro Kusumi 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
6292d60b848STomohiro Kusumi 	hammer2_off_t image_size, used_size = 0;
6302d60b848STomohiro Kusumi 	int num_level1, delta_num_level1;
6312d60b848STomohiro Kusumi 
6322d60b848STomohiro Kusumi 	/* use 4 volume headers by default */
6332d60b848STomohiro Kusumi 	num_level1 = h2_opt->num_volhdr * 2; /* default 4 x 2 */
6342d60b848STomohiro Kusumi 	assert(num_level1 != 0);
6352d60b848STomohiro Kusumi 	assert(num_level1 <= 8);
6362d60b848STomohiro Kusumi 
6372d60b848STomohiro Kusumi 	/* add 4MiB segment for each level1 */
6382d60b848STomohiro Kusumi 	used_size += HAMMER2_ZONE_SEG64 * num_level1;
6392d60b848STomohiro Kusumi 
6402d60b848STomohiro Kusumi 	/* add boot/aux area, but exact size unknown at this point */
6412d60b848STomohiro Kusumi 	used_size += HAMMER2_BOOT_NOM_BYTES + HAMMER2_AUX_NOM_BYTES;
6422d60b848STomohiro Kusumi 
6432d60b848STomohiro Kusumi 	/* add data size */
6442d60b848STomohiro Kusumi 	used_size += fsopts->size;
6452d60b848STomohiro Kusumi 
6462d60b848STomohiro Kusumi 	/* XXX add extra level1 for meta data and indirect blocks */
6472d60b848STomohiro Kusumi 	used_size += HAMMER2_FREEMAP_LEVEL1_SIZE;
6482d60b848STomohiro Kusumi 
6492d60b848STomohiro Kusumi 	/* XXX add extra level1 for safety */
6502d60b848STomohiro Kusumi 	if (used_size > HAMMER2_FREEMAP_LEVEL1_SIZE * 10)
6512d60b848STomohiro Kusumi 		used_size += HAMMER2_FREEMAP_LEVEL1_SIZE;
6522d60b848STomohiro Kusumi 
6532d60b848STomohiro Kusumi 	/* use 8GiB image size by default */
6542d60b848STomohiro Kusumi 	image_size = HAMMER2_FREEMAP_LEVEL1_SIZE * num_level1;
6552d60b848STomohiro Kusumi 	printf("trying default image size %s\n", sizetostr(image_size));
6562d60b848STomohiro Kusumi 
6572d60b848STomohiro Kusumi 	/* adjust if image size isn't large enough */
6582d60b848STomohiro Kusumi 	if (used_size > image_size) {
6592d60b848STomohiro Kusumi 		/* determine extra level1 needed */
6602d60b848STomohiro Kusumi 		delta_num_level1 = howmany(used_size - image_size,
6612d60b848STomohiro Kusumi 		    HAMMER2_FREEMAP_LEVEL1_SIZE);
6622d60b848STomohiro Kusumi 
6632d60b848STomohiro Kusumi 		/* adjust used size with 4MiB segment for each extra level1 */
6642d60b848STomohiro Kusumi 		used_size += HAMMER2_ZONE_SEG64 * delta_num_level1;
6652d60b848STomohiro Kusumi 
6662d60b848STomohiro Kusumi 		/* adjust image size with extra level1 */
6672d60b848STomohiro Kusumi 		image_size += HAMMER2_FREEMAP_LEVEL1_SIZE * delta_num_level1;
6682d60b848STomohiro Kusumi 		printf("trying adjusted image size %s\n",
6692d60b848STomohiro Kusumi 		    sizetostr(image_size));
6702d60b848STomohiro Kusumi 
6712d60b848STomohiro Kusumi 		if (used_size > image_size)
672f8a1147cSTomohiro Kusumi 			errx(1, "invalid used_size %lld > image_size %lld",
673f8a1147cSTomohiro Kusumi 			    (long long)used_size, (long long)image_size);
6742d60b848STomohiro Kusumi 	}
6752d60b848STomohiro Kusumi 
6762d60b848STomohiro Kusumi 	return image_size;
6772d60b848STomohiro Kusumi }
6782d60b848STomohiro Kusumi 
6792d60b848STomohiro Kusumi static const char *
hammer2_label_name(int label_type)6802d60b848STomohiro Kusumi hammer2_label_name(int label_type)
6812d60b848STomohiro Kusumi {
6822d60b848STomohiro Kusumi 	switch (label_type) {
6832d60b848STomohiro Kusumi 	case HAMMER2_LABEL_NONE:
6842d60b848STomohiro Kusumi 		return "NONE";
6852d60b848STomohiro Kusumi 	case HAMMER2_LABEL_BOOT:
6862d60b848STomohiro Kusumi 		return "BOOT";
6872d60b848STomohiro Kusumi 	case HAMMER2_LABEL_ROOT:
6882d60b848STomohiro Kusumi 		return "ROOT";
6892d60b848STomohiro Kusumi 	case HAMMER2_LABEL_DATA:
6902d60b848STomohiro Kusumi 		return "DATA";
6912d60b848STomohiro Kusumi 	default:
6922d60b848STomohiro Kusumi 		assert(0);
6932d60b848STomohiro Kusumi 	}
6942d60b848STomohiro Kusumi 	return NULL;
6952d60b848STomohiro Kusumi }
6962d60b848STomohiro Kusumi 
6972d60b848STomohiro Kusumi static void
hammer2_validate(const char * dir,fsnode * root,fsinfo_t * fsopts)6982d60b848STomohiro Kusumi hammer2_validate(const char *dir, fsnode *root, fsinfo_t *fsopts)
6992d60b848STomohiro Kusumi {
7002d60b848STomohiro Kusumi 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
7012d60b848STomohiro Kusumi 	hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
702afa5234bSTomohiro Kusumi 	hammer2_off_t image_size = 0, minsize, maxsize;
7032d60b848STomohiro Kusumi 	const char *s;
7042d60b848STomohiro Kusumi 
70570e962f7STomohiro Kusumi 	/* ioctl commands could have NULL dir / root */
7062d60b848STomohiro Kusumi 	assert(fsopts != NULL);
7072d60b848STomohiro Kusumi 
7082d60b848STomohiro Kusumi 	if (debug & DEBUG_FS_VALIDATE) {
7092d60b848STomohiro Kusumi 		APRINTF("before defaults set:\n");
7102d60b848STomohiro Kusumi 		hammer2_dump_fsinfo(fsopts);
7112d60b848STomohiro Kusumi 	}
7122d60b848STomohiro Kusumi 
7132d60b848STomohiro Kusumi 	/* makefs only supports "DATA" for default PFS label */
7142d60b848STomohiro Kusumi 	if (!h2_opt->label_specified) {
7152d60b848STomohiro Kusumi 		opt->DefaultLabelType = HAMMER2_LABEL_DATA;
7162d60b848STomohiro Kusumi 		s = hammer2_label_name(opt->DefaultLabelType);
7172d60b848STomohiro Kusumi 		printf("using default label \"%s\"\n", s);
7182d60b848STomohiro Kusumi 	}
7192d60b848STomohiro Kusumi 
7202d60b848STomohiro Kusumi 	/* set default mount PFS label */
7212d60b848STomohiro Kusumi 	if (!strcmp(h2_opt->mount_label, "")) {
7222d60b848STomohiro Kusumi 		s = hammer2_label_name(HAMMER2_LABEL_DATA);
7232d60b848STomohiro Kusumi 		strlcpy(h2_opt->mount_label, s, sizeof(h2_opt->mount_label));
7242d60b848STomohiro Kusumi 		printf("using default mount label \"%s\"\n", s);
7252d60b848STomohiro Kusumi 	}
7262d60b848STomohiro Kusumi 
7272d60b848STomohiro Kusumi 	/* set default number of volume headers */
7282d60b848STomohiro Kusumi 	if (!h2_opt->num_volhdr) {
7292d60b848STomohiro Kusumi 		h2_opt->num_volhdr = HAMMER2_NUM_VOLHDRS;
7302d60b848STomohiro Kusumi 		printf("using default %d volume headers\n", h2_opt->num_volhdr);
7312d60b848STomohiro Kusumi 	}
7322d60b848STomohiro Kusumi 
733917508cdSTomohiro Kusumi 	/* done if ioctl commands */
7349a149651STomohiro Kusumi 	if (h2_opt->ioctl_cmd) {
7359a149651STomohiro Kusumi 		if (h2_opt->ioctl_cmd == HAMMER2IOC_GROWFS)
736afa5234bSTomohiro Kusumi 			goto ignore_size_dir;
7379a149651STomohiro Kusumi 		else
7389a149651STomohiro Kusumi 			goto done;
7399a149651STomohiro Kusumi 	}
740a63188c8STomohiro Kusumi 
7412d60b848STomohiro Kusumi 	/* calculate data size */
7422d60b848STomohiro Kusumi 	if (fsopts->size != 0)
7432d60b848STomohiro Kusumi 		fsopts->size = 0; /* shouldn't reach here to begin with */
744a63188c8STomohiro Kusumi 	if (root == NULL)
745a63188c8STomohiro Kusumi 		errx(1, "fsnode tree not constructed");
7462d60b848STomohiro Kusumi 	hammer2_size_dir(root, fsopts);
7472d60b848STomohiro Kusumi 	printf("estimated data size %s from %lld inode\n",
7482d60b848STomohiro Kusumi 	    sizetostr(fsopts->size), (long long)fsopts->inodes);
7492d60b848STomohiro Kusumi 
7502d60b848STomohiro Kusumi 	/* determine image size from data size */
7512d60b848STomohiro Kusumi 	image_size = hammer2_image_size(fsopts);
7522d60b848STomohiro Kusumi 	assert((image_size & HAMMER2_FREEMAP_LEVEL1_MASK) == 0);
753afa5234bSTomohiro Kusumi ignore_size_dir:
7542d60b848STomohiro Kusumi 	minsize = roundup(fsopts->minsize, HAMMER2_FREEMAP_LEVEL1_SIZE);
7552d60b848STomohiro Kusumi 	maxsize = roundup(fsopts->maxsize, HAMMER2_FREEMAP_LEVEL1_SIZE);
7562d60b848STomohiro Kusumi 	if (image_size < minsize)
7572d60b848STomohiro Kusumi 		image_size = minsize;
7582d60b848STomohiro Kusumi 	else if (maxsize > 0 && image_size > maxsize)
759f8a1147cSTomohiro Kusumi 		errx(1, "`%s' size of %lld is larger than the maxsize of %lld",
760f8a1147cSTomohiro Kusumi 		    dir, (long long)image_size, (long long)maxsize);
7612d60b848STomohiro Kusumi 
7622d60b848STomohiro Kusumi 	assert((image_size & HAMMER2_FREEMAP_LEVEL1_MASK) == 0);
7632d60b848STomohiro Kusumi 	h2_opt->image_size = image_size;
7642d60b848STomohiro Kusumi 	printf("using %s image size\n", sizetostr(h2_opt->image_size));
765a63188c8STomohiro Kusumi done:
7662d60b848STomohiro Kusumi 	if (debug & DEBUG_FS_VALIDATE) {
7672d60b848STomohiro Kusumi 		APRINTF("after defaults set:\n");
7682d60b848STomohiro Kusumi 		hammer2_dump_fsinfo(fsopts);
7692d60b848STomohiro Kusumi 	}
7702d60b848STomohiro Kusumi }
7712d60b848STomohiro Kusumi 
7722d60b848STomohiro Kusumi static void
hammer2_dump_fsinfo(fsinfo_t * fsopts)7732d60b848STomohiro Kusumi hammer2_dump_fsinfo(fsinfo_t *fsopts)
7742d60b848STomohiro Kusumi {
7752d60b848STomohiro Kusumi 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
7762d60b848STomohiro Kusumi 	hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
7772d60b848STomohiro Kusumi 	int i;
7782d60b848STomohiro Kusumi 	char *s;
7792d60b848STomohiro Kusumi 
7802d60b848STomohiro Kusumi 	assert(fsopts != NULL);
7812d60b848STomohiro Kusumi 
7822d60b848STomohiro Kusumi 	APRINTF("fsinfo_t at %p\n", fsopts);
7832d60b848STomohiro Kusumi 
7842d60b848STomohiro Kusumi 	printf("\tinodes %lld\n", (long long)fsopts->inodes);
7852d60b848STomohiro Kusumi 	printf("\tsize %lld, minsize %lld, maxsize %lld\n",
7862d60b848STomohiro Kusumi 	    (long long)fsopts->size,
7872d60b848STomohiro Kusumi 	    (long long)fsopts->minsize,
7882d60b848STomohiro Kusumi 	    (long long)fsopts->maxsize);
7892d60b848STomohiro Kusumi 
7902d60b848STomohiro Kusumi 	printf("\thammer2_debug 0x%x\n", hammer2_debug);
7912d60b848STomohiro Kusumi 
7922d60b848STomohiro Kusumi 	printf("\tlabel_specified %d\n", h2_opt->label_specified);
7932d60b848STomohiro Kusumi 	printf("\tmount_label \"%s\"\n", h2_opt->mount_label);
7942d60b848STomohiro Kusumi 	printf("\tnum_volhdr %d\n", h2_opt->num_volhdr);
7959a149651STomohiro Kusumi 	printf("\tioctl_cmd %ld\n", h2_opt->ioctl_cmd);
79648ed4577STomohiro Kusumi 	printf("\temergency_mode %d\n", h2_opt->emergency_mode);
7973999233bSTomohiro Kusumi 	printf("\tpfs_cmd_name \"%s\"\n", h2_opt->pfs_cmd_name);
7983999233bSTomohiro Kusumi 	printf("\tpfs_name \"%s\"\n", h2_opt->pfs_name);
799465e1141STomohiro Kusumi 	printf("\tinode_cmd_name \"%s\"\n", h2_opt->inode_cmd_name);
800f804c425STomohiro Kusumi 	printf("\tinode_path \"%s\"\n", h2_opt->inode_path);
801917508cdSTomohiro Kusumi 	printf("\tdestroy_path \"%s\"\n", h2_opt->destroy_path);
802917508cdSTomohiro Kusumi 	printf("\tdestroy_inum %lld\n", (long long)h2_opt->destroy_inum);
8035e8b0eb7STomohiro Kusumi 	printf("\tread_path \"%s\"\n", h2_opt->read_path);
804f8a1147cSTomohiro Kusumi 	printf("\timage_size 0x%llx\n", (long long)h2_opt->image_size);
8052d60b848STomohiro Kusumi 
8062d60b848STomohiro Kusumi 	printf("\tHammer2Version %d\n", opt->Hammer2Version);
8072d60b848STomohiro Kusumi 	printf("\tBootAreaSize 0x%jx\n", opt->BootAreaSize);
8082d60b848STomohiro Kusumi 	printf("\tAuxAreaSize 0x%jx\n", opt->AuxAreaSize);
8092d60b848STomohiro Kusumi 	printf("\tNLabels %d\n", opt->NLabels);
8102d60b848STomohiro Kusumi 	printf("\tCompType %d\n", opt->CompType);
8112d60b848STomohiro Kusumi 	printf("\tCheckType %d\n", opt->CheckType);
8122d60b848STomohiro Kusumi 	printf("\tDefaultLabelType %d\n", opt->DefaultLabelType);
8132d60b848STomohiro Kusumi 	printf("\tDebugOpt %d\n", opt->DebugOpt);
8142d60b848STomohiro Kusumi 
8152d60b848STomohiro Kusumi 	s = NULL;
8162d60b848STomohiro Kusumi 	hammer2_uuid_to_str(&opt->Hammer2_FSType, &s);
8172d60b848STomohiro Kusumi 	printf("\tHammer2_FSType \"%s\"\n", s);
8182d60b848STomohiro Kusumi 	s = NULL;
8192d60b848STomohiro Kusumi 	hammer2_uuid_to_str(&opt->Hammer2_VolFSID, &s);
8202d60b848STomohiro Kusumi 	printf("\tHammer2_VolFSID \"%s\"\n", s);
8212d60b848STomohiro Kusumi 	s = NULL;
8222d60b848STomohiro Kusumi 	hammer2_uuid_to_str(&opt->Hammer2_SupCLID, &s);
8232d60b848STomohiro Kusumi 	printf("\tHammer2_SupCLID \"%s\"\n", s);
8242d60b848STomohiro Kusumi 	s = NULL;
8252d60b848STomohiro Kusumi 	hammer2_uuid_to_str(&opt->Hammer2_SupFSID, &s);
8262d60b848STomohiro Kusumi 	printf("\tHammer2_SupFSID \"%s\"\n", s);
8272d60b848STomohiro Kusumi 
8282d60b848STomohiro Kusumi 	for (i = 0; i < opt->NLabels; i++) {
8292d60b848STomohiro Kusumi 		printf("\tLabel[%d] \"%s\"\n", i, opt->Label[i]);
8302d60b848STomohiro Kusumi 		s = NULL;
8312d60b848STomohiro Kusumi 		hammer2_uuid_to_str(&opt->Hammer2_PfsCLID[i], &s);
8322d60b848STomohiro Kusumi 		printf("\t Hammer2_PfsCLID[%d] \"%s\"\n", i, s);
8332d60b848STomohiro Kusumi 		s = NULL;
8342d60b848STomohiro Kusumi 		hammer2_uuid_to_str(&opt->Hammer2_PfsFSID[i], &s);
8352d60b848STomohiro Kusumi 		printf("\t Hammer2_PfsFSID[%d] \"%s\"\n", i, s);
8362d60b848STomohiro Kusumi 	}
8372d60b848STomohiro Kusumi 
8382d60b848STomohiro Kusumi 	free(s);
8392d60b848STomohiro Kusumi }
8402d60b848STomohiro Kusumi 
8412d60b848STomohiro Kusumi static int
hammer2_setup_blkdev(const char * image,fsinfo_t * fsopts)842*764bf12eSTomohiro Kusumi hammer2_setup_blkdev(const char *image, fsinfo_t *fsopts)
843*764bf12eSTomohiro Kusumi {
844*764bf12eSTomohiro Kusumi 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
845*764bf12eSTomohiro Kusumi 	hammer2_off_t size;
846*764bf12eSTomohiro Kusumi 
847*764bf12eSTomohiro Kusumi 	if ((fsopts->fd = open(image, O_RDWR)) == -1) {
848*764bf12eSTomohiro Kusumi 		warn("can't open `%s' for writing", image);
849*764bf12eSTomohiro Kusumi 		return -1;
850*764bf12eSTomohiro Kusumi 	}
851*764bf12eSTomohiro Kusumi 
852*764bf12eSTomohiro Kusumi 	size = check_volume(fsopts->fd);
853*764bf12eSTomohiro Kusumi 	if (h2_opt->image_size > size) {
854*764bf12eSTomohiro Kusumi 		warnx("image size %lld exceeds %s size %lld",
855*764bf12eSTomohiro Kusumi 		    (long long)h2_opt->image_size, image, (long long)size);
856*764bf12eSTomohiro Kusumi 		return -1;
857*764bf12eSTomohiro Kusumi 	}
858*764bf12eSTomohiro Kusumi 
859*764bf12eSTomohiro Kusumi 	return 0;
860*764bf12eSTomohiro Kusumi }
861*764bf12eSTomohiro Kusumi 
862*764bf12eSTomohiro Kusumi static int
hammer2_create_image(const char * image,fsinfo_t * fsopts)8632d60b848STomohiro Kusumi hammer2_create_image(const char *image, fsinfo_t *fsopts)
8642d60b848STomohiro Kusumi {
8652d60b848STomohiro Kusumi 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
8662d60b848STomohiro Kusumi 	hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
8672d60b848STomohiro Kusumi 	char *av[] = { (char *)image, }; /* XXX support multi-volumes */
8682d60b848STomohiro Kusumi 	char *buf;
8692d60b848STomohiro Kusumi 	int i, bufsize, oflags;
8702d60b848STomohiro Kusumi 	off_t bufrem;
871*764bf12eSTomohiro Kusumi 	struct stat st;
8722d60b848STomohiro Kusumi 
8732d60b848STomohiro Kusumi 	assert(image != NULL);
8742d60b848STomohiro Kusumi 	assert(fsopts != NULL);
8752d60b848STomohiro Kusumi 
876*764bf12eSTomohiro Kusumi 	/* check if image is blk or chr */
877*764bf12eSTomohiro Kusumi 	if (stat(image, &st) == 0) {
878*764bf12eSTomohiro Kusumi 		if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode)) {
879*764bf12eSTomohiro Kusumi 			if (hammer2_setup_blkdev(image, fsopts))
880*764bf12eSTomohiro Kusumi 				return -1;
881*764bf12eSTomohiro Kusumi 			goto done;
882*764bf12eSTomohiro Kusumi 		}
883*764bf12eSTomohiro Kusumi 	}
884*764bf12eSTomohiro Kusumi 
8852d60b848STomohiro Kusumi 	/* create image */
8862d60b848STomohiro Kusumi 	oflags = O_RDWR | O_CREAT;
8872d60b848STomohiro Kusumi 	if (fsopts->offset == 0)
8882d60b848STomohiro Kusumi 		oflags |= O_TRUNC;
8892d60b848STomohiro Kusumi 	if ((fsopts->fd = open(image, oflags, 0666)) == -1) {
8902d60b848STomohiro Kusumi 		warn("can't open `%s' for writing", image);
8912d60b848STomohiro Kusumi 		return -1;
8922d60b848STomohiro Kusumi 	}
8932d60b848STomohiro Kusumi 
8942d60b848STomohiro Kusumi 	/* zero image */
8952d60b848STomohiro Kusumi 	bufsize = HAMMER2_PBUFSIZE;
8962d60b848STomohiro Kusumi 	bufrem = h2_opt->image_size;
8972d60b848STomohiro Kusumi 	if (fsopts->sparse) {
8982d60b848STomohiro Kusumi 		if (ftruncate(fsopts->fd, bufrem) == -1) {
8992d60b848STomohiro Kusumi 			warn("sparse option disabled");
9002d60b848STomohiro Kusumi 			fsopts->sparse = 0;
9012d60b848STomohiro Kusumi 		}
9022d60b848STomohiro Kusumi 	}
9032d60b848STomohiro Kusumi 	if (fsopts->sparse) {
9042d60b848STomohiro Kusumi 		/* File truncated at bufrem. Remaining is 0 */
9052d60b848STomohiro Kusumi 		bufrem = 0;
9062d60b848STomohiro Kusumi 		buf = NULL;
9072d60b848STomohiro Kusumi 	} else {
9082d60b848STomohiro Kusumi 		if (debug & DEBUG_FS_CREATE_IMAGE)
9092d60b848STomohiro Kusumi 			APRINTF("zero-ing image `%s', %lld sectors, "
9102d60b848STomohiro Kusumi 			    "using %d byte chunks\n",
9112d60b848STomohiro Kusumi 			    image, (long long)bufrem, bufsize);
9122d60b848STomohiro Kusumi 		buf = ecalloc(1, bufsize);
9132d60b848STomohiro Kusumi 	}
9142d60b848STomohiro Kusumi 
9152d60b848STomohiro Kusumi 	if (fsopts->offset != 0) {
9162d60b848STomohiro Kusumi 		if (lseek(fsopts->fd, fsopts->offset, SEEK_SET) == -1) {
9172d60b848STomohiro Kusumi 			warn("can't seek");
9182d60b848STomohiro Kusumi 			free(buf);
9192d60b848STomohiro Kusumi 			return -1;
9202d60b848STomohiro Kusumi 		}
9212d60b848STomohiro Kusumi 	}
9222d60b848STomohiro Kusumi 
9232d60b848STomohiro Kusumi 	while (bufrem > 0) {
9242d60b848STomohiro Kusumi 		i = write(fsopts->fd, buf, MIN(bufsize, bufrem));
9252d60b848STomohiro Kusumi 		if (i == -1) {
9262d60b848STomohiro Kusumi 			warn("zeroing image, %lld bytes to go",
9272d60b848STomohiro Kusumi 			    (long long)bufrem);
9282d60b848STomohiro Kusumi 			free(buf);
9292d60b848STomohiro Kusumi 			return -1;
9302d60b848STomohiro Kusumi 		}
9312d60b848STomohiro Kusumi 		bufrem -= i;
9322d60b848STomohiro Kusumi 	}
9332d60b848STomohiro Kusumi 	if (buf)
9342d60b848STomohiro Kusumi 		free(buf);
935*764bf12eSTomohiro Kusumi done:
9362d60b848STomohiro Kusumi 	/* make the file system */
9372d60b848STomohiro Kusumi 	if (debug & DEBUG_FS_CREATE_IMAGE)
9382d60b848STomohiro Kusumi 		APRINTF("calling mkfs(\"%s\", ...)\n", image);
9392d60b848STomohiro Kusumi 	hammer2_mkfs(1, av, opt); /* success if returned */
9402d60b848STomohiro Kusumi 
9412d60b848STomohiro Kusumi 	return fsopts->fd;
9422d60b848STomohiro Kusumi }
9432d60b848STomohiro Kusumi 
9442d60b848STomohiro Kusumi static off_t
hammer2_phys_size(off_t size)9452d60b848STomohiro Kusumi hammer2_phys_size(off_t size)
9462d60b848STomohiro Kusumi {
9472d60b848STomohiro Kusumi 	off_t radix_size, phys_size = 0;
9482d60b848STomohiro Kusumi 	int i;
9492d60b848STomohiro Kusumi 
9502d60b848STomohiro Kusumi 	if (size > HAMMER2_PBUFSIZE) {
9512d60b848STomohiro Kusumi 		phys_size += rounddown(size, HAMMER2_PBUFSIZE);
9522d60b848STomohiro Kusumi 		size = size % HAMMER2_PBUFSIZE;
9532d60b848STomohiro Kusumi 	}
9542d60b848STomohiro Kusumi 
9552d60b848STomohiro Kusumi 	for (i = HAMMER2_RADIX_MIN; i <= HAMMER2_RADIX_MAX; i++) {
9562d60b848STomohiro Kusumi 		radix_size = 1UL << i;
9572d60b848STomohiro Kusumi 		if (radix_size >= size) {
9582d60b848STomohiro Kusumi 			phys_size += radix_size;
9592d60b848STomohiro Kusumi 			break;
9602d60b848STomohiro Kusumi 		}
9612d60b848STomohiro Kusumi 	}
9622d60b848STomohiro Kusumi 
9632d60b848STomohiro Kusumi 	return phys_size;
9642d60b848STomohiro Kusumi }
9652d60b848STomohiro Kusumi 
9662d60b848STomohiro Kusumi /* calculate data size */
9672d60b848STomohiro Kusumi static void
hammer2_size_dir(fsnode * root,fsinfo_t * fsopts)9682d60b848STomohiro Kusumi hammer2_size_dir(fsnode *root, fsinfo_t *fsopts)
9692d60b848STomohiro Kusumi {
9702d60b848STomohiro Kusumi 	fsnode *node;
9712d60b848STomohiro Kusumi 
9722d60b848STomohiro Kusumi 	assert(fsopts != NULL);
9732d60b848STomohiro Kusumi 
9742d60b848STomohiro Kusumi 	if (debug & DEBUG_FS_SIZE_DIR)
9752d60b848STomohiro Kusumi 		APRINTF("entry: bytes %lld inodes %lld\n",
9762d60b848STomohiro Kusumi 		    (long long)fsopts->size, (long long)fsopts->inodes);
9772d60b848STomohiro Kusumi 
9782d60b848STomohiro Kusumi 	for (node = root; node != NULL; node = node->next) {
9792d60b848STomohiro Kusumi 		if (node == root) { /* we're at "." */
9802d60b848STomohiro Kusumi 			assert(strcmp(node->name, ".") == 0);
9812d60b848STomohiro Kusumi 		} else if ((node->inode->flags & FI_SIZED) == 0) {
9822d60b848STomohiro Kusumi 			/* don't count duplicate names */
9832d60b848STomohiro Kusumi 			node->inode->flags |= FI_SIZED;
9842d60b848STomohiro Kusumi 			if (debug & DEBUG_FS_SIZE_DIR_NODE)
9852d60b848STomohiro Kusumi 				APRINTF("`%s' size %lld\n",
9862d60b848STomohiro Kusumi 				    node->name,
9872d60b848STomohiro Kusumi 				    (long long)node->inode->st.st_size);
9882d60b848STomohiro Kusumi 			fsopts->inodes++;
9892d60b848STomohiro Kusumi 			fsopts->size += sizeof(hammer2_inode_data_t);
9902d60b848STomohiro Kusumi 			if (node->type == S_IFREG) {
9912d60b848STomohiro Kusumi 				size_t st_size = node->inode->st.st_size;
9922d60b848STomohiro Kusumi 				if (st_size > HAMMER2_EMBEDDED_BYTES)
9932d60b848STomohiro Kusumi 					fsopts->size += hammer2_phys_size(st_size);
9942d60b848STomohiro Kusumi 			} else if (node->type == S_IFLNK) {
9952d60b848STomohiro Kusumi 				size_t nlen = strlen(node->symlink);
9962d60b848STomohiro Kusumi 				if (nlen > HAMMER2_EMBEDDED_BYTES)
9972d60b848STomohiro Kusumi 					fsopts->size += hammer2_phys_size(nlen);
9982d60b848STomohiro Kusumi 			}
9992d60b848STomohiro Kusumi 		}
10002d60b848STomohiro Kusumi 		if (node->type == S_IFDIR)
10012d60b848STomohiro Kusumi 			hammer2_size_dir(node->child, fsopts);
10022d60b848STomohiro Kusumi 	}
10032d60b848STomohiro Kusumi 
10042d60b848STomohiro Kusumi 	if (debug & DEBUG_FS_SIZE_DIR)
10052d60b848STomohiro Kusumi 		APRINTF("exit: size %lld inodes %lld\n",
10062d60b848STomohiro Kusumi 		    (long long)fsopts->size, (long long)fsopts->inodes);
10072d60b848STomohiro Kusumi }
10082d60b848STomohiro Kusumi 
10092d60b848STomohiro Kusumi static void
hammer2_print(const struct m_vnode * dvp,const struct m_vnode * vp,const fsnode * node,int depth,const char * msg)10106bcbb706STomohiro Kusumi hammer2_print(const struct m_vnode *dvp, const struct m_vnode *vp,
10112d60b848STomohiro Kusumi     const fsnode *node, int depth, const char *msg)
10122d60b848STomohiro Kusumi {
10132d60b848STomohiro Kusumi 	if (debug & DEBUG_FS_POPULATE) {
10142d60b848STomohiro Kusumi 		if (1) {
10152d60b848STomohiro Kusumi 			int indent = depth * 2;
10162d60b848STomohiro Kusumi 			char *type;
10172d60b848STomohiro Kusumi 			if (S_ISDIR(node->type))
10182d60b848STomohiro Kusumi 				type = "dir";
10192d60b848STomohiro Kusumi 			else if (S_ISREG(node->type))
10202d60b848STomohiro Kusumi 				type = "reg";
10212d60b848STomohiro Kusumi 			else if (S_ISLNK(node->type))
10222d60b848STomohiro Kusumi 				type = "lnk";
10234d8112c5STomohiro Kusumi 			else if (S_ISFIFO(node->type))
10244d8112c5STomohiro Kusumi 				type = "fifo";
10252d60b848STomohiro Kusumi 			else
10262d60b848STomohiro Kusumi 				type = "???";
10272d60b848STomohiro Kusumi 			printf("%*.*s", indent, indent, "");
10282d60b848STomohiro Kusumi 			printf("dvp=%p/%d vp=%p/%d \"%s\" %s %s\n",
10292d60b848STomohiro Kusumi 			    dvp, dvp ? VTOI(dvp)->refs : 0,
10302d60b848STomohiro Kusumi 			    vp, vp ? VTOI(vp)->refs : 0,
10312d60b848STomohiro Kusumi 			    node->name, type, msg);
10322d60b848STomohiro Kusumi 		} else {
10332d60b848STomohiro Kusumi 			char type;
10342d60b848STomohiro Kusumi 			if (S_ISDIR(node->type))
10352d60b848STomohiro Kusumi 				type = 'd';
10362d60b848STomohiro Kusumi 			else if (S_ISREG(node->type))
10372d60b848STomohiro Kusumi 				type = 'r';
10382d60b848STomohiro Kusumi 			else if (S_ISLNK(node->type))
10392d60b848STomohiro Kusumi 				type = 'l';
10404d8112c5STomohiro Kusumi 			else if (S_ISFIFO(node->type))
10414d8112c5STomohiro Kusumi 				type = 'f';
10422d60b848STomohiro Kusumi 			else
10432d60b848STomohiro Kusumi 				type = '?';
10442d60b848STomohiro Kusumi 			printf("%c", type);
10452d60b848STomohiro Kusumi 			fflush(stdout);
10462d60b848STomohiro Kusumi 		}
10472d60b848STomohiro Kusumi 	}
10482d60b848STomohiro Kusumi }
10492d60b848STomohiro Kusumi 
10502d60b848STomohiro Kusumi static int
hammer2_populate_dir(struct m_vnode * dvp,const char * dir,fsnode * root,fsnode * parent,fsinfo_t * fsopts,int depth)10516bcbb706STomohiro Kusumi hammer2_populate_dir(struct m_vnode *dvp, const char *dir, fsnode *root,
10522d60b848STomohiro Kusumi     fsnode *parent, fsinfo_t *fsopts, int depth)
10532d60b848STomohiro Kusumi {
10542d60b848STomohiro Kusumi 	fsnode *cur;
10556bcbb706STomohiro Kusumi 	struct m_vnode *vp;
10562d60b848STomohiro Kusumi 	struct stat st;
10572d60b848STomohiro Kusumi 	char f[MAXPATHLEN];
10582d60b848STomohiro Kusumi 	const char *path;
1059213ebac1STomohiro Kusumi 	int hardlink;
10602d60b848STomohiro Kusumi 	int error;
10612d60b848STomohiro Kusumi 
10622d60b848STomohiro Kusumi 	assert(dvp != NULL);
10632d60b848STomohiro Kusumi 	assert(dir != NULL);
10642d60b848STomohiro Kusumi 	assert(root != NULL);
10652d60b848STomohiro Kusumi 	assert(parent != NULL);
10662d60b848STomohiro Kusumi 	assert(fsopts != NULL);
10672d60b848STomohiro Kusumi 
10682d60b848STomohiro Kusumi 	/* assert root directory */
10692d60b848STomohiro Kusumi 	assert(S_ISDIR(root->type));
10702d60b848STomohiro Kusumi 	assert(!strcmp(root->name, "."));
10712d60b848STomohiro Kusumi 	assert(!root->child);
10722d60b848STomohiro Kusumi 	assert(!root->parent || root->parent->child == root);
10732d60b848STomohiro Kusumi 
10742d60b848STomohiro Kusumi 	hammer2_print(dvp, NULL, root, depth, "enter");
10752d60b848STomohiro Kusumi 	if (stat(dir, &st) == -1)
10762d60b848STomohiro Kusumi 		err(1, "no such path %s", dir);
10772d60b848STomohiro Kusumi 	if (!S_ISDIR(st.st_mode))
10782d60b848STomohiro Kusumi 		errx(1, "no such dir %s", dir);
10792d60b848STomohiro Kusumi 
10802d60b848STomohiro Kusumi 	for (cur = root->next; cur != NULL; cur = cur->next) {
1081ddd1d3d1STomohiro Kusumi 		/* global variable for HAMMER2 vnops */
1082ddd1d3d1STomohiro Kusumi 		hammer2_curnode = cur;
1083ddd1d3d1STomohiro Kusumi 
10842d60b848STomohiro Kusumi 		/* construct source path */
10852d60b848STomohiro Kusumi 		if (cur->contents) {
10862d60b848STomohiro Kusumi 			path = cur->contents;
10872d60b848STomohiro Kusumi 		} else {
10882d60b848STomohiro Kusumi 			if (snprintf(f, sizeof(f), "%s/%s/%s",
10892d60b848STomohiro Kusumi 			    cur->root, cur->path, cur->name) >= (int)sizeof(f))
1090d5e693f1STomohiro Kusumi 				errx(1, "path %s too long", f);
10912d60b848STomohiro Kusumi 			path = f;
10922d60b848STomohiro Kusumi 		}
10932de31a7dSTomohiro Kusumi 		if (S_ISLNK(cur->type)) {
10942de31a7dSTomohiro Kusumi 			if (lstat(path, &st) == -1)
10952de31a7dSTomohiro Kusumi 				err(1, "no such symlink %s", path);
10962de31a7dSTomohiro Kusumi 		} else {
10972d60b848STomohiro Kusumi 			if (stat(path, &st) == -1)
10982de31a7dSTomohiro Kusumi 				err(1, "no such path %s", path);
10992de31a7dSTomohiro Kusumi 		}
11002d60b848STomohiro Kusumi 
11012d60b848STomohiro Kusumi 		/* update node state */
11022d60b848STomohiro Kusumi 		if ((cur->inode->flags & FI_ALLOCATED) == 0) {
11032d60b848STomohiro Kusumi 			cur->inode->flags |= FI_ALLOCATED;
11042d60b848STomohiro Kusumi 			if (cur != root)
11052d60b848STomohiro Kusumi 				cur->parent = parent;
11062d60b848STomohiro Kusumi 		}
11072d60b848STomohiro Kusumi 
1108213ebac1STomohiro Kusumi 		/* detect hardlink */
1109213ebac1STomohiro Kusumi 		if (cur->inode->flags & FI_WRITTEN) {
1110213ebac1STomohiro Kusumi 			assert(!S_ISDIR(cur->type));
1111213ebac1STomohiro Kusumi 			hardlink = 1;
1112213ebac1STomohiro Kusumi 		} else {
1113213ebac1STomohiro Kusumi 			hardlink = 0;
1114213ebac1STomohiro Kusumi 		}
11152d60b848STomohiro Kusumi 		cur->inode->flags |= FI_WRITTEN;
11162d60b848STomohiro Kusumi 
11172d60b848STomohiro Kusumi 		/* make sure it doesn't exist yet */
11182d60b848STomohiro Kusumi 		vp = NULL;
11192d60b848STomohiro Kusumi 		error = hammer2_nresolve(dvp, &vp, cur->name,
11202d60b848STomohiro Kusumi 		    strlen(cur->name));
11212d60b848STomohiro Kusumi 		if (!error)
11222d60b848STomohiro Kusumi 			errx(1, "hammer2_nresolve(\"%s\") already exists",
11232d60b848STomohiro Kusumi 			    cur->name);
11242d60b848STomohiro Kusumi 		hammer2_print(dvp, vp, cur, depth, "nresolve");
11252d60b848STomohiro Kusumi 
11262d60b848STomohiro Kusumi 		/* if directory, mkdir and recurse */
11272d60b848STomohiro Kusumi 		if (S_ISDIR(cur->type)) {
11282d60b848STomohiro Kusumi 			assert(cur->child);
11292d60b848STomohiro Kusumi 
11302d60b848STomohiro Kusumi 			vp = NULL;
11312d60b848STomohiro Kusumi 			error = hammer2_nmkdir(dvp, &vp, cur->name,
11328f9d1da1STomohiro Kusumi 			    strlen(cur->name), cur->inode->st.st_mode);
11332d60b848STomohiro Kusumi 			if (error)
11342d60b848STomohiro Kusumi 				errx(1, "hammer2_nmkdir(\"%s\") failed: %s",
11352d60b848STomohiro Kusumi 				    cur->name, strerror(error));
11362d60b848STomohiro Kusumi 			assert(vp);
11372d60b848STomohiro Kusumi 			hammer2_print(dvp, vp, cur, depth, "nmkdir");
11382d60b848STomohiro Kusumi 
11392d60b848STomohiro Kusumi 			error = hammer2_populate_dir(vp, path, cur->child, cur,
11402d60b848STomohiro Kusumi 			    fsopts, depth + 1);
11412d60b848STomohiro Kusumi 			if (error)
11422d60b848STomohiro Kusumi 				errx(1, "failed to populate %s: %s",
11432d60b848STomohiro Kusumi 				    path, strerror(error));
11443bffb051STomohiro Kusumi 			cur->inode->param = vp;
11452d60b848STomohiro Kusumi 			continue;
11462d60b848STomohiro Kusumi 		}
11472d60b848STomohiro Kusumi 
11482d60b848STomohiro Kusumi 		/* if regular file, creat and write its data */
1149213ebac1STomohiro Kusumi 		if (S_ISREG(cur->type) && !hardlink) {
11502d60b848STomohiro Kusumi 			assert(cur->child == NULL);
11512d60b848STomohiro Kusumi 
11522d60b848STomohiro Kusumi 			vp = NULL;
11532d60b848STomohiro Kusumi 			error = hammer2_ncreate(dvp, &vp, cur->name,
11548f9d1da1STomohiro Kusumi 			    strlen(cur->name), cur->inode->st.st_mode);
11552d60b848STomohiro Kusumi 			if (error)
11562d60b848STomohiro Kusumi 				errx(1, "hammer2_ncreate(\"%s\") failed: %s",
11572d60b848STomohiro Kusumi 				    cur->name, strerror(error));
11582d60b848STomohiro Kusumi 			assert(vp);
11592d60b848STomohiro Kusumi 			hammer2_print(dvp, vp, cur, depth, "ncreate");
11602d60b848STomohiro Kusumi 
11612d60b848STomohiro Kusumi 			error = hammer2_write_file(vp, path, cur);
11622d60b848STomohiro Kusumi 			if (error)
11632d60b848STomohiro Kusumi 				errx(1, "hammer2_write_file(\"%s\") failed: %s",
11642d60b848STomohiro Kusumi 				    path, strerror(error));
11653bffb051STomohiro Kusumi 			cur->inode->param = vp;
1166213ebac1STomohiro Kusumi 			continue;
1167213ebac1STomohiro Kusumi 		}
1168213ebac1STomohiro Kusumi 
11692d60b848STomohiro Kusumi 		/* if symlink, create a symlink against target */
11702d60b848STomohiro Kusumi 		if (S_ISLNK(cur->type)) {
11712d60b848STomohiro Kusumi 			assert(cur->child == NULL);
11722d60b848STomohiro Kusumi 
11732d60b848STomohiro Kusumi 			vp = NULL;
11742d60b848STomohiro Kusumi 			error = hammer2_nsymlink(dvp, &vp, cur->name,
11758f9d1da1STomohiro Kusumi 			    strlen(cur->name), cur->symlink,
11768f9d1da1STomohiro Kusumi 			    cur->inode->st.st_mode);
11772d60b848STomohiro Kusumi 			if (error)
11782d60b848STomohiro Kusumi 				errx(1, "hammer2_nsymlink(\"%s\") failed: %s",
11792d60b848STomohiro Kusumi 				    cur->name, strerror(error));
11802d60b848STomohiro Kusumi 			assert(vp);
11812d60b848STomohiro Kusumi 			hammer2_print(dvp, vp, cur, depth, "nsymlink");
11823bffb051STomohiro Kusumi 			cur->inode->param = vp;
11832d60b848STomohiro Kusumi 			continue;
11842d60b848STomohiro Kusumi 		}
11852d60b848STomohiro Kusumi 
11864d8112c5STomohiro Kusumi 		/* if fifo, create a fifo */
118748a07aadSTomohiro Kusumi 		if (S_ISFIFO(cur->type) && !hardlink) {
11884d8112c5STomohiro Kusumi 			assert(cur->child == NULL);
11894d8112c5STomohiro Kusumi 
11904d8112c5STomohiro Kusumi 			vp = NULL;
11914d8112c5STomohiro Kusumi 			error = hammer2_nmknod(dvp, &vp, cur->name,
11928f9d1da1STomohiro Kusumi 			    strlen(cur->name), VFIFO, cur->inode->st.st_mode);
11934d8112c5STomohiro Kusumi 			if (error)
11944d8112c5STomohiro Kusumi 				errx(1, "hammer2_nmknod(\"%s\") failed: %s",
11954d8112c5STomohiro Kusumi 				    cur->name, strerror(error));
11964d8112c5STomohiro Kusumi 			assert(vp);
11974d8112c5STomohiro Kusumi 			hammer2_print(dvp, vp, cur, depth, "nmknod");
11983bffb051STomohiro Kusumi 			cur->inode->param = vp;
11994d8112c5STomohiro Kusumi 			continue;
12004d8112c5STomohiro Kusumi 		}
12014d8112c5STomohiro Kusumi 
120248a07aadSTomohiro Kusumi 		/* if hardlink, creat a hardlink */
120348a07aadSTomohiro Kusumi 		if ((S_ISREG(cur->type) || S_ISFIFO(cur->type)) && hardlink) {
120448a07aadSTomohiro Kusumi 			char buf[64];
120548a07aadSTomohiro Kusumi 			assert(cur->child == NULL);
120648a07aadSTomohiro Kusumi 
120748a07aadSTomohiro Kusumi 			/* source vnode must not be NULL */
12083bffb051STomohiro Kusumi 			vp = cur->inode->param;
120948a07aadSTomohiro Kusumi 			assert(vp);
121048a07aadSTomohiro Kusumi 			/* currently these conditions must be true */
121148a07aadSTomohiro Kusumi 			assert(vp->v_data);
121248a07aadSTomohiro Kusumi 			assert(vp->v_type == VREG || vp->v_type == VFIFO);
121348a07aadSTomohiro Kusumi 			assert(vp->v_logical);
121448a07aadSTomohiro Kusumi 			assert(!vp->v_vflushed);
121548a07aadSTomohiro Kusumi 			assert(vp->v_malloced);
121648a07aadSTomohiro Kusumi 			assert(VTOI(vp)->refs > 0);
121748a07aadSTomohiro Kusumi 
121848a07aadSTomohiro Kusumi 			error = hammer2_nlink(dvp, vp, cur->name,
121948a07aadSTomohiro Kusumi 			    strlen(cur->name));
122048a07aadSTomohiro Kusumi 			if (error)
122148a07aadSTomohiro Kusumi 				errx(1, "hammer2_nlink(\"%s\") failed: %s",
122248a07aadSTomohiro Kusumi 				    cur->name, strerror(error));
1223f8a1147cSTomohiro Kusumi 			snprintf(buf, sizeof(buf), "nlink=%lld",
1224f8a1147cSTomohiro Kusumi 			    (long long)VTOI(vp)->meta.nlinks);
122548a07aadSTomohiro Kusumi 			hammer2_print(dvp, vp, cur, depth, buf);
122648a07aadSTomohiro Kusumi 			continue;
122748a07aadSTomohiro Kusumi 		}
122848a07aadSTomohiro Kusumi 
12292d60b848STomohiro Kusumi 		/* other types are unsupported */
12302d60b848STomohiro Kusumi 		printf("ignore %s 0%o\n", path, cur->type);
12312d60b848STomohiro Kusumi 	}
12322d60b848STomohiro Kusumi 
12332d60b848STomohiro Kusumi 	return 0;
12342d60b848STomohiro Kusumi }
12352d60b848STomohiro Kusumi 
12362d60b848STomohiro Kusumi static int
hammer2_write_file(struct m_vnode * vp,const char * path,fsnode * node)12376bcbb706STomohiro Kusumi hammer2_write_file(struct m_vnode *vp, const char *path, fsnode *node)
12382d60b848STomohiro Kusumi {
12392d60b848STomohiro Kusumi 	struct stat *st = &node->inode->st;
12402d60b848STomohiro Kusumi 	size_t nsize, bufsize;
12412d60b848STomohiro Kusumi 	off_t offset;
12422d60b848STomohiro Kusumi 	int fd, error;
12432d60b848STomohiro Kusumi 	char *p;
12442d60b848STomohiro Kusumi 
12452d60b848STomohiro Kusumi 	nsize = st->st_size;
12462d60b848STomohiro Kusumi 	if (nsize == 0)
12472d60b848STomohiro Kusumi 		return 0;
12482d60b848STomohiro Kusumi 	/* check nsize vs maximum file size */
12492d60b848STomohiro Kusumi 
12502d60b848STomohiro Kusumi 	fd = open(path, O_RDONLY);
12512d60b848STomohiro Kusumi 	if (fd < 0)
12522d60b848STomohiro Kusumi 		err(1, "failed to open %s", path);
12532d60b848STomohiro Kusumi 
12542d60b848STomohiro Kusumi 	p = mmap(0, nsize, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0);
12552d60b848STomohiro Kusumi 	if (p == MAP_FAILED)
12562d60b848STomohiro Kusumi 		err(1, "failed to mmap %s", path);
12572d60b848STomohiro Kusumi 	close(fd);
12582d60b848STomohiro Kusumi 
12592d60b848STomohiro Kusumi 	for (offset = 0; offset < nsize; ) {
12602d60b848STomohiro Kusumi 		bufsize = MIN(nsize - offset, HAMMER2_PBUFSIZE);
12612d60b848STomohiro Kusumi 		assert(bufsize <= HAMMER2_PBUFSIZE);
12622d60b848STomohiro Kusumi 		error = hammer2_write(vp, p + offset, bufsize, offset);
12632d60b848STomohiro Kusumi 		if (error)
12642d60b848STomohiro Kusumi 			errx(1, "failed to write to %s vnode: %s",
12652d60b848STomohiro Kusumi 			    path, strerror(error));
12662d60b848STomohiro Kusumi 		offset += bufsize;
12672d60b848STomohiro Kusumi 		if (bufsize == HAMMER2_PBUFSIZE)
12682d60b848STomohiro Kusumi 			assert((offset & (HAMMER2_PBUFSIZE - 1)) == 0);
12692d60b848STomohiro Kusumi 	}
12702d60b848STomohiro Kusumi 	munmap(p, nsize);
12712d60b848STomohiro Kusumi 
12722d60b848STomohiro Kusumi 	return 0;
12732d60b848STomohiro Kusumi }
1274a63188c8STomohiro Kusumi 
1275f72350acSTomohiro Kusumi static int
trim_char(char * p,char c)12766857f034STomohiro Kusumi trim_char(char *p, char c)
12776857f034STomohiro Kusumi {
12786857f034STomohiro Kusumi 	char *o, tmp[PATH_MAX];
1279bca6a9a0STomohiro Kusumi 	bool prev_was_c;
12806857f034STomohiro Kusumi 	size_t n;
12816857f034STomohiro Kusumi 	int i;
12826857f034STomohiro Kusumi 
128393bffd46STomohiro Kusumi 	assert(p);
128493bffd46STomohiro Kusumi 	/* nothing to do */
128593bffd46STomohiro Kusumi 	if (strlen(p) == 0)
128693bffd46STomohiro Kusumi 		return 0;
128793bffd46STomohiro Kusumi 
12886857f034STomohiro Kusumi 	strlcpy(tmp, p, sizeof(tmp));
12896857f034STomohiro Kusumi 	if (strncmp(tmp, p, sizeof(tmp)))
12906857f034STomohiro Kusumi 		return ENOSPC;
12916857f034STomohiro Kusumi 
12926857f034STomohiro Kusumi 	/* trim consecutive */
1293bca6a9a0STomohiro Kusumi 	prev_was_c = false;
12946857f034STomohiro Kusumi 	o = p;
12956857f034STomohiro Kusumi 	n = strlen(p);
12966857f034STomohiro Kusumi 
12976857f034STomohiro Kusumi 	for (i = 0; i < n; i++) {
12986857f034STomohiro Kusumi 		if (tmp[i] == c) {
1299bca6a9a0STomohiro Kusumi 			if (!prev_was_c)
13006857f034STomohiro Kusumi 				*p++ = tmp[i];
1301bca6a9a0STomohiro Kusumi 			prev_was_c = true;
13026857f034STomohiro Kusumi 		} else {
13036857f034STomohiro Kusumi 			*p++ = tmp[i];
1304bca6a9a0STomohiro Kusumi 			prev_was_c = false;
13056857f034STomohiro Kusumi 		}
13066857f034STomohiro Kusumi 	}
13076857f034STomohiro Kusumi 	*p = 0;
13086857f034STomohiro Kusumi 	assert(strlen(p) <= strlen(tmp));
13096857f034STomohiro Kusumi 
13106857f034STomohiro Kusumi 	/* assert no consecutive */
1311bca6a9a0STomohiro Kusumi 	prev_was_c = false;
13126857f034STomohiro Kusumi 	p = o;
13136857f034STomohiro Kusumi 	n = strlen(p);
13146857f034STomohiro Kusumi 
13156857f034STomohiro Kusumi 	for (i = 0; i < n; i++) {
13166857f034STomohiro Kusumi 		if (p[i] == c) {
1317bca6a9a0STomohiro Kusumi 			assert(!prev_was_c);
1318bca6a9a0STomohiro Kusumi 			prev_was_c = true;
13196857f034STomohiro Kusumi 		} else {
1320bca6a9a0STomohiro Kusumi 			prev_was_c = false;
13216857f034STomohiro Kusumi 		}
13226857f034STomohiro Kusumi 	}
13236857f034STomohiro Kusumi 
13246857f034STomohiro Kusumi 	/* trim leading */
13256857f034STomohiro Kusumi 	if (*p == c)
13266857f034STomohiro Kusumi 		memmove(p, p + 1, strlen(p + 1) + 1);
13276857f034STomohiro Kusumi 	assert(*p != '/');
13286857f034STomohiro Kusumi 
13296857f034STomohiro Kusumi 	/* trim trailing */
13306857f034STomohiro Kusumi 	p += strlen(p);
13316857f034STomohiro Kusumi 	p--;
13326857f034STomohiro Kusumi 	if (*p == c)
13336857f034STomohiro Kusumi 		*p = 0;
13346857f034STomohiro Kusumi 	assert(p[strlen(p) - 1] != '/');
13356857f034STomohiro Kusumi 
13366857f034STomohiro Kusumi 	return 0;
13376857f034STomohiro Kusumi }
13386857f034STomohiro Kusumi 
13396857f034STomohiro Kusumi static int
trim_slash(char * p)13406857f034STomohiro Kusumi trim_slash(char *p)
13416857f034STomohiro Kusumi {
13426857f034STomohiro Kusumi 	return trim_char(p, '/');
13436857f034STomohiro Kusumi }
13446857f034STomohiro Kusumi 
134589a3eb16STomohiro Kusumi static bool
is_supported_link(const char * s)134689a3eb16STomohiro Kusumi is_supported_link(const char *s)
134789a3eb16STomohiro Kusumi {
134889a3eb16STomohiro Kusumi 	/* absolute path can't be supported */
134989a3eb16STomohiro Kusumi 	if (strlen(s) >= 1 && strncmp(s, "/", 1) == 0)
135089a3eb16STomohiro Kusumi 		return false;
135189a3eb16STomohiro Kusumi 
135289a3eb16STomohiro Kusumi 	/* XXX ".." is currently unsupported */
135389a3eb16STomohiro Kusumi 	if (strlen(s) >= 3 && strncmp(s, "../", 3) == 0)
135489a3eb16STomohiro Kusumi 		return false;
135589a3eb16STomohiro Kusumi 
135689a3eb16STomohiro Kusumi 	return true;
135789a3eb16STomohiro Kusumi }
135889a3eb16STomohiro Kusumi 
13596857f034STomohiro Kusumi static int
hammer2_version_get(struct m_vnode * vp)1360f72350acSTomohiro Kusumi hammer2_version_get(struct m_vnode *vp)
1361f72350acSTomohiro Kusumi {
1362f72350acSTomohiro Kusumi 	hammer2_dev_t *hmp;
1363f72350acSTomohiro Kusumi 
1364f72350acSTomohiro Kusumi 	hmp = VTOI(vp)->pmp->pfs_hmps[0];
1365f72350acSTomohiro Kusumi 	if (hmp == NULL)
1366f72350acSTomohiro Kusumi 		return EINVAL;
1367f72350acSTomohiro Kusumi 
1368f72350acSTomohiro Kusumi 	printf("version: %d\n", hmp->voldata.version);
1369f72350acSTomohiro Kusumi 
1370f72350acSTomohiro Kusumi 	return 0;
1371f72350acSTomohiro Kusumi }
1372f72350acSTomohiro Kusumi 
13733999233bSTomohiro Kusumi struct pfs_entry {
13743999233bSTomohiro Kusumi 	TAILQ_ENTRY(pfs_entry) entry;
13753999233bSTomohiro Kusumi 	char name[NAME_MAX+1];
13763999233bSTomohiro Kusumi 	char s[NAME_MAX+1];
13773999233bSTomohiro Kusumi };
13783999233bSTomohiro Kusumi 
13793999233bSTomohiro Kusumi static int
hammer2_pfs_get(struct m_vnode * vp)13803999233bSTomohiro Kusumi hammer2_pfs_get(struct m_vnode *vp)
13813999233bSTomohiro Kusumi {
13823999233bSTomohiro Kusumi 	hammer2_ioc_pfs_t pfs;
13833999233bSTomohiro Kusumi 	TAILQ_HEAD(, pfs_entry) head;
13843999233bSTomohiro Kusumi 	struct pfs_entry *p, *e;
13853999233bSTomohiro Kusumi 	char *pfs_id_str;
13863999233bSTomohiro Kusumi 	const char *type_str;
13873999233bSTomohiro Kusumi 	int error;
13883999233bSTomohiro Kusumi 
13893999233bSTomohiro Kusumi 	bzero(&pfs, sizeof(pfs));
13903999233bSTomohiro Kusumi 	TAILQ_INIT(&head);
13913999233bSTomohiro Kusumi 
13923999233bSTomohiro Kusumi 	while ((pfs.name_key = pfs.name_next) != (hammer2_key_t)-1) {
13933999233bSTomohiro Kusumi 		error = hammer2_ioctl_pfs_get(VTOI(vp), &pfs);
13943999233bSTomohiro Kusumi 		if (error)
13953999233bSTomohiro Kusumi 			return error;
13963999233bSTomohiro Kusumi 
13973999233bSTomohiro Kusumi 		pfs_id_str = NULL;
13983999233bSTomohiro Kusumi 		hammer2_uuid_to_str(&pfs.pfs_clid, &pfs_id_str);
13993999233bSTomohiro Kusumi 
14003999233bSTomohiro Kusumi 		if (pfs.pfs_type == HAMMER2_PFSTYPE_MASTER) {
14013999233bSTomohiro Kusumi 			if (pfs.pfs_subtype == HAMMER2_PFSSUBTYPE_NONE)
14023999233bSTomohiro Kusumi 				type_str = "MASTER";
14033999233bSTomohiro Kusumi 			else
14043999233bSTomohiro Kusumi 				type_str = hammer2_pfssubtype_to_str(
14053999233bSTomohiro Kusumi 				    pfs.pfs_subtype);
14063999233bSTomohiro Kusumi 		} else {
14073999233bSTomohiro Kusumi 			type_str = hammer2_pfstype_to_str(pfs.pfs_type);
14083999233bSTomohiro Kusumi 		}
140906d1ac82STomohiro Kusumi 		e = ecalloc(1, sizeof(*e));
14103999233bSTomohiro Kusumi 		snprintf(e->name, sizeof(e->name), "%s", pfs.name);
14113999233bSTomohiro Kusumi 		snprintf(e->s, sizeof(e->s), "%-11s %s", type_str, pfs_id_str);
14123999233bSTomohiro Kusumi 		free(pfs_id_str);
14133999233bSTomohiro Kusumi 
14143999233bSTomohiro Kusumi 		p = TAILQ_FIRST(&head);
14153999233bSTomohiro Kusumi 		while (p) {
14163999233bSTomohiro Kusumi 			if (strcmp(e->name, p->name) <= 0) {
14173999233bSTomohiro Kusumi 				TAILQ_INSERT_BEFORE(p, e, entry);
14183999233bSTomohiro Kusumi 				break;
14193999233bSTomohiro Kusumi 			}
14203999233bSTomohiro Kusumi 			p = TAILQ_NEXT(p, entry);
14213999233bSTomohiro Kusumi 		}
14223999233bSTomohiro Kusumi 		if (!p)
14233999233bSTomohiro Kusumi 			TAILQ_INSERT_TAIL(&head, e, entry);
14243999233bSTomohiro Kusumi 	}
14253999233bSTomohiro Kusumi 
14263999233bSTomohiro Kusumi 	printf("Type        "
14273999233bSTomohiro Kusumi 	    "ClusterId (pfs_clid)                 "
14283999233bSTomohiro Kusumi 	    "Label\n");
14293999233bSTomohiro Kusumi 	while ((p = TAILQ_FIRST(&head)) != NULL) {
14303999233bSTomohiro Kusumi 		printf("%s %s\n", p->s, p->name);
14313999233bSTomohiro Kusumi 		TAILQ_REMOVE(&head, p, entry);
14323999233bSTomohiro Kusumi 		free(p);
14333999233bSTomohiro Kusumi 	}
14343999233bSTomohiro Kusumi 
14353999233bSTomohiro Kusumi 	return 0;
14363999233bSTomohiro Kusumi }
14373999233bSTomohiro Kusumi 
14383999233bSTomohiro Kusumi static int
hammer2_pfs_lookup(struct m_vnode * vp,const char * pfs_name)14393999233bSTomohiro Kusumi hammer2_pfs_lookup(struct m_vnode *vp, const char *pfs_name)
14403999233bSTomohiro Kusumi {
14413999233bSTomohiro Kusumi 	hammer2_ioc_pfs_t pfs;
14423999233bSTomohiro Kusumi 	char *pfs_id_str;
14433999233bSTomohiro Kusumi 	int error;
14443999233bSTomohiro Kusumi 
14453999233bSTomohiro Kusumi 	bzero(&pfs, sizeof(pfs));
14463999233bSTomohiro Kusumi 	strlcpy(pfs.name, pfs_name, sizeof(pfs.name));
14473999233bSTomohiro Kusumi 
14483999233bSTomohiro Kusumi 	error = hammer2_ioctl_pfs_lookup(VTOI(vp), &pfs);
14493999233bSTomohiro Kusumi 	if (error == 0) {
14503999233bSTomohiro Kusumi 		printf("name: %s\n", pfs.name);
14513999233bSTomohiro Kusumi 		printf("type: %s\n", hammer2_pfstype_to_str(pfs.pfs_type));
14523999233bSTomohiro Kusumi 		printf("subtype: %s\n",
14533999233bSTomohiro Kusumi 		    hammer2_pfssubtype_to_str(pfs.pfs_subtype));
14543999233bSTomohiro Kusumi 
14553999233bSTomohiro Kusumi 		pfs_id_str = NULL;
14563999233bSTomohiro Kusumi 		hammer2_uuid_to_str(&pfs.pfs_fsid, &pfs_id_str);
14573999233bSTomohiro Kusumi 		printf("fsid: %s\n", pfs_id_str);
14583999233bSTomohiro Kusumi 		free(pfs_id_str);
14593999233bSTomohiro Kusumi 
14603999233bSTomohiro Kusumi 		pfs_id_str = NULL;
14613999233bSTomohiro Kusumi 		hammer2_uuid_to_str(&pfs.pfs_clid, &pfs_id_str);
14623999233bSTomohiro Kusumi 		printf("clid: %s\n", pfs_id_str);
14633999233bSTomohiro Kusumi 		free(pfs_id_str);
14643999233bSTomohiro Kusumi 	}
14653999233bSTomohiro Kusumi 
14663999233bSTomohiro Kusumi 	return error;
14673999233bSTomohiro Kusumi }
14683999233bSTomohiro Kusumi 
14693999233bSTomohiro Kusumi static int
hammer2_pfs_create(struct m_vnode * vp,const char * pfs_name)14703999233bSTomohiro Kusumi hammer2_pfs_create(struct m_vnode *vp, const char *pfs_name)
14713999233bSTomohiro Kusumi {
14723999233bSTomohiro Kusumi 	hammer2_ioc_pfs_t pfs;
14733999233bSTomohiro Kusumi 	int error;
14743999233bSTomohiro Kusumi 
14753999233bSTomohiro Kusumi 	bzero(&pfs, sizeof(pfs));
14763999233bSTomohiro Kusumi 	strlcpy(pfs.name, pfs_name, sizeof(pfs.name));
14773999233bSTomohiro Kusumi 	pfs.pfs_type = HAMMER2_PFSTYPE_MASTER;
14783999233bSTomohiro Kusumi 	uuid_create(&pfs.pfs_clid, NULL);
14793999233bSTomohiro Kusumi 	uuid_create(&pfs.pfs_fsid, NULL);
14803999233bSTomohiro Kusumi 
14813999233bSTomohiro Kusumi 	error = hammer2_ioctl_pfs_create(VTOI(vp), &pfs);
14823999233bSTomohiro Kusumi 	if (error == EEXIST)
14833999233bSTomohiro Kusumi 		fprintf(stderr,
14843999233bSTomohiro Kusumi 		    "NOTE: Typically the same name is "
14853999233bSTomohiro Kusumi 		    "used for cluster elements on "
14863999233bSTomohiro Kusumi 		    "different mounts,\n"
14873999233bSTomohiro Kusumi 		    "      but cluster elements on the "
14883999233bSTomohiro Kusumi 		    "same mount require unique names.\n"
14893999233bSTomohiro Kusumi 		    "hammer2: pfs_create(%s): already present\n",
14903999233bSTomohiro Kusumi 		    pfs_name);
14913999233bSTomohiro Kusumi 
14923999233bSTomohiro Kusumi 	return error;
14933999233bSTomohiro Kusumi }
14943999233bSTomohiro Kusumi 
14953999233bSTomohiro Kusumi static int
hammer2_pfs_delete(struct m_vnode * vp,const char * pfs_name)14963999233bSTomohiro Kusumi hammer2_pfs_delete(struct m_vnode *vp, const char *pfs_name)
14973999233bSTomohiro Kusumi {
14983999233bSTomohiro Kusumi 	hammer2_ioc_pfs_t pfs;
14993999233bSTomohiro Kusumi 
15003999233bSTomohiro Kusumi 	bzero(&pfs, sizeof(pfs));
15013999233bSTomohiro Kusumi 	strlcpy(pfs.name, pfs_name, sizeof(pfs.name));
15023999233bSTomohiro Kusumi 
15033999233bSTomohiro Kusumi 	return hammer2_ioctl_pfs_delete(VTOI(vp), &pfs);
15043999233bSTomohiro Kusumi }
15053999233bSTomohiro Kusumi 
15063999233bSTomohiro Kusumi static int
hammer2_pfs_snapshot(struct m_vnode * vp,const char * pfs_name,const char * mount_label)15073999233bSTomohiro Kusumi hammer2_pfs_snapshot(struct m_vnode *vp, const char *pfs_name,
15083999233bSTomohiro Kusumi     const char *mount_label)
15093999233bSTomohiro Kusumi {
15103999233bSTomohiro Kusumi 	hammer2_ioc_pfs_t pfs;
15113999233bSTomohiro Kusumi 	struct tm *tp;
15123999233bSTomohiro Kusumi 	time_t t;
15133999233bSTomohiro Kusumi 
15143999233bSTomohiro Kusumi 	bzero(&pfs, sizeof(pfs));
15153999233bSTomohiro Kusumi 	strlcpy(pfs.name, pfs_name, sizeof(pfs.name));
15163999233bSTomohiro Kusumi 
15173999233bSTomohiro Kusumi 	if (strlen(pfs.name) == 0) {
15183999233bSTomohiro Kusumi 		time(&t);
15193999233bSTomohiro Kusumi 		tp = localtime(&t);
15203999233bSTomohiro Kusumi 		snprintf(pfs.name, sizeof(pfs.name),
15213999233bSTomohiro Kusumi 		    "%s.%04d%02d%02d.%02d%02d%02d",
15223999233bSTomohiro Kusumi 		    mount_label,
15233999233bSTomohiro Kusumi 		    tp->tm_year + 1900,
15243999233bSTomohiro Kusumi 		    tp->tm_mon + 1,
15253999233bSTomohiro Kusumi 		    tp->tm_mday,
15263999233bSTomohiro Kusumi 		    tp->tm_hour,
15273999233bSTomohiro Kusumi 		    tp->tm_min,
15283999233bSTomohiro Kusumi 		    tp->tm_sec);
15293999233bSTomohiro Kusumi 	}
15303999233bSTomohiro Kusumi 
15313999233bSTomohiro Kusumi 	return hammer2_ioctl_pfs_snapshot(VTOI(vp), &pfs);
15323999233bSTomohiro Kusumi }
15333999233bSTomohiro Kusumi 
15343999233bSTomohiro Kusumi static int
hammer2_inode_getx(struct m_vnode * dvp,const char * f)1535f804c425STomohiro Kusumi hammer2_inode_getx(struct m_vnode *dvp, const char *f)
1536f804c425STomohiro Kusumi {
1537f804c425STomohiro Kusumi 	hammer2_ioc_inode_t inode;
1538f804c425STomohiro Kusumi 	hammer2_inode_t *ip;
1539f804c425STomohiro Kusumi 	hammer2_inode_meta_t *meta;
1540f804c425STomohiro Kusumi 	struct m_vnode *vp;
1541f804c425STomohiro Kusumi 	char *o, *p, *name, *str = NULL;
154289a3eb16STomohiro Kusumi 	char tmp[PATH_MAX];
1543f804c425STomohiro Kusumi 	int error;
1544f804c425STomohiro Kusumi 	uuid_t uuid;
1545f804c425STomohiro Kusumi 
1546f804c425STomohiro Kusumi 	assert(strlen(f) > 0);
15476857f034STomohiro Kusumi 	o = p = name = strdup(f);
1548f804c425STomohiro Kusumi 
15496857f034STomohiro Kusumi 	error = trim_slash(p);
15506857f034STomohiro Kusumi 	if (error)
15516857f034STomohiro Kusumi 		return error;
15525e8b0eb7STomohiro Kusumi 	if (strlen(p) == 0) {
15535e8b0eb7STomohiro Kusumi 		vp = dvp;
15545e8b0eb7STomohiro Kusumi 		goto start_ioctl;
15555e8b0eb7STomohiro Kusumi 	}
1556f804c425STomohiro Kusumi 
1557f804c425STomohiro Kusumi 	while ((p = strchr(p, '/')) != NULL) {
1558f804c425STomohiro Kusumi 		*p++ = 0; /* NULL terminate name */
155989a3eb16STomohiro Kusumi 		if (!strcmp(name, ".")) {
156089a3eb16STomohiro Kusumi 			name = p;
156189a3eb16STomohiro Kusumi 			continue;
156289a3eb16STomohiro Kusumi 		}
1563f804c425STomohiro Kusumi 		vp = NULL;
1564f804c425STomohiro Kusumi 		error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1565f804c425STomohiro Kusumi 		if (error)
1566f804c425STomohiro Kusumi 			return error;
1567f804c425STomohiro Kusumi 
1568f804c425STomohiro Kusumi 		ip = VTOI(vp);
156989a3eb16STomohiro Kusumi 		switch (ip->meta.type) {
157089a3eb16STomohiro Kusumi 		case HAMMER2_OBJTYPE_DIRECTORY:
157189a3eb16STomohiro Kusumi 			break;
157289a3eb16STomohiro Kusumi 		case HAMMER2_OBJTYPE_SOFTLINK:
157389a3eb16STomohiro Kusumi 			bzero(tmp, sizeof(tmp));
157489a3eb16STomohiro Kusumi 			error = hammer2_readlink(vp, tmp, sizeof(tmp));
157589a3eb16STomohiro Kusumi 			if (error)
157689a3eb16STomohiro Kusumi 				return error;
157789a3eb16STomohiro Kusumi 			if (!is_supported_link(tmp))
157889a3eb16STomohiro Kusumi 				return EINVAL;
157989a3eb16STomohiro Kusumi 			strlcat(tmp, "/", sizeof(tmp));
158089a3eb16STomohiro Kusumi 			strlcat(tmp, p, sizeof(tmp));
158189a3eb16STomohiro Kusumi 			error = trim_slash(tmp);
158289a3eb16STomohiro Kusumi 			if (error)
158389a3eb16STomohiro Kusumi 				return error;
158489a3eb16STomohiro Kusumi 			p = name = tmp;
158589a3eb16STomohiro Kusumi 			continue;
158689a3eb16STomohiro Kusumi 		default:
158789a3eb16STomohiro Kusumi 			return EINVAL;
158889a3eb16STomohiro Kusumi 		}
1589f804c425STomohiro Kusumi 
1590f804c425STomohiro Kusumi 		dvp = vp;
1591f804c425STomohiro Kusumi 		name = p;
1592f804c425STomohiro Kusumi 	}
1593f804c425STomohiro Kusumi 
1594f804c425STomohiro Kusumi 	error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1595f804c425STomohiro Kusumi 	if (error)
1596f804c425STomohiro Kusumi 		return error;
15975e8b0eb7STomohiro Kusumi start_ioctl:
1598f804c425STomohiro Kusumi 	bzero(&inode, sizeof(inode));
1599f804c425STomohiro Kusumi 	error = hammer2_ioctl_inode_get(VTOI(vp), &inode);
1600f804c425STomohiro Kusumi 	if (error)
1601f804c425STomohiro Kusumi 		return error;
1602f804c425STomohiro Kusumi 
1603f804c425STomohiro Kusumi 	meta = &inode.ip_data.meta;
1604f804c425STomohiro Kusumi 	printf("--------------------\n");
1605f804c425STomohiro Kusumi 	printf("flags = 0x%x\n", inode.flags);
1606f804c425STomohiro Kusumi 	printf("data_count = %ju\n", (uintmax_t)inode.data_count);
1607f804c425STomohiro Kusumi 	printf("inode_count = %ju\n", (uintmax_t)inode.inode_count);
1608f804c425STomohiro Kusumi 	printf("--------------------\n");
1609f804c425STomohiro Kusumi 	printf("version = %u\n", meta->version);
1610f804c425STomohiro Kusumi 	printf("pfs_subtype = %u (%s)\n", meta->pfs_subtype,
1611f804c425STomohiro Kusumi 	    hammer2_pfssubtype_to_str(meta->pfs_subtype));
1612f804c425STomohiro Kusumi 	printf("uflags = 0x%x\n", (unsigned int)meta->uflags);
1613f804c425STomohiro Kusumi 	printf("rmajor = %u\n", meta->rmajor);
1614f804c425STomohiro Kusumi 	printf("rminor = %u\n", meta->rminor);
1615f804c425STomohiro Kusumi 	printf("ctime = %s\n", hammer2_time64_to_str(meta->ctime, &str));
1616f804c425STomohiro Kusumi 	printf("mtime = %s\n", hammer2_time64_to_str(meta->mtime, &str));
1617f804c425STomohiro Kusumi 	printf("atime = %s\n", hammer2_time64_to_str(meta->atime, &str));
1618f804c425STomohiro Kusumi 	printf("btime = %s\n", hammer2_time64_to_str(meta->btime, &str));
1619f804c425STomohiro Kusumi 	uuid = meta->uid;
1620f804c425STomohiro Kusumi 	printf("uid = %s\n", hammer2_uuid_to_str(&uuid, &str));
1621f804c425STomohiro Kusumi 	uuid = meta->gid;
1622f804c425STomohiro Kusumi 	printf("gid = %s\n", hammer2_uuid_to_str(&uuid, &str));
1623f804c425STomohiro Kusumi 	printf("type = %u (%s)\n", meta->type,
1624f804c425STomohiro Kusumi 	    hammer2_iptype_to_str(meta->type));
1625f804c425STomohiro Kusumi 	printf("op_flags = 0x%x\n", meta->op_flags);
1626f804c425STomohiro Kusumi 	printf("cap_flags = 0x%x\n", meta->cap_flags);
1627f804c425STomohiro Kusumi 	printf("mode = 0%o\n", meta->mode);
1628f804c425STomohiro Kusumi 	printf("inum = 0x%jx\n", (uintmax_t)meta->inum);
1629f804c425STomohiro Kusumi 	printf("size = %ju\n", (uintmax_t)meta->size);
1630f804c425STomohiro Kusumi 	printf("nlinks = %ju\n", (uintmax_t)meta->nlinks);
1631f804c425STomohiro Kusumi 	printf("iparent = 0x%jx\n", (uintmax_t)meta->iparent);
1632f804c425STomohiro Kusumi 	printf("name_key = 0x%jx\n", (uintmax_t)meta->name_key);
1633f804c425STomohiro Kusumi 	printf("name_len = %u\n", meta->name_len);
1634f804c425STomohiro Kusumi 	printf("ncopies = %u\n", meta->ncopies);
163593bffd46STomohiro Kusumi 	printf("comp_algo = 0x%jx\n", (uintmax_t)meta->comp_algo);
1636f804c425STomohiro Kusumi 	printf("target_type = %u\n", meta->target_type);
1637f804c425STomohiro Kusumi 	printf("check_algo = %u\n", meta->check_algo);
1638f804c425STomohiro Kusumi 	printf("pfs_nmasters = %u\n", meta->pfs_nmasters);
1639f804c425STomohiro Kusumi 	printf("pfs_type = %u (%s)\n", meta->pfs_type,
1640f804c425STomohiro Kusumi 	    hammer2_pfstype_to_str(meta->pfs_type));
1641f804c425STomohiro Kusumi 	printf("pfs_inum = 0x%jx\n", (uintmax_t)meta->pfs_inum);
1642f804c425STomohiro Kusumi 	uuid = meta->pfs_clid;
1643f804c425STomohiro Kusumi 	printf("pfs_clid = %s\n", hammer2_uuid_to_str(&uuid, &str));
1644f804c425STomohiro Kusumi 	uuid = meta->pfs_fsid;
1645f804c425STomohiro Kusumi 	printf("pfs_fsid = %s\n", hammer2_uuid_to_str(&uuid, &str));
1646f804c425STomohiro Kusumi 	printf("data_quota = 0x%jx\n", (uintmax_t)meta->data_quota);
1647f804c425STomohiro Kusumi 	printf("inode_quota = 0x%jx\n", (uintmax_t)meta->inode_quota);
1648f804c425STomohiro Kusumi 	printf("pfs_lsnap_tid = 0x%jx\n", (uintmax_t)meta->pfs_lsnap_tid);
1649f804c425STomohiro Kusumi 	printf("decrypt_check = 0x%jx\n", (uintmax_t)meta->decrypt_check);
1650f804c425STomohiro Kusumi 	printf("--------------------\n");
1651f804c425STomohiro Kusumi 
1652f804c425STomohiro Kusumi 	free(o);
1653f804c425STomohiro Kusumi 
1654f804c425STomohiro Kusumi 	return error;
1655f804c425STomohiro Kusumi }
1656f804c425STomohiro Kusumi 
1657f804c425STomohiro Kusumi static int
hammer2_inode_setcheck(struct m_vnode * dvp,const char * f)1658465e1141STomohiro Kusumi hammer2_inode_setcheck(struct m_vnode *dvp, const char *f)
1659465e1141STomohiro Kusumi {
1660465e1141STomohiro Kusumi 	hammer2_ioc_inode_t inode;
1661465e1141STomohiro Kusumi 	hammer2_inode_t *ip;
1662465e1141STomohiro Kusumi 	struct m_vnode *vp;
1663465e1141STomohiro Kusumi 	char *o, *p, *name, *check_algo_str;
166489a3eb16STomohiro Kusumi 	char tmp[PATH_MAX];
1665465e1141STomohiro Kusumi 	const char *checks[] = { "none", "disabled", "crc32", "xxhash64",
1666465e1141STomohiro Kusumi 	    "sha192", };
1667465e1141STomohiro Kusumi 	int check_algo_idx, error;
1668465e1141STomohiro Kusumi 	uint8_t check_algo;
1669465e1141STomohiro Kusumi 
1670465e1141STomohiro Kusumi 	assert(strlen(f) > 0);
1671465e1141STomohiro Kusumi 	o = p = strdup(f);
1672465e1141STomohiro Kusumi 
1673465e1141STomohiro Kusumi 	p = strrchr(p, ':');
1674465e1141STomohiro Kusumi 	if (p == NULL)
1675465e1141STomohiro Kusumi 		return EINVAL;
1676465e1141STomohiro Kusumi 
1677465e1141STomohiro Kusumi 	*p++ = 0; /* NULL terminate path */
1678465e1141STomohiro Kusumi 	check_algo_str = p;
1679465e1141STomohiro Kusumi 	name = p = o;
1680465e1141STomohiro Kusumi 
168193bffd46STomohiro Kusumi 	/* fail if already empty before trim */
168293bffd46STomohiro Kusumi 	if (strlen(p) == 0)
168393bffd46STomohiro Kusumi 		return EINVAL;
168493bffd46STomohiro Kusumi 
16856857f034STomohiro Kusumi 	error = trim_slash(p);
16866857f034STomohiro Kusumi 	if (error)
16876857f034STomohiro Kusumi 		return error;
168893bffd46STomohiro Kusumi 	if (strlen(check_algo_str) == 0)
1689465e1141STomohiro Kusumi 		return EINVAL;
1690465e1141STomohiro Kusumi 
1691465e1141STomohiro Kusumi 	/* convert check_algo_str to check_algo_idx */
16926857f034STomohiro Kusumi 	check_algo_idx = nitems(checks);
1693465e1141STomohiro Kusumi 	while (--check_algo_idx >= 0)
1694465e1141STomohiro Kusumi 		if (strcasecmp(check_algo_str, checks[check_algo_idx]) == 0)
1695465e1141STomohiro Kusumi 			break;
1696465e1141STomohiro Kusumi 	if (check_algo_idx < 0) {
1697465e1141STomohiro Kusumi 		if (strcasecmp(check_algo_str, "default") == 0) {
1698465e1141STomohiro Kusumi 			check_algo_str = "xxhash64";
1699465e1141STomohiro Kusumi 			check_algo_idx = HAMMER2_CHECK_XXHASH64;
1700465e1141STomohiro Kusumi 		} else if (strcasecmp(check_algo_str, "disabled") == 0) {
1701465e1141STomohiro Kusumi 			check_algo_str = "disabled";
1702465e1141STomohiro Kusumi 			check_algo_idx = HAMMER2_CHECK_DISABLED;
1703465e1141STomohiro Kusumi 		} else {
1704465e1141STomohiro Kusumi 			printf("invalid check_algo_str: %s\n", check_algo_str);
1705465e1141STomohiro Kusumi 			return EINVAL;
1706465e1141STomohiro Kusumi 		}
1707465e1141STomohiro Kusumi 	}
1708465e1141STomohiro Kusumi 	check_algo = HAMMER2_ENC_ALGO(check_algo_idx);
1709465e1141STomohiro Kusumi 	printf("change %s to algo %d (%s)\n", p, check_algo, check_algo_str);
1710465e1141STomohiro Kusumi 
171193bffd46STomohiro Kusumi 	if (strlen(p) == 0) {
171293bffd46STomohiro Kusumi 		vp = dvp;
171393bffd46STomohiro Kusumi 		goto start_ioctl;
171493bffd46STomohiro Kusumi 	}
171593bffd46STomohiro Kusumi 
1716465e1141STomohiro Kusumi 	while ((p = strchr(p, '/')) != NULL) {
1717465e1141STomohiro Kusumi 		*p++ = 0; /* NULL terminate name */
171889a3eb16STomohiro Kusumi 		if (!strcmp(name, ".")) {
171989a3eb16STomohiro Kusumi 			name = p;
172089a3eb16STomohiro Kusumi 			continue;
172189a3eb16STomohiro Kusumi 		}
1722465e1141STomohiro Kusumi 		vp = NULL;
1723465e1141STomohiro Kusumi 		error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1724465e1141STomohiro Kusumi 		if (error)
1725465e1141STomohiro Kusumi 			return error;
1726465e1141STomohiro Kusumi 
1727465e1141STomohiro Kusumi 		ip = VTOI(vp);
172889a3eb16STomohiro Kusumi 		switch (ip->meta.type) {
172989a3eb16STomohiro Kusumi 		case HAMMER2_OBJTYPE_DIRECTORY:
173089a3eb16STomohiro Kusumi 			break;
173189a3eb16STomohiro Kusumi 		case HAMMER2_OBJTYPE_SOFTLINK:
173289a3eb16STomohiro Kusumi 			bzero(tmp, sizeof(tmp));
173389a3eb16STomohiro Kusumi 			error = hammer2_readlink(vp, tmp, sizeof(tmp));
173489a3eb16STomohiro Kusumi 			if (error)
173589a3eb16STomohiro Kusumi 				return error;
173689a3eb16STomohiro Kusumi 			if (!is_supported_link(tmp))
173789a3eb16STomohiro Kusumi 				return EINVAL;
173889a3eb16STomohiro Kusumi 			strlcat(tmp, "/", sizeof(tmp));
173989a3eb16STomohiro Kusumi 			strlcat(tmp, p, sizeof(tmp));
174089a3eb16STomohiro Kusumi 			error = trim_slash(tmp);
174189a3eb16STomohiro Kusumi 			if (error)
174289a3eb16STomohiro Kusumi 				return error;
174389a3eb16STomohiro Kusumi 			p = name = tmp;
174489a3eb16STomohiro Kusumi 			continue;
174589a3eb16STomohiro Kusumi 		default:
174689a3eb16STomohiro Kusumi 			return EINVAL;
174789a3eb16STomohiro Kusumi 		}
1748465e1141STomohiro Kusumi 
1749465e1141STomohiro Kusumi 		dvp = vp;
1750465e1141STomohiro Kusumi 		name = p;
1751465e1141STomohiro Kusumi 	}
1752465e1141STomohiro Kusumi 
1753465e1141STomohiro Kusumi 	error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1754465e1141STomohiro Kusumi 	if (error)
1755465e1141STomohiro Kusumi 		return error;
175693bffd46STomohiro Kusumi start_ioctl:
1757465e1141STomohiro Kusumi 	ip = VTOI(vp);
1758465e1141STomohiro Kusumi 
1759465e1141STomohiro Kusumi 	bzero(&inode, sizeof(inode));
1760465e1141STomohiro Kusumi 	error = hammer2_ioctl_inode_get(ip, &inode);
1761465e1141STomohiro Kusumi 	if (error)
1762465e1141STomohiro Kusumi 		return error;
1763465e1141STomohiro Kusumi 
1764465e1141STomohiro Kusumi 	inode.flags |= HAMMER2IOC_INODE_FLAG_CHECK;
1765465e1141STomohiro Kusumi 	inode.ip_data.meta.check_algo = check_algo;
1766465e1141STomohiro Kusumi 	error = hammer2_ioctl_inode_set(ip, &inode);
1767465e1141STomohiro Kusumi 	if (error)
1768465e1141STomohiro Kusumi 		return error;
1769465e1141STomohiro Kusumi 
1770465e1141STomohiro Kusumi 	free(o);
1771465e1141STomohiro Kusumi 
1772465e1141STomohiro Kusumi 	return error;
1773465e1141STomohiro Kusumi }
1774465e1141STomohiro Kusumi 
1775465e1141STomohiro Kusumi static int
hammer2_inode_setcomp(struct m_vnode * dvp,const char * f)1776465e1141STomohiro Kusumi hammer2_inode_setcomp(struct m_vnode *dvp, const char *f)
1777465e1141STomohiro Kusumi {
1778465e1141STomohiro Kusumi 	hammer2_ioc_inode_t inode;
1779465e1141STomohiro Kusumi 	hammer2_inode_t *ip;
1780465e1141STomohiro Kusumi 	struct m_vnode *vp;
1781465e1141STomohiro Kusumi 	char *o, *p, *name, *comp_algo_str, *comp_level_str;
178289a3eb16STomohiro Kusumi 	char tmp[PATH_MAX];
1783465e1141STomohiro Kusumi 	const char *comps[] = { "none", "autozero", "lz4", "zlib", };
1784465e1141STomohiro Kusumi 	int comp_algo_idx, comp_level_idx, error;
1785465e1141STomohiro Kusumi 	uint8_t comp_algo, comp_level;
1786465e1141STomohiro Kusumi 
1787465e1141STomohiro Kusumi 	assert(strlen(f) > 0);
1788465e1141STomohiro Kusumi 	o = p = strdup(f);
1789465e1141STomohiro Kusumi 
1790465e1141STomohiro Kusumi 	p = strrchr(p, ':');
1791465e1141STomohiro Kusumi 	if (p == NULL)
1792465e1141STomohiro Kusumi 		return EINVAL;
1793465e1141STomohiro Kusumi 
1794465e1141STomohiro Kusumi 	*p++ = 0; /* NULL terminate comp_algo_str */
1795465e1141STomohiro Kusumi 	comp_level_str = p;
1796465e1141STomohiro Kusumi 	p = o;
1797465e1141STomohiro Kusumi 
1798465e1141STomohiro Kusumi 	p = strrchr(p, ':');
1799465e1141STomohiro Kusumi 	if (p == NULL) {
1800465e1141STomohiro Kusumi 		/* comp_level_str not specified */
1801465e1141STomohiro Kusumi 		comp_algo_str = comp_level_str;
1802465e1141STomohiro Kusumi 		comp_level_str = NULL;
1803465e1141STomohiro Kusumi 	} else {
1804465e1141STomohiro Kusumi 		*p++ = 0; /* NULL terminate path */
1805465e1141STomohiro Kusumi 		comp_algo_str = p;
1806465e1141STomohiro Kusumi 	}
1807465e1141STomohiro Kusumi 	name = p = o;
1808465e1141STomohiro Kusumi 
180993bffd46STomohiro Kusumi 	/* fail if already empty before trim */
181093bffd46STomohiro Kusumi 	if (strlen(p) == 0)
181193bffd46STomohiro Kusumi 		return EINVAL;
181293bffd46STomohiro Kusumi 
18136857f034STomohiro Kusumi 	error = trim_slash(p);
18146857f034STomohiro Kusumi 	if (error)
18156857f034STomohiro Kusumi 		return error;
181693bffd46STomohiro Kusumi 	if (strlen(comp_algo_str) == 0)
1817465e1141STomohiro Kusumi 		return EINVAL;
1818465e1141STomohiro Kusumi 
1819465e1141STomohiro Kusumi 	/* convert comp_algo_str to comp_algo_idx */
18206857f034STomohiro Kusumi 	comp_algo_idx = nitems(comps);
1821465e1141STomohiro Kusumi 	while (--comp_algo_idx >= 0)
1822465e1141STomohiro Kusumi 		if (strcasecmp(comp_algo_str, comps[comp_algo_idx]) == 0)
1823465e1141STomohiro Kusumi 			break;
1824465e1141STomohiro Kusumi 	if (comp_algo_idx < 0) {
1825465e1141STomohiro Kusumi 		if (strcasecmp(comp_algo_str, "default") == 0) {
1826465e1141STomohiro Kusumi 			comp_algo_str = "lz4";
1827465e1141STomohiro Kusumi 			comp_algo_idx = HAMMER2_COMP_LZ4;
1828465e1141STomohiro Kusumi 		} else if (strcasecmp(comp_algo_str, "disabled") == 0) {
1829465e1141STomohiro Kusumi 			comp_algo_str = "autozero";
1830465e1141STomohiro Kusumi 			comp_algo_idx = HAMMER2_COMP_AUTOZERO;
1831465e1141STomohiro Kusumi 		} else {
1832465e1141STomohiro Kusumi 			printf("invalid comp_algo_str: %s\n", comp_algo_str);
1833465e1141STomohiro Kusumi 			return EINVAL;
1834465e1141STomohiro Kusumi 		}
1835465e1141STomohiro Kusumi 	}
1836465e1141STomohiro Kusumi 	comp_algo = HAMMER2_ENC_ALGO(comp_algo_idx);
1837465e1141STomohiro Kusumi 
1838465e1141STomohiro Kusumi 	/* convert comp_level_str to comp_level_idx */
1839465e1141STomohiro Kusumi 	if (comp_level_str == NULL) {
1840465e1141STomohiro Kusumi 		comp_level_idx = 0;
1841bca6a9a0STomohiro Kusumi 	} else if (isdigit((int)comp_level_str[0])) {
1842465e1141STomohiro Kusumi 		comp_level_idx = strtol(comp_level_str, NULL, 0);
1843465e1141STomohiro Kusumi 	} else if (strcasecmp(comp_level_str, "default") == 0) {
1844465e1141STomohiro Kusumi 		comp_level_idx = 0;
1845465e1141STomohiro Kusumi 	} else {
1846465e1141STomohiro Kusumi 		printf("invalid comp_level_str: %s\n", comp_level_str);
1847465e1141STomohiro Kusumi 		return EINVAL;
1848465e1141STomohiro Kusumi 	}
1849465e1141STomohiro Kusumi 	if (comp_level_idx) {
1850465e1141STomohiro Kusumi 		switch (comp_algo) {
1851465e1141STomohiro Kusumi 		case HAMMER2_COMP_ZLIB:
1852465e1141STomohiro Kusumi 			if (comp_level_idx < 6 || comp_level_idx > 9) {
1853465e1141STomohiro Kusumi 				printf("unsupported comp_level %d for %s\n",
1854465e1141STomohiro Kusumi 				    comp_level_idx, comp_algo_str);
1855465e1141STomohiro Kusumi 				return EINVAL;
1856465e1141STomohiro Kusumi 			}
1857465e1141STomohiro Kusumi 			break;
1858465e1141STomohiro Kusumi 		default:
1859465e1141STomohiro Kusumi 			printf("unsupported comp_level %d for %s\n",
1860465e1141STomohiro Kusumi 			    comp_level_idx, comp_algo_str);
1861465e1141STomohiro Kusumi 			return EINVAL;
1862465e1141STomohiro Kusumi 		}
1863465e1141STomohiro Kusumi 	}
1864465e1141STomohiro Kusumi 	comp_level = HAMMER2_ENC_LEVEL(comp_level_idx);
1865465e1141STomohiro Kusumi 	printf("change %s to algo %d (%s) level %d\n",
1866465e1141STomohiro Kusumi 	    p, comp_algo, comp_algo_str, comp_level_idx);
1867465e1141STomohiro Kusumi 
186893bffd46STomohiro Kusumi 	if (strlen(p) == 0) {
186993bffd46STomohiro Kusumi 		vp = dvp;
187093bffd46STomohiro Kusumi 		goto start_ioctl;
187193bffd46STomohiro Kusumi 	}
187293bffd46STomohiro Kusumi 
1873465e1141STomohiro Kusumi 	while ((p = strchr(p, '/')) != NULL) {
1874465e1141STomohiro Kusumi 		*p++ = 0; /* NULL terminate name */
187589a3eb16STomohiro Kusumi 		if (!strcmp(name, ".")) {
187689a3eb16STomohiro Kusumi 			name = p;
187789a3eb16STomohiro Kusumi 			continue;
187889a3eb16STomohiro Kusumi 		}
1879465e1141STomohiro Kusumi 		vp = NULL;
1880465e1141STomohiro Kusumi 		error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1881465e1141STomohiro Kusumi 		if (error)
1882465e1141STomohiro Kusumi 			return error;
1883465e1141STomohiro Kusumi 
1884465e1141STomohiro Kusumi 		ip = VTOI(vp);
188589a3eb16STomohiro Kusumi 		switch (ip->meta.type) {
188689a3eb16STomohiro Kusumi 		case HAMMER2_OBJTYPE_DIRECTORY:
188789a3eb16STomohiro Kusumi 			break;
188889a3eb16STomohiro Kusumi 		case HAMMER2_OBJTYPE_SOFTLINK:
188989a3eb16STomohiro Kusumi 			bzero(tmp, sizeof(tmp));
189089a3eb16STomohiro Kusumi 			error = hammer2_readlink(vp, tmp, sizeof(tmp));
189189a3eb16STomohiro Kusumi 			if (error)
189289a3eb16STomohiro Kusumi 				return error;
189389a3eb16STomohiro Kusumi 			if (!is_supported_link(tmp))
189489a3eb16STomohiro Kusumi 				return EINVAL;
189589a3eb16STomohiro Kusumi 			strlcat(tmp, "/", sizeof(tmp));
189689a3eb16STomohiro Kusumi 			strlcat(tmp, p, sizeof(tmp));
189789a3eb16STomohiro Kusumi 			error = trim_slash(tmp);
189889a3eb16STomohiro Kusumi 			if (error)
189989a3eb16STomohiro Kusumi 				return error;
190089a3eb16STomohiro Kusumi 			p = name = tmp;
190189a3eb16STomohiro Kusumi 			continue;
190289a3eb16STomohiro Kusumi 		default:
190389a3eb16STomohiro Kusumi 			return EINVAL;
190489a3eb16STomohiro Kusumi 		}
1905465e1141STomohiro Kusumi 
1906465e1141STomohiro Kusumi 		dvp = vp;
1907465e1141STomohiro Kusumi 		name = p;
1908465e1141STomohiro Kusumi 	}
1909465e1141STomohiro Kusumi 
1910465e1141STomohiro Kusumi 	error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1911465e1141STomohiro Kusumi 	if (error)
1912465e1141STomohiro Kusumi 		return error;
191393bffd46STomohiro Kusumi start_ioctl:
1914465e1141STomohiro Kusumi 	ip = VTOI(vp);
1915465e1141STomohiro Kusumi 
1916465e1141STomohiro Kusumi 	bzero(&inode, sizeof(inode));
1917465e1141STomohiro Kusumi 	error = hammer2_ioctl_inode_get(ip, &inode);
1918465e1141STomohiro Kusumi 	if (error)
1919465e1141STomohiro Kusumi 		return error;
1920465e1141STomohiro Kusumi 
1921465e1141STomohiro Kusumi 	inode.flags |= HAMMER2IOC_INODE_FLAG_COMP;
1922465e1141STomohiro Kusumi 	inode.ip_data.meta.comp_algo = comp_algo | comp_level;
1923465e1141STomohiro Kusumi 	error = hammer2_ioctl_inode_set(ip, &inode);
1924465e1141STomohiro Kusumi 	if (error)
1925465e1141STomohiro Kusumi 		return error;
1926465e1141STomohiro Kusumi 
1927465e1141STomohiro Kusumi 	free(o);
1928465e1141STomohiro Kusumi 
1929465e1141STomohiro Kusumi 	return error;
1930465e1141STomohiro Kusumi }
1931465e1141STomohiro Kusumi 
1932465e1141STomohiro Kusumi static int
hammer2_bulkfree(struct m_vnode * vp)1933a63188c8STomohiro Kusumi hammer2_bulkfree(struct m_vnode *vp)
1934a63188c8STomohiro Kusumi {
1935a63188c8STomohiro Kusumi 	hammer2_ioc_bulkfree_t bfi;
1936a63188c8STomohiro Kusumi 	size_t usermem;
1937a63188c8STomohiro Kusumi 	size_t usermem_size = sizeof(usermem);
1938a63188c8STomohiro Kusumi 
1939a63188c8STomohiro Kusumi 	bzero(&bfi, sizeof(bfi));
1940a63188c8STomohiro Kusumi 	usermem = 0;
1941a63188c8STomohiro Kusumi 	if (sysctlbyname("hw.usermem", &usermem, &usermem_size, NULL, 0) == 0)
1942a63188c8STomohiro Kusumi 		bfi.size = usermem / 16;
1943a63188c8STomohiro Kusumi 	else
1944a63188c8STomohiro Kusumi 		bfi.size = 0;
1945a63188c8STomohiro Kusumi 	if (bfi.size < 8192 * 1024)
1946a63188c8STomohiro Kusumi 		bfi.size = 8192 * 1024;
1947a63188c8STomohiro Kusumi 
1948a63188c8STomohiro Kusumi 	return hammer2_ioctl_bulkfree_scan(VTOI(vp), &bfi);
1949a63188c8STomohiro Kusumi }
1950afa5234bSTomohiro Kusumi 
1951afa5234bSTomohiro Kusumi static int
hammer2_destroy_path(struct m_vnode * dvp,const char * f)1952ac97ce60STomohiro Kusumi hammer2_destroy_path(struct m_vnode *dvp, const char *f)
1953917508cdSTomohiro Kusumi {
1954917508cdSTomohiro Kusumi 	hammer2_ioc_destroy_t destroy;
1955ac97ce60STomohiro Kusumi 	hammer2_inode_t *ip;
1956ac97ce60STomohiro Kusumi 	struct m_vnode *vp;
1957ac97ce60STomohiro Kusumi 	char *o, *p, *name;
195889a3eb16STomohiro Kusumi 	char tmp[PATH_MAX];
1959b4807901STomohiro Kusumi 	int error;
1960917508cdSTomohiro Kusumi 
196106d1ac82STomohiro Kusumi 	assert(strlen(f) > 0);
19626857f034STomohiro Kusumi 	o = p = name = strdup(f);
1963ac97ce60STomohiro Kusumi 
19646857f034STomohiro Kusumi 	error = trim_slash(p);
19656857f034STomohiro Kusumi 	if (error)
19666857f034STomohiro Kusumi 		return error;
1967ac97ce60STomohiro Kusumi 	if (strlen(p) == 0)
1968ac97ce60STomohiro Kusumi 		return EINVAL;
1969ac97ce60STomohiro Kusumi 
1970ac97ce60STomohiro Kusumi 	while ((p = strchr(p, '/')) != NULL) {
1971ac97ce60STomohiro Kusumi 		*p++ = 0; /* NULL terminate name */
197289a3eb16STomohiro Kusumi 		if (!strcmp(name, ".")) {
197389a3eb16STomohiro Kusumi 			name = p;
197489a3eb16STomohiro Kusumi 			continue;
197589a3eb16STomohiro Kusumi 		}
1976ac97ce60STomohiro Kusumi 		vp = NULL;
1977ac97ce60STomohiro Kusumi 		error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1978ac97ce60STomohiro Kusumi 		if (error)
1979ac97ce60STomohiro Kusumi 			return error;
1980ac97ce60STomohiro Kusumi 
1981ac97ce60STomohiro Kusumi 		ip = VTOI(vp);
198289a3eb16STomohiro Kusumi 		switch (ip->meta.type) {
198389a3eb16STomohiro Kusumi 		case HAMMER2_OBJTYPE_DIRECTORY:
198489a3eb16STomohiro Kusumi 			break;
198589a3eb16STomohiro Kusumi 		case HAMMER2_OBJTYPE_SOFTLINK:
198689a3eb16STomohiro Kusumi 			bzero(tmp, sizeof(tmp));
198789a3eb16STomohiro Kusumi 			error = hammer2_readlink(vp, tmp, sizeof(tmp));
198889a3eb16STomohiro Kusumi 			if (error)
198989a3eb16STomohiro Kusumi 				return error;
199089a3eb16STomohiro Kusumi 			if (!is_supported_link(tmp))
199189a3eb16STomohiro Kusumi 				return EINVAL;
199289a3eb16STomohiro Kusumi 			strlcat(tmp, "/", sizeof(tmp));
199389a3eb16STomohiro Kusumi 			strlcat(tmp, p, sizeof(tmp));
199489a3eb16STomohiro Kusumi 			error = trim_slash(tmp);
199589a3eb16STomohiro Kusumi 			if (error)
199689a3eb16STomohiro Kusumi 				return error;
199789a3eb16STomohiro Kusumi 			p = name = tmp;
199889a3eb16STomohiro Kusumi 			continue;
199989a3eb16STomohiro Kusumi 		default:
200089a3eb16STomohiro Kusumi 			return EINVAL;
200189a3eb16STomohiro Kusumi 		}
2002ac97ce60STomohiro Kusumi 
2003ac97ce60STomohiro Kusumi 		dvp = vp;
2004ac97ce60STomohiro Kusumi 		name = p;
2005ac97ce60STomohiro Kusumi 	}
2006ac97ce60STomohiro Kusumi 
2007ba024da8STomohiro Kusumi 	/* XXX When does (or why does not) ioctl modify this inode ? */
2008ba024da8STomohiro Kusumi 	hammer2_inode_modify(VTOI(dvp));
2009ba024da8STomohiro Kusumi 
2010917508cdSTomohiro Kusumi 	bzero(&destroy, sizeof(destroy));
2011917508cdSTomohiro Kusumi 	destroy.cmd = HAMMER2_DELETE_FILE;
2012ac97ce60STomohiro Kusumi 	snprintf(destroy.path, sizeof(destroy.path), "%s", name);
2013917508cdSTomohiro Kusumi 
2014917508cdSTomohiro Kusumi 	printf("%s\t", f);
2015917508cdSTomohiro Kusumi 	fflush(stdout);
2016917508cdSTomohiro Kusumi 
2017ac97ce60STomohiro Kusumi 	error = hammer2_ioctl_destroy(VTOI(dvp), &destroy);
2018917508cdSTomohiro Kusumi 	if (error)
2019917508cdSTomohiro Kusumi 		printf("%s\n", strerror(error));
2020917508cdSTomohiro Kusumi 	else
2021917508cdSTomohiro Kusumi 		printf("ok\n");
2022ac97ce60STomohiro Kusumi 	free(o);
2023917508cdSTomohiro Kusumi 
2024917508cdSTomohiro Kusumi 	return error;
2025917508cdSTomohiro Kusumi }
2026917508cdSTomohiro Kusumi 
2027917508cdSTomohiro Kusumi static int
hammer2_destroy_inum(struct m_vnode * vp,hammer2_tid_t inum)2028917508cdSTomohiro Kusumi hammer2_destroy_inum(struct m_vnode *vp, hammer2_tid_t inum)
2029917508cdSTomohiro Kusumi {
2030917508cdSTomohiro Kusumi 	hammer2_ioc_destroy_t destroy;
2031b4807901STomohiro Kusumi 	int error;
2032917508cdSTomohiro Kusumi 
2033917508cdSTomohiro Kusumi 	bzero(&destroy, sizeof(destroy));
2034917508cdSTomohiro Kusumi 	destroy.cmd = HAMMER2_DELETE_INUM;
2035917508cdSTomohiro Kusumi 	destroy.inum = inum;
2036917508cdSTomohiro Kusumi 
2037917508cdSTomohiro Kusumi 	printf("%jd\t", (intmax_t)destroy.inum);
2038917508cdSTomohiro Kusumi 	fflush(stdout);
2039917508cdSTomohiro Kusumi 
2040917508cdSTomohiro Kusumi 	error = hammer2_ioctl_destroy(VTOI(vp), &destroy);
2041917508cdSTomohiro Kusumi 	if (error)
2042917508cdSTomohiro Kusumi 		printf("%s\n", strerror(error));
2043917508cdSTomohiro Kusumi 	else
2044917508cdSTomohiro Kusumi 		printf("ok\n");
2045917508cdSTomohiro Kusumi 
2046917508cdSTomohiro Kusumi 	return error;
2047917508cdSTomohiro Kusumi }
2048917508cdSTomohiro Kusumi 
2049917508cdSTomohiro Kusumi static int
hammer2_growfs(struct m_vnode * vp,hammer2_off_t size)2050afa5234bSTomohiro Kusumi hammer2_growfs(struct m_vnode *vp, hammer2_off_t size)
2051afa5234bSTomohiro Kusumi {
2052afa5234bSTomohiro Kusumi 	hammer2_ioc_growfs_t growfs;
2053afa5234bSTomohiro Kusumi 	int error;
2054afa5234bSTomohiro Kusumi 
2055afa5234bSTomohiro Kusumi 	bzero(&growfs, sizeof(growfs));
2056afa5234bSTomohiro Kusumi 	growfs.size = size;
2057afa5234bSTomohiro Kusumi 
2058afa5234bSTomohiro Kusumi 	error = hammer2_ioctl_growfs(VTOI(vp), &growfs, NULL);
2059afa5234bSTomohiro Kusumi 	if (!error) {
2060afa5234bSTomohiro Kusumi 		if (growfs.modified)
2061afa5234bSTomohiro Kusumi 			printf("grown to %016jx\n", (intmax_t)growfs.size);
2062afa5234bSTomohiro Kusumi 		else
2063afa5234bSTomohiro Kusumi 			printf("no size change - %016jx\n",
2064afa5234bSTomohiro Kusumi 			    (intmax_t)growfs.size);
2065afa5234bSTomohiro Kusumi 	}
2066afa5234bSTomohiro Kusumi 
2067afa5234bSTomohiro Kusumi 	return error;
2068afa5234bSTomohiro Kusumi }
20696857f034STomohiro Kusumi 
20702d0322dbSTomohiro Kusumi struct hammer2_link {
20712d0322dbSTomohiro Kusumi 	TAILQ_ENTRY(hammer2_link) entry;
20722d0322dbSTomohiro Kusumi 	hammer2_tid_t inum;
20732d0322dbSTomohiro Kusumi 	uint64_t nlinks;
20742d0322dbSTomohiro Kusumi 	char path[PATH_MAX];
20752d0322dbSTomohiro Kusumi };
20762d0322dbSTomohiro Kusumi 
20772d0322dbSTomohiro Kusumi TAILQ_HEAD(hammer2_linkq, hammer2_link);
20782d0322dbSTomohiro Kusumi 
20792d0322dbSTomohiro Kusumi static void
hammer2_linkq_init(struct hammer2_linkq * linkq)20802d0322dbSTomohiro Kusumi hammer2_linkq_init(struct hammer2_linkq *linkq)
20812d0322dbSTomohiro Kusumi {
20822d0322dbSTomohiro Kusumi 	TAILQ_INIT(linkq);
20832d0322dbSTomohiro Kusumi }
20842d0322dbSTomohiro Kusumi 
20852d0322dbSTomohiro Kusumi static void
hammer2_linkq_cleanup(struct hammer2_linkq * linkq,bool is_root)20862db6f39cSTomohiro Kusumi hammer2_linkq_cleanup(struct hammer2_linkq *linkq, bool is_root)
20872d0322dbSTomohiro Kusumi {
20882d0322dbSTomohiro Kusumi 	struct hammer2_link *e;
20892d0322dbSTomohiro Kusumi 	int count = 0;
20902d0322dbSTomohiro Kusumi 
20912d0322dbSTomohiro Kusumi 	/*
20922db6f39cSTomohiro Kusumi 	 * If is_root is true, linkq must be empty, or link count is broken.
20932d0322dbSTomohiro Kusumi 	 * Note that if an image was made by makefs, hardlinks in the source
20942db6f39cSTomohiro Kusumi 	 * directory became hardlinks in the image only if >1 links existed under
20952d0322dbSTomohiro Kusumi 	 * that directory, as makefs doesn't determine hardlink via link count.
20962d0322dbSTomohiro Kusumi 	 */
20972d0322dbSTomohiro Kusumi 	while ((e = TAILQ_FIRST(linkq)) != NULL) {
20982d0322dbSTomohiro Kusumi 		count++;
20992d0322dbSTomohiro Kusumi 		TAILQ_REMOVE(linkq, e, entry);
21002d0322dbSTomohiro Kusumi 		free(e);
21012d0322dbSTomohiro Kusumi 	}
21022d0322dbSTomohiro Kusumi 	assert(TAILQ_EMPTY(linkq));
21032d0322dbSTomohiro Kusumi 
21042db6f39cSTomohiro Kusumi 	if (count && is_root)
21052d0322dbSTomohiro Kusumi 		errx(1, "%d link entries remained", count);
21062d0322dbSTomohiro Kusumi }
21072d0322dbSTomohiro Kusumi 
21082d0322dbSTomohiro Kusumi static void
hammer2_linkq_add(struct hammer2_linkq * linkq,hammer2_tid_t inum,uint64_t nlinks,const char * path)21092d0322dbSTomohiro Kusumi hammer2_linkq_add(struct hammer2_linkq *linkq, hammer2_tid_t inum,
21102d0322dbSTomohiro Kusumi     uint64_t nlinks, const char *path)
21112d0322dbSTomohiro Kusumi {
21122d0322dbSTomohiro Kusumi 	struct hammer2_link *e;
21132d0322dbSTomohiro Kusumi 	int count = 0;
21142d0322dbSTomohiro Kusumi 
21152d0322dbSTomohiro Kusumi 	e = ecalloc(1, sizeof(*e));
21162d0322dbSTomohiro Kusumi 	e->inum = inum;
21172d0322dbSTomohiro Kusumi 	e->nlinks = nlinks;
21182d0322dbSTomohiro Kusumi 	strlcpy(e->path, path, sizeof(e->path));
21192d0322dbSTomohiro Kusumi 	TAILQ_INSERT_TAIL(linkq, e, entry);
21202d0322dbSTomohiro Kusumi 
21212d0322dbSTomohiro Kusumi 	TAILQ_FOREACH(e, linkq, entry)
21222d0322dbSTomohiro Kusumi 		if (e->inum == inum)
21232d0322dbSTomohiro Kusumi 			count++;
21242d0322dbSTomohiro Kusumi 	if (count > 1)
21252d0322dbSTomohiro Kusumi 		errx(1, "%d link entries exist for inum %jd",
21262d0322dbSTomohiro Kusumi 		    count, (intmax_t)inum);
21272d0322dbSTomohiro Kusumi }
21282d0322dbSTomohiro Kusumi 
21292d0322dbSTomohiro Kusumi static void
hammer2_linkq_del(struct hammer2_linkq * linkq,hammer2_tid_t inum)21302d0322dbSTomohiro Kusumi hammer2_linkq_del(struct hammer2_linkq *linkq, hammer2_tid_t inum)
21312d0322dbSTomohiro Kusumi {
21322d0322dbSTomohiro Kusumi 	struct hammer2_link *e, *next;
21332d0322dbSTomohiro Kusumi 
21342d0322dbSTomohiro Kusumi 	TAILQ_FOREACH_MUTABLE(e, linkq, entry, next)
21352d0322dbSTomohiro Kusumi 		if (e->inum == inum) {
21362d0322dbSTomohiro Kusumi 			e->nlinks--;
21372d0322dbSTomohiro Kusumi 			if (e->nlinks == 1) {
21382d0322dbSTomohiro Kusumi 				TAILQ_REMOVE(linkq, e, entry);
21392d0322dbSTomohiro Kusumi 				free(e);
21402d0322dbSTomohiro Kusumi 			}
21412d0322dbSTomohiro Kusumi 		}
21422d0322dbSTomohiro Kusumi }
21432d0322dbSTomohiro Kusumi 
21446da97f51STomohiro Kusumi static void
hammer2_utimes(struct m_vnode * vp,const char * f)21456da97f51STomohiro Kusumi hammer2_utimes(struct m_vnode *vp, const char *f)
21466da97f51STomohiro Kusumi {
21476da97f51STomohiro Kusumi 	hammer2_inode_t *ip = VTOI(vp);
21486da97f51STomohiro Kusumi 	struct timeval tv[2];
21496da97f51STomohiro Kusumi 
21506da97f51STomohiro Kusumi 	hammer2_time_to_timeval(ip->meta.atime, &tv[0]);
21516da97f51STomohiro Kusumi 	hammer2_time_to_timeval(ip->meta.mtime, &tv[1]);
21526da97f51STomohiro Kusumi 
21536da97f51STomohiro Kusumi 	utimes(f, tv); /* ignore failure */
21546da97f51STomohiro Kusumi }
21556da97f51STomohiro Kusumi 
2156fc4148feSTomohiro Kusumi static int
hammer2_readx_directory(struct m_vnode * dvp,const char * dir,const char * name,struct hammer2_linkq * linkq)21572d0322dbSTomohiro Kusumi hammer2_readx_directory(struct m_vnode *dvp, const char *dir, const char *name,
21582d0322dbSTomohiro Kusumi     struct hammer2_linkq *linkq)
21595e8b0eb7STomohiro Kusumi {
21605e8b0eb7STomohiro Kusumi 	struct m_vnode *vp;
21615e8b0eb7STomohiro Kusumi 	struct dirent *dp;
21625e8b0eb7STomohiro Kusumi 	struct stat st;
21635e8b0eb7STomohiro Kusumi 	char *buf, tmp[PATH_MAX];
21645e8b0eb7STomohiro Kusumi 	off_t offset = 0;
21655e8b0eb7STomohiro Kusumi 	int ndirent = 0;
21665e8b0eb7STomohiro Kusumi 	int eofflag = 0;
21675e8b0eb7STomohiro Kusumi 	int i, error;
21685e8b0eb7STomohiro Kusumi 
21695e8b0eb7STomohiro Kusumi 	snprintf(tmp, sizeof(tmp), "%s/%s", dir, name);
21705e8b0eb7STomohiro Kusumi 	if (stat(tmp, &st) == -1 && mkdir(tmp, 0666) == -1)
21715e8b0eb7STomohiro Kusumi 		err(1, "failed to mkdir %s", tmp);
21725e8b0eb7STomohiro Kusumi 
21732db6f39cSTomohiro Kusumi 	buf = ecalloc(1, HAMMER2_PBUFSIZE);
21745e8b0eb7STomohiro Kusumi 
21755e8b0eb7STomohiro Kusumi 	while (!eofflag) {
21765e8b0eb7STomohiro Kusumi 		error = hammer2_readdir(dvp, buf, HAMMER2_PBUFSIZE, &offset,
21775e8b0eb7STomohiro Kusumi 		    &ndirent, &eofflag);
21785e8b0eb7STomohiro Kusumi 		if (error)
21795e8b0eb7STomohiro Kusumi 			errx(1, "failed to readdir");
21805e8b0eb7STomohiro Kusumi 		dp = (void *)buf;
21815e8b0eb7STomohiro Kusumi 
21825e8b0eb7STomohiro Kusumi 		for (i = 0; i < ndirent; i++) {
21835e8b0eb7STomohiro Kusumi 			if (strcmp(dp->d_name, ".") &&
21845e8b0eb7STomohiro Kusumi 			    strcmp(dp->d_name, "..")) {
21855e8b0eb7STomohiro Kusumi 				error = hammer2_nresolve(dvp, &vp, dp->d_name,
21865e8b0eb7STomohiro Kusumi 				    strlen(dp->d_name));
21875e8b0eb7STomohiro Kusumi 				if (error)
21885e8b0eb7STomohiro Kusumi 					return error;
21895e8b0eb7STomohiro Kusumi 				error = hammer2_readx_handle(vp, tmp,
21902d0322dbSTomohiro Kusumi 				    dp->d_name, linkq);
21915e8b0eb7STomohiro Kusumi 				if (error)
21925e8b0eb7STomohiro Kusumi 					return error;
21935e8b0eb7STomohiro Kusumi 			}
21945e8b0eb7STomohiro Kusumi 			dp = (void *)((char *)dp +
21955e8b0eb7STomohiro Kusumi 			    _DIRENT_RECLEN(dp->d_namlen));
21965e8b0eb7STomohiro Kusumi 		}
21975e8b0eb7STomohiro Kusumi 	}
21985e8b0eb7STomohiro Kusumi 
21995e8b0eb7STomohiro Kusumi 	free(buf);
22006da97f51STomohiro Kusumi 	hammer2_utimes(dvp, tmp);
22015e8b0eb7STomohiro Kusumi 
22025e8b0eb7STomohiro Kusumi 	return 0;
22035e8b0eb7STomohiro Kusumi }
22045e8b0eb7STomohiro Kusumi 
22055e8b0eb7STomohiro Kusumi static int
hammer2_readx_link(struct m_vnode * vp,const char * src,const char * lnk,struct hammer2_linkq * linkq)22062d0322dbSTomohiro Kusumi hammer2_readx_link(struct m_vnode *vp, const char *src, const char *lnk,
22072d0322dbSTomohiro Kusumi     struct hammer2_linkq *linkq)
22085e8b0eb7STomohiro Kusumi {
22095e8b0eb7STomohiro Kusumi 	hammer2_inode_t *ip = VTOI(vp);
22102d0322dbSTomohiro Kusumi 	struct stat st;
22112d0322dbSTomohiro Kusumi 	int error;
22122d0322dbSTomohiro Kusumi 
22132d0322dbSTomohiro Kusumi 	if (!stat(lnk, &st)) {
22142d0322dbSTomohiro Kusumi 		error = unlink(lnk);
22152d0322dbSTomohiro Kusumi 		if (error)
22162d0322dbSTomohiro Kusumi 			return error;
22172d0322dbSTomohiro Kusumi 	}
22182d0322dbSTomohiro Kusumi 
22192d0322dbSTomohiro Kusumi 	error = link(src, lnk);
22202d0322dbSTomohiro Kusumi 	if (error)
22212d0322dbSTomohiro Kusumi 		return error;
22222d0322dbSTomohiro Kusumi 
22232d0322dbSTomohiro Kusumi 	hammer2_linkq_del(linkq, ip->meta.inum);
22242d0322dbSTomohiro Kusumi 
22252d0322dbSTomohiro Kusumi 	return 0;
22262d0322dbSTomohiro Kusumi }
22272d0322dbSTomohiro Kusumi 
22282d0322dbSTomohiro Kusumi static int
hammer2_readx_regfile(struct m_vnode * vp,const char * dir,const char * name,struct hammer2_linkq * linkq)22292d0322dbSTomohiro Kusumi hammer2_readx_regfile(struct m_vnode *vp, const char *dir, const char *name,
22302d0322dbSTomohiro Kusumi     struct hammer2_linkq *linkq)
22312d0322dbSTomohiro Kusumi {
22322d0322dbSTomohiro Kusumi 	hammer2_inode_t *ip = VTOI(vp);
22332d0322dbSTomohiro Kusumi 	struct hammer2_link *e;
22345e8b0eb7STomohiro Kusumi 	char *buf, out[PATH_MAX];
22355e8b0eb7STomohiro Kusumi 	size_t resid, n;
22365e8b0eb7STomohiro Kusumi 	off_t offset;
22375e8b0eb7STomohiro Kusumi 	int fd, error;
22382d0322dbSTomohiro Kusumi 	bool found = false;
22395e8b0eb7STomohiro Kusumi 
22405e8b0eb7STomohiro Kusumi 	snprintf(out, sizeof(out), "%s/%s", dir, name);
22412d0322dbSTomohiro Kusumi 
22422d0322dbSTomohiro Kusumi 	if (ip->meta.nlinks > 1) {
22432d0322dbSTomohiro Kusumi 		TAILQ_FOREACH(e, linkq, entry)
22442d0322dbSTomohiro Kusumi 			if (e->inum == ip->meta.inum) {
22452d0322dbSTomohiro Kusumi 				found = true;
22462d0322dbSTomohiro Kusumi 				error = hammer2_readx_link(vp, e->path, out,
22472d0322dbSTomohiro Kusumi 				    linkq);
22482d0322dbSTomohiro Kusumi 				if (error == 0)
22492d0322dbSTomohiro Kusumi 					return 0;
22502d0322dbSTomohiro Kusumi 				/* ignore failure */
22512d0322dbSTomohiro Kusumi 			}
22522d0322dbSTomohiro Kusumi 		if (!found)
22532d0322dbSTomohiro Kusumi 			hammer2_linkq_add(linkq, ip->meta.inum, ip->meta.nlinks,
22542d0322dbSTomohiro Kusumi 			    out);
22552d0322dbSTomohiro Kusumi 	}
22562d0322dbSTomohiro Kusumi 
22575e8b0eb7STomohiro Kusumi 	fd = open(out, O_WRONLY | O_CREAT | O_TRUNC, 0666);
22585e8b0eb7STomohiro Kusumi 	if (fd == -1)
22595e8b0eb7STomohiro Kusumi 		err(1, "failed to create %s", out);
22605e8b0eb7STomohiro Kusumi 
22612db6f39cSTomohiro Kusumi 	buf = ecalloc(1, HAMMER2_PBUFSIZE);
22625e8b0eb7STomohiro Kusumi 	resid = ip->meta.size;
22635e8b0eb7STomohiro Kusumi 	offset = 0;
22645e8b0eb7STomohiro Kusumi 
22655e8b0eb7STomohiro Kusumi 	while (resid > 0) {
22665e8b0eb7STomohiro Kusumi 		bzero(buf, HAMMER2_PBUFSIZE);
22675e8b0eb7STomohiro Kusumi 		error = hammer2_read(vp, buf, HAMMER2_PBUFSIZE, offset);
22685e8b0eb7STomohiro Kusumi 		if (error)
22695e8b0eb7STomohiro Kusumi 			errx(1, "failed to read from %s", name);
22705e8b0eb7STomohiro Kusumi 
22715e8b0eb7STomohiro Kusumi 		n = resid >= HAMMER2_PBUFSIZE ? HAMMER2_PBUFSIZE : resid;
22725e8b0eb7STomohiro Kusumi 		error = write(fd, buf, n);
22735e8b0eb7STomohiro Kusumi 		if (error == -1)
22745e8b0eb7STomohiro Kusumi 			err(1, "failed to write to %s", out);
22755e8b0eb7STomohiro Kusumi 		else if (error != n)
22765e8b0eb7STomohiro Kusumi 			return EINVAL;
22775e8b0eb7STomohiro Kusumi 
22785e8b0eb7STomohiro Kusumi 		resid -= n;
22795e8b0eb7STomohiro Kusumi 		offset += HAMMER2_PBUFSIZE;
22805e8b0eb7STomohiro Kusumi 	}
22815e8b0eb7STomohiro Kusumi 	fsync(fd);
22825e8b0eb7STomohiro Kusumi 	close(fd);
22835e8b0eb7STomohiro Kusumi 
22845e8b0eb7STomohiro Kusumi 	free(buf);
22856da97f51STomohiro Kusumi 	hammer2_utimes(vp, out);
22865e8b0eb7STomohiro Kusumi 
22875e8b0eb7STomohiro Kusumi 	return 0;
22885e8b0eb7STomohiro Kusumi }
22895e8b0eb7STomohiro Kusumi 
22905e8b0eb7STomohiro Kusumi static int
hammer2_readx_handle(struct m_vnode * vp,const char * dir,const char * name,struct hammer2_linkq * linkq)22912d0322dbSTomohiro Kusumi hammer2_readx_handle(struct m_vnode *vp, const char *dir, const char *name,
22922d0322dbSTomohiro Kusumi     struct hammer2_linkq *linkq)
22935e8b0eb7STomohiro Kusumi {
22945e8b0eb7STomohiro Kusumi 	hammer2_inode_t *ip = VTOI(vp);
22955e8b0eb7STomohiro Kusumi 
22965e8b0eb7STomohiro Kusumi 	switch (ip->meta.type) {
22975e8b0eb7STomohiro Kusumi 	case HAMMER2_OBJTYPE_DIRECTORY:
22982d0322dbSTomohiro Kusumi 		return hammer2_readx_directory(vp, dir, name, linkq);
22995e8b0eb7STomohiro Kusumi 	case HAMMER2_OBJTYPE_REGFILE:
23002d0322dbSTomohiro Kusumi 		return hammer2_readx_regfile(vp, dir, name, linkq);
23015e8b0eb7STomohiro Kusumi 	default:
23025e8b0eb7STomohiro Kusumi 		/* XXX */
23035e8b0eb7STomohiro Kusumi 		printf("ignore inode %jd %s \"%s\"\n",
23045e8b0eb7STomohiro Kusumi 		    (intmax_t)ip->meta.inum,
23055e8b0eb7STomohiro Kusumi 		    hammer2_iptype_to_str(ip->meta.type),
23065e8b0eb7STomohiro Kusumi 		    name);
23075e8b0eb7STomohiro Kusumi 		return 0;
23085e8b0eb7STomohiro Kusumi 	}
23095e8b0eb7STomohiro Kusumi 	return EINVAL;
23105e8b0eb7STomohiro Kusumi }
23115e8b0eb7STomohiro Kusumi 
23125e8b0eb7STomohiro Kusumi static int
hammer2_readx(struct m_vnode * dvp,const char * dir,const char * f)2313fc4148feSTomohiro Kusumi hammer2_readx(struct m_vnode *dvp, const char *dir, const char *f)
2314fc4148feSTomohiro Kusumi {
2315fc4148feSTomohiro Kusumi 	hammer2_inode_t *ip;
23162d0322dbSTomohiro Kusumi 	struct hammer2_linkq linkq;
23172db6f39cSTomohiro Kusumi 	struct m_vnode *vp, *ovp = dvp;
23185e8b0eb7STomohiro Kusumi 	char *o, *p, *name;
23195e8b0eb7STomohiro Kusumi 	char tmp[PATH_MAX];
23205e8b0eb7STomohiro Kusumi 	int error;
2321fc4148feSTomohiro Kusumi 
2322fc4148feSTomohiro Kusumi 	if (dir == NULL)
2323fc4148feSTomohiro Kusumi 		return EINVAL;
2324fc4148feSTomohiro Kusumi 
2325fc4148feSTomohiro Kusumi 	assert(strlen(f) > 0);
2326fc4148feSTomohiro Kusumi 	o = p = name = strdup(f);
2327fc4148feSTomohiro Kusumi 
2328fc4148feSTomohiro Kusumi 	error = trim_slash(p);
2329fc4148feSTomohiro Kusumi 	if (error)
2330fc4148feSTomohiro Kusumi 		return error;
23315e8b0eb7STomohiro Kusumi 	if (strlen(p) == 0) {
23325e8b0eb7STomohiro Kusumi 		vp = dvp;
23332db6f39cSTomohiro Kusumi 		goto start_read;
23345e8b0eb7STomohiro Kusumi 	}
2335fc4148feSTomohiro Kusumi 
2336fc4148feSTomohiro Kusumi 	while ((p = strchr(p, '/')) != NULL) {
2337fc4148feSTomohiro Kusumi 		*p++ = 0; /* NULL terminate name */
2338fc4148feSTomohiro Kusumi 		if (!strcmp(name, ".")) {
2339fc4148feSTomohiro Kusumi 			name = p;
2340fc4148feSTomohiro Kusumi 			continue;
2341fc4148feSTomohiro Kusumi 		}
2342fc4148feSTomohiro Kusumi 		vp = NULL;
2343fc4148feSTomohiro Kusumi 		error = hammer2_nresolve(dvp, &vp, name, strlen(name));
2344fc4148feSTomohiro Kusumi 		if (error)
2345fc4148feSTomohiro Kusumi 			return error;
2346fc4148feSTomohiro Kusumi 
2347fc4148feSTomohiro Kusumi 		ip = VTOI(vp);
2348fc4148feSTomohiro Kusumi 		switch (ip->meta.type) {
2349fc4148feSTomohiro Kusumi 		case HAMMER2_OBJTYPE_DIRECTORY:
2350fc4148feSTomohiro Kusumi 			break;
2351fc4148feSTomohiro Kusumi 		case HAMMER2_OBJTYPE_SOFTLINK:
2352fc4148feSTomohiro Kusumi 			bzero(tmp, sizeof(tmp));
2353fc4148feSTomohiro Kusumi 			error = hammer2_readlink(vp, tmp, sizeof(tmp));
2354fc4148feSTomohiro Kusumi 			if (error)
2355fc4148feSTomohiro Kusumi 				return error;
2356fc4148feSTomohiro Kusumi 			if (!is_supported_link(tmp))
2357fc4148feSTomohiro Kusumi 				return EINVAL;
2358fc4148feSTomohiro Kusumi 			strlcat(tmp, "/", sizeof(tmp));
2359fc4148feSTomohiro Kusumi 			strlcat(tmp, p, sizeof(tmp));
2360fc4148feSTomohiro Kusumi 			error = trim_slash(tmp);
2361fc4148feSTomohiro Kusumi 			if (error)
2362fc4148feSTomohiro Kusumi 				return error;
2363fc4148feSTomohiro Kusumi 			p = name = tmp;
2364fc4148feSTomohiro Kusumi 			continue;
2365fc4148feSTomohiro Kusumi 		default:
2366fc4148feSTomohiro Kusumi 			return EINVAL;
2367fc4148feSTomohiro Kusumi 		}
2368fc4148feSTomohiro Kusumi 
2369fc4148feSTomohiro Kusumi 		dvp = vp;
2370fc4148feSTomohiro Kusumi 		name = p;
2371fc4148feSTomohiro Kusumi 	}
2372fc4148feSTomohiro Kusumi 
2373fc4148feSTomohiro Kusumi 	error = hammer2_nresolve(dvp, &vp, name, strlen(name));
2374fc4148feSTomohiro Kusumi 	if (error)
2375fc4148feSTomohiro Kusumi 		return error;
23762db6f39cSTomohiro Kusumi start_read:
23772d0322dbSTomohiro Kusumi 	hammer2_linkq_init(&linkq);
23782d0322dbSTomohiro Kusumi 	error = hammer2_readx_handle(vp, dir, name, &linkq);
23792db6f39cSTomohiro Kusumi 	hammer2_linkq_cleanup(&linkq, vp == ovp);
2380fc4148feSTomohiro Kusumi 	if (error)
23815e8b0eb7STomohiro Kusumi 		return error;
2382fc4148feSTomohiro Kusumi 
2383fc4148feSTomohiro Kusumi 	free(o);
2384fc4148feSTomohiro Kusumi 
2385fc4148feSTomohiro Kusumi 	return 0;
2386fc4148feSTomohiro Kusumi }
2387fc4148feSTomohiro Kusumi 
23886857f034STomohiro Kusumi static void
assert_trim_slash(const char * input,const char * expected)23896857f034STomohiro Kusumi assert_trim_slash(const char *input, const char *expected)
23906857f034STomohiro Kusumi {
23916857f034STomohiro Kusumi 	char tmp[PATH_MAX];
23926857f034STomohiro Kusumi 	int error;
23936857f034STomohiro Kusumi 
23946857f034STomohiro Kusumi 	strlcpy(tmp, input, sizeof(tmp));
23956857f034STomohiro Kusumi 	error = trim_slash(tmp);
23966857f034STomohiro Kusumi 	if (error)
23976857f034STomohiro Kusumi 		errx(1, "input \"%s\" error %d", input, error);
23986857f034STomohiro Kusumi 
23996857f034STomohiro Kusumi 	if (strncmp(tmp, expected, sizeof(tmp)))
24006857f034STomohiro Kusumi 		errx(1, "input \"%s\" result \"%s\" vs expected \"%s\"",
24016857f034STomohiro Kusumi 		    input, tmp, expected);
24026857f034STomohiro Kusumi }
24036857f034STomohiro Kusumi 
24046857f034STomohiro Kusumi static void
unittest_trim_slash(void)24056857f034STomohiro Kusumi unittest_trim_slash(void)
24066857f034STomohiro Kusumi {
24076857f034STomohiro Kusumi 	assert_trim_slash("", "");
24086857f034STomohiro Kusumi 	assert_trim_slash("/", "");
24096857f034STomohiro Kusumi 	assert_trim_slash("//", "");
24106857f034STomohiro Kusumi 	assert_trim_slash("///", "");
24116857f034STomohiro Kusumi 
24126857f034STomohiro Kusumi 	assert_trim_slash("makefs", "makefs");
24136857f034STomohiro Kusumi 	assert_trim_slash("/makefs", "makefs");
24146857f034STomohiro Kusumi 	assert_trim_slash("//makefs", "makefs");
24156857f034STomohiro Kusumi 	assert_trim_slash("makefs/", "makefs");
24166857f034STomohiro Kusumi 	assert_trim_slash("makefs//", "makefs");
24176857f034STomohiro Kusumi 	assert_trim_slash("/makefs/", "makefs");
24186857f034STomohiro Kusumi 	assert_trim_slash("//makefs//", "makefs");
24196857f034STomohiro Kusumi 
24206857f034STomohiro Kusumi 	assert_trim_slash("sys/vfs/hammer2", "sys/vfs/hammer2");
24216857f034STomohiro Kusumi 	assert_trim_slash("/sys/vfs/hammer2", "sys/vfs/hammer2");
24226857f034STomohiro Kusumi 	assert_trim_slash("//sys/vfs/hammer2", "sys/vfs/hammer2");
24236857f034STomohiro Kusumi 	assert_trim_slash("///sys/vfs/hammer2", "sys/vfs/hammer2");
24246857f034STomohiro Kusumi 	assert_trim_slash("sys/vfs/hammer2/", "sys/vfs/hammer2");
24256857f034STomohiro Kusumi 	assert_trim_slash("sys/vfs/hammer2//", "sys/vfs/hammer2");
24266857f034STomohiro Kusumi 	assert_trim_slash("sys/vfs/hammer2///", "sys/vfs/hammer2");
24276857f034STomohiro Kusumi 	assert_trim_slash("/sys/vfs/hammer2/", "sys/vfs/hammer2");
24286857f034STomohiro Kusumi 	assert_trim_slash("//sys//vfs//hammer2//", "sys/vfs/hammer2");
24296857f034STomohiro Kusumi 	assert_trim_slash("///sys///vfs///hammer2///", "sys/vfs/hammer2");
24306857f034STomohiro Kusumi 
2431bca6a9a0STomohiro Kusumi 	APRINTF("success\n");
24326857f034STomohiro Kusumi }
2433