xref: /dragonfly/usr.sbin/makefs/hammer2.c (revision 2d60b848)
1*2d60b848STomohiro Kusumi /*
2*2d60b848STomohiro Kusumi  * SPDX-License-Identifier: BSD-3-Clause
3*2d60b848STomohiro Kusumi  *
4*2d60b848STomohiro Kusumi  * Copyright (c) 2022 Tomohiro Kusumi <tkusumi@netbsd.org>
5*2d60b848STomohiro Kusumi  * Copyright (c) 2011-2022 The DragonFly Project.  All rights reserved.
6*2d60b848STomohiro Kusumi  *
7*2d60b848STomohiro Kusumi  * Redistribution and use in source and binary forms, with or without
8*2d60b848STomohiro Kusumi  * modification, are permitted provided that the following conditions
9*2d60b848STomohiro Kusumi  * are met:
10*2d60b848STomohiro Kusumi  *
11*2d60b848STomohiro Kusumi  * 1. Redistributions of source code must retain the above copyright
12*2d60b848STomohiro Kusumi  *    notice, this list of conditions and the following disclaimer.
13*2d60b848STomohiro Kusumi  * 2. Redistributions in binary form must reproduce the above copyright
14*2d60b848STomohiro Kusumi  *    notice, this list of conditions and the following disclaimer in
15*2d60b848STomohiro Kusumi  *    the documentation and/or other materials provided with the
16*2d60b848STomohiro Kusumi  *    distribution.
17*2d60b848STomohiro Kusumi  * 3. Neither the name of The DragonFly Project nor the names of its
18*2d60b848STomohiro Kusumi  *    contributors may be used to endorse or promote products derived
19*2d60b848STomohiro Kusumi  *    from this software without specific, prior written permission.
20*2d60b848STomohiro Kusumi  *
21*2d60b848STomohiro Kusumi  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22*2d60b848STomohiro Kusumi  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23*2d60b848STomohiro Kusumi  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24*2d60b848STomohiro Kusumi  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25*2d60b848STomohiro Kusumi  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26*2d60b848STomohiro Kusumi  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27*2d60b848STomohiro Kusumi  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28*2d60b848STomohiro Kusumi  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29*2d60b848STomohiro Kusumi  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30*2d60b848STomohiro Kusumi  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31*2d60b848STomohiro Kusumi  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32*2d60b848STomohiro Kusumi  * SUCH DAMAGE.
33*2d60b848STomohiro Kusumi  */
34*2d60b848STomohiro Kusumi 
35*2d60b848STomohiro Kusumi #if HAVE_NBTOOL_CONFIG_H
36*2d60b848STomohiro Kusumi #include "nbtool_config.h"
37*2d60b848STomohiro Kusumi #endif
38*2d60b848STomohiro Kusumi 
39*2d60b848STomohiro Kusumi #include <sys/param.h>
40*2d60b848STomohiro Kusumi #include <sys/stat.h>
41*2d60b848STomohiro Kusumi #include <sys/sysctl.h>
42*2d60b848STomohiro Kusumi #include <sys/mman.h>
43*2d60b848STomohiro Kusumi 
44*2d60b848STomohiro Kusumi #include <stdio.h>
45*2d60b848STomohiro Kusumi #include <stdlib.h>
46*2d60b848STomohiro Kusumi #include <stdbool.h>
47*2d60b848STomohiro Kusumi #include <string.h>
48*2d60b848STomohiro Kusumi #include <unistd.h>
49*2d60b848STomohiro Kusumi #include <fcntl.h>
50*2d60b848STomohiro Kusumi #include <time.h>
51*2d60b848STomohiro Kusumi #include <err.h>
52*2d60b848STomohiro Kusumi #include <assert.h>
53*2d60b848STomohiro Kusumi #include <util.h>
54*2d60b848STomohiro Kusumi 
55*2d60b848STomohiro Kusumi #include "makefs.h"
56*2d60b848STomohiro Kusumi #include "hammer2.h"
57*2d60b848STomohiro Kusumi 
58*2d60b848STomohiro Kusumi #define APRINTF(X, ...)	\
59*2d60b848STomohiro Kusumi     printf("%s: " X, __func__, ## __VA_ARGS__)
60*2d60b848STomohiro Kusumi 
61*2d60b848STomohiro Kusumi static void hammer2_dump_fsinfo(fsinfo_t *);
62*2d60b848STomohiro Kusumi static int hammer2_create_image(const char *, fsinfo_t *);
63*2d60b848STomohiro Kusumi static int hammer2_populate_dir(struct vnode *, const char *, fsnode *,
64*2d60b848STomohiro Kusumi     fsnode *, fsinfo_t *, int);
65*2d60b848STomohiro Kusumi static void hammer2_validate(const char *, fsnode *, fsinfo_t *);
66*2d60b848STomohiro Kusumi static void hammer2_size_dir(fsnode *, fsinfo_t *);
67*2d60b848STomohiro Kusumi static int hammer2_write_file(struct vnode *, const char *, fsnode *);
68*2d60b848STomohiro Kusumi 
69*2d60b848STomohiro Kusumi void
70*2d60b848STomohiro Kusumi hammer2_prep_opts(fsinfo_t *fsopts)
71*2d60b848STomohiro Kusumi {
72*2d60b848STomohiro Kusumi 	hammer2_makefs_options_t *h2_opt = ecalloc(1, sizeof(*h2_opt));
73*2d60b848STomohiro Kusumi 	hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
74*2d60b848STomohiro Kusumi 
75*2d60b848STomohiro Kusumi 	const option_t hammer2_options[] = {
76*2d60b848STomohiro Kusumi 		/* newfs_hammer2(8) compatible options */
77*2d60b848STomohiro Kusumi 		{ 'b', "BootAreaSize", NULL, OPT_STRBUF, 0, 0, "boot area size" },
78*2d60b848STomohiro Kusumi 		{ 'r', "AuxAreaSize", NULL, OPT_STRBUF, 0, 0, "aux area size" },
79*2d60b848STomohiro Kusumi 		{ 'V', "Hammer2Version", NULL, OPT_STRBUF, 0, 0, "file system version" },
80*2d60b848STomohiro Kusumi 		{ 'L', "Label", NULL, OPT_STRBUF, 0, 0, "PFS label" },
81*2d60b848STomohiro Kusumi 		/* makefs(8) specific options */
82*2d60b848STomohiro Kusumi 		{ 'm', "MountLabel", NULL, OPT_STRBUF, 0, 0, "destination PFS label" },
83*2d60b848STomohiro Kusumi 		{ 'v', "NumVolhdr", &h2_opt->num_volhdr, OPT_INT32,
84*2d60b848STomohiro Kusumi 		    1, HAMMER2_NUM_VOLHDRS, "number of volume headers" },
85*2d60b848STomohiro Kusumi 		{ 'd', "Hammer2Debug", NULL, OPT_STRBUF, 0, 0, "debug tunable" },
86*2d60b848STomohiro Kusumi 		{ .name = NULL },
87*2d60b848STomohiro Kusumi 	};
88*2d60b848STomohiro Kusumi 
89*2d60b848STomohiro Kusumi 	hammer2_mkfs_init(opt);
90*2d60b848STomohiro Kusumi 
91*2d60b848STomohiro Kusumi 	/* make this tunable ? */
92*2d60b848STomohiro Kusumi 	assert(opt->CompType == HAMMER2_COMP_NEWFS_DEFAULT);
93*2d60b848STomohiro Kusumi 	assert(opt->CheckType == HAMMER2_CHECK_XXHASH64);
94*2d60b848STomohiro Kusumi 
95*2d60b848STomohiro Kusumi 	/* force debug mode for mkfs */
96*2d60b848STomohiro Kusumi 	opt->DebugOpt = 1;
97*2d60b848STomohiro Kusumi 
98*2d60b848STomohiro Kusumi 	fsopts->fs_specific = h2_opt;
99*2d60b848STomohiro Kusumi 	fsopts->fs_options = copy_opts(hammer2_options);
100*2d60b848STomohiro Kusumi 	fsopts->sectorsize = DEV_BSIZE;
101*2d60b848STomohiro Kusumi }
102*2d60b848STomohiro Kusumi 
103*2d60b848STomohiro Kusumi void
104*2d60b848STomohiro Kusumi hammer2_cleanup_opts(fsinfo_t *fsopts)
105*2d60b848STomohiro Kusumi {
106*2d60b848STomohiro Kusumi 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
107*2d60b848STomohiro Kusumi 	hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
108*2d60b848STomohiro Kusumi 
109*2d60b848STomohiro Kusumi 	hammer2_mkfs_cleanup(opt);
110*2d60b848STomohiro Kusumi 
111*2d60b848STomohiro Kusumi 	free(h2_opt);
112*2d60b848STomohiro Kusumi 	free(fsopts->fs_options);
113*2d60b848STomohiro Kusumi }
114*2d60b848STomohiro Kusumi 
115*2d60b848STomohiro Kusumi int
116*2d60b848STomohiro Kusumi hammer2_parse_opts(const char *option, fsinfo_t *fsopts)
117*2d60b848STomohiro Kusumi {
118*2d60b848STomohiro Kusumi 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
119*2d60b848STomohiro Kusumi 	hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
120*2d60b848STomohiro Kusumi 
121*2d60b848STomohiro Kusumi 	option_t *hammer2_options = fsopts->fs_options;
122*2d60b848STomohiro Kusumi 	char buf[1024]; /* > HAMMER2_INODE_MAXNAME */
123*2d60b848STomohiro Kusumi 	int i;
124*2d60b848STomohiro Kusumi 
125*2d60b848STomohiro Kusumi 	assert(option != NULL);
126*2d60b848STomohiro Kusumi 	assert(fsopts != NULL);
127*2d60b848STomohiro Kusumi 
128*2d60b848STomohiro Kusumi 	if (debug & DEBUG_FS_PARSE_OPTS)
129*2d60b848STomohiro Kusumi 		APRINTF("got `%s'\n", option);
130*2d60b848STomohiro Kusumi 
131*2d60b848STomohiro Kusumi 	i = set_option(hammer2_options, option, buf, sizeof(buf));
132*2d60b848STomohiro Kusumi 	if (i == -1)
133*2d60b848STomohiro Kusumi 		return 0;
134*2d60b848STomohiro Kusumi 
135*2d60b848STomohiro Kusumi 	if (hammer2_options[i].name == NULL)
136*2d60b848STomohiro Kusumi 		abort();
137*2d60b848STomohiro Kusumi 
138*2d60b848STomohiro Kusumi 	switch (hammer2_options[i].letter) {
139*2d60b848STomohiro Kusumi 	case 'b':
140*2d60b848STomohiro Kusumi 		opt->BootAreaSize = getsize(buf, HAMMER2_NEWFS_ALIGN,
141*2d60b848STomohiro Kusumi 		    HAMMER2_BOOT_MAX_BYTES, 2);
142*2d60b848STomohiro Kusumi 		break;
143*2d60b848STomohiro Kusumi 	case 'r':
144*2d60b848STomohiro Kusumi 		opt->AuxAreaSize = getsize(buf, HAMMER2_NEWFS_ALIGN,
145*2d60b848STomohiro Kusumi 		    HAMMER2_AUX_MAX_BYTES, 2);
146*2d60b848STomohiro Kusumi 		break;
147*2d60b848STomohiro Kusumi 	case 'V':
148*2d60b848STomohiro Kusumi 		opt->Hammer2Version = strtol(buf, NULL, 0);
149*2d60b848STomohiro Kusumi 		if (opt->Hammer2Version < HAMMER2_VOL_VERSION_MIN ||
150*2d60b848STomohiro Kusumi 		    opt->Hammer2Version >= HAMMER2_VOL_VERSION_WIP)
151*2d60b848STomohiro Kusumi 			errx(1, "I don't understand how to format "
152*2d60b848STomohiro Kusumi 			    "HAMMER2 version %d",
153*2d60b848STomohiro Kusumi 			    opt->Hammer2Version);
154*2d60b848STomohiro Kusumi 		break;
155*2d60b848STomohiro Kusumi 	case 'L':
156*2d60b848STomohiro Kusumi 		h2_opt->label_specified = 1;
157*2d60b848STomohiro Kusumi 		if (strcasecmp(buf, "none") == 0)
158*2d60b848STomohiro Kusumi 			break;
159*2d60b848STomohiro Kusumi 		if (opt->NLabels >= MAXLABELS)
160*2d60b848STomohiro Kusumi 			errx(1, "Limit of %d local labels", MAXLABELS - 1);
161*2d60b848STomohiro Kusumi 		if (strlen(buf) == 0)
162*2d60b848STomohiro Kusumi 			errx(1, "Volume label '%s' cannot be 0-length", buf);
163*2d60b848STomohiro Kusumi 		if (strlen(buf) >= HAMMER2_INODE_MAXNAME)
164*2d60b848STomohiro Kusumi 			errx(1, "Volume label '%s' is too long (%d chars max)",
165*2d60b848STomohiro Kusumi 			    buf, HAMMER2_INODE_MAXNAME - 1);
166*2d60b848STomohiro Kusumi 		opt->Label[opt->NLabels++] = strdup(buf);
167*2d60b848STomohiro Kusumi 		break;
168*2d60b848STomohiro Kusumi 	case 'm':
169*2d60b848STomohiro Kusumi 		if (strlen(buf) == 0)
170*2d60b848STomohiro Kusumi 			errx(1, "Volume label '%s' cannot be 0-length", buf);
171*2d60b848STomohiro Kusumi 		if (strlen(buf) >= HAMMER2_INODE_MAXNAME)
172*2d60b848STomohiro Kusumi 			errx(1, "Volume label '%s' is too long (%d chars max)",
173*2d60b848STomohiro Kusumi 			    buf, HAMMER2_INODE_MAXNAME - 1);
174*2d60b848STomohiro Kusumi 		strlcpy(h2_opt->mount_label, buf, sizeof(h2_opt->mount_label));
175*2d60b848STomohiro Kusumi 		break;
176*2d60b848STomohiro Kusumi 	case 'd':
177*2d60b848STomohiro Kusumi 		hammer2_debug = strtoll(buf, NULL, 0);
178*2d60b848STomohiro Kusumi 		break;
179*2d60b848STomohiro Kusumi 	default:
180*2d60b848STomohiro Kusumi 		break;
181*2d60b848STomohiro Kusumi 	}
182*2d60b848STomohiro Kusumi 
183*2d60b848STomohiro Kusumi 	return 1;
184*2d60b848STomohiro Kusumi }
185*2d60b848STomohiro Kusumi 
186*2d60b848STomohiro Kusumi void
187*2d60b848STomohiro Kusumi hammer2_makefs(const char *image, const char *dir, fsnode *root,
188*2d60b848STomohiro Kusumi     fsinfo_t *fsopts)
189*2d60b848STomohiro Kusumi {
190*2d60b848STomohiro Kusumi 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
191*2d60b848STomohiro Kusumi 	struct mount mp;
192*2d60b848STomohiro Kusumi 	struct hammer2_mount_info info;
193*2d60b848STomohiro Kusumi 	struct vnode devvp, *vroot;
194*2d60b848STomohiro Kusumi 	hammer2_inode_t *iroot;
195*2d60b848STomohiro Kusumi 	struct timeval start;
196*2d60b848STomohiro Kusumi 	int error;
197*2d60b848STomohiro Kusumi 
198*2d60b848STomohiro Kusumi 	assert(image != NULL);
199*2d60b848STomohiro Kusumi 	assert(dir != NULL);
200*2d60b848STomohiro Kusumi 	assert(root != NULL);
201*2d60b848STomohiro Kusumi 	assert(fsopts != NULL);
202*2d60b848STomohiro Kusumi 
203*2d60b848STomohiro Kusumi 	if (debug & DEBUG_FS_MAKEFS)
204*2d60b848STomohiro Kusumi 		APRINTF("image \"%s\" directory \"%s\" root %p\n",
205*2d60b848STomohiro Kusumi 		    image, dir, root);
206*2d60b848STomohiro Kusumi 
207*2d60b848STomohiro Kusumi 	/* validate tree and options */
208*2d60b848STomohiro Kusumi 	TIMER_START(start);
209*2d60b848STomohiro Kusumi 	hammer2_validate(dir, root, fsopts);
210*2d60b848STomohiro Kusumi 	TIMER_RESULTS(start, "hammer2_validate");
211*2d60b848STomohiro Kusumi 
212*2d60b848STomohiro Kusumi 	/* create image */
213*2d60b848STomohiro Kusumi 	TIMER_START(start);
214*2d60b848STomohiro Kusumi 	if (hammer2_create_image(image, fsopts) == -1)
215*2d60b848STomohiro Kusumi 		errx(1, "image file `%s' not created", image);
216*2d60b848STomohiro Kusumi 	TIMER_RESULTS(start, "hammer2_create_image");
217*2d60b848STomohiro Kusumi 
218*2d60b848STomohiro Kusumi 	if (debug & DEBUG_FS_MAKEFS)
219*2d60b848STomohiro Kusumi 		putchar('\n');
220*2d60b848STomohiro Kusumi 
221*2d60b848STomohiro Kusumi 	/* vfs init */
222*2d60b848STomohiro Kusumi 	error = hammer2_vfs_init();
223*2d60b848STomohiro Kusumi 	if (error)
224*2d60b848STomohiro Kusumi 		errx(1, "failed to vfs init, error %d", error);
225*2d60b848STomohiro Kusumi 
226*2d60b848STomohiro Kusumi 	/* mount image */
227*2d60b848STomohiro Kusumi 	memset(&devvp, 0, sizeof(devvp));
228*2d60b848STomohiro Kusumi 	devvp.fs = fsopts;
229*2d60b848STomohiro Kusumi 	memset(&mp, 0, sizeof(mp));
230*2d60b848STomohiro Kusumi 	memset(&info, 0, sizeof(info));
231*2d60b848STomohiro Kusumi 	error = hammer2_vfs_mount(&devvp, &mp, h2_opt->mount_label, &info);
232*2d60b848STomohiro Kusumi 	if (error)
233*2d60b848STomohiro Kusumi 		errx(1, "failed to mount, error %d", error);
234*2d60b848STomohiro Kusumi 	assert(mp.mnt_data);
235*2d60b848STomohiro Kusumi 
236*2d60b848STomohiro Kusumi 	/* get root vnode */
237*2d60b848STomohiro Kusumi 	vroot = NULL;
238*2d60b848STomohiro Kusumi 	error = hammer2_vfs_root(&mp, &vroot);
239*2d60b848STomohiro Kusumi 	if (error)
240*2d60b848STomohiro Kusumi 		errx(1, "failed to get root vnode, error %d", error);
241*2d60b848STomohiro Kusumi 	assert(vroot);
242*2d60b848STomohiro Kusumi 
243*2d60b848STomohiro Kusumi 	iroot = VTOI(vroot);
244*2d60b848STomohiro Kusumi 	assert(iroot);
245*2d60b848STomohiro Kusumi 	printf("root inode inum %ld, mode 0%o, refs %d\n",
246*2d60b848STomohiro Kusumi 	    iroot->meta.inum, iroot->meta.mode, iroot->refs);
247*2d60b848STomohiro Kusumi 
248*2d60b848STomohiro Kusumi 	/* populate image */
249*2d60b848STomohiro Kusumi 	printf("populating `%s'\n", image);
250*2d60b848STomohiro Kusumi 	TIMER_START(start);
251*2d60b848STomohiro Kusumi 	if (hammer2_populate_dir(vroot, dir, root, root, fsopts, 0))
252*2d60b848STomohiro Kusumi 		errx(1, "image file `%s' not populated", image);
253*2d60b848STomohiro Kusumi 	TIMER_RESULTS(start, "hammer2_populate_dir");
254*2d60b848STomohiro Kusumi 
255*2d60b848STomohiro Kusumi 	/* unmount image */
256*2d60b848STomohiro Kusumi 	error = hammer2_vfs_unmount(&mp, 0);
257*2d60b848STomohiro Kusumi 	if (error)
258*2d60b848STomohiro Kusumi 		errx(1, "failed to unmount, error %d", error);
259*2d60b848STomohiro Kusumi 
260*2d60b848STomohiro Kusumi 	/* check leaked resource */
261*2d60b848STomohiro Kusumi 	if (vnode_count)
262*2d60b848STomohiro Kusumi 		printf("XXX %ld vnode left\n", vnode_count);
263*2d60b848STomohiro Kusumi 	if (hammer2_chain_allocs)
264*2d60b848STomohiro Kusumi 		printf("XXX %ld chain left\n", hammer2_chain_allocs);
265*2d60b848STomohiro Kusumi 	bcleanup();
266*2d60b848STomohiro Kusumi 
267*2d60b848STomohiro Kusumi 	/* vfs uninit */
268*2d60b848STomohiro Kusumi 	error = hammer2_vfs_uninit();
269*2d60b848STomohiro Kusumi 	if (error)
270*2d60b848STomohiro Kusumi 		errx(1, "failed to vfs uninit, error %d", error);
271*2d60b848STomohiro Kusumi 
272*2d60b848STomohiro Kusumi 	if (close(fsopts->fd) == -1)
273*2d60b848STomohiro Kusumi 		err(1, "closing `%s'", image);
274*2d60b848STomohiro Kusumi 	fsopts->fd = -1;
275*2d60b848STomohiro Kusumi 
276*2d60b848STomohiro Kusumi 	printf("image `%s' complete\n", image);
277*2d60b848STomohiro Kusumi }
278*2d60b848STomohiro Kusumi 
279*2d60b848STomohiro Kusumi /* end of public functions */
280*2d60b848STomohiro Kusumi 
281*2d60b848STomohiro Kusumi static hammer2_off_t
282*2d60b848STomohiro Kusumi hammer2_image_size(fsinfo_t *fsopts)
283*2d60b848STomohiro Kusumi {
284*2d60b848STomohiro Kusumi 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
285*2d60b848STomohiro Kusumi 	hammer2_off_t image_size, used_size = 0;
286*2d60b848STomohiro Kusumi 	int num_level1, delta_num_level1;
287*2d60b848STomohiro Kusumi 
288*2d60b848STomohiro Kusumi 	/* use 4 volume headers by default */
289*2d60b848STomohiro Kusumi 	num_level1 = h2_opt->num_volhdr * 2; /* default 4 x 2 */
290*2d60b848STomohiro Kusumi 	assert(num_level1 != 0);
291*2d60b848STomohiro Kusumi 	assert(num_level1 <= 8);
292*2d60b848STomohiro Kusumi 
293*2d60b848STomohiro Kusumi 	/* add 4MiB segment for each level1 */
294*2d60b848STomohiro Kusumi 	used_size += HAMMER2_ZONE_SEG64 * num_level1;
295*2d60b848STomohiro Kusumi 
296*2d60b848STomohiro Kusumi 	/* add boot/aux area, but exact size unknown at this point */
297*2d60b848STomohiro Kusumi 	used_size += HAMMER2_BOOT_NOM_BYTES + HAMMER2_AUX_NOM_BYTES;
298*2d60b848STomohiro Kusumi 
299*2d60b848STomohiro Kusumi 	/* add data size */
300*2d60b848STomohiro Kusumi 	used_size += fsopts->size;
301*2d60b848STomohiro Kusumi 
302*2d60b848STomohiro Kusumi 	/* XXX add extra level1 for meta data and indirect blocks */
303*2d60b848STomohiro Kusumi 	used_size += HAMMER2_FREEMAP_LEVEL1_SIZE;
304*2d60b848STomohiro Kusumi 
305*2d60b848STomohiro Kusumi 	/* XXX add extra level1 for safety */
306*2d60b848STomohiro Kusumi 	if (used_size > HAMMER2_FREEMAP_LEVEL1_SIZE * 10)
307*2d60b848STomohiro Kusumi 		used_size += HAMMER2_FREEMAP_LEVEL1_SIZE;
308*2d60b848STomohiro Kusumi 
309*2d60b848STomohiro Kusumi 	/* use 8GiB image size by default */
310*2d60b848STomohiro Kusumi 	image_size = HAMMER2_FREEMAP_LEVEL1_SIZE * num_level1;
311*2d60b848STomohiro Kusumi 	printf("trying default image size %s\n", sizetostr(image_size));
312*2d60b848STomohiro Kusumi 
313*2d60b848STomohiro Kusumi 	/* adjust if image size isn't large enough */
314*2d60b848STomohiro Kusumi 	if (used_size > image_size) {
315*2d60b848STomohiro Kusumi 		/* determine extra level1 needed */
316*2d60b848STomohiro Kusumi 		delta_num_level1 = howmany(used_size - image_size,
317*2d60b848STomohiro Kusumi 		    HAMMER2_FREEMAP_LEVEL1_SIZE);
318*2d60b848STomohiro Kusumi 
319*2d60b848STomohiro Kusumi 		/* adjust used size with 4MiB segment for each extra level1 */
320*2d60b848STomohiro Kusumi 		used_size += HAMMER2_ZONE_SEG64 * delta_num_level1;
321*2d60b848STomohiro Kusumi 
322*2d60b848STomohiro Kusumi 		/* adjust image size with extra level1 */
323*2d60b848STomohiro Kusumi 		image_size += HAMMER2_FREEMAP_LEVEL1_SIZE * delta_num_level1;
324*2d60b848STomohiro Kusumi 		printf("trying adjusted image size %s\n",
325*2d60b848STomohiro Kusumi 		    sizetostr(image_size));
326*2d60b848STomohiro Kusumi 
327*2d60b848STomohiro Kusumi 		if (used_size > image_size)
328*2d60b848STomohiro Kusumi 			errx(1, "invalid used_size %ld > image_size %ld",
329*2d60b848STomohiro Kusumi 			    used_size, image_size);
330*2d60b848STomohiro Kusumi 	}
331*2d60b848STomohiro Kusumi 
332*2d60b848STomohiro Kusumi 	return image_size;
333*2d60b848STomohiro Kusumi }
334*2d60b848STomohiro Kusumi 
335*2d60b848STomohiro Kusumi static const char *
336*2d60b848STomohiro Kusumi hammer2_label_name(int label_type)
337*2d60b848STomohiro Kusumi {
338*2d60b848STomohiro Kusumi 	switch (label_type) {
339*2d60b848STomohiro Kusumi 	case HAMMER2_LABEL_NONE:
340*2d60b848STomohiro Kusumi 		return "NONE";
341*2d60b848STomohiro Kusumi 	case HAMMER2_LABEL_BOOT:
342*2d60b848STomohiro Kusumi 		return "BOOT";
343*2d60b848STomohiro Kusumi 	case HAMMER2_LABEL_ROOT:
344*2d60b848STomohiro Kusumi 		return "ROOT";
345*2d60b848STomohiro Kusumi 	case HAMMER2_LABEL_DATA:
346*2d60b848STomohiro Kusumi 		return "DATA";
347*2d60b848STomohiro Kusumi 	default:
348*2d60b848STomohiro Kusumi 		assert(0);
349*2d60b848STomohiro Kusumi 	}
350*2d60b848STomohiro Kusumi 	return NULL;
351*2d60b848STomohiro Kusumi }
352*2d60b848STomohiro Kusumi 
353*2d60b848STomohiro Kusumi static void
354*2d60b848STomohiro Kusumi hammer2_validate(const char *dir, fsnode *root, fsinfo_t *fsopts)
355*2d60b848STomohiro Kusumi {
356*2d60b848STomohiro Kusumi 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
357*2d60b848STomohiro Kusumi 	hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
358*2d60b848STomohiro Kusumi 	hammer2_off_t image_size, minsize, maxsize;
359*2d60b848STomohiro Kusumi 	const char *s;
360*2d60b848STomohiro Kusumi 
361*2d60b848STomohiro Kusumi 	assert(dir != NULL);
362*2d60b848STomohiro Kusumi 	assert(root != NULL);
363*2d60b848STomohiro Kusumi 	assert(fsopts != NULL);
364*2d60b848STomohiro Kusumi 
365*2d60b848STomohiro Kusumi 	if (debug & DEBUG_FS_VALIDATE) {
366*2d60b848STomohiro Kusumi 		APRINTF("before defaults set:\n");
367*2d60b848STomohiro Kusumi 		hammer2_dump_fsinfo(fsopts);
368*2d60b848STomohiro Kusumi 	}
369*2d60b848STomohiro Kusumi 
370*2d60b848STomohiro Kusumi 	/* makefs only supports "DATA" for default PFS label */
371*2d60b848STomohiro Kusumi 	if (!h2_opt->label_specified) {
372*2d60b848STomohiro Kusumi 		opt->DefaultLabelType = HAMMER2_LABEL_DATA;
373*2d60b848STomohiro Kusumi 		s = hammer2_label_name(opt->DefaultLabelType);
374*2d60b848STomohiro Kusumi 		printf("using default label \"%s\"\n", s);
375*2d60b848STomohiro Kusumi 	}
376*2d60b848STomohiro Kusumi 
377*2d60b848STomohiro Kusumi 	/* set default mount PFS label */
378*2d60b848STomohiro Kusumi 	if (!strcmp(h2_opt->mount_label, "")) {
379*2d60b848STomohiro Kusumi 		s = hammer2_label_name(HAMMER2_LABEL_DATA);
380*2d60b848STomohiro Kusumi 		strlcpy(h2_opt->mount_label, s, sizeof(h2_opt->mount_label));
381*2d60b848STomohiro Kusumi 		printf("using default mount label \"%s\"\n", s);
382*2d60b848STomohiro Kusumi 	}
383*2d60b848STomohiro Kusumi 
384*2d60b848STomohiro Kusumi 	/* set default number of volume headers */
385*2d60b848STomohiro Kusumi 	if (!h2_opt->num_volhdr) {
386*2d60b848STomohiro Kusumi 		h2_opt->num_volhdr = HAMMER2_NUM_VOLHDRS;
387*2d60b848STomohiro Kusumi 		printf("using default %d volume headers\n", h2_opt->num_volhdr);
388*2d60b848STomohiro Kusumi 	}
389*2d60b848STomohiro Kusumi 
390*2d60b848STomohiro Kusumi 	/* calculate data size */
391*2d60b848STomohiro Kusumi 	if (fsopts->size != 0)
392*2d60b848STomohiro Kusumi 		fsopts->size = 0; /* shouldn't reach here to begin with */
393*2d60b848STomohiro Kusumi 	hammer2_size_dir(root, fsopts);
394*2d60b848STomohiro Kusumi 	printf("estimated data size %s from %lld inode\n",
395*2d60b848STomohiro Kusumi 	    sizetostr(fsopts->size), (long long)fsopts->inodes);
396*2d60b848STomohiro Kusumi 
397*2d60b848STomohiro Kusumi 	/* determine image size from data size */
398*2d60b848STomohiro Kusumi 	image_size = hammer2_image_size(fsopts);
399*2d60b848STomohiro Kusumi 	assert((image_size & HAMMER2_FREEMAP_LEVEL1_MASK) == 0);
400*2d60b848STomohiro Kusumi 
401*2d60b848STomohiro Kusumi 	minsize = roundup(fsopts->minsize, HAMMER2_FREEMAP_LEVEL1_SIZE);
402*2d60b848STomohiro Kusumi 	maxsize = roundup(fsopts->maxsize, HAMMER2_FREEMAP_LEVEL1_SIZE);
403*2d60b848STomohiro Kusumi 	if (image_size < minsize)
404*2d60b848STomohiro Kusumi 		image_size = minsize;
405*2d60b848STomohiro Kusumi 	else if (maxsize > 0 && image_size > maxsize)
406*2d60b848STomohiro Kusumi 		errx(1, "`%s' size of %ld is larger than the maxsize of %ld",
407*2d60b848STomohiro Kusumi 		    dir, image_size, maxsize);
408*2d60b848STomohiro Kusumi 
409*2d60b848STomohiro Kusumi 	assert((image_size & HAMMER2_FREEMAP_LEVEL1_MASK) == 0);
410*2d60b848STomohiro Kusumi 	h2_opt->image_size = image_size;
411*2d60b848STomohiro Kusumi 	printf("using %s image size\n", sizetostr(h2_opt->image_size));
412*2d60b848STomohiro Kusumi 
413*2d60b848STomohiro Kusumi 	if (debug & DEBUG_FS_VALIDATE) {
414*2d60b848STomohiro Kusumi 		APRINTF("after defaults set:\n");
415*2d60b848STomohiro Kusumi 		hammer2_dump_fsinfo(fsopts);
416*2d60b848STomohiro Kusumi 	}
417*2d60b848STomohiro Kusumi }
418*2d60b848STomohiro Kusumi 
419*2d60b848STomohiro Kusumi static void
420*2d60b848STomohiro Kusumi hammer2_dump_fsinfo(fsinfo_t *fsopts)
421*2d60b848STomohiro Kusumi {
422*2d60b848STomohiro Kusumi 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
423*2d60b848STomohiro Kusumi 	hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
424*2d60b848STomohiro Kusumi 	int i;
425*2d60b848STomohiro Kusumi 	char *s;
426*2d60b848STomohiro Kusumi 
427*2d60b848STomohiro Kusumi 	assert(fsopts != NULL);
428*2d60b848STomohiro Kusumi 
429*2d60b848STomohiro Kusumi 	APRINTF("fsinfo_t at %p\n", fsopts);
430*2d60b848STomohiro Kusumi 
431*2d60b848STomohiro Kusumi 	printf("\tinodes %lld\n", (long long)fsopts->inodes);
432*2d60b848STomohiro Kusumi 	printf("\tsize %lld, minsize %lld, maxsize %lld\n",
433*2d60b848STomohiro Kusumi 	    (long long)fsopts->size,
434*2d60b848STomohiro Kusumi 	    (long long)fsopts->minsize,
435*2d60b848STomohiro Kusumi 	    (long long)fsopts->maxsize);
436*2d60b848STomohiro Kusumi 
437*2d60b848STomohiro Kusumi 	printf("\thammer2_debug 0x%x\n", hammer2_debug);
438*2d60b848STomohiro Kusumi 
439*2d60b848STomohiro Kusumi 	printf("\tlabel_specified %d\n", h2_opt->label_specified);
440*2d60b848STomohiro Kusumi 	printf("\tmount_label \"%s\"\n", h2_opt->mount_label);
441*2d60b848STomohiro Kusumi 	printf("\tnum_volhdr %d\n", h2_opt->num_volhdr);
442*2d60b848STomohiro Kusumi 	printf("\timage_size 0x%lx\n", h2_opt->image_size);
443*2d60b848STomohiro Kusumi 
444*2d60b848STomohiro Kusumi 	printf("\tHammer2Version %d\n", opt->Hammer2Version);
445*2d60b848STomohiro Kusumi 	printf("\tBootAreaSize 0x%jx\n", opt->BootAreaSize);
446*2d60b848STomohiro Kusumi 	printf("\tAuxAreaSize 0x%jx\n", opt->AuxAreaSize);
447*2d60b848STomohiro Kusumi 	printf("\tNLabels %d\n", opt->NLabels);
448*2d60b848STomohiro Kusumi 	printf("\tCompType %d\n", opt->CompType);
449*2d60b848STomohiro Kusumi 	printf("\tCheckType %d\n", opt->CheckType);
450*2d60b848STomohiro Kusumi 	printf("\tDefaultLabelType %d\n", opt->DefaultLabelType);
451*2d60b848STomohiro Kusumi 	printf("\tDebugOpt %d\n", opt->DebugOpt);
452*2d60b848STomohiro Kusumi 
453*2d60b848STomohiro Kusumi 	s = NULL;
454*2d60b848STomohiro Kusumi 	hammer2_uuid_to_str(&opt->Hammer2_FSType, &s);
455*2d60b848STomohiro Kusumi 	printf("\tHammer2_FSType \"%s\"\n", s);
456*2d60b848STomohiro Kusumi 	s = NULL;
457*2d60b848STomohiro Kusumi 	hammer2_uuid_to_str(&opt->Hammer2_VolFSID, &s);
458*2d60b848STomohiro Kusumi 	printf("\tHammer2_VolFSID \"%s\"\n", s);
459*2d60b848STomohiro Kusumi 	s = NULL;
460*2d60b848STomohiro Kusumi 	hammer2_uuid_to_str(&opt->Hammer2_SupCLID, &s);
461*2d60b848STomohiro Kusumi 	printf("\tHammer2_SupCLID \"%s\"\n", s);
462*2d60b848STomohiro Kusumi 	s = NULL;
463*2d60b848STomohiro Kusumi 	hammer2_uuid_to_str(&opt->Hammer2_SupFSID, &s);
464*2d60b848STomohiro Kusumi 	printf("\tHammer2_SupFSID \"%s\"\n", s);
465*2d60b848STomohiro Kusumi 
466*2d60b848STomohiro Kusumi 	for (i = 0; i < opt->NLabels; i++) {
467*2d60b848STomohiro Kusumi 		printf("\tLabel[%d] \"%s\"\n", i, opt->Label[i]);
468*2d60b848STomohiro Kusumi 		s = NULL;
469*2d60b848STomohiro Kusumi 		hammer2_uuid_to_str(&opt->Hammer2_PfsCLID[i], &s);
470*2d60b848STomohiro Kusumi 		printf("\t Hammer2_PfsCLID[%d] \"%s\"\n", i, s);
471*2d60b848STomohiro Kusumi 		s = NULL;
472*2d60b848STomohiro Kusumi 		hammer2_uuid_to_str(&opt->Hammer2_PfsFSID[i], &s);
473*2d60b848STomohiro Kusumi 		printf("\t Hammer2_PfsFSID[%d] \"%s\"\n", i, s);
474*2d60b848STomohiro Kusumi 	}
475*2d60b848STomohiro Kusumi 
476*2d60b848STomohiro Kusumi 	free(s);
477*2d60b848STomohiro Kusumi }
478*2d60b848STomohiro Kusumi 
479*2d60b848STomohiro Kusumi static int
480*2d60b848STomohiro Kusumi hammer2_create_image(const char *image, fsinfo_t *fsopts)
481*2d60b848STomohiro Kusumi {
482*2d60b848STomohiro Kusumi 	hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
483*2d60b848STomohiro Kusumi 	hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
484*2d60b848STomohiro Kusumi 	char *av[] = { (char *)image, }; /* XXX support multi-volumes */
485*2d60b848STomohiro Kusumi 	char *buf;
486*2d60b848STomohiro Kusumi 	int i, bufsize, oflags;
487*2d60b848STomohiro Kusumi 	off_t bufrem;
488*2d60b848STomohiro Kusumi 
489*2d60b848STomohiro Kusumi 	assert(image != NULL);
490*2d60b848STomohiro Kusumi 	assert(fsopts != NULL);
491*2d60b848STomohiro Kusumi 
492*2d60b848STomohiro Kusumi 	/* create image */
493*2d60b848STomohiro Kusumi 	oflags = O_RDWR | O_CREAT;
494*2d60b848STomohiro Kusumi 	if (fsopts->offset == 0)
495*2d60b848STomohiro Kusumi 		oflags |= O_TRUNC;
496*2d60b848STomohiro Kusumi 	if ((fsopts->fd = open(image, oflags, 0666)) == -1) {
497*2d60b848STomohiro Kusumi 		warn("can't open `%s' for writing", image);
498*2d60b848STomohiro Kusumi 		return -1;
499*2d60b848STomohiro Kusumi 	}
500*2d60b848STomohiro Kusumi 
501*2d60b848STomohiro Kusumi 	/* zero image */
502*2d60b848STomohiro Kusumi 	bufsize = HAMMER2_PBUFSIZE;
503*2d60b848STomohiro Kusumi 	bufrem = h2_opt->image_size;
504*2d60b848STomohiro Kusumi 	if (fsopts->sparse) {
505*2d60b848STomohiro Kusumi 		if (ftruncate(fsopts->fd, bufrem) == -1) {
506*2d60b848STomohiro Kusumi 			warn("sparse option disabled");
507*2d60b848STomohiro Kusumi 			fsopts->sparse = 0;
508*2d60b848STomohiro Kusumi 		}
509*2d60b848STomohiro Kusumi 	}
510*2d60b848STomohiro Kusumi 	if (fsopts->sparse) {
511*2d60b848STomohiro Kusumi 		/* File truncated at bufrem. Remaining is 0 */
512*2d60b848STomohiro Kusumi 		bufrem = 0;
513*2d60b848STomohiro Kusumi 		buf = NULL;
514*2d60b848STomohiro Kusumi 	} else {
515*2d60b848STomohiro Kusumi 		if (debug & DEBUG_FS_CREATE_IMAGE)
516*2d60b848STomohiro Kusumi 			APRINTF("zero-ing image `%s', %lld sectors, "
517*2d60b848STomohiro Kusumi 			    "using %d byte chunks\n",
518*2d60b848STomohiro Kusumi 			    image, (long long)bufrem, bufsize);
519*2d60b848STomohiro Kusumi 		buf = ecalloc(1, bufsize);
520*2d60b848STomohiro Kusumi 	}
521*2d60b848STomohiro Kusumi 
522*2d60b848STomohiro Kusumi 	if (fsopts->offset != 0) {
523*2d60b848STomohiro Kusumi 		if (lseek(fsopts->fd, fsopts->offset, SEEK_SET) == -1) {
524*2d60b848STomohiro Kusumi 			warn("can't seek");
525*2d60b848STomohiro Kusumi 			free(buf);
526*2d60b848STomohiro Kusumi 			return -1;
527*2d60b848STomohiro Kusumi 		}
528*2d60b848STomohiro Kusumi 	}
529*2d60b848STomohiro Kusumi 
530*2d60b848STomohiro Kusumi 	while (bufrem > 0) {
531*2d60b848STomohiro Kusumi 		i = write(fsopts->fd, buf, MIN(bufsize, bufrem));
532*2d60b848STomohiro Kusumi 		if (i == -1) {
533*2d60b848STomohiro Kusumi 			warn("zeroing image, %lld bytes to go",
534*2d60b848STomohiro Kusumi 			    (long long)bufrem);
535*2d60b848STomohiro Kusumi 			free(buf);
536*2d60b848STomohiro Kusumi 			return -1;
537*2d60b848STomohiro Kusumi 		}
538*2d60b848STomohiro Kusumi 		bufrem -= i;
539*2d60b848STomohiro Kusumi 	}
540*2d60b848STomohiro Kusumi 	if (buf)
541*2d60b848STomohiro Kusumi 		free(buf);
542*2d60b848STomohiro Kusumi 
543*2d60b848STomohiro Kusumi 	/* make the file system */
544*2d60b848STomohiro Kusumi 	if (debug & DEBUG_FS_CREATE_IMAGE)
545*2d60b848STomohiro Kusumi 		APRINTF("calling mkfs(\"%s\", ...)\n", image);
546*2d60b848STomohiro Kusumi 	hammer2_mkfs(1, av, opt); /* success if returned */
547*2d60b848STomohiro Kusumi 
548*2d60b848STomohiro Kusumi 	return fsopts->fd;
549*2d60b848STomohiro Kusumi }
550*2d60b848STomohiro Kusumi 
551*2d60b848STomohiro Kusumi static off_t
552*2d60b848STomohiro Kusumi hammer2_phys_size(off_t size)
553*2d60b848STomohiro Kusumi {
554*2d60b848STomohiro Kusumi 	off_t radix_size, phys_size = 0;
555*2d60b848STomohiro Kusumi 	int i;
556*2d60b848STomohiro Kusumi 
557*2d60b848STomohiro Kusumi 	if (size > HAMMER2_PBUFSIZE) {
558*2d60b848STomohiro Kusumi 		phys_size += rounddown(size, HAMMER2_PBUFSIZE);
559*2d60b848STomohiro Kusumi 		size = size % HAMMER2_PBUFSIZE;
560*2d60b848STomohiro Kusumi 	}
561*2d60b848STomohiro Kusumi 
562*2d60b848STomohiro Kusumi 	for (i = HAMMER2_RADIX_MIN; i <= HAMMER2_RADIX_MAX; i++) {
563*2d60b848STomohiro Kusumi 		radix_size = 1UL << i;
564*2d60b848STomohiro Kusumi 		if (radix_size >= size) {
565*2d60b848STomohiro Kusumi 			phys_size += radix_size;
566*2d60b848STomohiro Kusumi 			break;
567*2d60b848STomohiro Kusumi 		}
568*2d60b848STomohiro Kusumi 	}
569*2d60b848STomohiro Kusumi 
570*2d60b848STomohiro Kusumi 	return phys_size;
571*2d60b848STomohiro Kusumi }
572*2d60b848STomohiro Kusumi 
573*2d60b848STomohiro Kusumi /* calculate data size */
574*2d60b848STomohiro Kusumi static void
575*2d60b848STomohiro Kusumi hammer2_size_dir(fsnode *root, fsinfo_t *fsopts)
576*2d60b848STomohiro Kusumi {
577*2d60b848STomohiro Kusumi 	fsnode *node;
578*2d60b848STomohiro Kusumi 	int curdirsize;
579*2d60b848STomohiro Kusumi 
580*2d60b848STomohiro Kusumi 	assert(fsopts != NULL);
581*2d60b848STomohiro Kusumi 
582*2d60b848STomohiro Kusumi 	if (debug & DEBUG_FS_SIZE_DIR)
583*2d60b848STomohiro Kusumi 		APRINTF("entry: bytes %lld inodes %lld\n",
584*2d60b848STomohiro Kusumi 		    (long long)fsopts->size, (long long)fsopts->inodes);
585*2d60b848STomohiro Kusumi 
586*2d60b848STomohiro Kusumi 	curdirsize = 0;
587*2d60b848STomohiro Kusumi 	for (node = root; node != NULL; node = node->next) {
588*2d60b848STomohiro Kusumi 		if (node == root) { /* we're at "." */
589*2d60b848STomohiro Kusumi 			assert(strcmp(node->name, ".") == 0);
590*2d60b848STomohiro Kusumi 		} else if ((node->inode->flags & FI_SIZED) == 0) {
591*2d60b848STomohiro Kusumi 			/* don't count duplicate names */
592*2d60b848STomohiro Kusumi 			node->inode->flags |= FI_SIZED;
593*2d60b848STomohiro Kusumi 			if (debug & DEBUG_FS_SIZE_DIR_NODE)
594*2d60b848STomohiro Kusumi 				APRINTF("`%s' size %lld\n",
595*2d60b848STomohiro Kusumi 				    node->name,
596*2d60b848STomohiro Kusumi 				    (long long)node->inode->st.st_size);
597*2d60b848STomohiro Kusumi 			fsopts->inodes++;
598*2d60b848STomohiro Kusumi 			fsopts->size += sizeof(hammer2_inode_data_t);
599*2d60b848STomohiro Kusumi 			if (node->type == S_IFREG) {
600*2d60b848STomohiro Kusumi 				size_t st_size = node->inode->st.st_size;
601*2d60b848STomohiro Kusumi 				if (st_size > HAMMER2_EMBEDDED_BYTES)
602*2d60b848STomohiro Kusumi 					fsopts->size += hammer2_phys_size(st_size);
603*2d60b848STomohiro Kusumi 			} else if (node->type == S_IFLNK) {
604*2d60b848STomohiro Kusumi 				size_t nlen = strlen(node->symlink);
605*2d60b848STomohiro Kusumi 				if (nlen > HAMMER2_EMBEDDED_BYTES)
606*2d60b848STomohiro Kusumi 					fsopts->size += hammer2_phys_size(nlen);
607*2d60b848STomohiro Kusumi 			}
608*2d60b848STomohiro Kusumi 		}
609*2d60b848STomohiro Kusumi 		if (node->type == S_IFDIR)
610*2d60b848STomohiro Kusumi 			hammer2_size_dir(node->child, fsopts);
611*2d60b848STomohiro Kusumi 	}
612*2d60b848STomohiro Kusumi 
613*2d60b848STomohiro Kusumi 	if (debug & DEBUG_FS_SIZE_DIR)
614*2d60b848STomohiro Kusumi 		APRINTF("exit: size %lld inodes %lld\n",
615*2d60b848STomohiro Kusumi 		    (long long)fsopts->size, (long long)fsopts->inodes);
616*2d60b848STomohiro Kusumi }
617*2d60b848STomohiro Kusumi 
618*2d60b848STomohiro Kusumi static void
619*2d60b848STomohiro Kusumi hammer2_print(const struct vnode *dvp, const struct vnode *vp,
620*2d60b848STomohiro Kusumi     const fsnode *node, int depth, const char *msg)
621*2d60b848STomohiro Kusumi {
622*2d60b848STomohiro Kusumi 	if (debug & DEBUG_FS_POPULATE) {
623*2d60b848STomohiro Kusumi 		if (1) {
624*2d60b848STomohiro Kusumi 			int indent = depth * 2;
625*2d60b848STomohiro Kusumi 			char *type;
626*2d60b848STomohiro Kusumi 			if (S_ISDIR(node->type))
627*2d60b848STomohiro Kusumi 				type = "dir";
628*2d60b848STomohiro Kusumi 			else if (S_ISREG(node->type))
629*2d60b848STomohiro Kusumi 				type = "reg";
630*2d60b848STomohiro Kusumi 			else if (S_ISLNK(node->type))
631*2d60b848STomohiro Kusumi 				type = "lnk";
632*2d60b848STomohiro Kusumi 			else
633*2d60b848STomohiro Kusumi 				type = "???";
634*2d60b848STomohiro Kusumi 			printf("%*.*s", indent, indent, "");
635*2d60b848STomohiro Kusumi 			printf("dvp=%p/%d vp=%p/%d \"%s\" %s %s\n",
636*2d60b848STomohiro Kusumi 			    dvp, dvp ? VTOI(dvp)->refs : 0,
637*2d60b848STomohiro Kusumi 			    vp, vp ? VTOI(vp)->refs : 0,
638*2d60b848STomohiro Kusumi 			    node->name, type, msg);
639*2d60b848STomohiro Kusumi 		} else {
640*2d60b848STomohiro Kusumi 			char type;
641*2d60b848STomohiro Kusumi 			if (S_ISDIR(node->type))
642*2d60b848STomohiro Kusumi 				type = 'd';
643*2d60b848STomohiro Kusumi 			else if (S_ISREG(node->type))
644*2d60b848STomohiro Kusumi 				type = 'r';
645*2d60b848STomohiro Kusumi 			else if (S_ISLNK(node->type))
646*2d60b848STomohiro Kusumi 				type = 'l';
647*2d60b848STomohiro Kusumi 			else
648*2d60b848STomohiro Kusumi 				type = '?';
649*2d60b848STomohiro Kusumi 			printf("%c", type);
650*2d60b848STomohiro Kusumi 			fflush(stdout);
651*2d60b848STomohiro Kusumi 		}
652*2d60b848STomohiro Kusumi 	}
653*2d60b848STomohiro Kusumi }
654*2d60b848STomohiro Kusumi 
655*2d60b848STomohiro Kusumi static int
656*2d60b848STomohiro Kusumi hammer2_populate_dir(struct vnode *dvp, const char *dir, fsnode *root,
657*2d60b848STomohiro Kusumi     fsnode *parent, fsinfo_t *fsopts, int depth)
658*2d60b848STomohiro Kusumi {
659*2d60b848STomohiro Kusumi 	fsnode *cur;
660*2d60b848STomohiro Kusumi 	struct vnode *vp;
661*2d60b848STomohiro Kusumi 	struct stat st;
662*2d60b848STomohiro Kusumi 	char f[MAXPATHLEN];
663*2d60b848STomohiro Kusumi 	const char *path;
664*2d60b848STomohiro Kusumi 	int error;
665*2d60b848STomohiro Kusumi 
666*2d60b848STomohiro Kusumi 	assert(dvp != NULL);
667*2d60b848STomohiro Kusumi 	assert(dir != NULL);
668*2d60b848STomohiro Kusumi 	assert(root != NULL);
669*2d60b848STomohiro Kusumi 	assert(parent != NULL);
670*2d60b848STomohiro Kusumi 	assert(fsopts != NULL);
671*2d60b848STomohiro Kusumi 
672*2d60b848STomohiro Kusumi 	/* assert root directory */
673*2d60b848STomohiro Kusumi 	assert(S_ISDIR(root->type));
674*2d60b848STomohiro Kusumi 	assert(!strcmp(root->name, "."));
675*2d60b848STomohiro Kusumi 	assert(!root->child);
676*2d60b848STomohiro Kusumi 	assert(!root->parent || root->parent->child == root);
677*2d60b848STomohiro Kusumi 
678*2d60b848STomohiro Kusumi 	hammer2_print(dvp, NULL, root, depth, "enter");
679*2d60b848STomohiro Kusumi 	if (stat(dir, &st) == -1)
680*2d60b848STomohiro Kusumi 		err(1, "no such path %s", dir);
681*2d60b848STomohiro Kusumi 	if (!S_ISDIR(st.st_mode))
682*2d60b848STomohiro Kusumi 		errx(1, "no such dir %s", dir);
683*2d60b848STomohiro Kusumi 
684*2d60b848STomohiro Kusumi 	for (cur = root->next; cur != NULL; cur = cur->next) {
685*2d60b848STomohiro Kusumi 		/* construct source path */
686*2d60b848STomohiro Kusumi 		if (cur->contents) {
687*2d60b848STomohiro Kusumi 			path = cur->contents;
688*2d60b848STomohiro Kusumi 		} else {
689*2d60b848STomohiro Kusumi 			if (S_ISDIR(cur->type)) {
690*2d60b848STomohiro Kusumi 				/* this should be same as root/path/name */
691*2d60b848STomohiro Kusumi 				if (snprintf(f, sizeof(f), "%s/%s",
692*2d60b848STomohiro Kusumi 				    dir, cur->name) >= (int)sizeof(f))
693*2d60b848STomohiro Kusumi 					errx(1, "pathname too long");
694*2d60b848STomohiro Kusumi 			} else {
695*2d60b848STomohiro Kusumi 				if (snprintf(f, sizeof(f), "%s/%s/%s",
696*2d60b848STomohiro Kusumi 				    cur->root, cur->path, cur->name) >= (int)sizeof(f))
697*2d60b848STomohiro Kusumi 					errx(1, "pathname too long");
698*2d60b848STomohiro Kusumi 			}
699*2d60b848STomohiro Kusumi 			path = f;
700*2d60b848STomohiro Kusumi 		}
701*2d60b848STomohiro Kusumi 		if (stat(path, &st) == -1)
702*2d60b848STomohiro Kusumi 			err(1, "no such file %s", f);
703*2d60b848STomohiro Kusumi 
704*2d60b848STomohiro Kusumi 		/* update node state */
705*2d60b848STomohiro Kusumi 		if ((cur->inode->flags & FI_ALLOCATED) == 0) {
706*2d60b848STomohiro Kusumi 			cur->inode->flags |= FI_ALLOCATED;
707*2d60b848STomohiro Kusumi 			if (cur != root)
708*2d60b848STomohiro Kusumi 				cur->parent = parent;
709*2d60b848STomohiro Kusumi 		}
710*2d60b848STomohiro Kusumi 
711*2d60b848STomohiro Kusumi 		if (cur->inode->flags & FI_WRITTEN)
712*2d60b848STomohiro Kusumi 			continue; /* hard link */
713*2d60b848STomohiro Kusumi 		cur->inode->flags |= FI_WRITTEN;
714*2d60b848STomohiro Kusumi 
715*2d60b848STomohiro Kusumi 		/* make sure it doesn't exist yet */
716*2d60b848STomohiro Kusumi 		vp = NULL;
717*2d60b848STomohiro Kusumi 		error = hammer2_nresolve(dvp, &vp, cur->name,
718*2d60b848STomohiro Kusumi 		    strlen(cur->name));
719*2d60b848STomohiro Kusumi 		if (!error)
720*2d60b848STomohiro Kusumi 			errx(1, "hammer2_nresolve(\"%s\") already exists",
721*2d60b848STomohiro Kusumi 			    cur->name);
722*2d60b848STomohiro Kusumi 		hammer2_print(dvp, vp, cur, depth, "nresolve");
723*2d60b848STomohiro Kusumi 
724*2d60b848STomohiro Kusumi 		/* if directory, mkdir and recurse */
725*2d60b848STomohiro Kusumi 		if (S_ISDIR(cur->type)) {
726*2d60b848STomohiro Kusumi 			assert(cur->child);
727*2d60b848STomohiro Kusumi 
728*2d60b848STomohiro Kusumi 			vp = NULL;
729*2d60b848STomohiro Kusumi 			error = hammer2_nmkdir(dvp, &vp, cur->name,
730*2d60b848STomohiro Kusumi 			    strlen(cur->name));
731*2d60b848STomohiro Kusumi 			if (error)
732*2d60b848STomohiro Kusumi 				errx(1, "hammer2_nmkdir(\"%s\") failed: %s",
733*2d60b848STomohiro Kusumi 				    cur->name, strerror(error));
734*2d60b848STomohiro Kusumi 			assert(vp);
735*2d60b848STomohiro Kusumi 			hammer2_print(dvp, vp, cur, depth, "nmkdir");
736*2d60b848STomohiro Kusumi 
737*2d60b848STomohiro Kusumi 			error = hammer2_populate_dir(vp, path, cur->child, cur,
738*2d60b848STomohiro Kusumi 			    fsopts, depth + 1);
739*2d60b848STomohiro Kusumi 			if (error)
740*2d60b848STomohiro Kusumi 				errx(1, "failed to populate %s: %s",
741*2d60b848STomohiro Kusumi 				    path, strerror(error));
742*2d60b848STomohiro Kusumi 			continue;
743*2d60b848STomohiro Kusumi 		}
744*2d60b848STomohiro Kusumi 
745*2d60b848STomohiro Kusumi 		/* if regular file, creat and write its data */
746*2d60b848STomohiro Kusumi 		if (S_ISREG(cur->type)) {
747*2d60b848STomohiro Kusumi 			assert(cur->child == NULL);
748*2d60b848STomohiro Kusumi 
749*2d60b848STomohiro Kusumi 			vp = NULL;
750*2d60b848STomohiro Kusumi 			error = hammer2_ncreate(dvp, &vp, cur->name,
751*2d60b848STomohiro Kusumi 			    strlen(cur->name));
752*2d60b848STomohiro Kusumi 			if (error)
753*2d60b848STomohiro Kusumi 				errx(1, "hammer2_ncreate(\"%s\") failed: %s",
754*2d60b848STomohiro Kusumi 				    cur->name, strerror(error));
755*2d60b848STomohiro Kusumi 			assert(vp);
756*2d60b848STomohiro Kusumi 			hammer2_print(dvp, vp, cur, depth, "ncreate");
757*2d60b848STomohiro Kusumi 
758*2d60b848STomohiro Kusumi 			error = hammer2_write_file(vp, path, cur);
759*2d60b848STomohiro Kusumi 			if (error)
760*2d60b848STomohiro Kusumi 				errx(1, "hammer2_write_file(\"%s\") failed: %s",
761*2d60b848STomohiro Kusumi 				    path, strerror(error));
762*2d60b848STomohiro Kusumi 			continue;
763*2d60b848STomohiro Kusumi 		}
764*2d60b848STomohiro Kusumi 
765*2d60b848STomohiro Kusumi 		/* if symlink, create a symlink against target */
766*2d60b848STomohiro Kusumi 		if (S_ISLNK(cur->type)) {
767*2d60b848STomohiro Kusumi 			assert(cur->child == NULL);
768*2d60b848STomohiro Kusumi 
769*2d60b848STomohiro Kusumi 			vp = NULL;
770*2d60b848STomohiro Kusumi 			error = hammer2_nsymlink(dvp, &vp, cur->name,
771*2d60b848STomohiro Kusumi 			    strlen(cur->name), cur->symlink);
772*2d60b848STomohiro Kusumi 			if (error)
773*2d60b848STomohiro Kusumi 				errx(1, "hammer2_nsymlink(\"%s\") failed: %s",
774*2d60b848STomohiro Kusumi 				    cur->name, strerror(error));
775*2d60b848STomohiro Kusumi 			assert(vp);
776*2d60b848STomohiro Kusumi 			hammer2_print(dvp, vp, cur, depth, "nsymlink");
777*2d60b848STomohiro Kusumi 			continue;
778*2d60b848STomohiro Kusumi 		}
779*2d60b848STomohiro Kusumi 
780*2d60b848STomohiro Kusumi 		/* other types are unsupported */
781*2d60b848STomohiro Kusumi 		printf("ignore %s 0%o\n", path, cur->type);
782*2d60b848STomohiro Kusumi 	}
783*2d60b848STomohiro Kusumi 
784*2d60b848STomohiro Kusumi 	return 0;
785*2d60b848STomohiro Kusumi }
786*2d60b848STomohiro Kusumi 
787*2d60b848STomohiro Kusumi static int
788*2d60b848STomohiro Kusumi hammer2_write_file(struct vnode *vp, const char *path, fsnode *node)
789*2d60b848STomohiro Kusumi {
790*2d60b848STomohiro Kusumi 	struct stat *st = &node->inode->st;
791*2d60b848STomohiro Kusumi 	size_t nsize, bufsize;
792*2d60b848STomohiro Kusumi 	off_t offset;
793*2d60b848STomohiro Kusumi 	int fd, error;
794*2d60b848STomohiro Kusumi 	char *p;
795*2d60b848STomohiro Kusumi 
796*2d60b848STomohiro Kusumi 	nsize = st->st_size;
797*2d60b848STomohiro Kusumi 	if (nsize == 0)
798*2d60b848STomohiro Kusumi 		return 0;
799*2d60b848STomohiro Kusumi 	/* check nsize vs maximum file size */
800*2d60b848STomohiro Kusumi 
801*2d60b848STomohiro Kusumi 	fd = open(path, O_RDONLY);
802*2d60b848STomohiro Kusumi 	if (fd < 0)
803*2d60b848STomohiro Kusumi 		err(1, "failed to open %s", path);
804*2d60b848STomohiro Kusumi 
805*2d60b848STomohiro Kusumi 	p = mmap(0, nsize, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0);
806*2d60b848STomohiro Kusumi 	if (p == MAP_FAILED)
807*2d60b848STomohiro Kusumi 		err(1, "failed to mmap %s", path);
808*2d60b848STomohiro Kusumi 	close(fd);
809*2d60b848STomohiro Kusumi 
810*2d60b848STomohiro Kusumi 	for (offset = 0; offset < nsize; ) {
811*2d60b848STomohiro Kusumi 		bufsize = MIN(nsize - offset, HAMMER2_PBUFSIZE);
812*2d60b848STomohiro Kusumi 		assert(bufsize <= HAMMER2_PBUFSIZE);
813*2d60b848STomohiro Kusumi 		error = hammer2_write(vp, p + offset, bufsize, offset);
814*2d60b848STomohiro Kusumi 		if (error)
815*2d60b848STomohiro Kusumi 			errx(1, "failed to write to %s vnode: %s",
816*2d60b848STomohiro Kusumi 			    path, strerror(error));
817*2d60b848STomohiro Kusumi 		offset += bufsize;
818*2d60b848STomohiro Kusumi 		if (bufsize == HAMMER2_PBUFSIZE)
819*2d60b848STomohiro Kusumi 			assert((offset & (HAMMER2_PBUFSIZE - 1)) == 0);
820*2d60b848STomohiro Kusumi 	}
821*2d60b848STomohiro Kusumi 	munmap(p, nsize);
822*2d60b848STomohiro Kusumi 
823*2d60b848STomohiro Kusumi 	return 0;
824*2d60b848STomohiro Kusumi }
825