xref: /dragonfly/usr.sbin/makefs/hammer2.c (revision f804c425)
1 /*
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 2022 Tomohiro Kusumi <tkusumi@netbsd.org>
5  * Copyright (c) 2011-2022 The DragonFly Project.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #if HAVE_NBTOOL_CONFIG_H
36 #include "nbtool_config.h"
37 #endif
38 
39 #include <sys/param.h>
40 #include <sys/stat.h>
41 #include <sys/sysctl.h>
42 #include <sys/mman.h>
43 
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <stdbool.h>
47 #include <string.h>
48 #include <unistd.h>
49 #include <fcntl.h>
50 #include <time.h>
51 #include <err.h>
52 #include <assert.h>
53 #include <util.h>
54 
55 #include "makefs.h"
56 #include "hammer2.h"
57 
58 #define APRINTF(X, ...)	\
59     printf("%s: " X, __func__, ## __VA_ARGS__)
60 
61 static void hammer2_parse_pfs_opts(const char *, fsinfo_t *);
62 static void hammer2_parse_inode_opts(const char *, fsinfo_t *);
63 static void hammer2_dump_fsinfo(fsinfo_t *);
64 static int hammer2_create_image(const char *, fsinfo_t *);
65 static int hammer2_populate_dir(struct m_vnode *, const char *, fsnode *,
66     fsnode *, fsinfo_t *, int);
67 static void hammer2_validate(const char *, fsnode *, fsinfo_t *);
68 static void hammer2_size_dir(fsnode *, fsinfo_t *);
69 static int hammer2_write_file(struct m_vnode *, const char *, fsnode *);
70 static int hammer2_version_get(struct m_vnode *);
71 static int hammer2_pfs_get(struct m_vnode *);
72 static int hammer2_pfs_lookup(struct m_vnode *, const char *);
73 static int hammer2_pfs_create(struct m_vnode *, const char *);
74 static int hammer2_pfs_delete(struct m_vnode *, const char *);
75 static int hammer2_pfs_snapshot(struct m_vnode *, const char *, const char *);
76 static int hammer2_inode_getx(struct m_vnode *vp, const char *);
77 static int hammer2_bulkfree(struct m_vnode *);
78 static int hammer2_destroy_path(struct m_vnode *, const char *);
79 static int hammer2_destroy_inum(struct m_vnode *, hammer2_tid_t);
80 static int hammer2_growfs(struct m_vnode *, hammer2_off_t);
81 
82 fsnode *hammer2_curnode;
83 
84 void
85 hammer2_prep_opts(fsinfo_t *fsopts)
86 {
87 	hammer2_makefs_options_t *h2_opt = ecalloc(1, sizeof(*h2_opt));
88 	hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
89 
90 	const option_t hammer2_options[] = {
91 		/* newfs_hammer2(8) compatible options */
92 		{ 'b', "BootAreaSize", NULL, OPT_STRBUF, 0, 0, "boot area size" },
93 		{ 'r', "AuxAreaSize", NULL, OPT_STRBUF, 0, 0, "aux area size" },
94 		{ 'V', "Hammer2Version", NULL, OPT_STRBUF, 0, 0, "file system version" },
95 		{ 'L', "Label", NULL, OPT_STRBUF, 0, 0, "PFS label" },
96 		/* makefs(8) specific options */
97 		{ 'm', "MountLabel", NULL, OPT_STRBUF, 0, 0, "destination PFS label" },
98 		{ 'v', "NumVolhdr", &h2_opt->num_volhdr, OPT_INT32,
99 		    1, HAMMER2_NUM_VOLHDRS, "number of volume headers" },
100 		{ 'd', "Hammer2Debug", NULL, OPT_STRBUF, 0, 0, "debug tunable" },
101 		{ 'E', "EmergencyMode", &h2_opt->emergency_mode, OPT_BOOL, 0, 0,
102 		    "emergency mode" },
103 		{ 'P', "PFS", NULL, OPT_STRBUF, 0, 0, "offline PFS" },
104 		{ 'I', "Inode", NULL, OPT_STRBUF, 0, 0, "offline inode" },
105 		{ 'B', "Bulkfree", NULL, OPT_STRBUF, 0, 0, "offline bulkfree" },
106 		{ 'D', "Destroy", NULL, OPT_STRBUF, 0, 0, "offline destroy" },
107 		{ 'G', "Growfs", NULL, OPT_STRBUF, 0, 0, "offline growfs" },
108 		{ .name = NULL },
109 	};
110 
111 	hammer2_mkfs_init(opt);
112 
113 	/* make this tunable ? */
114 	assert(opt->CompType == HAMMER2_COMP_NEWFS_DEFAULT);
115 	assert(opt->CheckType == HAMMER2_CHECK_XXHASH64);
116 
117 	/* force debug mode for mkfs */
118 	opt->DebugOpt = 1;
119 
120 	fsopts->fs_specific = h2_opt;
121 	fsopts->fs_options = copy_opts(hammer2_options);
122 	fsopts->sectorsize = DEV_BSIZE;
123 }
124 
125 void
126 hammer2_cleanup_opts(fsinfo_t *fsopts)
127 {
128 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
129 	hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
130 
131 	hammer2_mkfs_cleanup(opt);
132 
133 	free(h2_opt);
134 	free(fsopts->fs_options);
135 }
136 
137 int
138 hammer2_parse_opts(const char *option, fsinfo_t *fsopts)
139 {
140 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
141 	hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
142 
143 	option_t *hammer2_options = fsopts->fs_options;
144 	char buf[1024]; /* > HAMMER2_INODE_MAXNAME */
145 	int i;
146 
147 	assert(option != NULL);
148 	assert(fsopts != NULL);
149 
150 	if (debug & DEBUG_FS_PARSE_OPTS)
151 		APRINTF("got `%s'\n", option);
152 
153 	i = set_option(hammer2_options, option, buf, sizeof(buf));
154 	if (i == -1)
155 		return 0;
156 
157 	if (hammer2_options[i].name == NULL)
158 		abort();
159 
160 	switch (hammer2_options[i].letter) {
161 	case 'b':
162 		opt->BootAreaSize = getsize(buf, HAMMER2_NEWFS_ALIGN,
163 		    HAMMER2_BOOT_MAX_BYTES, 2);
164 		break;
165 	case 'r':
166 		opt->AuxAreaSize = getsize(buf, HAMMER2_NEWFS_ALIGN,
167 		    HAMMER2_AUX_MAX_BYTES, 2);
168 		break;
169 	case 'V':
170 		if (strlen(buf) == 0) {
171 			h2_opt->ioctl_cmd = HAMMER2IOC_VERSION_GET;
172 		} else {
173 			opt->Hammer2Version = strtol(buf, NULL, 0);
174 			if (opt->Hammer2Version < HAMMER2_VOL_VERSION_MIN ||
175 			    opt->Hammer2Version >= HAMMER2_VOL_VERSION_WIP)
176 				errx(1, "I don't understand how to format "
177 				    "HAMMER2 version %d",
178 				    opt->Hammer2Version);
179 		}
180 		break;
181 	case 'L':
182 		h2_opt->label_specified = 1;
183 		if (strcasecmp(buf, "none") == 0)
184 			break;
185 		if (opt->NLabels >= MAXLABELS)
186 			errx(1, "Limit of %d local labels", MAXLABELS - 1);
187 		if (strlen(buf) == 0)
188 			errx(1, "Volume label '%s' cannot be 0-length", buf);
189 		if (strlen(buf) >= HAMMER2_INODE_MAXNAME)
190 			errx(1, "Volume label '%s' is too long (%d chars max)",
191 			    buf, HAMMER2_INODE_MAXNAME - 1);
192 		opt->Label[opt->NLabels++] = strdup(buf);
193 		break;
194 	case 'm':
195 		if (strlen(buf) == 0)
196 			errx(1, "Volume label '%s' cannot be 0-length", buf);
197 		if (strlen(buf) >= HAMMER2_INODE_MAXNAME)
198 			errx(1, "Volume label '%s' is too long (%d chars max)",
199 			    buf, HAMMER2_INODE_MAXNAME - 1);
200 		strlcpy(h2_opt->mount_label, buf, sizeof(h2_opt->mount_label));
201 		break;
202 	case 'd':
203 		hammer2_debug = strtoll(buf, NULL, 0);
204 		break;
205 	case 'P':
206 		if (strlen(buf) == 0)
207 			errx(1, "PFS argument '%s' cannot be 0-length", buf);
208 		hammer2_parse_pfs_opts(buf, fsopts);
209 		break;
210 	case 'I':
211 		if (strlen(buf) == 0)
212 			errx(1, "Inode argument '%s' cannot be 0-length", buf);
213 		hammer2_parse_inode_opts(buf, fsopts);
214 		break;
215 	case 'B':
216 		h2_opt->ioctl_cmd = HAMMER2IOC_BULKFREE_SCAN;
217 		break;
218 	case 'D':
219 		h2_opt->ioctl_cmd = HAMMER2IOC_DESTROY;
220 		if (strlen(buf) == 0)
221 			errx(1, "Destroy argument '%s' cannot be 0-length", buf);
222 		if (buf[0] == '/') {
223 			strlcpy(h2_opt->destroy_path, buf + 1,
224 			    sizeof(h2_opt->destroy_path));
225 		} else if (strncmp(buf, "0x", 2) == 0 ||
226 		    (buf[0] >= '0' && buf[0] <= '9')) {
227 			h2_opt->destroy_inum = strtoull(buf, NULL, 0);
228 			if (errno)
229 				err(1, "strtoull");
230 		} else {
231 			errx(1, "Invalid destroy argument %s", buf);
232 		}
233 		break;
234 	case 'G':
235 		h2_opt->ioctl_cmd = HAMMER2IOC_GROWFS;
236 		break;
237 	default:
238 		break;
239 	}
240 
241 	return 1;
242 }
243 
244 void
245 hammer2_makefs(const char *image, const char *dir, fsnode *root,
246     fsinfo_t *fsopts)
247 {
248 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
249 	struct mount mp;
250 	struct hammer2_mount_info info;
251 	struct m_vnode devvp, *vroot;
252 	hammer2_inode_t *iroot;
253 	struct timeval start;
254 	int error;
255 
256 	/* ioctl commands could have NULL root */
257 	assert(image != NULL);
258 	assert(dir != NULL);
259 	assert(fsopts != NULL);
260 
261 	if (debug & DEBUG_FS_MAKEFS)
262 		APRINTF("image \"%s\" directory \"%s\" root %p\n",
263 		    image, dir, root);
264 
265 	/* validate tree and options */
266 	TIMER_START(start);
267 	hammer2_validate(dir, root, fsopts);
268 	TIMER_RESULTS(start, "hammer2_validate");
269 
270 	if (h2_opt->ioctl_cmd) {
271 		/* open existing image */
272 		fsopts->fd = open(image, O_RDWR);
273 		if (fsopts->fd < 0)
274 			err(1, "failed to open `%s'", image);
275 	} else {
276 		/* create image */
277 		TIMER_START(start);
278 		if (hammer2_create_image(image, fsopts) == -1)
279 			errx(1, "image file `%s' not created", image);
280 		TIMER_RESULTS(start, "hammer2_create_image");
281 	}
282 	assert(fsopts->fd > 0);
283 
284 	if (debug & DEBUG_FS_MAKEFS)
285 		putchar('\n');
286 
287 	/* vfs init */
288 	error = hammer2_vfs_init();
289 	if (error)
290 		errx(1, "failed to vfs init, error %d", error);
291 
292 	/* mount image */
293 	memset(&devvp, 0, sizeof(devvp));
294 	devvp.fs = fsopts;
295 	memset(&mp, 0, sizeof(mp));
296 	memset(&info, 0, sizeof(info));
297 	error = hammer2_vfs_mount(&devvp, &mp, h2_opt->mount_label, &info);
298 	if (error)
299 		errx(1, "failed to mount, error %d", error);
300 	assert(mp.mnt_data);
301 
302 	/* get root vnode */
303 	vroot = NULL;
304 	error = hammer2_vfs_root(&mp, &vroot);
305 	if (error)
306 		errx(1, "failed to get root vnode, error %d", error);
307 	assert(vroot);
308 
309 	iroot = VTOI(vroot);
310 	assert(iroot);
311 	printf("root inode inum %lld, mode 0%o, refs %d\n",
312 	    (long long)iroot->meta.inum, iroot->meta.mode, iroot->refs);
313 
314 	if (h2_opt->emergency_mode)
315 		hammer2_ioctl_emerg_mode(iroot, 1);
316 
317 	switch (h2_opt->ioctl_cmd) {
318 	case HAMMER2IOC_VERSION_GET:
319 		printf("version get `%s'\n", image);
320 		TIMER_START(start);
321 		error = hammer2_version_get(vroot);
322 		if (error)
323 			errx(1, "version get `%s' failed '%s'", image,
324 			    strerror(error));
325 		TIMER_RESULTS(start, "hammer2_version_get");
326 		break;
327 	case HAMMER2IOC_PFS_GET:
328 		printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image);
329 		TIMER_START(start);
330 		error = hammer2_pfs_get(vroot);
331 		if (error)
332 			errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name,
333 			    image, strerror(error));
334 		TIMER_RESULTS(start, "hammer2_pfs_get");
335 		break;
336 	case HAMMER2IOC_PFS_LOOKUP:
337 		printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image);
338 		TIMER_START(start);
339 		error = hammer2_pfs_lookup(vroot, h2_opt->pfs_name);
340 		if (error)
341 			errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name,
342 			    image, strerror(error));
343 		TIMER_RESULTS(start, "hammer2_pfs_lookup");
344 		break;
345 	case HAMMER2IOC_PFS_CREATE:
346 		printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image);
347 		TIMER_START(start);
348 		error = hammer2_pfs_create(vroot, h2_opt->pfs_name);
349 		if (error)
350 			errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name,
351 			    image, strerror(error));
352 		TIMER_RESULTS(start, "hammer2_pfs_create");
353 		break;
354 	case HAMMER2IOC_PFS_DELETE:
355 		printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image);
356 		TIMER_START(start);
357 		error = hammer2_pfs_delete(vroot, h2_opt->pfs_name);
358 		if (error)
359 			errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name,
360 			    image, strerror(error));
361 		TIMER_RESULTS(start, "hammer2_pfs_delete");
362 		break;
363 	case HAMMER2IOC_PFS_SNAPSHOT:
364 		printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image);
365 		TIMER_START(start);
366 		error = hammer2_pfs_snapshot(vroot, h2_opt->pfs_name,
367 		    h2_opt->mount_label);
368 		if (error)
369 			errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name,
370 			    image, strerror(error));
371 		TIMER_RESULTS(start, "hammer2_pfs_snapshot");
372 		break;
373 	case HAMMER2IOC_INODE_GET:
374 		printf("inode get `%s'\n", image);
375 		TIMER_START(start);
376 		error = hammer2_inode_getx(vroot, h2_opt->inode_path);
377 		if (error)
378 			errx(1, "inode get `%s' failed '%s'", image,
379 			    strerror(error));
380 		TIMER_RESULTS(start, "hammer2_inode_getx");
381 		break;
382 	case HAMMER2IOC_BULKFREE_SCAN:
383 		printf("bulkfree `%s'\n", image);
384 		TIMER_START(start);
385 		error = hammer2_bulkfree(vroot);
386 		if (error)
387 			errx(1, "bulkfree `%s' failed '%s'", image,
388 			    strerror(error));
389 		TIMER_RESULTS(start, "hammer2_bulkfree");
390 		break;
391 	case HAMMER2IOC_DESTROY:
392 		TIMER_START(start);
393 		if (strlen(h2_opt->destroy_path)) {
394 			printf("destroy `%s' in `%s'\n",
395 			    h2_opt->destroy_path, image);
396 			error = hammer2_destroy_path(vroot,
397 			    h2_opt->destroy_path);
398 			if (error)
399 				errx(1, "destroy `%s' in `%s' failed '%s'",
400 				    h2_opt->destroy_path, image,
401 				    strerror(error));
402 		} else {
403 			printf("destroy %lld in `%s'\n",
404 			    (long long)h2_opt->destroy_inum, image);
405 			error = hammer2_destroy_inum(vroot,
406 			    h2_opt->destroy_inum);
407 			if (error)
408 				errx(1, "destroy %lld in `%s' failed '%s'",
409 				    (long long)h2_opt->destroy_inum, image,
410 				    strerror(error));
411 		}
412 		TIMER_RESULTS(start, "hammer2_destroy");
413 		break;
414 	case HAMMER2IOC_GROWFS:
415 		printf("growfs `%s'\n", image);
416 		TIMER_START(start);
417 		error = hammer2_growfs(vroot, h2_opt->image_size);
418 		if (error)
419 			errx(1, "growfs `%s' failed '%s'", image,
420 			    strerror(error));
421 		TIMER_RESULTS(start, "hammer2_growfs");
422 		break;
423 	default:
424 		printf("populating `%s'\n", image);
425 		TIMER_START(start);
426 		if (hammer2_populate_dir(vroot, dir, root, root, fsopts, 0))
427 			errx(1, "image file `%s' not populated", image);
428 		TIMER_RESULTS(start, "hammer2_populate_dir");
429 		break;
430 	}
431 
432 	/* unmount image */
433 	error = hammer2_vfs_unmount(&mp, 0);
434 	if (error)
435 		errx(1, "failed to unmount, error %d", error);
436 
437 	/* check leaked resource */
438 	if (vnode_count)
439 		printf("XXX %lld vnode left\n", (long long)vnode_count);
440 	if (hammer2_chain_allocs)
441 		printf("XXX %ld chain left\n", hammer2_chain_allocs);
442 	bcleanup();
443 
444 	/* vfs uninit */
445 	error = hammer2_vfs_uninit();
446 	if (error)
447 		errx(1, "failed to vfs uninit, error %d", error);
448 
449 	if (close(fsopts->fd) == -1)
450 		err(1, "closing `%s'", image);
451 	fsopts->fd = -1;
452 
453 	printf("image `%s' complete\n", image);
454 }
455 
456 /* end of public functions */
457 
458 static void
459 hammer2_parse_pfs_opts(const char *buf, fsinfo_t *fsopts)
460 {
461 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
462 	char *o, *p;
463 	size_t n;
464 
465 	o = p = strdup(buf);
466 	p = strchr(p, ':');
467 	if (p != NULL) {
468 		*p++ = 0;
469 		n = strlen(p);
470 	} else {
471 		n = 0;
472 	}
473 
474 	if (!strcmp(o, "get") || !strcmp(o, "list")) {
475 		h2_opt->ioctl_cmd = HAMMER2IOC_PFS_GET;
476 	} else if (!strcmp(o, "lookup")) {
477 		if (n == 0 || n > NAME_MAX)
478 			errx(1, "invalid PFS name \"%s\"", p);
479 		h2_opt->ioctl_cmd = HAMMER2IOC_PFS_LOOKUP;
480 	} else if (!strcmp(o, "create")) {
481 		if (n == 0 || n > NAME_MAX)
482 			errx(1, "invalid PFS name \"%s\"", p);
483 		h2_opt->ioctl_cmd = HAMMER2IOC_PFS_CREATE;
484 	} else if (!strcmp(o, "delete")) {
485 		if (n == 0 || n > NAME_MAX)
486 			errx(1, "invalid PFS name \"%s\"", p);
487 		h2_opt->ioctl_cmd = HAMMER2IOC_PFS_DELETE;
488 	} else if (!strcmp(o, "snapshot")) {
489 		if (n > NAME_MAX)
490 			errx(1, "invalid PFS name \"%s\"", p);
491 		h2_opt->ioctl_cmd = HAMMER2IOC_PFS_SNAPSHOT;
492 	} else {
493 		errx(1, "invalid PFS command \"%s\"", o);
494 	}
495 
496 	strlcpy(h2_opt->pfs_cmd_name, o, sizeof(h2_opt->pfs_cmd_name));
497 	if (n > 0)
498 		strlcpy(h2_opt->pfs_name, p, sizeof(h2_opt->pfs_name));
499 
500 	free(o);
501 }
502 
503 static void
504 hammer2_parse_inode_opts(const char *buf, fsinfo_t *fsopts)
505 {
506 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
507 	char *o, *p;
508 	size_t n;
509 
510 	o = p = strdup(buf);
511 	p = strchr(p, ':');
512 	if (p != NULL) {
513 		*p++ = 0;
514 		n = strlen(p);
515 	} else {
516 		n = 0;
517 	}
518 
519 	if (!strcmp(o, "get")) {
520 		if (n == 0 || n > PATH_MAX)
521 			errx(1, "invalid file path \"%s\"", p);
522 		h2_opt->ioctl_cmd = HAMMER2IOC_INODE_GET;
523 	} else {
524 		errx(1, "invalid inode command \"%s\"", o);
525 	}
526 
527 	if (n > 0) {
528 		if (p[0] == '/')
529 			p++;
530 		strlcpy(h2_opt->inode_path, p, sizeof(h2_opt->inode_path));
531 	}
532 
533 	free(o);
534 }
535 
536 static hammer2_off_t
537 hammer2_image_size(fsinfo_t *fsopts)
538 {
539 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
540 	hammer2_off_t image_size, used_size = 0;
541 	int num_level1, delta_num_level1;
542 
543 	/* use 4 volume headers by default */
544 	num_level1 = h2_opt->num_volhdr * 2; /* default 4 x 2 */
545 	assert(num_level1 != 0);
546 	assert(num_level1 <= 8);
547 
548 	/* add 4MiB segment for each level1 */
549 	used_size += HAMMER2_ZONE_SEG64 * num_level1;
550 
551 	/* add boot/aux area, but exact size unknown at this point */
552 	used_size += HAMMER2_BOOT_NOM_BYTES + HAMMER2_AUX_NOM_BYTES;
553 
554 	/* add data size */
555 	used_size += fsopts->size;
556 
557 	/* XXX add extra level1 for meta data and indirect blocks */
558 	used_size += HAMMER2_FREEMAP_LEVEL1_SIZE;
559 
560 	/* XXX add extra level1 for safety */
561 	if (used_size > HAMMER2_FREEMAP_LEVEL1_SIZE * 10)
562 		used_size += HAMMER2_FREEMAP_LEVEL1_SIZE;
563 
564 	/* use 8GiB image size by default */
565 	image_size = HAMMER2_FREEMAP_LEVEL1_SIZE * num_level1;
566 	printf("trying default image size %s\n", sizetostr(image_size));
567 
568 	/* adjust if image size isn't large enough */
569 	if (used_size > image_size) {
570 		/* determine extra level1 needed */
571 		delta_num_level1 = howmany(used_size - image_size,
572 		    HAMMER2_FREEMAP_LEVEL1_SIZE);
573 
574 		/* adjust used size with 4MiB segment for each extra level1 */
575 		used_size += HAMMER2_ZONE_SEG64 * delta_num_level1;
576 
577 		/* adjust image size with extra level1 */
578 		image_size += HAMMER2_FREEMAP_LEVEL1_SIZE * delta_num_level1;
579 		printf("trying adjusted image size %s\n",
580 		    sizetostr(image_size));
581 
582 		if (used_size > image_size)
583 			errx(1, "invalid used_size %lld > image_size %lld",
584 			    (long long)used_size, (long long)image_size);
585 	}
586 
587 	return image_size;
588 }
589 
590 static const char *
591 hammer2_label_name(int label_type)
592 {
593 	switch (label_type) {
594 	case HAMMER2_LABEL_NONE:
595 		return "NONE";
596 	case HAMMER2_LABEL_BOOT:
597 		return "BOOT";
598 	case HAMMER2_LABEL_ROOT:
599 		return "ROOT";
600 	case HAMMER2_LABEL_DATA:
601 		return "DATA";
602 	default:
603 		assert(0);
604 	}
605 	return NULL;
606 }
607 
608 static void
609 hammer2_validate(const char *dir, fsnode *root, fsinfo_t *fsopts)
610 {
611 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
612 	hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
613 	hammer2_off_t image_size = 0, minsize, maxsize;
614 	const char *s;
615 
616 	assert(dir != NULL);
617 	assert(fsopts != NULL);
618 
619 	if (debug & DEBUG_FS_VALIDATE) {
620 		APRINTF("before defaults set:\n");
621 		hammer2_dump_fsinfo(fsopts);
622 	}
623 
624 	/* makefs only supports "DATA" for default PFS label */
625 	if (!h2_opt->label_specified) {
626 		opt->DefaultLabelType = HAMMER2_LABEL_DATA;
627 		s = hammer2_label_name(opt->DefaultLabelType);
628 		printf("using default label \"%s\"\n", s);
629 	}
630 
631 	/* set default mount PFS label */
632 	if (!strcmp(h2_opt->mount_label, "")) {
633 		s = hammer2_label_name(HAMMER2_LABEL_DATA);
634 		strlcpy(h2_opt->mount_label, s, sizeof(h2_opt->mount_label));
635 		printf("using default mount label \"%s\"\n", s);
636 	}
637 
638 	/* set default number of volume headers */
639 	if (!h2_opt->num_volhdr) {
640 		h2_opt->num_volhdr = HAMMER2_NUM_VOLHDRS;
641 		printf("using default %d volume headers\n", h2_opt->num_volhdr);
642 	}
643 
644 	/* done if ioctl commands */
645 	if (h2_opt->ioctl_cmd) {
646 		if (h2_opt->ioctl_cmd == HAMMER2IOC_GROWFS)
647 			goto ignore_size_dir;
648 		else
649 			goto done;
650 	}
651 
652 	/* calculate data size */
653 	if (fsopts->size != 0)
654 		fsopts->size = 0; /* shouldn't reach here to begin with */
655 	if (root == NULL)
656 		errx(1, "fsnode tree not constructed");
657 	hammer2_size_dir(root, fsopts);
658 	printf("estimated data size %s from %lld inode\n",
659 	    sizetostr(fsopts->size), (long long)fsopts->inodes);
660 
661 	/* determine image size from data size */
662 	image_size = hammer2_image_size(fsopts);
663 	assert((image_size & HAMMER2_FREEMAP_LEVEL1_MASK) == 0);
664 ignore_size_dir:
665 	minsize = roundup(fsopts->minsize, HAMMER2_FREEMAP_LEVEL1_SIZE);
666 	maxsize = roundup(fsopts->maxsize, HAMMER2_FREEMAP_LEVEL1_SIZE);
667 	if (image_size < minsize)
668 		image_size = minsize;
669 	else if (maxsize > 0 && image_size > maxsize)
670 		errx(1, "`%s' size of %lld is larger than the maxsize of %lld",
671 		    dir, (long long)image_size, (long long)maxsize);
672 
673 	assert((image_size & HAMMER2_FREEMAP_LEVEL1_MASK) == 0);
674 	h2_opt->image_size = image_size;
675 	printf("using %s image size\n", sizetostr(h2_opt->image_size));
676 done:
677 	if (debug & DEBUG_FS_VALIDATE) {
678 		APRINTF("after defaults set:\n");
679 		hammer2_dump_fsinfo(fsopts);
680 	}
681 }
682 
683 static void
684 hammer2_dump_fsinfo(fsinfo_t *fsopts)
685 {
686 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
687 	hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
688 	int i;
689 	char *s;
690 
691 	assert(fsopts != NULL);
692 
693 	APRINTF("fsinfo_t at %p\n", fsopts);
694 
695 	printf("\tinodes %lld\n", (long long)fsopts->inodes);
696 	printf("\tsize %lld, minsize %lld, maxsize %lld\n",
697 	    (long long)fsopts->size,
698 	    (long long)fsopts->minsize,
699 	    (long long)fsopts->maxsize);
700 
701 	printf("\thammer2_debug 0x%x\n", hammer2_debug);
702 
703 	printf("\tlabel_specified %d\n", h2_opt->label_specified);
704 	printf("\tmount_label \"%s\"\n", h2_opt->mount_label);
705 	printf("\tnum_volhdr %d\n", h2_opt->num_volhdr);
706 	printf("\tioctl_cmd %ld\n", h2_opt->ioctl_cmd);
707 	printf("\temergency_mode %d\n", h2_opt->emergency_mode);
708 	printf("\tpfs_cmd_name \"%s\"\n", h2_opt->pfs_cmd_name);
709 	printf("\tpfs_name \"%s\"\n", h2_opt->pfs_name);
710 	printf("\tinode_path \"%s\"\n", h2_opt->inode_path);
711 	printf("\tdestroy_path \"%s\"\n", h2_opt->destroy_path);
712 	printf("\tdestroy_inum %lld\n", (long long)h2_opt->destroy_inum);
713 	printf("\timage_size 0x%llx\n", (long long)h2_opt->image_size);
714 
715 	printf("\tHammer2Version %d\n", opt->Hammer2Version);
716 	printf("\tBootAreaSize 0x%jx\n", opt->BootAreaSize);
717 	printf("\tAuxAreaSize 0x%jx\n", opt->AuxAreaSize);
718 	printf("\tNLabels %d\n", opt->NLabels);
719 	printf("\tCompType %d\n", opt->CompType);
720 	printf("\tCheckType %d\n", opt->CheckType);
721 	printf("\tDefaultLabelType %d\n", opt->DefaultLabelType);
722 	printf("\tDebugOpt %d\n", opt->DebugOpt);
723 
724 	s = NULL;
725 	hammer2_uuid_to_str(&opt->Hammer2_FSType, &s);
726 	printf("\tHammer2_FSType \"%s\"\n", s);
727 	s = NULL;
728 	hammer2_uuid_to_str(&opt->Hammer2_VolFSID, &s);
729 	printf("\tHammer2_VolFSID \"%s\"\n", s);
730 	s = NULL;
731 	hammer2_uuid_to_str(&opt->Hammer2_SupCLID, &s);
732 	printf("\tHammer2_SupCLID \"%s\"\n", s);
733 	s = NULL;
734 	hammer2_uuid_to_str(&opt->Hammer2_SupFSID, &s);
735 	printf("\tHammer2_SupFSID \"%s\"\n", s);
736 
737 	for (i = 0; i < opt->NLabels; i++) {
738 		printf("\tLabel[%d] \"%s\"\n", i, opt->Label[i]);
739 		s = NULL;
740 		hammer2_uuid_to_str(&opt->Hammer2_PfsCLID[i], &s);
741 		printf("\t Hammer2_PfsCLID[%d] \"%s\"\n", i, s);
742 		s = NULL;
743 		hammer2_uuid_to_str(&opt->Hammer2_PfsFSID[i], &s);
744 		printf("\t Hammer2_PfsFSID[%d] \"%s\"\n", i, s);
745 	}
746 
747 	free(s);
748 }
749 
750 static int
751 hammer2_create_image(const char *image, fsinfo_t *fsopts)
752 {
753 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
754 	hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
755 	char *av[] = { (char *)image, }; /* XXX support multi-volumes */
756 	char *buf;
757 	int i, bufsize, oflags;
758 	off_t bufrem;
759 
760 	assert(image != NULL);
761 	assert(fsopts != NULL);
762 
763 	/* create image */
764 	oflags = O_RDWR | O_CREAT;
765 	if (fsopts->offset == 0)
766 		oflags |= O_TRUNC;
767 	if ((fsopts->fd = open(image, oflags, 0666)) == -1) {
768 		warn("can't open `%s' for writing", image);
769 		return -1;
770 	}
771 
772 	/* zero image */
773 	bufsize = HAMMER2_PBUFSIZE;
774 	bufrem = h2_opt->image_size;
775 	if (fsopts->sparse) {
776 		if (ftruncate(fsopts->fd, bufrem) == -1) {
777 			warn("sparse option disabled");
778 			fsopts->sparse = 0;
779 		}
780 	}
781 	if (fsopts->sparse) {
782 		/* File truncated at bufrem. Remaining is 0 */
783 		bufrem = 0;
784 		buf = NULL;
785 	} else {
786 		if (debug & DEBUG_FS_CREATE_IMAGE)
787 			APRINTF("zero-ing image `%s', %lld sectors, "
788 			    "using %d byte chunks\n",
789 			    image, (long long)bufrem, bufsize);
790 		buf = ecalloc(1, bufsize);
791 	}
792 
793 	if (fsopts->offset != 0) {
794 		if (lseek(fsopts->fd, fsopts->offset, SEEK_SET) == -1) {
795 			warn("can't seek");
796 			free(buf);
797 			return -1;
798 		}
799 	}
800 
801 	while (bufrem > 0) {
802 		i = write(fsopts->fd, buf, MIN(bufsize, bufrem));
803 		if (i == -1) {
804 			warn("zeroing image, %lld bytes to go",
805 			    (long long)bufrem);
806 			free(buf);
807 			return -1;
808 		}
809 		bufrem -= i;
810 	}
811 	if (buf)
812 		free(buf);
813 
814 	/* make the file system */
815 	if (debug & DEBUG_FS_CREATE_IMAGE)
816 		APRINTF("calling mkfs(\"%s\", ...)\n", image);
817 	hammer2_mkfs(1, av, opt); /* success if returned */
818 
819 	return fsopts->fd;
820 }
821 
822 static off_t
823 hammer2_phys_size(off_t size)
824 {
825 	off_t radix_size, phys_size = 0;
826 	int i;
827 
828 	if (size > HAMMER2_PBUFSIZE) {
829 		phys_size += rounddown(size, HAMMER2_PBUFSIZE);
830 		size = size % HAMMER2_PBUFSIZE;
831 	}
832 
833 	for (i = HAMMER2_RADIX_MIN; i <= HAMMER2_RADIX_MAX; i++) {
834 		radix_size = 1UL << i;
835 		if (radix_size >= size) {
836 			phys_size += radix_size;
837 			break;
838 		}
839 	}
840 
841 	return phys_size;
842 }
843 
844 /* calculate data size */
845 static void
846 hammer2_size_dir(fsnode *root, fsinfo_t *fsopts)
847 {
848 	fsnode *node;
849 
850 	assert(fsopts != NULL);
851 
852 	if (debug & DEBUG_FS_SIZE_DIR)
853 		APRINTF("entry: bytes %lld inodes %lld\n",
854 		    (long long)fsopts->size, (long long)fsopts->inodes);
855 
856 	for (node = root; node != NULL; node = node->next) {
857 		if (node == root) { /* we're at "." */
858 			assert(strcmp(node->name, ".") == 0);
859 		} else if ((node->inode->flags & FI_SIZED) == 0) {
860 			/* don't count duplicate names */
861 			node->inode->flags |= FI_SIZED;
862 			if (debug & DEBUG_FS_SIZE_DIR_NODE)
863 				APRINTF("`%s' size %lld\n",
864 				    node->name,
865 				    (long long)node->inode->st.st_size);
866 			fsopts->inodes++;
867 			fsopts->size += sizeof(hammer2_inode_data_t);
868 			if (node->type == S_IFREG) {
869 				size_t st_size = node->inode->st.st_size;
870 				if (st_size > HAMMER2_EMBEDDED_BYTES)
871 					fsopts->size += hammer2_phys_size(st_size);
872 			} else if (node->type == S_IFLNK) {
873 				size_t nlen = strlen(node->symlink);
874 				if (nlen > HAMMER2_EMBEDDED_BYTES)
875 					fsopts->size += hammer2_phys_size(nlen);
876 			}
877 		}
878 		if (node->type == S_IFDIR)
879 			hammer2_size_dir(node->child, fsopts);
880 	}
881 
882 	if (debug & DEBUG_FS_SIZE_DIR)
883 		APRINTF("exit: size %lld inodes %lld\n",
884 		    (long long)fsopts->size, (long long)fsopts->inodes);
885 }
886 
887 static void
888 hammer2_print(const struct m_vnode *dvp, const struct m_vnode *vp,
889     const fsnode *node, int depth, const char *msg)
890 {
891 	if (debug & DEBUG_FS_POPULATE) {
892 		if (1) {
893 			int indent = depth * 2;
894 			char *type;
895 			if (S_ISDIR(node->type))
896 				type = "dir";
897 			else if (S_ISREG(node->type))
898 				type = "reg";
899 			else if (S_ISLNK(node->type))
900 				type = "lnk";
901 			else if (S_ISFIFO(node->type))
902 				type = "fifo";
903 			else
904 				type = "???";
905 			printf("%*.*s", indent, indent, "");
906 			printf("dvp=%p/%d vp=%p/%d \"%s\" %s %s\n",
907 			    dvp, dvp ? VTOI(dvp)->refs : 0,
908 			    vp, vp ? VTOI(vp)->refs : 0,
909 			    node->name, type, msg);
910 		} else {
911 			char type;
912 			if (S_ISDIR(node->type))
913 				type = 'd';
914 			else if (S_ISREG(node->type))
915 				type = 'r';
916 			else if (S_ISLNK(node->type))
917 				type = 'l';
918 			else if (S_ISFIFO(node->type))
919 				type = 'f';
920 			else
921 				type = '?';
922 			printf("%c", type);
923 			fflush(stdout);
924 		}
925 	}
926 }
927 
928 static int
929 hammer2_populate_dir(struct m_vnode *dvp, const char *dir, fsnode *root,
930     fsnode *parent, fsinfo_t *fsopts, int depth)
931 {
932 	fsnode *cur;
933 	struct m_vnode *vp;
934 	struct stat st;
935 	char f[MAXPATHLEN];
936 	const char *path;
937 	int hardlink;
938 	int error;
939 
940 	assert(dvp != NULL);
941 	assert(dir != NULL);
942 	assert(root != NULL);
943 	assert(parent != NULL);
944 	assert(fsopts != NULL);
945 
946 	/* assert root directory */
947 	assert(S_ISDIR(root->type));
948 	assert(!strcmp(root->name, "."));
949 	assert(!root->child);
950 	assert(!root->parent || root->parent->child == root);
951 
952 	hammer2_print(dvp, NULL, root, depth, "enter");
953 	if (stat(dir, &st) == -1)
954 		err(1, "no such path %s", dir);
955 	if (!S_ISDIR(st.st_mode))
956 		errx(1, "no such dir %s", dir);
957 
958 	for (cur = root->next; cur != NULL; cur = cur->next) {
959 		/* global variable for HAMMER2 vnops */
960 		hammer2_curnode = cur;
961 
962 		/* construct source path */
963 		if (cur->contents) {
964 			path = cur->contents;
965 		} else {
966 			if (S_ISDIR(cur->type)) {
967 				/* this should be same as root/path/name */
968 				if (snprintf(f, sizeof(f), "%s/%s",
969 				    dir, cur->name) >= (int)sizeof(f))
970 					errx(1, "path %s too long", f);
971 			} else {
972 				if (snprintf(f, sizeof(f), "%s/%s/%s",
973 				    cur->root, cur->path, cur->name) >= (int)sizeof(f))
974 					errx(1, "path %s too long", f);
975 			}
976 			path = f;
977 		}
978 		if (stat(path, &st) == -1)
979 			err(1, "no such file %s", f);
980 
981 		/* update node state */
982 		if ((cur->inode->flags & FI_ALLOCATED) == 0) {
983 			cur->inode->flags |= FI_ALLOCATED;
984 			if (cur != root)
985 				cur->parent = parent;
986 		}
987 
988 		/* detect hardlink */
989 		if (cur->inode->flags & FI_WRITTEN) {
990 			assert(!S_ISDIR(cur->type));
991 			hardlink = 1;
992 		} else {
993 			hardlink = 0;
994 		}
995 		cur->inode->flags |= FI_WRITTEN;
996 
997 		/* make sure it doesn't exist yet */
998 		vp = NULL;
999 		error = hammer2_nresolve(dvp, &vp, cur->name,
1000 		    strlen(cur->name));
1001 		if (!error)
1002 			errx(1, "hammer2_nresolve(\"%s\") already exists",
1003 			    cur->name);
1004 		hammer2_print(dvp, vp, cur, depth, "nresolve");
1005 
1006 		/* if directory, mkdir and recurse */
1007 		if (S_ISDIR(cur->type)) {
1008 			assert(cur->child);
1009 
1010 			vp = NULL;
1011 			error = hammer2_nmkdir(dvp, &vp, cur->name,
1012 			    strlen(cur->name), cur->inode->st.st_mode);
1013 			if (error)
1014 				errx(1, "hammer2_nmkdir(\"%s\") failed: %s",
1015 				    cur->name, strerror(error));
1016 			assert(vp);
1017 			hammer2_print(dvp, vp, cur, depth, "nmkdir");
1018 
1019 			error = hammer2_populate_dir(vp, path, cur->child, cur,
1020 			    fsopts, depth + 1);
1021 			if (error)
1022 				errx(1, "failed to populate %s: %s",
1023 				    path, strerror(error));
1024 			cur->inode->param = vp;
1025 			continue;
1026 		}
1027 
1028 		/* if regular file, creat and write its data */
1029 		if (S_ISREG(cur->type) && !hardlink) {
1030 			assert(cur->child == NULL);
1031 
1032 			vp = NULL;
1033 			error = hammer2_ncreate(dvp, &vp, cur->name,
1034 			    strlen(cur->name), cur->inode->st.st_mode);
1035 			if (error)
1036 				errx(1, "hammer2_ncreate(\"%s\") failed: %s",
1037 				    cur->name, strerror(error));
1038 			assert(vp);
1039 			hammer2_print(dvp, vp, cur, depth, "ncreate");
1040 
1041 			error = hammer2_write_file(vp, path, cur);
1042 			if (error)
1043 				errx(1, "hammer2_write_file(\"%s\") failed: %s",
1044 				    path, strerror(error));
1045 			cur->inode->param = vp;
1046 			continue;
1047 		}
1048 
1049 		/* if symlink, create a symlink against target */
1050 		if (S_ISLNK(cur->type)) {
1051 			assert(cur->child == NULL);
1052 
1053 			vp = NULL;
1054 			error = hammer2_nsymlink(dvp, &vp, cur->name,
1055 			    strlen(cur->name), cur->symlink,
1056 			    cur->inode->st.st_mode);
1057 			if (error)
1058 				errx(1, "hammer2_nsymlink(\"%s\") failed: %s",
1059 				    cur->name, strerror(error));
1060 			assert(vp);
1061 			hammer2_print(dvp, vp, cur, depth, "nsymlink");
1062 			cur->inode->param = vp;
1063 			continue;
1064 		}
1065 
1066 		/* if fifo, create a fifo */
1067 		if (S_ISFIFO(cur->type) && !hardlink) {
1068 			assert(cur->child == NULL);
1069 
1070 			vp = NULL;
1071 			error = hammer2_nmknod(dvp, &vp, cur->name,
1072 			    strlen(cur->name), VFIFO, cur->inode->st.st_mode);
1073 			if (error)
1074 				errx(1, "hammer2_nmknod(\"%s\") failed: %s",
1075 				    cur->name, strerror(error));
1076 			assert(vp);
1077 			hammer2_print(dvp, vp, cur, depth, "nmknod");
1078 			cur->inode->param = vp;
1079 			continue;
1080 		}
1081 
1082 		/* if hardlink, creat a hardlink */
1083 		if ((S_ISREG(cur->type) || S_ISFIFO(cur->type)) && hardlink) {
1084 			char buf[64];
1085 			assert(cur->child == NULL);
1086 
1087 			/* source vnode must not be NULL */
1088 			vp = cur->inode->param;
1089 			assert(vp);
1090 			/* currently these conditions must be true */
1091 			assert(vp->v_data);
1092 			assert(vp->v_type == VREG || vp->v_type == VFIFO);
1093 			assert(vp->v_logical);
1094 			assert(!vp->v_vflushed);
1095 			assert(vp->v_malloced);
1096 			assert(VTOI(vp)->refs > 0);
1097 
1098 			error = hammer2_nlink(dvp, vp, cur->name,
1099 			    strlen(cur->name));
1100 			if (error)
1101 				errx(1, "hammer2_nlink(\"%s\") failed: %s",
1102 				    cur->name, strerror(error));
1103 			snprintf(buf, sizeof(buf), "nlink=%lld",
1104 			    (long long)VTOI(vp)->meta.nlinks);
1105 			hammer2_print(dvp, vp, cur, depth, buf);
1106 			continue;
1107 		}
1108 
1109 		/* other types are unsupported */
1110 		printf("ignore %s 0%o\n", path, cur->type);
1111 	}
1112 
1113 	return 0;
1114 }
1115 
1116 static int
1117 hammer2_write_file(struct m_vnode *vp, const char *path, fsnode *node)
1118 {
1119 	struct stat *st = &node->inode->st;
1120 	size_t nsize, bufsize;
1121 	off_t offset;
1122 	int fd, error;
1123 	char *p;
1124 
1125 	nsize = st->st_size;
1126 	if (nsize == 0)
1127 		return 0;
1128 	/* check nsize vs maximum file size */
1129 
1130 	fd = open(path, O_RDONLY);
1131 	if (fd < 0)
1132 		err(1, "failed to open %s", path);
1133 
1134 	p = mmap(0, nsize, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0);
1135 	if (p == MAP_FAILED)
1136 		err(1, "failed to mmap %s", path);
1137 	close(fd);
1138 
1139 	for (offset = 0; offset < nsize; ) {
1140 		bufsize = MIN(nsize - offset, HAMMER2_PBUFSIZE);
1141 		assert(bufsize <= HAMMER2_PBUFSIZE);
1142 		error = hammer2_write(vp, p + offset, bufsize, offset);
1143 		if (error)
1144 			errx(1, "failed to write to %s vnode: %s",
1145 			    path, strerror(error));
1146 		offset += bufsize;
1147 		if (bufsize == HAMMER2_PBUFSIZE)
1148 			assert((offset & (HAMMER2_PBUFSIZE - 1)) == 0);
1149 	}
1150 	munmap(p, nsize);
1151 
1152 	return 0;
1153 }
1154 
1155 static int
1156 hammer2_version_get(struct m_vnode *vp)
1157 {
1158 	hammer2_dev_t *hmp;
1159 
1160 	hmp = VTOI(vp)->pmp->pfs_hmps[0];
1161 	if (hmp == NULL)
1162 		return EINVAL;
1163 
1164 	printf("version: %d\n", hmp->voldata.version);
1165 
1166 	return 0;
1167 }
1168 
1169 struct pfs_entry {
1170 	TAILQ_ENTRY(pfs_entry) entry;
1171 	char name[NAME_MAX+1];
1172 	char s[NAME_MAX+1];
1173 };
1174 
1175 static int
1176 hammer2_pfs_get(struct m_vnode *vp)
1177 {
1178 	hammer2_ioc_pfs_t pfs;
1179 	TAILQ_HEAD(, pfs_entry) head;
1180 	struct pfs_entry *p, *e;
1181 	char *pfs_id_str;
1182 	const char *type_str;
1183 	int error;
1184 
1185 	bzero(&pfs, sizeof(pfs));
1186 	TAILQ_INIT(&head);
1187 
1188 	while ((pfs.name_key = pfs.name_next) != (hammer2_key_t)-1) {
1189 		error = hammer2_ioctl_pfs_get(VTOI(vp), &pfs);
1190 		if (error)
1191 			return error;
1192 
1193 		pfs_id_str = NULL;
1194 		hammer2_uuid_to_str(&pfs.pfs_clid, &pfs_id_str);
1195 
1196 		if (pfs.pfs_type == HAMMER2_PFSTYPE_MASTER) {
1197 			if (pfs.pfs_subtype == HAMMER2_PFSSUBTYPE_NONE)
1198 				type_str = "MASTER";
1199 			else
1200 				type_str = hammer2_pfssubtype_to_str(
1201 				    pfs.pfs_subtype);
1202 		} else {
1203 			type_str = hammer2_pfstype_to_str(pfs.pfs_type);
1204 		}
1205 		e = ecalloc(1, sizeof(*e));
1206 		snprintf(e->name, sizeof(e->name), "%s", pfs.name);
1207 		snprintf(e->s, sizeof(e->s), "%-11s %s", type_str, pfs_id_str);
1208 		free(pfs_id_str);
1209 
1210 		p = TAILQ_FIRST(&head);
1211 		while (p) {
1212 			if (strcmp(e->name, p->name) <= 0) {
1213 				TAILQ_INSERT_BEFORE(p, e, entry);
1214 				break;
1215 			}
1216 			p = TAILQ_NEXT(p, entry);
1217 		}
1218 		if (!p)
1219 			TAILQ_INSERT_TAIL(&head, e, entry);
1220 	}
1221 
1222 	printf("Type        "
1223 	    "ClusterId (pfs_clid)                 "
1224 	    "Label\n");
1225 	while ((p = TAILQ_FIRST(&head)) != NULL) {
1226 		printf("%s %s\n", p->s, p->name);
1227 		TAILQ_REMOVE(&head, p, entry);
1228 		free(p);
1229 	}
1230 
1231 	return 0;
1232 }
1233 
1234 static int
1235 hammer2_pfs_lookup(struct m_vnode *vp, const char *pfs_name)
1236 {
1237 	hammer2_ioc_pfs_t pfs;
1238 	char *pfs_id_str;
1239 	int error;
1240 
1241 	bzero(&pfs, sizeof(pfs));
1242 	strlcpy(pfs.name, pfs_name, sizeof(pfs.name));
1243 
1244 	error = hammer2_ioctl_pfs_lookup(VTOI(vp), &pfs);
1245 	if (error == 0) {
1246 		printf("name: %s\n", pfs.name);
1247 		printf("type: %s\n", hammer2_pfstype_to_str(pfs.pfs_type));
1248 		printf("subtype: %s\n",
1249 		    hammer2_pfssubtype_to_str(pfs.pfs_subtype));
1250 
1251 		pfs_id_str = NULL;
1252 		hammer2_uuid_to_str(&pfs.pfs_fsid, &pfs_id_str);
1253 		printf("fsid: %s\n", pfs_id_str);
1254 		free(pfs_id_str);
1255 
1256 		pfs_id_str = NULL;
1257 		hammer2_uuid_to_str(&pfs.pfs_clid, &pfs_id_str);
1258 		printf("clid: %s\n", pfs_id_str);
1259 		free(pfs_id_str);
1260 	}
1261 
1262 	return error;
1263 }
1264 
1265 static int
1266 hammer2_pfs_create(struct m_vnode *vp, const char *pfs_name)
1267 {
1268 	hammer2_ioc_pfs_t pfs;
1269 	int error;
1270 
1271 	bzero(&pfs, sizeof(pfs));
1272 	strlcpy(pfs.name, pfs_name, sizeof(pfs.name));
1273 	pfs.pfs_type = HAMMER2_PFSTYPE_MASTER;
1274 	uuid_create(&pfs.pfs_clid, NULL);
1275 	uuid_create(&pfs.pfs_fsid, NULL);
1276 
1277 	error = hammer2_ioctl_pfs_create(VTOI(vp), &pfs);
1278 	if (error == EEXIST)
1279 		fprintf(stderr,
1280 		    "NOTE: Typically the same name is "
1281 		    "used for cluster elements on "
1282 		    "different mounts,\n"
1283 		    "      but cluster elements on the "
1284 		    "same mount require unique names.\n"
1285 		    "hammer2: pfs_create(%s): already present\n",
1286 		    pfs_name);
1287 
1288 	return error;
1289 }
1290 
1291 static int
1292 hammer2_pfs_delete(struct m_vnode *vp, const char *pfs_name)
1293 {
1294 	hammer2_ioc_pfs_t pfs;
1295 
1296 	bzero(&pfs, sizeof(pfs));
1297 	strlcpy(pfs.name, pfs_name, sizeof(pfs.name));
1298 
1299 	return hammer2_ioctl_pfs_delete(VTOI(vp), &pfs);
1300 }
1301 
1302 static int
1303 hammer2_pfs_snapshot(struct m_vnode *vp, const char *pfs_name,
1304     const char *mount_label)
1305 {
1306 	hammer2_ioc_pfs_t pfs;
1307 	struct tm *tp;
1308 	time_t t;
1309 
1310 	bzero(&pfs, sizeof(pfs));
1311 	strlcpy(pfs.name, pfs_name, sizeof(pfs.name));
1312 
1313 	if (strlen(pfs.name) == 0) {
1314 		time(&t);
1315 		tp = localtime(&t);
1316 		snprintf(pfs.name, sizeof(pfs.name),
1317 		    "%s.%04d%02d%02d.%02d%02d%02d",
1318 		    mount_label,
1319 		    tp->tm_year + 1900,
1320 		    tp->tm_mon + 1,
1321 		    tp->tm_mday,
1322 		    tp->tm_hour,
1323 		    tp->tm_min,
1324 		    tp->tm_sec);
1325 	}
1326 
1327 	return hammer2_ioctl_pfs_snapshot(VTOI(vp), &pfs);
1328 }
1329 
1330 static int
1331 hammer2_inode_getx(struct m_vnode *dvp, const char *f)
1332 {
1333 	hammer2_ioc_inode_t inode;
1334 	hammer2_inode_t *ip;
1335 	hammer2_inode_meta_t *meta;
1336 	struct m_vnode *vp;
1337 	char *o, *p, *name, *str = NULL;
1338 	int error;
1339 	uuid_t uuid;
1340 
1341 	assert(strlen(f) > 0);
1342 	o = p = strdup(f);
1343 
1344 	/* trim trailing '/' */
1345 	p += strlen(p);
1346 	p--;
1347 	while (p >= o && *p == '/')
1348 		p--;
1349 	*++p = 0;
1350 
1351 	name = p = o;
1352 	if (strlen(p) == 0)
1353 		return EINVAL;
1354 
1355 	while ((p = strchr(p, '/')) != NULL) {
1356 		*p++ = 0; /* NULL terminate name */
1357 		vp = NULL;
1358 		error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1359 		if (error)
1360 			return error;
1361 
1362 		ip = VTOI(vp);
1363 		assert(ip->meta.type == HAMMER2_OBJTYPE_DIRECTORY);
1364 
1365 		dvp = vp;
1366 		name = p;
1367 	}
1368 
1369 	error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1370 	if (error)
1371 		return error;
1372 
1373 	bzero(&inode, sizeof(inode));
1374 	error = hammer2_ioctl_inode_get(VTOI(vp), &inode);
1375 	if (error)
1376 		return error;
1377 
1378 	meta = &inode.ip_data.meta;
1379 	printf("--------------------\n");
1380 	printf("flags = 0x%x\n", inode.flags);
1381 	printf("data_count = %ju\n", (uintmax_t)inode.data_count);
1382 	printf("inode_count = %ju\n", (uintmax_t)inode.inode_count);
1383 	printf("--------------------\n");
1384 	printf("version = %u\n", meta->version);
1385 	printf("pfs_subtype = %u (%s)\n", meta->pfs_subtype,
1386 	    hammer2_pfssubtype_to_str(meta->pfs_subtype));
1387 	printf("uflags = 0x%x\n", (unsigned int)meta->uflags);
1388 	printf("rmajor = %u\n", meta->rmajor);
1389 	printf("rminor = %u\n", meta->rminor);
1390 	printf("ctime = %s\n", hammer2_time64_to_str(meta->ctime, &str));
1391 	printf("mtime = %s\n", hammer2_time64_to_str(meta->mtime, &str));
1392 	printf("atime = %s\n", hammer2_time64_to_str(meta->atime, &str));
1393 	printf("btime = %s\n", hammer2_time64_to_str(meta->btime, &str));
1394 	uuid = meta->uid;
1395 	printf("uid = %s\n", hammer2_uuid_to_str(&uuid, &str));
1396 	uuid = meta->gid;
1397 	printf("gid = %s\n", hammer2_uuid_to_str(&uuid, &str));
1398 	printf("type = %u (%s)\n", meta->type,
1399 	    hammer2_iptype_to_str(meta->type));
1400 	printf("op_flags = 0x%x\n", meta->op_flags);
1401 	printf("cap_flags = 0x%x\n", meta->cap_flags);
1402 	printf("mode = 0%o\n", meta->mode);
1403 	printf("inum = 0x%jx\n", (uintmax_t)meta->inum);
1404 	printf("size = %ju\n", (uintmax_t)meta->size);
1405 	printf("nlinks = %ju\n", (uintmax_t)meta->nlinks);
1406 	printf("iparent = 0x%jx\n", (uintmax_t)meta->iparent);
1407 	printf("name_key = 0x%jx\n", (uintmax_t)meta->name_key);
1408 	printf("name_len = %u\n", meta->name_len);
1409 	printf("ncopies = %u\n", meta->ncopies);
1410 	printf("comp_algo = %u\n", meta->comp_algo);
1411 	printf("target_type = %u\n", meta->target_type);
1412 	printf("check_algo = %u\n", meta->check_algo);
1413 	printf("pfs_nmasters = %u\n", meta->pfs_nmasters);
1414 	printf("pfs_type = %u (%s)\n", meta->pfs_type,
1415 	    hammer2_pfstype_to_str(meta->pfs_type));
1416 	printf("pfs_inum = 0x%jx\n", (uintmax_t)meta->pfs_inum);
1417 	uuid = meta->pfs_clid;
1418 	printf("pfs_clid = %s\n", hammer2_uuid_to_str(&uuid, &str));
1419 	uuid = meta->pfs_fsid;
1420 	printf("pfs_fsid = %s\n", hammer2_uuid_to_str(&uuid, &str));
1421 	printf("data_quota = 0x%jx\n", (uintmax_t)meta->data_quota);
1422 	printf("inode_quota = 0x%jx\n", (uintmax_t)meta->inode_quota);
1423 	printf("pfs_lsnap_tid = 0x%jx\n", (uintmax_t)meta->pfs_lsnap_tid);
1424 	printf("decrypt_check = 0x%jx\n", (uintmax_t)meta->decrypt_check);
1425 	printf("--------------------\n");
1426 
1427 	free(o);
1428 
1429 	return error;
1430 }
1431 
1432 static int
1433 hammer2_bulkfree(struct m_vnode *vp)
1434 {
1435 	hammer2_ioc_bulkfree_t bfi;
1436 	size_t usermem;
1437 	size_t usermem_size = sizeof(usermem);
1438 
1439 	bzero(&bfi, sizeof(bfi));
1440 	usermem = 0;
1441 	if (sysctlbyname("hw.usermem", &usermem, &usermem_size, NULL, 0) == 0)
1442 		bfi.size = usermem / 16;
1443 	else
1444 		bfi.size = 0;
1445 	if (bfi.size < 8192 * 1024)
1446 		bfi.size = 8192 * 1024;
1447 
1448 	return hammer2_ioctl_bulkfree_scan(VTOI(vp), &bfi);
1449 }
1450 
1451 static int
1452 hammer2_destroy_path(struct m_vnode *dvp, const char *f)
1453 {
1454 	hammer2_ioc_destroy_t destroy;
1455 	hammer2_inode_t *ip;
1456 	struct m_vnode *vp;
1457 	char *o, *p, *name;
1458 	int error;
1459 
1460 	assert(strlen(f) > 0);
1461 	o = p = strdup(f);
1462 
1463 	/* trim trailing '/' */
1464 	p += strlen(p);
1465 	p--;
1466 	while (p >= o && *p == '/')
1467 		p--;
1468 	*++p = 0;
1469 
1470 	name = p = o;
1471 	if (strlen(p) == 0)
1472 		return EINVAL;
1473 
1474 	while ((p = strchr(p, '/')) != NULL) {
1475 		*p++ = 0; /* NULL terminate name */
1476 		vp = NULL;
1477 		error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1478 		if (error)
1479 			return error;
1480 
1481 		ip = VTOI(vp);
1482 		assert(ip->meta.type == HAMMER2_OBJTYPE_DIRECTORY);
1483 		/* XXX When does (or why does not) ioctl modify this inode ? */
1484 		hammer2_inode_modify(ip);
1485 
1486 		dvp = vp;
1487 		name = p;
1488 	}
1489 
1490 	bzero(&destroy, sizeof(destroy));
1491 	destroy.cmd = HAMMER2_DELETE_FILE;
1492 	snprintf(destroy.path, sizeof(destroy.path), "%s", name);
1493 
1494 	printf("%s\t", f);
1495 	fflush(stdout);
1496 
1497 	error = hammer2_ioctl_destroy(VTOI(dvp), &destroy);
1498 	if (error)
1499 		printf("%s\n", strerror(error));
1500 	else
1501 		printf("ok\n");
1502 	free(o);
1503 
1504 	return error;
1505 }
1506 
1507 static int
1508 hammer2_destroy_inum(struct m_vnode *vp, hammer2_tid_t inum)
1509 {
1510 	hammer2_ioc_destroy_t destroy;
1511 	int error;
1512 
1513 	bzero(&destroy, sizeof(destroy));
1514 	destroy.cmd = HAMMER2_DELETE_INUM;
1515 	destroy.inum = inum;
1516 
1517 	printf("%jd\t", (intmax_t)destroy.inum);
1518 	fflush(stdout);
1519 
1520 	error = hammer2_ioctl_destroy(VTOI(vp), &destroy);
1521 	if (error)
1522 		printf("%s\n", strerror(error));
1523 	else
1524 		printf("ok\n");
1525 
1526 	return error;
1527 }
1528 
1529 static int
1530 hammer2_growfs(struct m_vnode *vp, hammer2_off_t size)
1531 {
1532 	hammer2_ioc_growfs_t growfs;
1533 	int error;
1534 
1535 	bzero(&growfs, sizeof(growfs));
1536 	growfs.size = size;
1537 
1538 	error = hammer2_ioctl_growfs(VTOI(vp), &growfs, NULL);
1539 	if (!error) {
1540 		if (growfs.modified)
1541 			printf("grown to %016jx\n", (intmax_t)growfs.size);
1542 		else
1543 			printf("no size change - %016jx\n",
1544 			    (intmax_t)growfs.size);
1545 	}
1546 
1547 	return error;
1548 }
1549