usr.sbin/makefs/hammer2: Remove redundant hammer2_inode_modify()
[dragonfly.git] / usr.sbin / makefs / hammer2.c
blobf3939f9b0578d283658b2662c44980c5a658ac4f
1 /*
2 * SPDX-License-Identifier: BSD-3-Clause
4 * Copyright (c) 2022 Tomohiro Kusumi <tkusumi@netbsd.org>
5 * Copyright (c) 2011-2022 The DragonFly Project. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
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.
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.
35 #if HAVE_NBTOOL_CONFIG_H
36 #include "nbtool_config.h"
37 #endif
39 #include <sys/param.h>
40 #include <sys/stat.h>
41 #include <sys/sysctl.h>
42 #include <sys/mman.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <stdbool.h>
47 #include <string.h>
48 #include <ctype.h>
49 #include <unistd.h>
50 #include <fcntl.h>
51 #include <time.h>
52 #include <err.h>
53 #include <assert.h>
54 #include <util.h>
56 #include "makefs.h"
57 #include "hammer2.h"
59 #define APRINTF(X, ...) \
60 printf("%s: " X, __func__, ## __VA_ARGS__)
62 static void hammer2_parse_pfs_opts(const char *, fsinfo_t *);
63 static void hammer2_parse_inode_opts(const char *, fsinfo_t *);
64 static void hammer2_dump_fsinfo(fsinfo_t *);
65 static int hammer2_create_image(const char *, fsinfo_t *);
66 static int hammer2_populate_dir(struct m_vnode *, const char *, fsnode *,
67 fsnode *, fsinfo_t *, int);
68 static void hammer2_validate(const char *, fsnode *, fsinfo_t *);
69 static void hammer2_size_dir(fsnode *, fsinfo_t *);
70 static int hammer2_write_file(struct m_vnode *, const char *, fsnode *);
71 static int hammer2_version_get(struct m_vnode *);
72 static int hammer2_pfs_get(struct m_vnode *);
73 static int hammer2_pfs_lookup(struct m_vnode *, const char *);
74 static int hammer2_pfs_create(struct m_vnode *, const char *);
75 static int hammer2_pfs_delete(struct m_vnode *, const char *);
76 static int hammer2_pfs_snapshot(struct m_vnode *, const char *, const char *);
77 static int hammer2_inode_getx(struct m_vnode *, const char *);
78 static int hammer2_inode_setcheck(struct m_vnode *, const char *);
79 static int hammer2_inode_setcomp(struct m_vnode *, const char *);
80 static int hammer2_bulkfree(struct m_vnode *);
81 static int hammer2_destroy_path(struct m_vnode *, const char *);
82 static int hammer2_destroy_inum(struct m_vnode *, hammer2_tid_t);
83 static int hammer2_growfs(struct m_vnode *, hammer2_off_t);
84 static void unittest_trim_slash(void);
86 fsnode *hammer2_curnode;
88 void
89 hammer2_prep_opts(fsinfo_t *fsopts)
91 hammer2_makefs_options_t *h2_opt = ecalloc(1, sizeof(*h2_opt));
92 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
94 const option_t hammer2_options[] = {
95 /* newfs_hammer2(8) compatible options */
96 { 'b', "BootAreaSize", NULL, OPT_STRBUF, 0, 0, "boot area size" },
97 { 'r', "AuxAreaSize", NULL, OPT_STRBUF, 0, 0, "aux area size" },
98 { 'V', "Hammer2Version", NULL, OPT_STRBUF, 0, 0, "file system version" },
99 { 'L', "Label", NULL, OPT_STRBUF, 0, 0, "PFS label" },
100 /* makefs(8) specific options */
101 { 'm', "MountLabel", NULL, OPT_STRBUF, 0, 0, "destination PFS label" },
102 { 'v', "NumVolhdr", &h2_opt->num_volhdr, OPT_INT32,
103 1, HAMMER2_NUM_VOLHDRS, "number of volume headers" },
104 { 'd', "Hammer2Debug", NULL, OPT_STRBUF, 0, 0, "debug tunable" },
105 { 'E', "EmergencyMode", &h2_opt->emergency_mode, OPT_BOOL, 0, 0,
106 "emergency mode" },
107 { 'P', "PFS", NULL, OPT_STRBUF, 0, 0, "offline PFS" },
108 { 'I', "Inode", NULL, OPT_STRBUF, 0, 0, "offline inode" },
109 { 'B', "Bulkfree", NULL, OPT_STRBUF, 0, 0, "offline bulkfree" },
110 { 'D', "Destroy", NULL, OPT_STRBUF, 0, 0, "offline destroy" },
111 { 'G', "Growfs", NULL, OPT_STRBUF, 0, 0, "offline growfs" },
112 { .name = NULL },
115 hammer2_mkfs_init(opt);
117 /* make this tunable ? */
118 assert(opt->CompType == HAMMER2_COMP_NEWFS_DEFAULT);
119 assert(opt->CheckType == HAMMER2_CHECK_XXHASH64);
121 /* force debug mode for mkfs */
122 opt->DebugOpt = 1;
124 fsopts->fs_specific = h2_opt;
125 fsopts->fs_options = copy_opts(hammer2_options);
126 fsopts->sectorsize = DEV_BSIZE;
129 void
130 hammer2_cleanup_opts(fsinfo_t *fsopts)
132 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
133 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
135 hammer2_mkfs_cleanup(opt);
137 free(h2_opt);
138 free(fsopts->fs_options);
142 hammer2_parse_opts(const char *option, fsinfo_t *fsopts)
144 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
145 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
147 option_t *hammer2_options = fsopts->fs_options;
148 char buf[1024]; /* > HAMMER2_INODE_MAXNAME */
149 int i;
151 assert(option != NULL);
152 assert(fsopts != NULL);
154 if (debug & DEBUG_FS_PARSE_OPTS)
155 APRINTF("got `%s'\n", option);
157 i = set_option(hammer2_options, option, buf, sizeof(buf));
158 if (i == -1)
159 return 0;
161 if (hammer2_options[i].name == NULL)
162 abort();
164 switch (hammer2_options[i].letter) {
165 case 'b':
166 opt->BootAreaSize = getsize(buf, HAMMER2_NEWFS_ALIGN,
167 HAMMER2_BOOT_MAX_BYTES, 2);
168 break;
169 case 'r':
170 opt->AuxAreaSize = getsize(buf, HAMMER2_NEWFS_ALIGN,
171 HAMMER2_AUX_MAX_BYTES, 2);
172 break;
173 case 'V':
174 if (strlen(buf) == 0) {
175 h2_opt->ioctl_cmd = HAMMER2IOC_VERSION_GET;
176 } else {
177 opt->Hammer2Version = strtol(buf, NULL, 0);
178 if (opt->Hammer2Version < HAMMER2_VOL_VERSION_MIN ||
179 opt->Hammer2Version >= HAMMER2_VOL_VERSION_WIP)
180 errx(1, "I don't understand how to format "
181 "HAMMER2 version %d",
182 opt->Hammer2Version);
184 break;
185 case 'L':
186 h2_opt->label_specified = 1;
187 if (strcasecmp(buf, "none") == 0)
188 break;
189 if (opt->NLabels >= MAXLABELS)
190 errx(1, "Limit of %d local labels", MAXLABELS - 1);
191 if (strlen(buf) == 0)
192 errx(1, "Volume label '%s' cannot be 0-length", buf);
193 if (strlen(buf) >= HAMMER2_INODE_MAXNAME)
194 errx(1, "Volume label '%s' is too long (%d chars max)",
195 buf, HAMMER2_INODE_MAXNAME - 1);
196 opt->Label[opt->NLabels++] = strdup(buf);
197 break;
198 case 'm':
199 if (strlen(buf) == 0)
200 errx(1, "Volume label '%s' cannot be 0-length", buf);
201 if (strlen(buf) >= HAMMER2_INODE_MAXNAME)
202 errx(1, "Volume label '%s' is too long (%d chars max)",
203 buf, HAMMER2_INODE_MAXNAME - 1);
204 strlcpy(h2_opt->mount_label, buf, sizeof(h2_opt->mount_label));
205 break;
206 case 'd':
207 hammer2_debug = strtoll(buf, NULL, 0);
208 break;
209 case 'P':
210 if (strlen(buf) == 0)
211 errx(1, "PFS argument '%s' cannot be 0-length", buf);
212 hammer2_parse_pfs_opts(buf, fsopts);
213 break;
214 case 'I':
215 if (strlen(buf) == 0)
216 errx(1, "Inode argument '%s' cannot be 0-length", buf);
217 hammer2_parse_inode_opts(buf, fsopts);
218 break;
219 case 'B':
220 h2_opt->ioctl_cmd = HAMMER2IOC_BULKFREE_SCAN;
221 break;
222 case 'D':
223 h2_opt->ioctl_cmd = HAMMER2IOC_DESTROY;
224 if (strlen(buf) == 0)
225 errx(1, "Destroy argument '%s' cannot be 0-length", buf);
226 if (buf[0] == '/') {
227 strlcpy(h2_opt->destroy_path, buf,
228 sizeof(h2_opt->destroy_path));
229 } else if (strncmp(buf, "0x", 2) == 0 ||
230 (buf[0] >= '0' && buf[0] <= '9')) {
231 h2_opt->destroy_inum = strtoull(buf, NULL, 0);
232 if (errno)
233 err(1, "strtoull");
234 } else {
235 errx(1, "Invalid destroy argument %s", buf);
237 break;
238 case 'G':
239 h2_opt->ioctl_cmd = HAMMER2IOC_GROWFS;
240 break;
241 default:
242 break;
245 if (hammer2_debug && h2_opt->ioctl_cmd)
246 unittest_trim_slash();
248 return 1;
251 void
252 hammer2_makefs(const char *image, const char *dir, fsnode *root,
253 fsinfo_t *fsopts)
255 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
256 struct mount mp;
257 struct hammer2_mount_info info;
258 struct m_vnode devvp, *vroot;
259 hammer2_inode_t *iroot;
260 struct timeval start;
261 int error;
263 /* ioctl commands could have NULL dir / root */
264 assert(image != NULL);
265 assert(fsopts != NULL);
267 if (debug & DEBUG_FS_MAKEFS)
268 APRINTF("image \"%s\" directory \"%s\" root %p\n",
269 image, dir, root);
271 /* validate tree and options */
272 TIMER_START(start);
273 hammer2_validate(dir, root, fsopts);
274 TIMER_RESULTS(start, "hammer2_validate");
276 if (h2_opt->ioctl_cmd) {
277 /* open existing image */
278 fsopts->fd = open(image, O_RDWR);
279 if (fsopts->fd < 0)
280 err(1, "failed to open `%s'", image);
281 } else {
282 /* create image */
283 TIMER_START(start);
284 if (hammer2_create_image(image, fsopts) == -1)
285 errx(1, "image file `%s' not created", image);
286 TIMER_RESULTS(start, "hammer2_create_image");
288 assert(fsopts->fd > 0);
290 if (debug & DEBUG_FS_MAKEFS)
291 putchar('\n');
293 /* vfs init */
294 error = hammer2_vfs_init();
295 if (error)
296 errx(1, "failed to vfs init, error %d", error);
298 /* mount image */
299 memset(&devvp, 0, sizeof(devvp));
300 devvp.fs = fsopts;
301 memset(&mp, 0, sizeof(mp));
302 memset(&info, 0, sizeof(info));
303 error = hammer2_vfs_mount(&devvp, &mp, h2_opt->mount_label, &info);
304 if (error)
305 errx(1, "failed to mount, error %d", error);
306 assert(mp.mnt_data);
308 /* get root vnode */
309 vroot = NULL;
310 error = hammer2_vfs_root(&mp, &vroot);
311 if (error)
312 errx(1, "failed to get root vnode, error %d", error);
313 assert(vroot);
315 iroot = VTOI(vroot);
316 assert(iroot);
317 printf("root inode inum %lld, mode 0%o, refs %d\n",
318 (long long)iroot->meta.inum, iroot->meta.mode, iroot->refs);
320 if (h2_opt->emergency_mode)
321 hammer2_ioctl_emerg_mode(iroot, 1);
323 switch (h2_opt->ioctl_cmd) {
324 case HAMMER2IOC_VERSION_GET:
325 printf("version get `%s'\n", image);
326 TIMER_START(start);
327 error = hammer2_version_get(vroot);
328 if (error)
329 errx(1, "version get `%s' failed '%s'", image,
330 strerror(error));
331 TIMER_RESULTS(start, "hammer2_version_get");
332 break;
333 case HAMMER2IOC_PFS_GET:
334 printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image);
335 TIMER_START(start);
336 error = hammer2_pfs_get(vroot);
337 if (error)
338 errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name,
339 image, strerror(error));
340 TIMER_RESULTS(start, "hammer2_pfs_get");
341 break;
342 case HAMMER2IOC_PFS_LOOKUP:
343 printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image);
344 TIMER_START(start);
345 error = hammer2_pfs_lookup(vroot, h2_opt->pfs_name);
346 if (error)
347 errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name,
348 image, strerror(error));
349 TIMER_RESULTS(start, "hammer2_pfs_lookup");
350 break;
351 case HAMMER2IOC_PFS_CREATE:
352 printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image);
353 TIMER_START(start);
354 error = hammer2_pfs_create(vroot, h2_opt->pfs_name);
355 if (error)
356 errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name,
357 image, strerror(error));
358 TIMER_RESULTS(start, "hammer2_pfs_create");
359 break;
360 case HAMMER2IOC_PFS_DELETE:
361 printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image);
362 TIMER_START(start);
363 error = hammer2_pfs_delete(vroot, h2_opt->pfs_name);
364 if (error)
365 errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name,
366 image, strerror(error));
367 TIMER_RESULTS(start, "hammer2_pfs_delete");
368 break;
369 case HAMMER2IOC_PFS_SNAPSHOT:
370 printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image);
371 TIMER_START(start);
372 error = hammer2_pfs_snapshot(vroot, h2_opt->pfs_name,
373 h2_opt->mount_label);
374 if (error)
375 errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name,
376 image, strerror(error));
377 TIMER_RESULTS(start, "hammer2_pfs_snapshot");
378 break;
379 case HAMMER2IOC_INODE_GET:
380 printf("inode %s `%s'\n", h2_opt->inode_cmd_name, image);
381 TIMER_START(start);
382 error = hammer2_inode_getx(vroot, h2_opt->inode_path);
383 if (error)
384 errx(1, "inode %s `%s' failed '%s'",
385 h2_opt->inode_cmd_name, image, strerror(error));
386 TIMER_RESULTS(start, "hammer2_inode_getx");
387 break;
388 case HAMMER2IOC_INODE_SET:
389 printf("inode %s `%s'\n", h2_opt->inode_cmd_name, image);
390 TIMER_START(start);
391 if (!strcmp(h2_opt->inode_cmd_name, "setcheck")) {
392 error = hammer2_inode_setcheck(vroot,
393 h2_opt->inode_path);
394 if (error)
395 errx(1, "inode %s `%s' failed '%s'",
396 h2_opt->inode_cmd_name, image,
397 strerror(error));
398 } else if (!strcmp(h2_opt->inode_cmd_name, "setcomp")) {
399 error = hammer2_inode_setcomp(vroot,
400 h2_opt->inode_path);
401 if (error)
402 errx(1, "inode %s `%s' failed '%s'",
403 h2_opt->inode_cmd_name, image,
404 strerror(error));
405 } else {
406 assert(0);
408 TIMER_RESULTS(start, "hammer2_inode_setx");
409 break;
410 case HAMMER2IOC_BULKFREE_SCAN:
411 printf("bulkfree `%s'\n", image);
412 TIMER_START(start);
413 error = hammer2_bulkfree(vroot);
414 if (error)
415 errx(1, "bulkfree `%s' failed '%s'", image,
416 strerror(error));
417 TIMER_RESULTS(start, "hammer2_bulkfree");
418 break;
419 case HAMMER2IOC_DESTROY:
420 TIMER_START(start);
421 if (strlen(h2_opt->destroy_path)) {
422 printf("destroy `%s' in `%s'\n",
423 h2_opt->destroy_path, image);
424 error = hammer2_destroy_path(vroot,
425 h2_opt->destroy_path);
426 if (error)
427 errx(1, "destroy `%s' in `%s' failed '%s'",
428 h2_opt->destroy_path, image,
429 strerror(error));
430 } else {
431 printf("destroy %lld in `%s'\n",
432 (long long)h2_opt->destroy_inum, image);
433 error = hammer2_destroy_inum(vroot,
434 h2_opt->destroy_inum);
435 if (error)
436 errx(1, "destroy %lld in `%s' failed '%s'",
437 (long long)h2_opt->destroy_inum, image,
438 strerror(error));
440 TIMER_RESULTS(start, "hammer2_destroy");
441 break;
442 case HAMMER2IOC_GROWFS:
443 printf("growfs `%s'\n", image);
444 TIMER_START(start);
445 error = hammer2_growfs(vroot, h2_opt->image_size);
446 if (error)
447 errx(1, "growfs `%s' failed '%s'", image,
448 strerror(error));
449 TIMER_RESULTS(start, "hammer2_growfs");
450 break;
451 default:
452 printf("populating `%s'\n", image);
453 TIMER_START(start);
454 if (hammer2_populate_dir(vroot, dir, root, root, fsopts, 0))
455 errx(1, "image file `%s' not populated", image);
456 TIMER_RESULTS(start, "hammer2_populate_dir");
457 break;
460 /* unmount image */
461 error = hammer2_vfs_unmount(&mp, 0);
462 if (error)
463 errx(1, "failed to unmount, error %d", error);
465 /* check leaked resource */
466 if (vnode_count)
467 printf("XXX %lld vnode left\n", (long long)vnode_count);
468 if (hammer2_chain_allocs)
469 printf("XXX %ld chain left\n", hammer2_chain_allocs);
470 bcleanup();
472 /* vfs uninit */
473 error = hammer2_vfs_uninit();
474 if (error)
475 errx(1, "failed to vfs uninit, error %d", error);
477 if (close(fsopts->fd) == -1)
478 err(1, "closing `%s'", image);
479 fsopts->fd = -1;
481 printf("image `%s' complete\n", image);
484 /* end of public functions */
486 static void
487 hammer2_parse_pfs_opts(const char *buf, fsinfo_t *fsopts)
489 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
490 char *o, *p;
491 size_t n;
493 o = p = strdup(buf);
494 p = strchr(p, ':');
495 if (p != NULL) {
496 *p++ = 0;
497 n = strlen(p);
498 } else {
499 n = 0;
502 if (!strcmp(o, "get") || !strcmp(o, "list")) {
503 h2_opt->ioctl_cmd = HAMMER2IOC_PFS_GET;
504 } else if (!strcmp(o, "lookup")) {
505 if (n == 0 || n > NAME_MAX)
506 errx(1, "invalid PFS name \"%s\"", p);
507 h2_opt->ioctl_cmd = HAMMER2IOC_PFS_LOOKUP;
508 } else if (!strcmp(o, "create")) {
509 if (n == 0 || n > NAME_MAX)
510 errx(1, "invalid PFS name \"%s\"", p);
511 h2_opt->ioctl_cmd = HAMMER2IOC_PFS_CREATE;
512 } else if (!strcmp(o, "delete")) {
513 if (n == 0 || n > NAME_MAX)
514 errx(1, "invalid PFS name \"%s\"", p);
515 h2_opt->ioctl_cmd = HAMMER2IOC_PFS_DELETE;
516 } else if (!strcmp(o, "snapshot")) {
517 if (n > NAME_MAX)
518 errx(1, "invalid PFS name \"%s\"", p);
519 h2_opt->ioctl_cmd = HAMMER2IOC_PFS_SNAPSHOT;
520 } else {
521 errx(1, "invalid PFS command \"%s\"", o);
524 strlcpy(h2_opt->pfs_cmd_name, o, sizeof(h2_opt->pfs_cmd_name));
525 if (n > 0)
526 strlcpy(h2_opt->pfs_name, p, sizeof(h2_opt->pfs_name));
528 free(o);
531 static void
532 hammer2_parse_inode_opts(const char *buf, fsinfo_t *fsopts)
534 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
535 char *o, *p;
536 size_t n;
538 o = p = strdup(buf);
539 p = strchr(p, ':');
540 if (p != NULL) {
541 *p++ = 0;
542 n = strlen(p);
543 } else {
544 n = 0;
547 if (!strcmp(o, "get")) {
548 if (n == 0 || n > PATH_MAX)
549 errx(1, "invalid file path \"%s\"", p);
550 h2_opt->ioctl_cmd = HAMMER2IOC_INODE_GET;
551 } else if (!strcmp(o, "setcheck")) {
552 if (n == 0 || n > PATH_MAX - 10)
553 errx(1, "invalid argument \"%s\"", p);
554 h2_opt->ioctl_cmd = HAMMER2IOC_INODE_SET;
555 } else if (!strcmp(o, "setcomp")) {
556 if (n == 0 || n > PATH_MAX - 10)
557 errx(1, "invalid argument \"%s\"", p);
558 h2_opt->ioctl_cmd = HAMMER2IOC_INODE_SET;
559 } else {
560 errx(1, "invalid inode command \"%s\"", o);
563 strlcpy(h2_opt->inode_cmd_name, o, sizeof(h2_opt->inode_cmd_name));
564 if (n > 0)
565 strlcpy(h2_opt->inode_path, p, sizeof(h2_opt->inode_path));
567 free(o);
570 static hammer2_off_t
571 hammer2_image_size(fsinfo_t *fsopts)
573 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
574 hammer2_off_t image_size, used_size = 0;
575 int num_level1, delta_num_level1;
577 /* use 4 volume headers by default */
578 num_level1 = h2_opt->num_volhdr * 2; /* default 4 x 2 */
579 assert(num_level1 != 0);
580 assert(num_level1 <= 8);
582 /* add 4MiB segment for each level1 */
583 used_size += HAMMER2_ZONE_SEG64 * num_level1;
585 /* add boot/aux area, but exact size unknown at this point */
586 used_size += HAMMER2_BOOT_NOM_BYTES + HAMMER2_AUX_NOM_BYTES;
588 /* add data size */
589 used_size += fsopts->size;
591 /* XXX add extra level1 for meta data and indirect blocks */
592 used_size += HAMMER2_FREEMAP_LEVEL1_SIZE;
594 /* XXX add extra level1 for safety */
595 if (used_size > HAMMER2_FREEMAP_LEVEL1_SIZE * 10)
596 used_size += HAMMER2_FREEMAP_LEVEL1_SIZE;
598 /* use 8GiB image size by default */
599 image_size = HAMMER2_FREEMAP_LEVEL1_SIZE * num_level1;
600 printf("trying default image size %s\n", sizetostr(image_size));
602 /* adjust if image size isn't large enough */
603 if (used_size > image_size) {
604 /* determine extra level1 needed */
605 delta_num_level1 = howmany(used_size - image_size,
606 HAMMER2_FREEMAP_LEVEL1_SIZE);
608 /* adjust used size with 4MiB segment for each extra level1 */
609 used_size += HAMMER2_ZONE_SEG64 * delta_num_level1;
611 /* adjust image size with extra level1 */
612 image_size += HAMMER2_FREEMAP_LEVEL1_SIZE * delta_num_level1;
613 printf("trying adjusted image size %s\n",
614 sizetostr(image_size));
616 if (used_size > image_size)
617 errx(1, "invalid used_size %lld > image_size %lld",
618 (long long)used_size, (long long)image_size);
621 return image_size;
624 static const char *
625 hammer2_label_name(int label_type)
627 switch (label_type) {
628 case HAMMER2_LABEL_NONE:
629 return "NONE";
630 case HAMMER2_LABEL_BOOT:
631 return "BOOT";
632 case HAMMER2_LABEL_ROOT:
633 return "ROOT";
634 case HAMMER2_LABEL_DATA:
635 return "DATA";
636 default:
637 assert(0);
639 return NULL;
642 static void
643 hammer2_validate(const char *dir, fsnode *root, fsinfo_t *fsopts)
645 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
646 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
647 hammer2_off_t image_size = 0, minsize, maxsize;
648 const char *s;
650 /* ioctl commands could have NULL dir / root */
651 assert(fsopts != NULL);
653 if (debug & DEBUG_FS_VALIDATE) {
654 APRINTF("before defaults set:\n");
655 hammer2_dump_fsinfo(fsopts);
658 /* makefs only supports "DATA" for default PFS label */
659 if (!h2_opt->label_specified) {
660 opt->DefaultLabelType = HAMMER2_LABEL_DATA;
661 s = hammer2_label_name(opt->DefaultLabelType);
662 printf("using default label \"%s\"\n", s);
665 /* set default mount PFS label */
666 if (!strcmp(h2_opt->mount_label, "")) {
667 s = hammer2_label_name(HAMMER2_LABEL_DATA);
668 strlcpy(h2_opt->mount_label, s, sizeof(h2_opt->mount_label));
669 printf("using default mount label \"%s\"\n", s);
672 /* set default number of volume headers */
673 if (!h2_opt->num_volhdr) {
674 h2_opt->num_volhdr = HAMMER2_NUM_VOLHDRS;
675 printf("using default %d volume headers\n", h2_opt->num_volhdr);
678 /* done if ioctl commands */
679 if (h2_opt->ioctl_cmd) {
680 if (h2_opt->ioctl_cmd == HAMMER2IOC_GROWFS)
681 goto ignore_size_dir;
682 else
683 goto done;
686 /* calculate data size */
687 if (fsopts->size != 0)
688 fsopts->size = 0; /* shouldn't reach here to begin with */
689 if (root == NULL)
690 errx(1, "fsnode tree not constructed");
691 hammer2_size_dir(root, fsopts);
692 printf("estimated data size %s from %lld inode\n",
693 sizetostr(fsopts->size), (long long)fsopts->inodes);
695 /* determine image size from data size */
696 image_size = hammer2_image_size(fsopts);
697 assert((image_size & HAMMER2_FREEMAP_LEVEL1_MASK) == 0);
698 ignore_size_dir:
699 minsize = roundup(fsopts->minsize, HAMMER2_FREEMAP_LEVEL1_SIZE);
700 maxsize = roundup(fsopts->maxsize, HAMMER2_FREEMAP_LEVEL1_SIZE);
701 if (image_size < minsize)
702 image_size = minsize;
703 else if (maxsize > 0 && image_size > maxsize)
704 errx(1, "`%s' size of %lld is larger than the maxsize of %lld",
705 dir, (long long)image_size, (long long)maxsize);
707 assert((image_size & HAMMER2_FREEMAP_LEVEL1_MASK) == 0);
708 h2_opt->image_size = image_size;
709 printf("using %s image size\n", sizetostr(h2_opt->image_size));
710 done:
711 if (debug & DEBUG_FS_VALIDATE) {
712 APRINTF("after defaults set:\n");
713 hammer2_dump_fsinfo(fsopts);
717 static void
718 hammer2_dump_fsinfo(fsinfo_t *fsopts)
720 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
721 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
722 int i;
723 char *s;
725 assert(fsopts != NULL);
727 APRINTF("fsinfo_t at %p\n", fsopts);
729 printf("\tinodes %lld\n", (long long)fsopts->inodes);
730 printf("\tsize %lld, minsize %lld, maxsize %lld\n",
731 (long long)fsopts->size,
732 (long long)fsopts->minsize,
733 (long long)fsopts->maxsize);
735 printf("\thammer2_debug 0x%x\n", hammer2_debug);
737 printf("\tlabel_specified %d\n", h2_opt->label_specified);
738 printf("\tmount_label \"%s\"\n", h2_opt->mount_label);
739 printf("\tnum_volhdr %d\n", h2_opt->num_volhdr);
740 printf("\tioctl_cmd %ld\n", h2_opt->ioctl_cmd);
741 printf("\temergency_mode %d\n", h2_opt->emergency_mode);
742 printf("\tpfs_cmd_name \"%s\"\n", h2_opt->pfs_cmd_name);
743 printf("\tpfs_name \"%s\"\n", h2_opt->pfs_name);
744 printf("\tinode_cmd_name \"%s\"\n", h2_opt->inode_cmd_name);
745 printf("\tinode_path \"%s\"\n", h2_opt->inode_path);
746 printf("\tdestroy_path \"%s\"\n", h2_opt->destroy_path);
747 printf("\tdestroy_inum %lld\n", (long long)h2_opt->destroy_inum);
748 printf("\timage_size 0x%llx\n", (long long)h2_opt->image_size);
750 printf("\tHammer2Version %d\n", opt->Hammer2Version);
751 printf("\tBootAreaSize 0x%jx\n", opt->BootAreaSize);
752 printf("\tAuxAreaSize 0x%jx\n", opt->AuxAreaSize);
753 printf("\tNLabels %d\n", opt->NLabels);
754 printf("\tCompType %d\n", opt->CompType);
755 printf("\tCheckType %d\n", opt->CheckType);
756 printf("\tDefaultLabelType %d\n", opt->DefaultLabelType);
757 printf("\tDebugOpt %d\n", opt->DebugOpt);
759 s = NULL;
760 hammer2_uuid_to_str(&opt->Hammer2_FSType, &s);
761 printf("\tHammer2_FSType \"%s\"\n", s);
762 s = NULL;
763 hammer2_uuid_to_str(&opt->Hammer2_VolFSID, &s);
764 printf("\tHammer2_VolFSID \"%s\"\n", s);
765 s = NULL;
766 hammer2_uuid_to_str(&opt->Hammer2_SupCLID, &s);
767 printf("\tHammer2_SupCLID \"%s\"\n", s);
768 s = NULL;
769 hammer2_uuid_to_str(&opt->Hammer2_SupFSID, &s);
770 printf("\tHammer2_SupFSID \"%s\"\n", s);
772 for (i = 0; i < opt->NLabels; i++) {
773 printf("\tLabel[%d] \"%s\"\n", i, opt->Label[i]);
774 s = NULL;
775 hammer2_uuid_to_str(&opt->Hammer2_PfsCLID[i], &s);
776 printf("\t Hammer2_PfsCLID[%d] \"%s\"\n", i, s);
777 s = NULL;
778 hammer2_uuid_to_str(&opt->Hammer2_PfsFSID[i], &s);
779 printf("\t Hammer2_PfsFSID[%d] \"%s\"\n", i, s);
782 free(s);
785 static int
786 hammer2_create_image(const char *image, fsinfo_t *fsopts)
788 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
789 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
790 char *av[] = { (char *)image, }; /* XXX support multi-volumes */
791 char *buf;
792 int i, bufsize, oflags;
793 off_t bufrem;
795 assert(image != NULL);
796 assert(fsopts != NULL);
798 /* create image */
799 oflags = O_RDWR | O_CREAT;
800 if (fsopts->offset == 0)
801 oflags |= O_TRUNC;
802 if ((fsopts->fd = open(image, oflags, 0666)) == -1) {
803 warn("can't open `%s' for writing", image);
804 return -1;
807 /* zero image */
808 bufsize = HAMMER2_PBUFSIZE;
809 bufrem = h2_opt->image_size;
810 if (fsopts->sparse) {
811 if (ftruncate(fsopts->fd, bufrem) == -1) {
812 warn("sparse option disabled");
813 fsopts->sparse = 0;
816 if (fsopts->sparse) {
817 /* File truncated at bufrem. Remaining is 0 */
818 bufrem = 0;
819 buf = NULL;
820 } else {
821 if (debug & DEBUG_FS_CREATE_IMAGE)
822 APRINTF("zero-ing image `%s', %lld sectors, "
823 "using %d byte chunks\n",
824 image, (long long)bufrem, bufsize);
825 buf = ecalloc(1, bufsize);
828 if (fsopts->offset != 0) {
829 if (lseek(fsopts->fd, fsopts->offset, SEEK_SET) == -1) {
830 warn("can't seek");
831 free(buf);
832 return -1;
836 while (bufrem > 0) {
837 i = write(fsopts->fd, buf, MIN(bufsize, bufrem));
838 if (i == -1) {
839 warn("zeroing image, %lld bytes to go",
840 (long long)bufrem);
841 free(buf);
842 return -1;
844 bufrem -= i;
846 if (buf)
847 free(buf);
849 /* make the file system */
850 if (debug & DEBUG_FS_CREATE_IMAGE)
851 APRINTF("calling mkfs(\"%s\", ...)\n", image);
852 hammer2_mkfs(1, av, opt); /* success if returned */
854 return fsopts->fd;
857 static off_t
858 hammer2_phys_size(off_t size)
860 off_t radix_size, phys_size = 0;
861 int i;
863 if (size > HAMMER2_PBUFSIZE) {
864 phys_size += rounddown(size, HAMMER2_PBUFSIZE);
865 size = size % HAMMER2_PBUFSIZE;
868 for (i = HAMMER2_RADIX_MIN; i <= HAMMER2_RADIX_MAX; i++) {
869 radix_size = 1UL << i;
870 if (radix_size >= size) {
871 phys_size += radix_size;
872 break;
876 return phys_size;
879 /* calculate data size */
880 static void
881 hammer2_size_dir(fsnode *root, fsinfo_t *fsopts)
883 fsnode *node;
885 assert(fsopts != NULL);
887 if (debug & DEBUG_FS_SIZE_DIR)
888 APRINTF("entry: bytes %lld inodes %lld\n",
889 (long long)fsopts->size, (long long)fsopts->inodes);
891 for (node = root; node != NULL; node = node->next) {
892 if (node == root) { /* we're at "." */
893 assert(strcmp(node->name, ".") == 0);
894 } else if ((node->inode->flags & FI_SIZED) == 0) {
895 /* don't count duplicate names */
896 node->inode->flags |= FI_SIZED;
897 if (debug & DEBUG_FS_SIZE_DIR_NODE)
898 APRINTF("`%s' size %lld\n",
899 node->name,
900 (long long)node->inode->st.st_size);
901 fsopts->inodes++;
902 fsopts->size += sizeof(hammer2_inode_data_t);
903 if (node->type == S_IFREG) {
904 size_t st_size = node->inode->st.st_size;
905 if (st_size > HAMMER2_EMBEDDED_BYTES)
906 fsopts->size += hammer2_phys_size(st_size);
907 } else if (node->type == S_IFLNK) {
908 size_t nlen = strlen(node->symlink);
909 if (nlen > HAMMER2_EMBEDDED_BYTES)
910 fsopts->size += hammer2_phys_size(nlen);
913 if (node->type == S_IFDIR)
914 hammer2_size_dir(node->child, fsopts);
917 if (debug & DEBUG_FS_SIZE_DIR)
918 APRINTF("exit: size %lld inodes %lld\n",
919 (long long)fsopts->size, (long long)fsopts->inodes);
922 static void
923 hammer2_print(const struct m_vnode *dvp, const struct m_vnode *vp,
924 const fsnode *node, int depth, const char *msg)
926 if (debug & DEBUG_FS_POPULATE) {
927 if (1) {
928 int indent = depth * 2;
929 char *type;
930 if (S_ISDIR(node->type))
931 type = "dir";
932 else if (S_ISREG(node->type))
933 type = "reg";
934 else if (S_ISLNK(node->type))
935 type = "lnk";
936 else if (S_ISFIFO(node->type))
937 type = "fifo";
938 else
939 type = "???";
940 printf("%*.*s", indent, indent, "");
941 printf("dvp=%p/%d vp=%p/%d \"%s\" %s %s\n",
942 dvp, dvp ? VTOI(dvp)->refs : 0,
943 vp, vp ? VTOI(vp)->refs : 0,
944 node->name, type, msg);
945 } else {
946 char type;
947 if (S_ISDIR(node->type))
948 type = 'd';
949 else if (S_ISREG(node->type))
950 type = 'r';
951 else if (S_ISLNK(node->type))
952 type = 'l';
953 else if (S_ISFIFO(node->type))
954 type = 'f';
955 else
956 type = '?';
957 printf("%c", type);
958 fflush(stdout);
963 static int
964 hammer2_populate_dir(struct m_vnode *dvp, const char *dir, fsnode *root,
965 fsnode *parent, fsinfo_t *fsopts, int depth)
967 fsnode *cur;
968 struct m_vnode *vp;
969 struct stat st;
970 char f[MAXPATHLEN];
971 const char *path;
972 int hardlink;
973 int error;
975 assert(dvp != NULL);
976 assert(dir != NULL);
977 assert(root != NULL);
978 assert(parent != NULL);
979 assert(fsopts != NULL);
981 /* assert root directory */
982 assert(S_ISDIR(root->type));
983 assert(!strcmp(root->name, "."));
984 assert(!root->child);
985 assert(!root->parent || root->parent->child == root);
987 hammer2_print(dvp, NULL, root, depth, "enter");
988 if (stat(dir, &st) == -1)
989 err(1, "no such path %s", dir);
990 if (!S_ISDIR(st.st_mode))
991 errx(1, "no such dir %s", dir);
993 for (cur = root->next; cur != NULL; cur = cur->next) {
994 /* global variable for HAMMER2 vnops */
995 hammer2_curnode = cur;
997 /* construct source path */
998 if (cur->contents) {
999 path = cur->contents;
1000 } else {
1001 if (S_ISDIR(cur->type)) {
1002 /* this should be same as root/path/name */
1003 if (snprintf(f, sizeof(f), "%s/%s",
1004 dir, cur->name) >= (int)sizeof(f))
1005 errx(1, "path %s too long", f);
1006 } else {
1007 if (snprintf(f, sizeof(f), "%s/%s/%s",
1008 cur->root, cur->path, cur->name) >= (int)sizeof(f))
1009 errx(1, "path %s too long", f);
1011 path = f;
1013 if (stat(path, &st) == -1)
1014 err(1, "no such file %s", f);
1016 /* update node state */
1017 if ((cur->inode->flags & FI_ALLOCATED) == 0) {
1018 cur->inode->flags |= FI_ALLOCATED;
1019 if (cur != root)
1020 cur->parent = parent;
1023 /* detect hardlink */
1024 if (cur->inode->flags & FI_WRITTEN) {
1025 assert(!S_ISDIR(cur->type));
1026 hardlink = 1;
1027 } else {
1028 hardlink = 0;
1030 cur->inode->flags |= FI_WRITTEN;
1032 /* make sure it doesn't exist yet */
1033 vp = NULL;
1034 error = hammer2_nresolve(dvp, &vp, cur->name,
1035 strlen(cur->name));
1036 if (!error)
1037 errx(1, "hammer2_nresolve(\"%s\") already exists",
1038 cur->name);
1039 hammer2_print(dvp, vp, cur, depth, "nresolve");
1041 /* if directory, mkdir and recurse */
1042 if (S_ISDIR(cur->type)) {
1043 assert(cur->child);
1045 vp = NULL;
1046 error = hammer2_nmkdir(dvp, &vp, cur->name,
1047 strlen(cur->name), cur->inode->st.st_mode);
1048 if (error)
1049 errx(1, "hammer2_nmkdir(\"%s\") failed: %s",
1050 cur->name, strerror(error));
1051 assert(vp);
1052 hammer2_print(dvp, vp, cur, depth, "nmkdir");
1054 error = hammer2_populate_dir(vp, path, cur->child, cur,
1055 fsopts, depth + 1);
1056 if (error)
1057 errx(1, "failed to populate %s: %s",
1058 path, strerror(error));
1059 cur->inode->param = vp;
1060 continue;
1063 /* if regular file, creat and write its data */
1064 if (S_ISREG(cur->type) && !hardlink) {
1065 assert(cur->child == NULL);
1067 vp = NULL;
1068 error = hammer2_ncreate(dvp, &vp, cur->name,
1069 strlen(cur->name), cur->inode->st.st_mode);
1070 if (error)
1071 errx(1, "hammer2_ncreate(\"%s\") failed: %s",
1072 cur->name, strerror(error));
1073 assert(vp);
1074 hammer2_print(dvp, vp, cur, depth, "ncreate");
1076 error = hammer2_write_file(vp, path, cur);
1077 if (error)
1078 errx(1, "hammer2_write_file(\"%s\") failed: %s",
1079 path, strerror(error));
1080 cur->inode->param = vp;
1081 continue;
1084 /* if symlink, create a symlink against target */
1085 if (S_ISLNK(cur->type)) {
1086 assert(cur->child == NULL);
1088 vp = NULL;
1089 error = hammer2_nsymlink(dvp, &vp, cur->name,
1090 strlen(cur->name), cur->symlink,
1091 cur->inode->st.st_mode);
1092 if (error)
1093 errx(1, "hammer2_nsymlink(\"%s\") failed: %s",
1094 cur->name, strerror(error));
1095 assert(vp);
1096 hammer2_print(dvp, vp, cur, depth, "nsymlink");
1097 cur->inode->param = vp;
1098 continue;
1101 /* if fifo, create a fifo */
1102 if (S_ISFIFO(cur->type) && !hardlink) {
1103 assert(cur->child == NULL);
1105 vp = NULL;
1106 error = hammer2_nmknod(dvp, &vp, cur->name,
1107 strlen(cur->name), VFIFO, cur->inode->st.st_mode);
1108 if (error)
1109 errx(1, "hammer2_nmknod(\"%s\") failed: %s",
1110 cur->name, strerror(error));
1111 assert(vp);
1112 hammer2_print(dvp, vp, cur, depth, "nmknod");
1113 cur->inode->param = vp;
1114 continue;
1117 /* if hardlink, creat a hardlink */
1118 if ((S_ISREG(cur->type) || S_ISFIFO(cur->type)) && hardlink) {
1119 char buf[64];
1120 assert(cur->child == NULL);
1122 /* source vnode must not be NULL */
1123 vp = cur->inode->param;
1124 assert(vp);
1125 /* currently these conditions must be true */
1126 assert(vp->v_data);
1127 assert(vp->v_type == VREG || vp->v_type == VFIFO);
1128 assert(vp->v_logical);
1129 assert(!vp->v_vflushed);
1130 assert(vp->v_malloced);
1131 assert(VTOI(vp)->refs > 0);
1133 error = hammer2_nlink(dvp, vp, cur->name,
1134 strlen(cur->name));
1135 if (error)
1136 errx(1, "hammer2_nlink(\"%s\") failed: %s",
1137 cur->name, strerror(error));
1138 snprintf(buf, sizeof(buf), "nlink=%lld",
1139 (long long)VTOI(vp)->meta.nlinks);
1140 hammer2_print(dvp, vp, cur, depth, buf);
1141 continue;
1144 /* other types are unsupported */
1145 printf("ignore %s 0%o\n", path, cur->type);
1148 return 0;
1151 static int
1152 hammer2_write_file(struct m_vnode *vp, const char *path, fsnode *node)
1154 struct stat *st = &node->inode->st;
1155 size_t nsize, bufsize;
1156 off_t offset;
1157 int fd, error;
1158 char *p;
1160 nsize = st->st_size;
1161 if (nsize == 0)
1162 return 0;
1163 /* check nsize vs maximum file size */
1165 fd = open(path, O_RDONLY);
1166 if (fd < 0)
1167 err(1, "failed to open %s", path);
1169 p = mmap(0, nsize, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0);
1170 if (p == MAP_FAILED)
1171 err(1, "failed to mmap %s", path);
1172 close(fd);
1174 for (offset = 0; offset < nsize; ) {
1175 bufsize = MIN(nsize - offset, HAMMER2_PBUFSIZE);
1176 assert(bufsize <= HAMMER2_PBUFSIZE);
1177 error = hammer2_write(vp, p + offset, bufsize, offset);
1178 if (error)
1179 errx(1, "failed to write to %s vnode: %s",
1180 path, strerror(error));
1181 offset += bufsize;
1182 if (bufsize == HAMMER2_PBUFSIZE)
1183 assert((offset & (HAMMER2_PBUFSIZE - 1)) == 0);
1185 munmap(p, nsize);
1187 return 0;
1190 static int
1191 trim_char(char *p, char c)
1193 char *o, tmp[PATH_MAX];
1194 bool prev_was_c;
1195 size_t n;
1196 int i;
1198 strlcpy(tmp, p, sizeof(tmp));
1199 if (strncmp(tmp, p, sizeof(tmp)))
1200 return ENOSPC;
1202 /* trim consecutive */
1203 prev_was_c = false;
1204 o = p;
1205 n = strlen(p);
1207 for (i = 0; i < n; i++) {
1208 if (tmp[i] == c) {
1209 if (!prev_was_c)
1210 *p++ = tmp[i];
1211 prev_was_c = true;
1212 } else {
1213 *p++ = tmp[i];
1214 prev_was_c = false;
1217 *p = 0;
1218 assert(strlen(p) <= strlen(tmp));
1220 /* assert no consecutive */
1221 prev_was_c = false;
1222 p = o;
1223 n = strlen(p);
1225 for (i = 0; i < n; i++) {
1226 if (p[i] == c) {
1227 assert(!prev_was_c);
1228 prev_was_c = true;
1229 } else {
1230 prev_was_c = false;
1234 /* trim leading */
1235 if (*p == c)
1236 memmove(p, p + 1, strlen(p + 1) + 1);
1237 assert(*p != '/');
1239 /* trim trailing */
1240 p += strlen(p);
1241 p--;
1242 if (*p == c)
1243 *p = 0;
1244 assert(p[strlen(p) - 1] != '/');
1246 return 0;
1249 static int
1250 trim_slash(char *p)
1252 return trim_char(p, '/');
1255 static int
1256 hammer2_version_get(struct m_vnode *vp)
1258 hammer2_dev_t *hmp;
1260 hmp = VTOI(vp)->pmp->pfs_hmps[0];
1261 if (hmp == NULL)
1262 return EINVAL;
1264 printf("version: %d\n", hmp->voldata.version);
1266 return 0;
1269 struct pfs_entry {
1270 TAILQ_ENTRY(pfs_entry) entry;
1271 char name[NAME_MAX+1];
1272 char s[NAME_MAX+1];
1275 static int
1276 hammer2_pfs_get(struct m_vnode *vp)
1278 hammer2_ioc_pfs_t pfs;
1279 TAILQ_HEAD(, pfs_entry) head;
1280 struct pfs_entry *p, *e;
1281 char *pfs_id_str;
1282 const char *type_str;
1283 int error;
1285 bzero(&pfs, sizeof(pfs));
1286 TAILQ_INIT(&head);
1288 while ((pfs.name_key = pfs.name_next) != (hammer2_key_t)-1) {
1289 error = hammer2_ioctl_pfs_get(VTOI(vp), &pfs);
1290 if (error)
1291 return error;
1293 pfs_id_str = NULL;
1294 hammer2_uuid_to_str(&pfs.pfs_clid, &pfs_id_str);
1296 if (pfs.pfs_type == HAMMER2_PFSTYPE_MASTER) {
1297 if (pfs.pfs_subtype == HAMMER2_PFSSUBTYPE_NONE)
1298 type_str = "MASTER";
1299 else
1300 type_str = hammer2_pfssubtype_to_str(
1301 pfs.pfs_subtype);
1302 } else {
1303 type_str = hammer2_pfstype_to_str(pfs.pfs_type);
1305 e = ecalloc(1, sizeof(*e));
1306 snprintf(e->name, sizeof(e->name), "%s", pfs.name);
1307 snprintf(e->s, sizeof(e->s), "%-11s %s", type_str, pfs_id_str);
1308 free(pfs_id_str);
1310 p = TAILQ_FIRST(&head);
1311 while (p) {
1312 if (strcmp(e->name, p->name) <= 0) {
1313 TAILQ_INSERT_BEFORE(p, e, entry);
1314 break;
1316 p = TAILQ_NEXT(p, entry);
1318 if (!p)
1319 TAILQ_INSERT_TAIL(&head, e, entry);
1322 printf("Type "
1323 "ClusterId (pfs_clid) "
1324 "Label\n");
1325 while ((p = TAILQ_FIRST(&head)) != NULL) {
1326 printf("%s %s\n", p->s, p->name);
1327 TAILQ_REMOVE(&head, p, entry);
1328 free(p);
1331 return 0;
1334 static int
1335 hammer2_pfs_lookup(struct m_vnode *vp, const char *pfs_name)
1337 hammer2_ioc_pfs_t pfs;
1338 char *pfs_id_str;
1339 int error;
1341 bzero(&pfs, sizeof(pfs));
1342 strlcpy(pfs.name, pfs_name, sizeof(pfs.name));
1344 error = hammer2_ioctl_pfs_lookup(VTOI(vp), &pfs);
1345 if (error == 0) {
1346 printf("name: %s\n", pfs.name);
1347 printf("type: %s\n", hammer2_pfstype_to_str(pfs.pfs_type));
1348 printf("subtype: %s\n",
1349 hammer2_pfssubtype_to_str(pfs.pfs_subtype));
1351 pfs_id_str = NULL;
1352 hammer2_uuid_to_str(&pfs.pfs_fsid, &pfs_id_str);
1353 printf("fsid: %s\n", pfs_id_str);
1354 free(pfs_id_str);
1356 pfs_id_str = NULL;
1357 hammer2_uuid_to_str(&pfs.pfs_clid, &pfs_id_str);
1358 printf("clid: %s\n", pfs_id_str);
1359 free(pfs_id_str);
1362 return error;
1365 static int
1366 hammer2_pfs_create(struct m_vnode *vp, const char *pfs_name)
1368 hammer2_ioc_pfs_t pfs;
1369 int error;
1371 bzero(&pfs, sizeof(pfs));
1372 strlcpy(pfs.name, pfs_name, sizeof(pfs.name));
1373 pfs.pfs_type = HAMMER2_PFSTYPE_MASTER;
1374 uuid_create(&pfs.pfs_clid, NULL);
1375 uuid_create(&pfs.pfs_fsid, NULL);
1377 error = hammer2_ioctl_pfs_create(VTOI(vp), &pfs);
1378 if (error == EEXIST)
1379 fprintf(stderr,
1380 "NOTE: Typically the same name is "
1381 "used for cluster elements on "
1382 "different mounts,\n"
1383 " but cluster elements on the "
1384 "same mount require unique names.\n"
1385 "hammer2: pfs_create(%s): already present\n",
1386 pfs_name);
1388 return error;
1391 static int
1392 hammer2_pfs_delete(struct m_vnode *vp, const char *pfs_name)
1394 hammer2_ioc_pfs_t pfs;
1396 bzero(&pfs, sizeof(pfs));
1397 strlcpy(pfs.name, pfs_name, sizeof(pfs.name));
1399 return hammer2_ioctl_pfs_delete(VTOI(vp), &pfs);
1402 static int
1403 hammer2_pfs_snapshot(struct m_vnode *vp, const char *pfs_name,
1404 const char *mount_label)
1406 hammer2_ioc_pfs_t pfs;
1407 struct tm *tp;
1408 time_t t;
1410 bzero(&pfs, sizeof(pfs));
1411 strlcpy(pfs.name, pfs_name, sizeof(pfs.name));
1413 if (strlen(pfs.name) == 0) {
1414 time(&t);
1415 tp = localtime(&t);
1416 snprintf(pfs.name, sizeof(pfs.name),
1417 "%s.%04d%02d%02d.%02d%02d%02d",
1418 mount_label,
1419 tp->tm_year + 1900,
1420 tp->tm_mon + 1,
1421 tp->tm_mday,
1422 tp->tm_hour,
1423 tp->tm_min,
1424 tp->tm_sec);
1427 return hammer2_ioctl_pfs_snapshot(VTOI(vp), &pfs);
1430 static int
1431 hammer2_inode_getx(struct m_vnode *dvp, const char *f)
1433 hammer2_ioc_inode_t inode;
1434 hammer2_inode_t *ip;
1435 hammer2_inode_meta_t *meta;
1436 struct m_vnode *vp;
1437 char *o, *p, *name, *str = NULL;
1438 int error;
1439 uuid_t uuid;
1441 assert(strlen(f) > 0);
1442 o = p = name = strdup(f);
1444 error = trim_slash(p);
1445 if (error)
1446 return error;
1447 if (strlen(p) == 0)
1448 return EINVAL;
1450 while ((p = strchr(p, '/')) != NULL) {
1451 *p++ = 0; /* NULL terminate name */
1452 vp = NULL;
1453 error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1454 if (error)
1455 return error;
1457 ip = VTOI(vp);
1458 assert(ip->meta.type == HAMMER2_OBJTYPE_DIRECTORY);
1460 dvp = vp;
1461 name = p;
1464 error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1465 if (error)
1466 return error;
1468 bzero(&inode, sizeof(inode));
1469 error = hammer2_ioctl_inode_get(VTOI(vp), &inode);
1470 if (error)
1471 return error;
1473 meta = &inode.ip_data.meta;
1474 printf("--------------------\n");
1475 printf("flags = 0x%x\n", inode.flags);
1476 printf("data_count = %ju\n", (uintmax_t)inode.data_count);
1477 printf("inode_count = %ju\n", (uintmax_t)inode.inode_count);
1478 printf("--------------------\n");
1479 printf("version = %u\n", meta->version);
1480 printf("pfs_subtype = %u (%s)\n", meta->pfs_subtype,
1481 hammer2_pfssubtype_to_str(meta->pfs_subtype));
1482 printf("uflags = 0x%x\n", (unsigned int)meta->uflags);
1483 printf("rmajor = %u\n", meta->rmajor);
1484 printf("rminor = %u\n", meta->rminor);
1485 printf("ctime = %s\n", hammer2_time64_to_str(meta->ctime, &str));
1486 printf("mtime = %s\n", hammer2_time64_to_str(meta->mtime, &str));
1487 printf("atime = %s\n", hammer2_time64_to_str(meta->atime, &str));
1488 printf("btime = %s\n", hammer2_time64_to_str(meta->btime, &str));
1489 uuid = meta->uid;
1490 printf("uid = %s\n", hammer2_uuid_to_str(&uuid, &str));
1491 uuid = meta->gid;
1492 printf("gid = %s\n", hammer2_uuid_to_str(&uuid, &str));
1493 printf("type = %u (%s)\n", meta->type,
1494 hammer2_iptype_to_str(meta->type));
1495 printf("op_flags = 0x%x\n", meta->op_flags);
1496 printf("cap_flags = 0x%x\n", meta->cap_flags);
1497 printf("mode = 0%o\n", meta->mode);
1498 printf("inum = 0x%jx\n", (uintmax_t)meta->inum);
1499 printf("size = %ju\n", (uintmax_t)meta->size);
1500 printf("nlinks = %ju\n", (uintmax_t)meta->nlinks);
1501 printf("iparent = 0x%jx\n", (uintmax_t)meta->iparent);
1502 printf("name_key = 0x%jx\n", (uintmax_t)meta->name_key);
1503 printf("name_len = %u\n", meta->name_len);
1504 printf("ncopies = %u\n", meta->ncopies);
1505 printf("comp_algo = %u\n", meta->comp_algo);
1506 printf("target_type = %u\n", meta->target_type);
1507 printf("check_algo = %u\n", meta->check_algo);
1508 printf("pfs_nmasters = %u\n", meta->pfs_nmasters);
1509 printf("pfs_type = %u (%s)\n", meta->pfs_type,
1510 hammer2_pfstype_to_str(meta->pfs_type));
1511 printf("pfs_inum = 0x%jx\n", (uintmax_t)meta->pfs_inum);
1512 uuid = meta->pfs_clid;
1513 printf("pfs_clid = %s\n", hammer2_uuid_to_str(&uuid, &str));
1514 uuid = meta->pfs_fsid;
1515 printf("pfs_fsid = %s\n", hammer2_uuid_to_str(&uuid, &str));
1516 printf("data_quota = 0x%jx\n", (uintmax_t)meta->data_quota);
1517 printf("inode_quota = 0x%jx\n", (uintmax_t)meta->inode_quota);
1518 printf("pfs_lsnap_tid = 0x%jx\n", (uintmax_t)meta->pfs_lsnap_tid);
1519 printf("decrypt_check = 0x%jx\n", (uintmax_t)meta->decrypt_check);
1520 printf("--------------------\n");
1522 free(o);
1524 return error;
1527 static int
1528 hammer2_inode_setcheck(struct m_vnode *dvp, const char *f)
1530 hammer2_ioc_inode_t inode;
1531 hammer2_inode_t *ip;
1532 struct m_vnode *vp;
1533 char *o, *p, *name, *check_algo_str;
1534 const char *checks[] = { "none", "disabled", "crc32", "xxhash64",
1535 "sha192", };
1536 int check_algo_idx, error;
1537 uint8_t check_algo;
1539 assert(strlen(f) > 0);
1540 o = p = strdup(f);
1542 p = strrchr(p, ':');
1543 if (p == NULL)
1544 return EINVAL;
1546 *p++ = 0; /* NULL terminate path */
1547 check_algo_str = p;
1548 name = p = o;
1550 error = trim_slash(p);
1551 if (error)
1552 return error;
1553 if (strlen(p) == 0 || strlen(check_algo_str) == 0)
1554 return EINVAL;
1556 /* convert check_algo_str to check_algo_idx */
1557 check_algo_idx = nitems(checks);
1558 while (--check_algo_idx >= 0)
1559 if (strcasecmp(check_algo_str, checks[check_algo_idx]) == 0)
1560 break;
1561 if (check_algo_idx < 0) {
1562 if (strcasecmp(check_algo_str, "default") == 0) {
1563 check_algo_str = "xxhash64";
1564 check_algo_idx = HAMMER2_CHECK_XXHASH64;
1565 } else if (strcasecmp(check_algo_str, "disabled") == 0) {
1566 check_algo_str = "disabled";
1567 check_algo_idx = HAMMER2_CHECK_DISABLED;
1568 } else {
1569 printf("invalid check_algo_str: %s\n", check_algo_str);
1570 return EINVAL;
1573 check_algo = HAMMER2_ENC_ALGO(check_algo_idx);
1574 printf("change %s to algo %d (%s)\n", p, check_algo, check_algo_str);
1576 while ((p = strchr(p, '/')) != NULL) {
1577 *p++ = 0; /* NULL terminate name */
1578 vp = NULL;
1579 error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1580 if (error)
1581 return error;
1583 ip = VTOI(vp);
1584 assert(ip->meta.type == HAMMER2_OBJTYPE_DIRECTORY);
1586 dvp = vp;
1587 name = p;
1590 error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1591 if (error)
1592 return error;
1593 ip = VTOI(vp);
1595 bzero(&inode, sizeof(inode));
1596 error = hammer2_ioctl_inode_get(ip, &inode);
1597 if (error)
1598 return error;
1600 inode.flags |= HAMMER2IOC_INODE_FLAG_CHECK;
1601 inode.ip_data.meta.check_algo = check_algo;
1602 error = hammer2_ioctl_inode_set(ip, &inode);
1603 if (error)
1604 return error;
1606 free(o);
1608 return error;
1611 static int
1612 hammer2_inode_setcomp(struct m_vnode *dvp, const char *f)
1614 hammer2_ioc_inode_t inode;
1615 hammer2_inode_t *ip;
1616 struct m_vnode *vp;
1617 char *o, *p, *name, *comp_algo_str, *comp_level_str;
1618 const char *comps[] = { "none", "autozero", "lz4", "zlib", };
1619 int comp_algo_idx, comp_level_idx, error;
1620 uint8_t comp_algo, comp_level;
1622 assert(strlen(f) > 0);
1623 o = p = strdup(f);
1625 p = strrchr(p, ':');
1626 if (p == NULL)
1627 return EINVAL;
1629 *p++ = 0; /* NULL terminate comp_algo_str */
1630 comp_level_str = p;
1631 p = o;
1633 p = strrchr(p, ':');
1634 if (p == NULL) {
1635 /* comp_level_str not specified */
1636 comp_algo_str = comp_level_str;
1637 comp_level_str = NULL;
1638 } else {
1639 *p++ = 0; /* NULL terminate path */
1640 comp_algo_str = p;
1642 name = p = o;
1644 error = trim_slash(p);
1645 if (error)
1646 return error;
1647 if (strlen(p) == 0 || strlen(comp_algo_str) == 0)
1648 return EINVAL;
1650 /* convert comp_algo_str to comp_algo_idx */
1651 comp_algo_idx = nitems(comps);
1652 while (--comp_algo_idx >= 0)
1653 if (strcasecmp(comp_algo_str, comps[comp_algo_idx]) == 0)
1654 break;
1655 if (comp_algo_idx < 0) {
1656 if (strcasecmp(comp_algo_str, "default") == 0) {
1657 comp_algo_str = "lz4";
1658 comp_algo_idx = HAMMER2_COMP_LZ4;
1659 } else if (strcasecmp(comp_algo_str, "disabled") == 0) {
1660 comp_algo_str = "autozero";
1661 comp_algo_idx = HAMMER2_COMP_AUTOZERO;
1662 } else {
1663 printf("invalid comp_algo_str: %s\n", comp_algo_str);
1664 return EINVAL;
1667 comp_algo = HAMMER2_ENC_ALGO(comp_algo_idx);
1669 /* convert comp_level_str to comp_level_idx */
1670 if (comp_level_str == NULL) {
1671 comp_level_idx = 0;
1672 } else if (isdigit((int)comp_level_str[0])) {
1673 comp_level_idx = strtol(comp_level_str, NULL, 0);
1674 } else if (strcasecmp(comp_level_str, "default") == 0) {
1675 comp_level_idx = 0;
1676 } else {
1677 printf("invalid comp_level_str: %s\n", comp_level_str);
1678 return EINVAL;
1680 if (comp_level_idx) {
1681 switch (comp_algo) {
1682 case HAMMER2_COMP_ZLIB:
1683 if (comp_level_idx < 6 || comp_level_idx > 9) {
1684 printf("unsupported comp_level %d for %s\n",
1685 comp_level_idx, comp_algo_str);
1686 return EINVAL;
1688 break;
1689 default:
1690 printf("unsupported comp_level %d for %s\n",
1691 comp_level_idx, comp_algo_str);
1692 return EINVAL;
1695 comp_level = HAMMER2_ENC_LEVEL(comp_level_idx);
1696 printf("change %s to algo %d (%s) level %d\n",
1697 p, comp_algo, comp_algo_str, comp_level_idx);
1699 while ((p = strchr(p, '/')) != NULL) {
1700 *p++ = 0; /* NULL terminate name */
1701 vp = NULL;
1702 error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1703 if (error)
1704 return error;
1706 ip = VTOI(vp);
1707 assert(ip->meta.type == HAMMER2_OBJTYPE_DIRECTORY);
1709 dvp = vp;
1710 name = p;
1713 error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1714 if (error)
1715 return error;
1716 ip = VTOI(vp);
1718 bzero(&inode, sizeof(inode));
1719 error = hammer2_ioctl_inode_get(ip, &inode);
1720 if (error)
1721 return error;
1723 inode.flags |= HAMMER2IOC_INODE_FLAG_COMP;
1724 inode.ip_data.meta.comp_algo = comp_algo | comp_level;
1725 error = hammer2_ioctl_inode_set(ip, &inode);
1726 if (error)
1727 return error;
1729 free(o);
1731 return error;
1734 static int
1735 hammer2_bulkfree(struct m_vnode *vp)
1737 hammer2_ioc_bulkfree_t bfi;
1738 size_t usermem;
1739 size_t usermem_size = sizeof(usermem);
1741 bzero(&bfi, sizeof(bfi));
1742 usermem = 0;
1743 if (sysctlbyname("hw.usermem", &usermem, &usermem_size, NULL, 0) == 0)
1744 bfi.size = usermem / 16;
1745 else
1746 bfi.size = 0;
1747 if (bfi.size < 8192 * 1024)
1748 bfi.size = 8192 * 1024;
1750 return hammer2_ioctl_bulkfree_scan(VTOI(vp), &bfi);
1753 static int
1754 hammer2_destroy_path(struct m_vnode *dvp, const char *f)
1756 hammer2_ioc_destroy_t destroy;
1757 hammer2_inode_t *ip;
1758 struct m_vnode *vp;
1759 char *o, *p, *name;
1760 int error;
1762 assert(strlen(f) > 0);
1763 o = p = name = strdup(f);
1765 error = trim_slash(p);
1766 if (error)
1767 return error;
1768 if (strlen(p) == 0)
1769 return EINVAL;
1771 while ((p = strchr(p, '/')) != NULL) {
1772 *p++ = 0; /* NULL terminate name */
1773 vp = NULL;
1774 error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1775 if (error)
1776 return error;
1778 ip = VTOI(vp);
1779 assert(ip->meta.type == HAMMER2_OBJTYPE_DIRECTORY);
1781 dvp = vp;
1782 name = p;
1785 /* XXX When does (or why does not) ioctl modify this inode ? */
1786 hammer2_inode_modify(VTOI(dvp));
1788 bzero(&destroy, sizeof(destroy));
1789 destroy.cmd = HAMMER2_DELETE_FILE;
1790 snprintf(destroy.path, sizeof(destroy.path), "%s", name);
1792 printf("%s\t", f);
1793 fflush(stdout);
1795 error = hammer2_ioctl_destroy(VTOI(dvp), &destroy);
1796 if (error)
1797 printf("%s\n", strerror(error));
1798 else
1799 printf("ok\n");
1800 free(o);
1802 return error;
1805 static int
1806 hammer2_destroy_inum(struct m_vnode *vp, hammer2_tid_t inum)
1808 hammer2_ioc_destroy_t destroy;
1809 int error;
1811 bzero(&destroy, sizeof(destroy));
1812 destroy.cmd = HAMMER2_DELETE_INUM;
1813 destroy.inum = inum;
1815 printf("%jd\t", (intmax_t)destroy.inum);
1816 fflush(stdout);
1818 error = hammer2_ioctl_destroy(VTOI(vp), &destroy);
1819 if (error)
1820 printf("%s\n", strerror(error));
1821 else
1822 printf("ok\n");
1824 return error;
1827 static int
1828 hammer2_growfs(struct m_vnode *vp, hammer2_off_t size)
1830 hammer2_ioc_growfs_t growfs;
1831 int error;
1833 bzero(&growfs, sizeof(growfs));
1834 growfs.size = size;
1836 error = hammer2_ioctl_growfs(VTOI(vp), &growfs, NULL);
1837 if (!error) {
1838 if (growfs.modified)
1839 printf("grown to %016jx\n", (intmax_t)growfs.size);
1840 else
1841 printf("no size change - %016jx\n",
1842 (intmax_t)growfs.size);
1845 return error;
1848 static void
1849 assert_trim_slash(const char *input, const char *expected)
1851 char tmp[PATH_MAX];
1852 int error;
1854 strlcpy(tmp, input, sizeof(tmp));
1855 error = trim_slash(tmp);
1856 if (error)
1857 errx(1, "input \"%s\" error %d", input, error);
1859 if (strncmp(tmp, expected, sizeof(tmp)))
1860 errx(1, "input \"%s\" result \"%s\" vs expected \"%s\"",
1861 input, tmp, expected);
1864 static void
1865 unittest_trim_slash(void)
1867 assert_trim_slash("", "");
1868 assert_trim_slash("/", "");
1869 assert_trim_slash("//", "");
1870 assert_trim_slash("///", "");
1872 assert_trim_slash("makefs", "makefs");
1873 assert_trim_slash("/makefs", "makefs");
1874 assert_trim_slash("//makefs", "makefs");
1875 assert_trim_slash("makefs/", "makefs");
1876 assert_trim_slash("makefs//", "makefs");
1877 assert_trim_slash("/makefs/", "makefs");
1878 assert_trim_slash("//makefs//", "makefs");
1880 assert_trim_slash("sys/vfs/hammer2", "sys/vfs/hammer2");
1881 assert_trim_slash("/sys/vfs/hammer2", "sys/vfs/hammer2");
1882 assert_trim_slash("//sys/vfs/hammer2", "sys/vfs/hammer2");
1883 assert_trim_slash("///sys/vfs/hammer2", "sys/vfs/hammer2");
1884 assert_trim_slash("sys/vfs/hammer2/", "sys/vfs/hammer2");
1885 assert_trim_slash("sys/vfs/hammer2//", "sys/vfs/hammer2");
1886 assert_trim_slash("sys/vfs/hammer2///", "sys/vfs/hammer2");
1887 assert_trim_slash("/sys/vfs/hammer2/", "sys/vfs/hammer2");
1888 assert_trim_slash("//sys//vfs//hammer2//", "sys/vfs/hammer2");
1889 assert_trim_slash("///sys///vfs///hammer2///", "sys/vfs/hammer2");
1891 APRINTF("success\n");