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
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
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
35 #if HAVE_NBTOOL_CONFIG_H
36 #include "nbtool_config.h"
39 #include <sys/param.h>
41 #include <sys/sysctl.h>
58 #define APRINTF(X, ...) \
59 printf("%s: " X, __func__, ## __VA_ARGS__)
61 static void hammer2_parse_pfs_opts(const char *, fsinfo_t
*);
62 static void hammer2_dump_fsinfo(fsinfo_t
*);
63 static int hammer2_create_image(const char *, fsinfo_t
*);
64 static int hammer2_populate_dir(struct m_vnode
*, const char *, fsnode
*,
65 fsnode
*, fsinfo_t
*, int);
66 static void hammer2_validate(const char *, fsnode
*, fsinfo_t
*);
67 static void hammer2_size_dir(fsnode
*, fsinfo_t
*);
68 static int hammer2_write_file(struct m_vnode
*, const char *, fsnode
*);
69 static int hammer2_version_get(struct m_vnode
*);
70 static int hammer2_pfs_get(struct m_vnode
*);
71 static int hammer2_pfs_lookup(struct m_vnode
*, const char *);
72 static int hammer2_pfs_create(struct m_vnode
*, const char *);
73 static int hammer2_pfs_delete(struct m_vnode
*, const char *);
74 static int hammer2_pfs_snapshot(struct m_vnode
*, const char *, const char *);
75 static int hammer2_bulkfree(struct m_vnode
*);
76 static int hammer2_destroy_path(struct m_vnode
*, const char *);
77 static int hammer2_destroy_inum(struct m_vnode
*, hammer2_tid_t
);
78 static int hammer2_growfs(struct m_vnode
*, hammer2_off_t
);
80 fsnode
*hammer2_curnode
;
83 hammer2_prep_opts(fsinfo_t
*fsopts
)
85 hammer2_makefs_options_t
*h2_opt
= ecalloc(1, sizeof(*h2_opt
));
86 hammer2_mkfs_options_t
*opt
= &h2_opt
->mkfs_options
;
88 const option_t hammer2_options
[] = {
89 /* newfs_hammer2(8) compatible options */
90 { 'b', "BootAreaSize", NULL
, OPT_STRBUF
, 0, 0, "boot area size" },
91 { 'r', "AuxAreaSize", NULL
, OPT_STRBUF
, 0, 0, "aux area size" },
92 { 'V', "Hammer2Version", NULL
, OPT_STRBUF
, 0, 0, "file system version" },
93 { 'L', "Label", NULL
, OPT_STRBUF
, 0, 0, "PFS label" },
94 /* makefs(8) specific options */
95 { 'm', "MountLabel", NULL
, OPT_STRBUF
, 0, 0, "destination PFS label" },
96 { 'v', "NumVolhdr", &h2_opt
->num_volhdr
, OPT_INT32
,
97 1, HAMMER2_NUM_VOLHDRS
, "number of volume headers" },
98 { 'd', "Hammer2Debug", NULL
, OPT_STRBUF
, 0, 0, "debug tunable" },
99 { 'E', "EmergencyMode", &h2_opt
->emergency_mode
, OPT_BOOL
, 0, 0,
101 { 'P', "PFS", NULL
, OPT_STRBUF
, 0, 0, "offline PFS" },
102 { 'B', "Bulkfree", NULL
, OPT_STRBUF
, 0, 0, "offline bulkfree" },
103 { 'D', "Destroy", NULL
, OPT_STRBUF
, 0, 0, "offline destroy" },
104 { 'G', "Growfs", NULL
, OPT_STRBUF
, 0, 0, "offline growfs" },
108 hammer2_mkfs_init(opt
);
110 /* make this tunable ? */
111 assert(opt
->CompType
== HAMMER2_COMP_NEWFS_DEFAULT
);
112 assert(opt
->CheckType
== HAMMER2_CHECK_XXHASH64
);
114 /* force debug mode for mkfs */
117 fsopts
->fs_specific
= h2_opt
;
118 fsopts
->fs_options
= copy_opts(hammer2_options
);
119 fsopts
->sectorsize
= DEV_BSIZE
;
123 hammer2_cleanup_opts(fsinfo_t
*fsopts
)
125 hammer2_makefs_options_t
*h2_opt
= fsopts
->fs_specific
;
126 hammer2_mkfs_options_t
*opt
= &h2_opt
->mkfs_options
;
128 hammer2_mkfs_cleanup(opt
);
131 free(fsopts
->fs_options
);
135 hammer2_parse_opts(const char *option
, fsinfo_t
*fsopts
)
137 hammer2_makefs_options_t
*h2_opt
= fsopts
->fs_specific
;
138 hammer2_mkfs_options_t
*opt
= &h2_opt
->mkfs_options
;
140 option_t
*hammer2_options
= fsopts
->fs_options
;
141 char buf
[1024]; /* > HAMMER2_INODE_MAXNAME */
144 assert(option
!= NULL
);
145 assert(fsopts
!= NULL
);
147 if (debug
& DEBUG_FS_PARSE_OPTS
)
148 APRINTF("got `%s'\n", option
);
150 i
= set_option(hammer2_options
, option
, buf
, sizeof(buf
));
154 if (hammer2_options
[i
].name
== NULL
)
157 switch (hammer2_options
[i
].letter
) {
159 opt
->BootAreaSize
= getsize(buf
, HAMMER2_NEWFS_ALIGN
,
160 HAMMER2_BOOT_MAX_BYTES
, 2);
163 opt
->AuxAreaSize
= getsize(buf
, HAMMER2_NEWFS_ALIGN
,
164 HAMMER2_AUX_MAX_BYTES
, 2);
167 if (strlen(buf
) == 0) {
168 h2_opt
->ioctl_cmd
= HAMMER2IOC_VERSION_GET
;
170 opt
->Hammer2Version
= strtol(buf
, NULL
, 0);
171 if (opt
->Hammer2Version
< HAMMER2_VOL_VERSION_MIN
||
172 opt
->Hammer2Version
>= HAMMER2_VOL_VERSION_WIP
)
173 errx(1, "I don't understand how to format "
174 "HAMMER2 version %d",
175 opt
->Hammer2Version
);
179 h2_opt
->label_specified
= 1;
180 if (strcasecmp(buf
, "none") == 0)
182 if (opt
->NLabels
>= MAXLABELS
)
183 errx(1, "Limit of %d local labels", MAXLABELS
- 1);
184 if (strlen(buf
) == 0)
185 errx(1, "Volume label '%s' cannot be 0-length", buf
);
186 if (strlen(buf
) >= HAMMER2_INODE_MAXNAME
)
187 errx(1, "Volume label '%s' is too long (%d chars max)",
188 buf
, HAMMER2_INODE_MAXNAME
- 1);
189 opt
->Label
[opt
->NLabels
++] = strdup(buf
);
192 if (strlen(buf
) == 0)
193 errx(1, "Volume label '%s' cannot be 0-length", buf
);
194 if (strlen(buf
) >= HAMMER2_INODE_MAXNAME
)
195 errx(1, "Volume label '%s' is too long (%d chars max)",
196 buf
, HAMMER2_INODE_MAXNAME
- 1);
197 strlcpy(h2_opt
->mount_label
, buf
, sizeof(h2_opt
->mount_label
));
200 hammer2_debug
= strtoll(buf
, NULL
, 0);
203 if (strlen(buf
) == 0)
204 errx(1, "PFS argument '%s' cannot be 0-length", buf
);
205 hammer2_parse_pfs_opts(buf
, fsopts
);
208 h2_opt
->ioctl_cmd
= HAMMER2IOC_BULKFREE_SCAN
;
211 h2_opt
->ioctl_cmd
= HAMMER2IOC_DESTROY
;
212 if (strlen(buf
) == 0)
213 errx(1, "Destroy argument '%s' cannot be 0-length", buf
);
215 strlcpy(h2_opt
->destroy_path
, buf
+ 1,
216 sizeof(h2_opt
->destroy_path
));
217 } else if (strncmp(buf
, "0x", 2) == 0 ||
218 (buf
[0] >= '0' && buf
[0] <= '9')) {
219 h2_opt
->destroy_inum
= strtoull(buf
, NULL
, 0);
223 errx(1, "Invalid destroy argument %s", buf
);
227 h2_opt
->ioctl_cmd
= HAMMER2IOC_GROWFS
;
237 hammer2_makefs(const char *image
, const char *dir
, fsnode
*root
,
240 hammer2_makefs_options_t
*h2_opt
= fsopts
->fs_specific
;
242 struct hammer2_mount_info info
;
243 struct m_vnode devvp
, *vroot
;
244 hammer2_inode_t
*iroot
;
245 struct timeval start
;
248 /* ioctl commands could have NULL root */
249 assert(image
!= NULL
);
251 assert(fsopts
!= NULL
);
253 if (debug
& DEBUG_FS_MAKEFS
)
254 APRINTF("image \"%s\" directory \"%s\" root %p\n",
257 /* validate tree and options */
259 hammer2_validate(dir
, root
, fsopts
);
260 TIMER_RESULTS(start
, "hammer2_validate");
262 if (h2_opt
->ioctl_cmd
) {
263 /* open existing image */
264 fsopts
->fd
= open(image
, O_RDWR
);
266 err(1, "failed to open `%s'", image
);
270 if (hammer2_create_image(image
, fsopts
) == -1)
271 errx(1, "image file `%s' not created", image
);
272 TIMER_RESULTS(start
, "hammer2_create_image");
274 assert(fsopts
->fd
> 0);
276 if (debug
& DEBUG_FS_MAKEFS
)
280 error
= hammer2_vfs_init();
282 errx(1, "failed to vfs init, error %d", error
);
285 memset(&devvp
, 0, sizeof(devvp
));
287 memset(&mp
, 0, sizeof(mp
));
288 memset(&info
, 0, sizeof(info
));
289 error
= hammer2_vfs_mount(&devvp
, &mp
, h2_opt
->mount_label
, &info
);
291 errx(1, "failed to mount, error %d", error
);
296 error
= hammer2_vfs_root(&mp
, &vroot
);
298 errx(1, "failed to get root vnode, error %d", error
);
303 printf("root inode inum %lld, mode 0%o, refs %d\n",
304 (long long)iroot
->meta
.inum
, iroot
->meta
.mode
, iroot
->refs
);
306 if (h2_opt
->emergency_mode
)
307 hammer2_ioctl_emerg_mode(iroot
, 1);
309 switch (h2_opt
->ioctl_cmd
) {
310 case HAMMER2IOC_VERSION_GET
:
311 printf("version get `%s'\n", image
);
313 error
= hammer2_version_get(vroot
);
315 errx(1, "version get `%s' failed '%s'", image
,
317 TIMER_RESULTS(start
, "hammer2_version_get");
319 case HAMMER2IOC_PFS_GET
:
320 printf("PFS %s `%s'\n", h2_opt
->pfs_cmd_name
, image
);
322 error
= hammer2_pfs_get(vroot
);
324 errx(1, "PFS %s`%s' failed '%s'", h2_opt
->pfs_cmd_name
,
325 image
, strerror(error
));
326 TIMER_RESULTS(start
, "hammer2_pfs_get");
328 case HAMMER2IOC_PFS_LOOKUP
:
329 printf("PFS %s `%s'\n", h2_opt
->pfs_cmd_name
, image
);
331 error
= hammer2_pfs_lookup(vroot
, h2_opt
->pfs_name
);
333 errx(1, "PFS %s`%s' failed '%s'", h2_opt
->pfs_cmd_name
,
334 image
, strerror(error
));
335 TIMER_RESULTS(start
, "hammer2_pfs_lookup");
337 case HAMMER2IOC_PFS_CREATE
:
338 printf("PFS %s `%s'\n", h2_opt
->pfs_cmd_name
, image
);
340 error
= hammer2_pfs_create(vroot
, h2_opt
->pfs_name
);
342 errx(1, "PFS %s`%s' failed '%s'", h2_opt
->pfs_cmd_name
,
343 image
, strerror(error
));
344 TIMER_RESULTS(start
, "hammer2_pfs_create");
346 case HAMMER2IOC_PFS_DELETE
:
347 printf("PFS %s `%s'\n", h2_opt
->pfs_cmd_name
, image
);
349 error
= hammer2_pfs_delete(vroot
, h2_opt
->pfs_name
);
351 errx(1, "PFS %s`%s' failed '%s'", h2_opt
->pfs_cmd_name
,
352 image
, strerror(error
));
353 TIMER_RESULTS(start
, "hammer2_pfs_delete");
355 case HAMMER2IOC_PFS_SNAPSHOT
:
356 printf("PFS %s `%s'\n", h2_opt
->pfs_cmd_name
, image
);
358 error
= hammer2_pfs_snapshot(vroot
, h2_opt
->pfs_name
,
359 h2_opt
->mount_label
);
361 errx(1, "PFS %s`%s' failed '%s'", h2_opt
->pfs_cmd_name
,
362 image
, strerror(error
));
363 TIMER_RESULTS(start
, "hammer2_pfs_snapshot");
365 case HAMMER2IOC_BULKFREE_SCAN
:
366 printf("bulkfree `%s'\n", image
);
368 error
= hammer2_bulkfree(vroot
);
370 errx(1, "bulkfree `%s' failed '%s'", image
,
372 TIMER_RESULTS(start
, "hammer2_bulkfree");
374 case HAMMER2IOC_DESTROY
:
376 if (strlen(h2_opt
->destroy_path
)) {
377 printf("destroy `%s' in `%s'\n",
378 h2_opt
->destroy_path
, image
);
379 error
= hammer2_destroy_path(vroot
,
380 h2_opt
->destroy_path
);
382 errx(1, "destroy `%s' in `%s' failed '%s'",
383 h2_opt
->destroy_path
, image
,
386 printf("destroy %lld in `%s'\n",
387 (long long)h2_opt
->destroy_inum
, image
);
388 error
= hammer2_destroy_inum(vroot
,
389 h2_opt
->destroy_inum
);
391 errx(1, "destroy %lld in `%s' failed '%s'",
392 (long long)h2_opt
->destroy_inum
, image
,
395 TIMER_RESULTS(start
, "hammer2_destroy");
397 case HAMMER2IOC_GROWFS
:
398 printf("growfs `%s'\n", image
);
400 error
= hammer2_growfs(vroot
, h2_opt
->image_size
);
402 errx(1, "growfs `%s' failed '%s'", image
,
404 TIMER_RESULTS(start
, "hammer2_growfs");
407 printf("populating `%s'\n", image
);
409 if (hammer2_populate_dir(vroot
, dir
, root
, root
, fsopts
, 0))
410 errx(1, "image file `%s' not populated", image
);
411 TIMER_RESULTS(start
, "hammer2_populate_dir");
416 error
= hammer2_vfs_unmount(&mp
, 0);
418 errx(1, "failed to unmount, error %d", error
);
420 /* check leaked resource */
422 printf("XXX %lld vnode left\n", (long long)vnode_count
);
423 if (hammer2_chain_allocs
)
424 printf("XXX %ld chain left\n", hammer2_chain_allocs
);
428 error
= hammer2_vfs_uninit();
430 errx(1, "failed to vfs uninit, error %d", error
);
432 if (close(fsopts
->fd
) == -1)
433 err(1, "closing `%s'", image
);
436 printf("image `%s' complete\n", image
);
439 /* end of public functions */
442 hammer2_parse_pfs_opts(const char *buf
, fsinfo_t
*fsopts
)
444 hammer2_makefs_options_t
*h2_opt
= fsopts
->fs_specific
;
457 if (!strcmp(o
, "get") || !strcmp(o
, "list")) {
458 h2_opt
->ioctl_cmd
= HAMMER2IOC_PFS_GET
;
459 } else if (!strcmp(o
, "lookup")) {
460 if (n
== 0 || n
> NAME_MAX
)
461 errx(1, "invalid PFS name \"%s\"", p
);
462 h2_opt
->ioctl_cmd
= HAMMER2IOC_PFS_LOOKUP
;
463 } else if (!strcmp(o
, "create")) {
464 if (n
== 0 || n
> NAME_MAX
)
465 errx(1, "invalid PFS name \"%s\"", p
);
466 h2_opt
->ioctl_cmd
= HAMMER2IOC_PFS_CREATE
;
467 } else if (!strcmp(o
, "delete")) {
468 if (n
== 0 || n
> NAME_MAX
)
469 errx(1, "invalid PFS name \"%s\"", p
);
470 h2_opt
->ioctl_cmd
= HAMMER2IOC_PFS_DELETE
;
471 } else if (!strcmp(o
, "snapshot")) {
473 errx(1, "invalid PFS name \"%s\"", p
);
474 h2_opt
->ioctl_cmd
= HAMMER2IOC_PFS_SNAPSHOT
;
476 errx(1, "invalid PFS command \"%s\"", o
);
479 strlcpy(h2_opt
->pfs_cmd_name
, o
, sizeof(h2_opt
->pfs_cmd_name
));
481 strlcpy(h2_opt
->pfs_name
, p
, sizeof(h2_opt
->pfs_name
));
487 hammer2_image_size(fsinfo_t
*fsopts
)
489 hammer2_makefs_options_t
*h2_opt
= fsopts
->fs_specific
;
490 hammer2_off_t image_size
, used_size
= 0;
491 int num_level1
, delta_num_level1
;
493 /* use 4 volume headers by default */
494 num_level1
= h2_opt
->num_volhdr
* 2; /* default 4 x 2 */
495 assert(num_level1
!= 0);
496 assert(num_level1
<= 8);
498 /* add 4MiB segment for each level1 */
499 used_size
+= HAMMER2_ZONE_SEG64
* num_level1
;
501 /* add boot/aux area, but exact size unknown at this point */
502 used_size
+= HAMMER2_BOOT_NOM_BYTES
+ HAMMER2_AUX_NOM_BYTES
;
505 used_size
+= fsopts
->size
;
507 /* XXX add extra level1 for meta data and indirect blocks */
508 used_size
+= HAMMER2_FREEMAP_LEVEL1_SIZE
;
510 /* XXX add extra level1 for safety */
511 if (used_size
> HAMMER2_FREEMAP_LEVEL1_SIZE
* 10)
512 used_size
+= HAMMER2_FREEMAP_LEVEL1_SIZE
;
514 /* use 8GiB image size by default */
515 image_size
= HAMMER2_FREEMAP_LEVEL1_SIZE
* num_level1
;
516 printf("trying default image size %s\n", sizetostr(image_size
));
518 /* adjust if image size isn't large enough */
519 if (used_size
> image_size
) {
520 /* determine extra level1 needed */
521 delta_num_level1
= howmany(used_size
- image_size
,
522 HAMMER2_FREEMAP_LEVEL1_SIZE
);
524 /* adjust used size with 4MiB segment for each extra level1 */
525 used_size
+= HAMMER2_ZONE_SEG64
* delta_num_level1
;
527 /* adjust image size with extra level1 */
528 image_size
+= HAMMER2_FREEMAP_LEVEL1_SIZE
* delta_num_level1
;
529 printf("trying adjusted image size %s\n",
530 sizetostr(image_size
));
532 if (used_size
> image_size
)
533 errx(1, "invalid used_size %lld > image_size %lld",
534 (long long)used_size
, (long long)image_size
);
541 hammer2_label_name(int label_type
)
543 switch (label_type
) {
544 case HAMMER2_LABEL_NONE
:
546 case HAMMER2_LABEL_BOOT
:
548 case HAMMER2_LABEL_ROOT
:
550 case HAMMER2_LABEL_DATA
:
559 hammer2_validate(const char *dir
, fsnode
*root
, fsinfo_t
*fsopts
)
561 hammer2_makefs_options_t
*h2_opt
= fsopts
->fs_specific
;
562 hammer2_mkfs_options_t
*opt
= &h2_opt
->mkfs_options
;
563 hammer2_off_t image_size
= 0, minsize
, maxsize
;
567 assert(fsopts
!= NULL
);
569 if (debug
& DEBUG_FS_VALIDATE
) {
570 APRINTF("before defaults set:\n");
571 hammer2_dump_fsinfo(fsopts
);
574 /* makefs only supports "DATA" for default PFS label */
575 if (!h2_opt
->label_specified
) {
576 opt
->DefaultLabelType
= HAMMER2_LABEL_DATA
;
577 s
= hammer2_label_name(opt
->DefaultLabelType
);
578 printf("using default label \"%s\"\n", s
);
581 /* set default mount PFS label */
582 if (!strcmp(h2_opt
->mount_label
, "")) {
583 s
= hammer2_label_name(HAMMER2_LABEL_DATA
);
584 strlcpy(h2_opt
->mount_label
, s
, sizeof(h2_opt
->mount_label
));
585 printf("using default mount label \"%s\"\n", s
);
588 /* set default number of volume headers */
589 if (!h2_opt
->num_volhdr
) {
590 h2_opt
->num_volhdr
= HAMMER2_NUM_VOLHDRS
;
591 printf("using default %d volume headers\n", h2_opt
->num_volhdr
);
594 /* done if ioctl commands */
595 if (h2_opt
->ioctl_cmd
) {
596 if (h2_opt
->ioctl_cmd
== HAMMER2IOC_GROWFS
)
597 goto ignore_size_dir
;
602 /* calculate data size */
603 if (fsopts
->size
!= 0)
604 fsopts
->size
= 0; /* shouldn't reach here to begin with */
606 errx(1, "fsnode tree not constructed");
607 hammer2_size_dir(root
, fsopts
);
608 printf("estimated data size %s from %lld inode\n",
609 sizetostr(fsopts
->size
), (long long)fsopts
->inodes
);
611 /* determine image size from data size */
612 image_size
= hammer2_image_size(fsopts
);
613 assert((image_size
& HAMMER2_FREEMAP_LEVEL1_MASK
) == 0);
615 minsize
= roundup(fsopts
->minsize
, HAMMER2_FREEMAP_LEVEL1_SIZE
);
616 maxsize
= roundup(fsopts
->maxsize
, HAMMER2_FREEMAP_LEVEL1_SIZE
);
617 if (image_size
< minsize
)
618 image_size
= minsize
;
619 else if (maxsize
> 0 && image_size
> maxsize
)
620 errx(1, "`%s' size of %lld is larger than the maxsize of %lld",
621 dir
, (long long)image_size
, (long long)maxsize
);
623 assert((image_size
& HAMMER2_FREEMAP_LEVEL1_MASK
) == 0);
624 h2_opt
->image_size
= image_size
;
625 printf("using %s image size\n", sizetostr(h2_opt
->image_size
));
627 if (debug
& DEBUG_FS_VALIDATE
) {
628 APRINTF("after defaults set:\n");
629 hammer2_dump_fsinfo(fsopts
);
634 hammer2_dump_fsinfo(fsinfo_t
*fsopts
)
636 hammer2_makefs_options_t
*h2_opt
= fsopts
->fs_specific
;
637 hammer2_mkfs_options_t
*opt
= &h2_opt
->mkfs_options
;
641 assert(fsopts
!= NULL
);
643 APRINTF("fsinfo_t at %p\n", fsopts
);
645 printf("\tinodes %lld\n", (long long)fsopts
->inodes
);
646 printf("\tsize %lld, minsize %lld, maxsize %lld\n",
647 (long long)fsopts
->size
,
648 (long long)fsopts
->minsize
,
649 (long long)fsopts
->maxsize
);
651 printf("\thammer2_debug 0x%x\n", hammer2_debug
);
653 printf("\tlabel_specified %d\n", h2_opt
->label_specified
);
654 printf("\tmount_label \"%s\"\n", h2_opt
->mount_label
);
655 printf("\tnum_volhdr %d\n", h2_opt
->num_volhdr
);
656 printf("\tioctl_cmd %ld\n", h2_opt
->ioctl_cmd
);
657 printf("\temergency_mode %d\n", h2_opt
->emergency_mode
);
658 printf("\tpfs_cmd_name \"%s\"\n", h2_opt
->pfs_cmd_name
);
659 printf("\tpfs_name \"%s\"\n", h2_opt
->pfs_name
);
660 printf("\tdestroy_path \"%s\"\n", h2_opt
->destroy_path
);
661 printf("\tdestroy_inum %lld\n", (long long)h2_opt
->destroy_inum
);
662 printf("\timage_size 0x%llx\n", (long long)h2_opt
->image_size
);
664 printf("\tHammer2Version %d\n", opt
->Hammer2Version
);
665 printf("\tBootAreaSize 0x%jx\n", opt
->BootAreaSize
);
666 printf("\tAuxAreaSize 0x%jx\n", opt
->AuxAreaSize
);
667 printf("\tNLabels %d\n", opt
->NLabels
);
668 printf("\tCompType %d\n", opt
->CompType
);
669 printf("\tCheckType %d\n", opt
->CheckType
);
670 printf("\tDefaultLabelType %d\n", opt
->DefaultLabelType
);
671 printf("\tDebugOpt %d\n", opt
->DebugOpt
);
674 hammer2_uuid_to_str(&opt
->Hammer2_FSType
, &s
);
675 printf("\tHammer2_FSType \"%s\"\n", s
);
677 hammer2_uuid_to_str(&opt
->Hammer2_VolFSID
, &s
);
678 printf("\tHammer2_VolFSID \"%s\"\n", s
);
680 hammer2_uuid_to_str(&opt
->Hammer2_SupCLID
, &s
);
681 printf("\tHammer2_SupCLID \"%s\"\n", s
);
683 hammer2_uuid_to_str(&opt
->Hammer2_SupFSID
, &s
);
684 printf("\tHammer2_SupFSID \"%s\"\n", s
);
686 for (i
= 0; i
< opt
->NLabels
; i
++) {
687 printf("\tLabel[%d] \"%s\"\n", i
, opt
->Label
[i
]);
689 hammer2_uuid_to_str(&opt
->Hammer2_PfsCLID
[i
], &s
);
690 printf("\t Hammer2_PfsCLID[%d] \"%s\"\n", i
, s
);
692 hammer2_uuid_to_str(&opt
->Hammer2_PfsFSID
[i
], &s
);
693 printf("\t Hammer2_PfsFSID[%d] \"%s\"\n", i
, s
);
700 hammer2_create_image(const char *image
, fsinfo_t
*fsopts
)
702 hammer2_makefs_options_t
*h2_opt
= fsopts
->fs_specific
;
703 hammer2_mkfs_options_t
*opt
= &h2_opt
->mkfs_options
;
704 char *av
[] = { (char *)image
, }; /* XXX support multi-volumes */
706 int i
, bufsize
, oflags
;
709 assert(image
!= NULL
);
710 assert(fsopts
!= NULL
);
713 oflags
= O_RDWR
| O_CREAT
;
714 if (fsopts
->offset
== 0)
716 if ((fsopts
->fd
= open(image
, oflags
, 0666)) == -1) {
717 warn("can't open `%s' for writing", image
);
722 bufsize
= HAMMER2_PBUFSIZE
;
723 bufrem
= h2_opt
->image_size
;
724 if (fsopts
->sparse
) {
725 if (ftruncate(fsopts
->fd
, bufrem
) == -1) {
726 warn("sparse option disabled");
730 if (fsopts
->sparse
) {
731 /* File truncated at bufrem. Remaining is 0 */
735 if (debug
& DEBUG_FS_CREATE_IMAGE
)
736 APRINTF("zero-ing image `%s', %lld sectors, "
737 "using %d byte chunks\n",
738 image
, (long long)bufrem
, bufsize
);
739 buf
= ecalloc(1, bufsize
);
742 if (fsopts
->offset
!= 0) {
743 if (lseek(fsopts
->fd
, fsopts
->offset
, SEEK_SET
) == -1) {
751 i
= write(fsopts
->fd
, buf
, MIN(bufsize
, bufrem
));
753 warn("zeroing image, %lld bytes to go",
763 /* make the file system */
764 if (debug
& DEBUG_FS_CREATE_IMAGE
)
765 APRINTF("calling mkfs(\"%s\", ...)\n", image
);
766 hammer2_mkfs(1, av
, opt
); /* success if returned */
772 hammer2_phys_size(off_t size
)
774 off_t radix_size
, phys_size
= 0;
777 if (size
> HAMMER2_PBUFSIZE
) {
778 phys_size
+= rounddown(size
, HAMMER2_PBUFSIZE
);
779 size
= size
% HAMMER2_PBUFSIZE
;
782 for (i
= HAMMER2_RADIX_MIN
; i
<= HAMMER2_RADIX_MAX
; i
++) {
783 radix_size
= 1UL << i
;
784 if (radix_size
>= size
) {
785 phys_size
+= radix_size
;
793 /* calculate data size */
795 hammer2_size_dir(fsnode
*root
, fsinfo_t
*fsopts
)
799 assert(fsopts
!= NULL
);
801 if (debug
& DEBUG_FS_SIZE_DIR
)
802 APRINTF("entry: bytes %lld inodes %lld\n",
803 (long long)fsopts
->size
, (long long)fsopts
->inodes
);
805 for (node
= root
; node
!= NULL
; node
= node
->next
) {
806 if (node
== root
) { /* we're at "." */
807 assert(strcmp(node
->name
, ".") == 0);
808 } else if ((node
->inode
->flags
& FI_SIZED
) == 0) {
809 /* don't count duplicate names */
810 node
->inode
->flags
|= FI_SIZED
;
811 if (debug
& DEBUG_FS_SIZE_DIR_NODE
)
812 APRINTF("`%s' size %lld\n",
814 (long long)node
->inode
->st
.st_size
);
816 fsopts
->size
+= sizeof(hammer2_inode_data_t
);
817 if (node
->type
== S_IFREG
) {
818 size_t st_size
= node
->inode
->st
.st_size
;
819 if (st_size
> HAMMER2_EMBEDDED_BYTES
)
820 fsopts
->size
+= hammer2_phys_size(st_size
);
821 } else if (node
->type
== S_IFLNK
) {
822 size_t nlen
= strlen(node
->symlink
);
823 if (nlen
> HAMMER2_EMBEDDED_BYTES
)
824 fsopts
->size
+= hammer2_phys_size(nlen
);
827 if (node
->type
== S_IFDIR
)
828 hammer2_size_dir(node
->child
, fsopts
);
831 if (debug
& DEBUG_FS_SIZE_DIR
)
832 APRINTF("exit: size %lld inodes %lld\n",
833 (long long)fsopts
->size
, (long long)fsopts
->inodes
);
837 hammer2_print(const struct m_vnode
*dvp
, const struct m_vnode
*vp
,
838 const fsnode
*node
, int depth
, const char *msg
)
840 if (debug
& DEBUG_FS_POPULATE
) {
842 int indent
= depth
* 2;
844 if (S_ISDIR(node
->type
))
846 else if (S_ISREG(node
->type
))
848 else if (S_ISLNK(node
->type
))
850 else if (S_ISFIFO(node
->type
))
854 printf("%*.*s", indent
, indent
, "");
855 printf("dvp=%p/%d vp=%p/%d \"%s\" %s %s\n",
856 dvp
, dvp
? VTOI(dvp
)->refs
: 0,
857 vp
, vp
? VTOI(vp
)->refs
: 0,
858 node
->name
, type
, msg
);
861 if (S_ISDIR(node
->type
))
863 else if (S_ISREG(node
->type
))
865 else if (S_ISLNK(node
->type
))
867 else if (S_ISFIFO(node
->type
))
878 hammer2_populate_dir(struct m_vnode
*dvp
, const char *dir
, fsnode
*root
,
879 fsnode
*parent
, fsinfo_t
*fsopts
, int depth
)
891 assert(root
!= NULL
);
892 assert(parent
!= NULL
);
893 assert(fsopts
!= NULL
);
895 /* assert root directory */
896 assert(S_ISDIR(root
->type
));
897 assert(!strcmp(root
->name
, "."));
898 assert(!root
->child
);
899 assert(!root
->parent
|| root
->parent
->child
== root
);
901 hammer2_print(dvp
, NULL
, root
, depth
, "enter");
902 if (stat(dir
, &st
) == -1)
903 err(1, "no such path %s", dir
);
904 if (!S_ISDIR(st
.st_mode
))
905 errx(1, "no such dir %s", dir
);
907 for (cur
= root
->next
; cur
!= NULL
; cur
= cur
->next
) {
908 /* global variable for HAMMER2 vnops */
909 hammer2_curnode
= cur
;
911 /* construct source path */
913 path
= cur
->contents
;
915 if (S_ISDIR(cur
->type
)) {
916 /* this should be same as root/path/name */
917 if (snprintf(f
, sizeof(f
), "%s/%s",
918 dir
, cur
->name
) >= (int)sizeof(f
))
919 errx(1, "path %s too long", f
);
921 if (snprintf(f
, sizeof(f
), "%s/%s/%s",
922 cur
->root
, cur
->path
, cur
->name
) >= (int)sizeof(f
))
923 errx(1, "path %s too long", f
);
927 if (stat(path
, &st
) == -1)
928 err(1, "no such file %s", f
);
930 /* update node state */
931 if ((cur
->inode
->flags
& FI_ALLOCATED
) == 0) {
932 cur
->inode
->flags
|= FI_ALLOCATED
;
934 cur
->parent
= parent
;
937 /* detect hardlink */
938 if (cur
->inode
->flags
& FI_WRITTEN
) {
939 assert(!S_ISDIR(cur
->type
));
944 cur
->inode
->flags
|= FI_WRITTEN
;
946 /* make sure it doesn't exist yet */
948 error
= hammer2_nresolve(dvp
, &vp
, cur
->name
,
951 errx(1, "hammer2_nresolve(\"%s\") already exists",
953 hammer2_print(dvp
, vp
, cur
, depth
, "nresolve");
955 /* if directory, mkdir and recurse */
956 if (S_ISDIR(cur
->type
)) {
960 error
= hammer2_nmkdir(dvp
, &vp
, cur
->name
,
961 strlen(cur
->name
), cur
->inode
->st
.st_mode
);
963 errx(1, "hammer2_nmkdir(\"%s\") failed: %s",
964 cur
->name
, strerror(error
));
966 hammer2_print(dvp
, vp
, cur
, depth
, "nmkdir");
968 error
= hammer2_populate_dir(vp
, path
, cur
->child
, cur
,
971 errx(1, "failed to populate %s: %s",
972 path
, strerror(error
));
973 cur
->inode
->param
= vp
;
977 /* if regular file, creat and write its data */
978 if (S_ISREG(cur
->type
) && !hardlink
) {
979 assert(cur
->child
== NULL
);
982 error
= hammer2_ncreate(dvp
, &vp
, cur
->name
,
983 strlen(cur
->name
), cur
->inode
->st
.st_mode
);
985 errx(1, "hammer2_ncreate(\"%s\") failed: %s",
986 cur
->name
, strerror(error
));
988 hammer2_print(dvp
, vp
, cur
, depth
, "ncreate");
990 error
= hammer2_write_file(vp
, path
, cur
);
992 errx(1, "hammer2_write_file(\"%s\") failed: %s",
993 path
, strerror(error
));
994 cur
->inode
->param
= vp
;
998 /* if symlink, create a symlink against target */
999 if (S_ISLNK(cur
->type
)) {
1000 assert(cur
->child
== NULL
);
1003 error
= hammer2_nsymlink(dvp
, &vp
, cur
->name
,
1004 strlen(cur
->name
), cur
->symlink
,
1005 cur
->inode
->st
.st_mode
);
1007 errx(1, "hammer2_nsymlink(\"%s\") failed: %s",
1008 cur
->name
, strerror(error
));
1010 hammer2_print(dvp
, vp
, cur
, depth
, "nsymlink");
1011 cur
->inode
->param
= vp
;
1015 /* if fifo, create a fifo */
1016 if (S_ISFIFO(cur
->type
) && !hardlink
) {
1017 assert(cur
->child
== NULL
);
1020 error
= hammer2_nmknod(dvp
, &vp
, cur
->name
,
1021 strlen(cur
->name
), VFIFO
, cur
->inode
->st
.st_mode
);
1023 errx(1, "hammer2_nmknod(\"%s\") failed: %s",
1024 cur
->name
, strerror(error
));
1026 hammer2_print(dvp
, vp
, cur
, depth
, "nmknod");
1027 cur
->inode
->param
= vp
;
1031 /* if hardlink, creat a hardlink */
1032 if ((S_ISREG(cur
->type
) || S_ISFIFO(cur
->type
)) && hardlink
) {
1034 assert(cur
->child
== NULL
);
1036 /* source vnode must not be NULL */
1037 vp
= cur
->inode
->param
;
1039 /* currently these conditions must be true */
1041 assert(vp
->v_type
== VREG
|| vp
->v_type
== VFIFO
);
1042 assert(vp
->v_logical
);
1043 assert(!vp
->v_vflushed
);
1044 assert(vp
->v_malloced
);
1045 assert(VTOI(vp
)->refs
> 0);
1047 error
= hammer2_nlink(dvp
, vp
, cur
->name
,
1050 errx(1, "hammer2_nlink(\"%s\") failed: %s",
1051 cur
->name
, strerror(error
));
1052 snprintf(buf
, sizeof(buf
), "nlink=%lld",
1053 (long long)VTOI(vp
)->meta
.nlinks
);
1054 hammer2_print(dvp
, vp
, cur
, depth
, buf
);
1058 /* other types are unsupported */
1059 printf("ignore %s 0%o\n", path
, cur
->type
);
1066 hammer2_write_file(struct m_vnode
*vp
, const char *path
, fsnode
*node
)
1068 struct stat
*st
= &node
->inode
->st
;
1069 size_t nsize
, bufsize
;
1074 nsize
= st
->st_size
;
1077 /* check nsize vs maximum file size */
1079 fd
= open(path
, O_RDONLY
);
1081 err(1, "failed to open %s", path
);
1083 p
= mmap(0, nsize
, PROT_READ
, MAP_FILE
|MAP_PRIVATE
, fd
, 0);
1084 if (p
== MAP_FAILED
)
1085 err(1, "failed to mmap %s", path
);
1088 for (offset
= 0; offset
< nsize
; ) {
1089 bufsize
= MIN(nsize
- offset
, HAMMER2_PBUFSIZE
);
1090 assert(bufsize
<= HAMMER2_PBUFSIZE
);
1091 error
= hammer2_write(vp
, p
+ offset
, bufsize
, offset
);
1093 errx(1, "failed to write to %s vnode: %s",
1094 path
, strerror(error
));
1096 if (bufsize
== HAMMER2_PBUFSIZE
)
1097 assert((offset
& (HAMMER2_PBUFSIZE
- 1)) == 0);
1105 hammer2_version_get(struct m_vnode
*vp
)
1109 hmp
= VTOI(vp
)->pmp
->pfs_hmps
[0];
1113 printf("version: %d\n", hmp
->voldata
.version
);
1119 TAILQ_ENTRY(pfs_entry
) entry
;
1120 char name
[NAME_MAX
+1];
1125 hammer2_pfs_get(struct m_vnode
*vp
)
1127 hammer2_ioc_pfs_t pfs
;
1128 TAILQ_HEAD(, pfs_entry
) head
;
1129 struct pfs_entry
*p
, *e
;
1131 const char *type_str
;
1134 bzero(&pfs
, sizeof(pfs
));
1137 while ((pfs
.name_key
= pfs
.name_next
) != (hammer2_key_t
)-1) {
1138 error
= hammer2_ioctl_pfs_get(VTOI(vp
), &pfs
);
1143 hammer2_uuid_to_str(&pfs
.pfs_clid
, &pfs_id_str
);
1145 if (pfs
.pfs_type
== HAMMER2_PFSTYPE_MASTER
) {
1146 if (pfs
.pfs_subtype
== HAMMER2_PFSSUBTYPE_NONE
)
1147 type_str
= "MASTER";
1149 type_str
= hammer2_pfssubtype_to_str(
1152 type_str
= hammer2_pfstype_to_str(pfs
.pfs_type
);
1154 e
= ecalloc(1, sizeof(*e
));
1155 snprintf(e
->name
, sizeof(e
->name
), "%s", pfs
.name
);
1156 snprintf(e
->s
, sizeof(e
->s
), "%-11s %s", type_str
, pfs_id_str
);
1159 p
= TAILQ_FIRST(&head
);
1161 if (strcmp(e
->name
, p
->name
) <= 0) {
1162 TAILQ_INSERT_BEFORE(p
, e
, entry
);
1165 p
= TAILQ_NEXT(p
, entry
);
1168 TAILQ_INSERT_TAIL(&head
, e
, entry
);
1172 "ClusterId (pfs_clid) "
1174 while ((p
= TAILQ_FIRST(&head
)) != NULL
) {
1175 printf("%s %s\n", p
->s
, p
->name
);
1176 TAILQ_REMOVE(&head
, p
, entry
);
1184 hammer2_pfs_lookup(struct m_vnode
*vp
, const char *pfs_name
)
1186 hammer2_ioc_pfs_t pfs
;
1190 bzero(&pfs
, sizeof(pfs
));
1191 strlcpy(pfs
.name
, pfs_name
, sizeof(pfs
.name
));
1193 error
= hammer2_ioctl_pfs_lookup(VTOI(vp
), &pfs
);
1195 printf("name: %s\n", pfs
.name
);
1196 printf("type: %s\n", hammer2_pfstype_to_str(pfs
.pfs_type
));
1197 printf("subtype: %s\n",
1198 hammer2_pfssubtype_to_str(pfs
.pfs_subtype
));
1201 hammer2_uuid_to_str(&pfs
.pfs_fsid
, &pfs_id_str
);
1202 printf("fsid: %s\n", pfs_id_str
);
1206 hammer2_uuid_to_str(&pfs
.pfs_clid
, &pfs_id_str
);
1207 printf("clid: %s\n", pfs_id_str
);
1215 hammer2_pfs_create(struct m_vnode
*vp
, const char *pfs_name
)
1217 hammer2_ioc_pfs_t pfs
;
1220 bzero(&pfs
, sizeof(pfs
));
1221 strlcpy(pfs
.name
, pfs_name
, sizeof(pfs
.name
));
1222 pfs
.pfs_type
= HAMMER2_PFSTYPE_MASTER
;
1223 uuid_create(&pfs
.pfs_clid
, NULL
);
1224 uuid_create(&pfs
.pfs_fsid
, NULL
);
1226 error
= hammer2_ioctl_pfs_create(VTOI(vp
), &pfs
);
1227 if (error
== EEXIST
)
1229 "NOTE: Typically the same name is "
1230 "used for cluster elements on "
1231 "different mounts,\n"
1232 " but cluster elements on the "
1233 "same mount require unique names.\n"
1234 "hammer2: pfs_create(%s): already present\n",
1241 hammer2_pfs_delete(struct m_vnode
*vp
, const char *pfs_name
)
1243 hammer2_ioc_pfs_t pfs
;
1245 bzero(&pfs
, sizeof(pfs
));
1246 strlcpy(pfs
.name
, pfs_name
, sizeof(pfs
.name
));
1248 return hammer2_ioctl_pfs_delete(VTOI(vp
), &pfs
);
1252 hammer2_pfs_snapshot(struct m_vnode
*vp
, const char *pfs_name
,
1253 const char *mount_label
)
1255 hammer2_ioc_pfs_t pfs
;
1259 bzero(&pfs
, sizeof(pfs
));
1260 strlcpy(pfs
.name
, pfs_name
, sizeof(pfs
.name
));
1262 if (strlen(pfs
.name
) == 0) {
1265 snprintf(pfs
.name
, sizeof(pfs
.name
),
1266 "%s.%04d%02d%02d.%02d%02d%02d",
1276 return hammer2_ioctl_pfs_snapshot(VTOI(vp
), &pfs
);
1280 hammer2_bulkfree(struct m_vnode
*vp
)
1282 hammer2_ioc_bulkfree_t bfi
;
1284 size_t usermem_size
= sizeof(usermem
);
1286 bzero(&bfi
, sizeof(bfi
));
1288 if (sysctlbyname("hw.usermem", &usermem
, &usermem_size
, NULL
, 0) == 0)
1289 bfi
.size
= usermem
/ 16;
1292 if (bfi
.size
< 8192 * 1024)
1293 bfi
.size
= 8192 * 1024;
1295 return hammer2_ioctl_bulkfree_scan(VTOI(vp
), &bfi
);
1299 hammer2_destroy_path(struct m_vnode
*dvp
, const char *f
)
1301 hammer2_ioc_destroy_t destroy
;
1302 hammer2_inode_t
*ip
;
1307 assert(strlen(f
) > 0);
1310 /* Trim trailing '/'. */
1313 while (p
>= o
&& *p
== '/')
1321 while ((p
= strchr(p
, '/')) != NULL
) {
1322 *p
++ = 0; /* NULL terminate name */
1324 error
= hammer2_nresolve(dvp
, &vp
, name
, strlen(name
));
1329 assert(ip
->meta
.type
== HAMMER2_OBJTYPE_DIRECTORY
);
1330 /* XXX When does (or why does not) ioctl modify this inode ? */
1331 hammer2_inode_modify(ip
);
1337 bzero(&destroy
, sizeof(destroy
));
1338 destroy
.cmd
= HAMMER2_DELETE_FILE
;
1339 snprintf(destroy
.path
, sizeof(destroy
.path
), "%s", name
);
1344 error
= hammer2_ioctl_destroy(VTOI(dvp
), &destroy
);
1346 printf("%s\n", strerror(error
));
1355 hammer2_destroy_inum(struct m_vnode
*vp
, hammer2_tid_t inum
)
1357 hammer2_ioc_destroy_t destroy
;
1360 bzero(&destroy
, sizeof(destroy
));
1361 destroy
.cmd
= HAMMER2_DELETE_INUM
;
1362 destroy
.inum
= inum
;
1364 printf("%jd\t", (intmax_t)destroy
.inum
);
1367 error
= hammer2_ioctl_destroy(VTOI(vp
), &destroy
);
1369 printf("%s\n", strerror(error
));
1377 hammer2_growfs(struct m_vnode
*vp
, hammer2_off_t size
)
1379 hammer2_ioc_growfs_t growfs
;
1382 bzero(&growfs
, sizeof(growfs
));
1385 error
= hammer2_ioctl_growfs(VTOI(vp
), &growfs
, NULL
);
1387 if (growfs
.modified
)
1388 printf("grown to %016jx\n", (intmax_t)growfs
.size
);
1390 printf("no size change - %016jx\n",
1391 (intmax_t)growfs
.size
);