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>
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
;
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,
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" },
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 */
124 fsopts
->fs_specific
= h2_opt
;
125 fsopts
->fs_options
= copy_opts(hammer2_options
);
126 fsopts
->sectorsize
= DEV_BSIZE
;
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
);
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 */
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
));
161 if (hammer2_options
[i
].name
== NULL
)
164 switch (hammer2_options
[i
].letter
) {
166 opt
->BootAreaSize
= getsize(buf
, HAMMER2_NEWFS_ALIGN
,
167 HAMMER2_BOOT_MAX_BYTES
, 2);
170 opt
->AuxAreaSize
= getsize(buf
, HAMMER2_NEWFS_ALIGN
,
171 HAMMER2_AUX_MAX_BYTES
, 2);
174 if (strlen(buf
) == 0) {
175 h2_opt
->ioctl_cmd
= HAMMER2IOC_VERSION_GET
;
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
);
186 h2_opt
->label_specified
= 1;
187 if (strcasecmp(buf
, "none") == 0)
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
);
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
));
207 hammer2_debug
= strtoll(buf
, NULL
, 0);
210 if (strlen(buf
) == 0)
211 errx(1, "PFS argument '%s' cannot be 0-length", buf
);
212 hammer2_parse_pfs_opts(buf
, fsopts
);
215 if (strlen(buf
) == 0)
216 errx(1, "Inode argument '%s' cannot be 0-length", buf
);
217 hammer2_parse_inode_opts(buf
, fsopts
);
220 h2_opt
->ioctl_cmd
= HAMMER2IOC_BULKFREE_SCAN
;
223 h2_opt
->ioctl_cmd
= HAMMER2IOC_DESTROY
;
224 if (strlen(buf
) == 0)
225 errx(1, "Destroy argument '%s' cannot be 0-length", buf
);
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);
235 errx(1, "Invalid destroy argument %s", buf
);
239 h2_opt
->ioctl_cmd
= HAMMER2IOC_GROWFS
;
245 if (hammer2_debug
&& h2_opt
->ioctl_cmd
)
246 unittest_trim_slash();
252 hammer2_makefs(const char *image
, const char *dir
, fsnode
*root
,
255 hammer2_makefs_options_t
*h2_opt
= fsopts
->fs_specific
;
257 struct hammer2_mount_info info
;
258 struct m_vnode devvp
, *vroot
;
259 hammer2_inode_t
*iroot
;
260 struct timeval start
;
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",
271 /* validate tree and options */
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
);
280 err(1, "failed to open `%s'", image
);
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
)
294 error
= hammer2_vfs_init();
296 errx(1, "failed to vfs init, error %d", error
);
299 memset(&devvp
, 0, sizeof(devvp
));
301 memset(&mp
, 0, sizeof(mp
));
302 memset(&info
, 0, sizeof(info
));
303 error
= hammer2_vfs_mount(&devvp
, &mp
, h2_opt
->mount_label
, &info
);
305 errx(1, "failed to mount, error %d", error
);
310 error
= hammer2_vfs_root(&mp
, &vroot
);
312 errx(1, "failed to get root vnode, error %d", error
);
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
);
327 error
= hammer2_version_get(vroot
);
329 errx(1, "version get `%s' failed '%s'", image
,
331 TIMER_RESULTS(start
, "hammer2_version_get");
333 case HAMMER2IOC_PFS_GET
:
334 printf("PFS %s `%s'\n", h2_opt
->pfs_cmd_name
, image
);
336 error
= hammer2_pfs_get(vroot
);
338 errx(1, "PFS %s`%s' failed '%s'", h2_opt
->pfs_cmd_name
,
339 image
, strerror(error
));
340 TIMER_RESULTS(start
, "hammer2_pfs_get");
342 case HAMMER2IOC_PFS_LOOKUP
:
343 printf("PFS %s `%s'\n", h2_opt
->pfs_cmd_name
, image
);
345 error
= hammer2_pfs_lookup(vroot
, h2_opt
->pfs_name
);
347 errx(1, "PFS %s`%s' failed '%s'", h2_opt
->pfs_cmd_name
,
348 image
, strerror(error
));
349 TIMER_RESULTS(start
, "hammer2_pfs_lookup");
351 case HAMMER2IOC_PFS_CREATE
:
352 printf("PFS %s `%s'\n", h2_opt
->pfs_cmd_name
, image
);
354 error
= hammer2_pfs_create(vroot
, h2_opt
->pfs_name
);
356 errx(1, "PFS %s`%s' failed '%s'", h2_opt
->pfs_cmd_name
,
357 image
, strerror(error
));
358 TIMER_RESULTS(start
, "hammer2_pfs_create");
360 case HAMMER2IOC_PFS_DELETE
:
361 printf("PFS %s `%s'\n", h2_opt
->pfs_cmd_name
, image
);
363 error
= hammer2_pfs_delete(vroot
, h2_opt
->pfs_name
);
365 errx(1, "PFS %s`%s' failed '%s'", h2_opt
->pfs_cmd_name
,
366 image
, strerror(error
));
367 TIMER_RESULTS(start
, "hammer2_pfs_delete");
369 case HAMMER2IOC_PFS_SNAPSHOT
:
370 printf("PFS %s `%s'\n", h2_opt
->pfs_cmd_name
, image
);
372 error
= hammer2_pfs_snapshot(vroot
, h2_opt
->pfs_name
,
373 h2_opt
->mount_label
);
375 errx(1, "PFS %s`%s' failed '%s'", h2_opt
->pfs_cmd_name
,
376 image
, strerror(error
));
377 TIMER_RESULTS(start
, "hammer2_pfs_snapshot");
379 case HAMMER2IOC_INODE_GET
:
380 printf("inode %s `%s'\n", h2_opt
->inode_cmd_name
, image
);
382 error
= hammer2_inode_getx(vroot
, h2_opt
->inode_path
);
384 errx(1, "inode %s `%s' failed '%s'",
385 h2_opt
->inode_cmd_name
, image
, strerror(error
));
386 TIMER_RESULTS(start
, "hammer2_inode_getx");
388 case HAMMER2IOC_INODE_SET
:
389 printf("inode %s `%s'\n", h2_opt
->inode_cmd_name
, image
);
391 if (!strcmp(h2_opt
->inode_cmd_name
, "setcheck")) {
392 error
= hammer2_inode_setcheck(vroot
,
395 errx(1, "inode %s `%s' failed '%s'",
396 h2_opt
->inode_cmd_name
, image
,
398 } else if (!strcmp(h2_opt
->inode_cmd_name
, "setcomp")) {
399 error
= hammer2_inode_setcomp(vroot
,
402 errx(1, "inode %s `%s' failed '%s'",
403 h2_opt
->inode_cmd_name
, image
,
408 TIMER_RESULTS(start
, "hammer2_inode_setx");
410 case HAMMER2IOC_BULKFREE_SCAN
:
411 printf("bulkfree `%s'\n", image
);
413 error
= hammer2_bulkfree(vroot
);
415 errx(1, "bulkfree `%s' failed '%s'", image
,
417 TIMER_RESULTS(start
, "hammer2_bulkfree");
419 case HAMMER2IOC_DESTROY
:
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
);
427 errx(1, "destroy `%s' in `%s' failed '%s'",
428 h2_opt
->destroy_path
, image
,
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
);
436 errx(1, "destroy %lld in `%s' failed '%s'",
437 (long long)h2_opt
->destroy_inum
, image
,
440 TIMER_RESULTS(start
, "hammer2_destroy");
442 case HAMMER2IOC_GROWFS
:
443 printf("growfs `%s'\n", image
);
445 error
= hammer2_growfs(vroot
, h2_opt
->image_size
);
447 errx(1, "growfs `%s' failed '%s'", image
,
449 TIMER_RESULTS(start
, "hammer2_growfs");
452 printf("populating `%s'\n", image
);
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");
461 error
= hammer2_vfs_unmount(&mp
, 0);
463 errx(1, "failed to unmount, error %d", error
);
465 /* check leaked resource */
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
);
473 error
= hammer2_vfs_uninit();
475 errx(1, "failed to vfs uninit, error %d", error
);
477 if (close(fsopts
->fd
) == -1)
478 err(1, "closing `%s'", image
);
481 printf("image `%s' complete\n", image
);
484 /* end of public functions */
487 hammer2_parse_pfs_opts(const char *buf
, fsinfo_t
*fsopts
)
489 hammer2_makefs_options_t
*h2_opt
= fsopts
->fs_specific
;
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")) {
518 errx(1, "invalid PFS name \"%s\"", p
);
519 h2_opt
->ioctl_cmd
= HAMMER2IOC_PFS_SNAPSHOT
;
521 errx(1, "invalid PFS command \"%s\"", o
);
524 strlcpy(h2_opt
->pfs_cmd_name
, o
, sizeof(h2_opt
->pfs_cmd_name
));
526 strlcpy(h2_opt
->pfs_name
, p
, sizeof(h2_opt
->pfs_name
));
532 hammer2_parse_inode_opts(const char *buf
, fsinfo_t
*fsopts
)
534 hammer2_makefs_options_t
*h2_opt
= fsopts
->fs_specific
;
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
;
560 errx(1, "invalid inode command \"%s\"", o
);
563 strlcpy(h2_opt
->inode_cmd_name
, o
, sizeof(h2_opt
->inode_cmd_name
));
565 strlcpy(h2_opt
->inode_path
, p
, sizeof(h2_opt
->inode_path
));
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
;
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
);
625 hammer2_label_name(int label_type
)
627 switch (label_type
) {
628 case HAMMER2_LABEL_NONE
:
630 case HAMMER2_LABEL_BOOT
:
632 case HAMMER2_LABEL_ROOT
:
634 case HAMMER2_LABEL_DATA
:
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
;
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
;
686 /* calculate data size */
687 if (fsopts
->size
!= 0)
688 fsopts
->size
= 0; /* shouldn't reach here to begin with */
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);
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
));
711 if (debug
& DEBUG_FS_VALIDATE
) {
712 APRINTF("after defaults set:\n");
713 hammer2_dump_fsinfo(fsopts
);
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
;
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
);
760 hammer2_uuid_to_str(&opt
->Hammer2_FSType
, &s
);
761 printf("\tHammer2_FSType \"%s\"\n", s
);
763 hammer2_uuid_to_str(&opt
->Hammer2_VolFSID
, &s
);
764 printf("\tHammer2_VolFSID \"%s\"\n", s
);
766 hammer2_uuid_to_str(&opt
->Hammer2_SupCLID
, &s
);
767 printf("\tHammer2_SupCLID \"%s\"\n", s
);
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
]);
775 hammer2_uuid_to_str(&opt
->Hammer2_PfsCLID
[i
], &s
);
776 printf("\t Hammer2_PfsCLID[%d] \"%s\"\n", i
, s
);
778 hammer2_uuid_to_str(&opt
->Hammer2_PfsFSID
[i
], &s
);
779 printf("\t Hammer2_PfsFSID[%d] \"%s\"\n", i
, s
);
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 */
792 int i
, bufsize
, oflags
;
795 assert(image
!= NULL
);
796 assert(fsopts
!= NULL
);
799 oflags
= O_RDWR
| O_CREAT
;
800 if (fsopts
->offset
== 0)
802 if ((fsopts
->fd
= open(image
, oflags
, 0666)) == -1) {
803 warn("can't open `%s' for writing", 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");
816 if (fsopts
->sparse
) {
817 /* File truncated at bufrem. Remaining is 0 */
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) {
837 i
= write(fsopts
->fd
, buf
, MIN(bufsize
, bufrem
));
839 warn("zeroing image, %lld bytes to go",
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 */
858 hammer2_phys_size(off_t size
)
860 off_t radix_size
, phys_size
= 0;
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
;
879 /* calculate data size */
881 hammer2_size_dir(fsnode
*root
, fsinfo_t
*fsopts
)
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",
900 (long long)node
->inode
->st
.st_size
);
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
);
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
) {
928 int indent
= depth
* 2;
930 if (S_ISDIR(node
->type
))
932 else if (S_ISREG(node
->type
))
934 else if (S_ISLNK(node
->type
))
936 else if (S_ISFIFO(node
->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
);
947 if (S_ISDIR(node
->type
))
949 else if (S_ISREG(node
->type
))
951 else if (S_ISLNK(node
->type
))
953 else if (S_ISFIFO(node
->type
))
964 hammer2_populate_dir(struct m_vnode
*dvp
, const char *dir
, fsnode
*root
,
965 fsnode
*parent
, fsinfo_t
*fsopts
, int depth
)
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 */
999 path
= cur
->contents
;
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
);
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
);
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
;
1020 cur
->parent
= parent
;
1023 /* detect hardlink */
1024 if (cur
->inode
->flags
& FI_WRITTEN
) {
1025 assert(!S_ISDIR(cur
->type
));
1030 cur
->inode
->flags
|= FI_WRITTEN
;
1032 /* make sure it doesn't exist yet */
1034 error
= hammer2_nresolve(dvp
, &vp
, cur
->name
,
1037 errx(1, "hammer2_nresolve(\"%s\") already exists",
1039 hammer2_print(dvp
, vp
, cur
, depth
, "nresolve");
1041 /* if directory, mkdir and recurse */
1042 if (S_ISDIR(cur
->type
)) {
1046 error
= hammer2_nmkdir(dvp
, &vp
, cur
->name
,
1047 strlen(cur
->name
), cur
->inode
->st
.st_mode
);
1049 errx(1, "hammer2_nmkdir(\"%s\") failed: %s",
1050 cur
->name
, strerror(error
));
1052 hammer2_print(dvp
, vp
, cur
, depth
, "nmkdir");
1054 error
= hammer2_populate_dir(vp
, path
, cur
->child
, cur
,
1057 errx(1, "failed to populate %s: %s",
1058 path
, strerror(error
));
1059 cur
->inode
->param
= vp
;
1063 /* if regular file, creat and write its data */
1064 if (S_ISREG(cur
->type
) && !hardlink
) {
1065 assert(cur
->child
== NULL
);
1068 error
= hammer2_ncreate(dvp
, &vp
, cur
->name
,
1069 strlen(cur
->name
), cur
->inode
->st
.st_mode
);
1071 errx(1, "hammer2_ncreate(\"%s\") failed: %s",
1072 cur
->name
, strerror(error
));
1074 hammer2_print(dvp
, vp
, cur
, depth
, "ncreate");
1076 error
= hammer2_write_file(vp
, path
, cur
);
1078 errx(1, "hammer2_write_file(\"%s\") failed: %s",
1079 path
, strerror(error
));
1080 cur
->inode
->param
= vp
;
1084 /* if symlink, create a symlink against target */
1085 if (S_ISLNK(cur
->type
)) {
1086 assert(cur
->child
== NULL
);
1089 error
= hammer2_nsymlink(dvp
, &vp
, cur
->name
,
1090 strlen(cur
->name
), cur
->symlink
,
1091 cur
->inode
->st
.st_mode
);
1093 errx(1, "hammer2_nsymlink(\"%s\") failed: %s",
1094 cur
->name
, strerror(error
));
1096 hammer2_print(dvp
, vp
, cur
, depth
, "nsymlink");
1097 cur
->inode
->param
= vp
;
1101 /* if fifo, create a fifo */
1102 if (S_ISFIFO(cur
->type
) && !hardlink
) {
1103 assert(cur
->child
== NULL
);
1106 error
= hammer2_nmknod(dvp
, &vp
, cur
->name
,
1107 strlen(cur
->name
), VFIFO
, cur
->inode
->st
.st_mode
);
1109 errx(1, "hammer2_nmknod(\"%s\") failed: %s",
1110 cur
->name
, strerror(error
));
1112 hammer2_print(dvp
, vp
, cur
, depth
, "nmknod");
1113 cur
->inode
->param
= vp
;
1117 /* if hardlink, creat a hardlink */
1118 if ((S_ISREG(cur
->type
) || S_ISFIFO(cur
->type
)) && hardlink
) {
1120 assert(cur
->child
== NULL
);
1122 /* source vnode must not be NULL */
1123 vp
= cur
->inode
->param
;
1125 /* currently these conditions must be true */
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
,
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
);
1144 /* other types are unsupported */
1145 printf("ignore %s 0%o\n", path
, cur
->type
);
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
;
1160 nsize
= st
->st_size
;
1163 /* check nsize vs maximum file size */
1165 fd
= open(path
, O_RDONLY
);
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
);
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
);
1179 errx(1, "failed to write to %s vnode: %s",
1180 path
, strerror(error
));
1182 if (bufsize
== HAMMER2_PBUFSIZE
)
1183 assert((offset
& (HAMMER2_PBUFSIZE
- 1)) == 0);
1191 trim_char(char *p
, char c
)
1193 char *o
, tmp
[PATH_MAX
];
1198 strlcpy(tmp
, p
, sizeof(tmp
));
1199 if (strncmp(tmp
, p
, sizeof(tmp
)))
1202 /* trim consecutive */
1207 for (i
= 0; i
< n
; i
++) {
1218 assert(strlen(p
) <= strlen(tmp
));
1220 /* assert no consecutive */
1225 for (i
= 0; i
< n
; i
++) {
1227 assert(!prev_was_c
);
1236 memmove(p
, p
+ 1, strlen(p
+ 1) + 1);
1244 assert(p
[strlen(p
) - 1] != '/');
1252 return trim_char(p
, '/');
1256 hammer2_version_get(struct m_vnode
*vp
)
1260 hmp
= VTOI(vp
)->pmp
->pfs_hmps
[0];
1264 printf("version: %d\n", hmp
->voldata
.version
);
1270 TAILQ_ENTRY(pfs_entry
) entry
;
1271 char name
[NAME_MAX
+1];
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
;
1282 const char *type_str
;
1285 bzero(&pfs
, sizeof(pfs
));
1288 while ((pfs
.name_key
= pfs
.name_next
) != (hammer2_key_t
)-1) {
1289 error
= hammer2_ioctl_pfs_get(VTOI(vp
), &pfs
);
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";
1300 type_str
= hammer2_pfssubtype_to_str(
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
);
1310 p
= TAILQ_FIRST(&head
);
1312 if (strcmp(e
->name
, p
->name
) <= 0) {
1313 TAILQ_INSERT_BEFORE(p
, e
, entry
);
1316 p
= TAILQ_NEXT(p
, entry
);
1319 TAILQ_INSERT_TAIL(&head
, e
, entry
);
1323 "ClusterId (pfs_clid) "
1325 while ((p
= TAILQ_FIRST(&head
)) != NULL
) {
1326 printf("%s %s\n", p
->s
, p
->name
);
1327 TAILQ_REMOVE(&head
, p
, entry
);
1335 hammer2_pfs_lookup(struct m_vnode
*vp
, const char *pfs_name
)
1337 hammer2_ioc_pfs_t pfs
;
1341 bzero(&pfs
, sizeof(pfs
));
1342 strlcpy(pfs
.name
, pfs_name
, sizeof(pfs
.name
));
1344 error
= hammer2_ioctl_pfs_lookup(VTOI(vp
), &pfs
);
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
));
1352 hammer2_uuid_to_str(&pfs
.pfs_fsid
, &pfs_id_str
);
1353 printf("fsid: %s\n", pfs_id_str
);
1357 hammer2_uuid_to_str(&pfs
.pfs_clid
, &pfs_id_str
);
1358 printf("clid: %s\n", pfs_id_str
);
1366 hammer2_pfs_create(struct m_vnode
*vp
, const char *pfs_name
)
1368 hammer2_ioc_pfs_t pfs
;
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
)
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",
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
);
1403 hammer2_pfs_snapshot(struct m_vnode
*vp
, const char *pfs_name
,
1404 const char *mount_label
)
1406 hammer2_ioc_pfs_t pfs
;
1410 bzero(&pfs
, sizeof(pfs
));
1411 strlcpy(pfs
.name
, pfs_name
, sizeof(pfs
.name
));
1413 if (strlen(pfs
.name
) == 0) {
1416 snprintf(pfs
.name
, sizeof(pfs
.name
),
1417 "%s.%04d%02d%02d.%02d%02d%02d",
1427 return hammer2_ioctl_pfs_snapshot(VTOI(vp
), &pfs
);
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
;
1437 char *o
, *p
, *name
, *str
= NULL
;
1441 assert(strlen(f
) > 0);
1442 o
= p
= name
= strdup(f
);
1444 error
= trim_slash(p
);
1450 while ((p
= strchr(p
, '/')) != NULL
) {
1451 *p
++ = 0; /* NULL terminate name */
1453 error
= hammer2_nresolve(dvp
, &vp
, name
, strlen(name
));
1458 assert(ip
->meta
.type
== HAMMER2_OBJTYPE_DIRECTORY
);
1464 error
= hammer2_nresolve(dvp
, &vp
, name
, strlen(name
));
1468 bzero(&inode
, sizeof(inode
));
1469 error
= hammer2_ioctl_inode_get(VTOI(vp
), &inode
);
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
));
1490 printf("uid = %s\n", hammer2_uuid_to_str(&uuid
, &str
));
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");
1528 hammer2_inode_setcheck(struct m_vnode
*dvp
, const char *f
)
1530 hammer2_ioc_inode_t inode
;
1531 hammer2_inode_t
*ip
;
1533 char *o
, *p
, *name
, *check_algo_str
;
1534 const char *checks
[] = { "none", "disabled", "crc32", "xxhash64",
1536 int check_algo_idx
, error
;
1539 assert(strlen(f
) > 0);
1542 p
= strrchr(p
, ':');
1546 *p
++ = 0; /* NULL terminate path */
1550 error
= trim_slash(p
);
1553 if (strlen(p
) == 0 || strlen(check_algo_str
) == 0)
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)
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
;
1569 printf("invalid check_algo_str: %s\n", check_algo_str
);
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 */
1579 error
= hammer2_nresolve(dvp
, &vp
, name
, strlen(name
));
1584 assert(ip
->meta
.type
== HAMMER2_OBJTYPE_DIRECTORY
);
1590 error
= hammer2_nresolve(dvp
, &vp
, name
, strlen(name
));
1595 bzero(&inode
, sizeof(inode
));
1596 error
= hammer2_ioctl_inode_get(ip
, &inode
);
1600 inode
.flags
|= HAMMER2IOC_INODE_FLAG_CHECK
;
1601 inode
.ip_data
.meta
.check_algo
= check_algo
;
1602 error
= hammer2_ioctl_inode_set(ip
, &inode
);
1612 hammer2_inode_setcomp(struct m_vnode
*dvp
, const char *f
)
1614 hammer2_ioc_inode_t inode
;
1615 hammer2_inode_t
*ip
;
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);
1625 p
= strrchr(p
, ':');
1629 *p
++ = 0; /* NULL terminate comp_algo_str */
1633 p
= strrchr(p
, ':');
1635 /* comp_level_str not specified */
1636 comp_algo_str
= comp_level_str
;
1637 comp_level_str
= NULL
;
1639 *p
++ = 0; /* NULL terminate path */
1644 error
= trim_slash(p
);
1647 if (strlen(p
) == 0 || strlen(comp_algo_str
) == 0)
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)
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
;
1663 printf("invalid comp_algo_str: %s\n", comp_algo_str
);
1667 comp_algo
= HAMMER2_ENC_ALGO(comp_algo_idx
);
1669 /* convert comp_level_str to comp_level_idx */
1670 if (comp_level_str
== NULL
) {
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) {
1677 printf("invalid comp_level_str: %s\n", comp_level_str
);
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
);
1690 printf("unsupported comp_level %d for %s\n",
1691 comp_level_idx
, comp_algo_str
);
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 */
1702 error
= hammer2_nresolve(dvp
, &vp
, name
, strlen(name
));
1707 assert(ip
->meta
.type
== HAMMER2_OBJTYPE_DIRECTORY
);
1713 error
= hammer2_nresolve(dvp
, &vp
, name
, strlen(name
));
1718 bzero(&inode
, sizeof(inode
));
1719 error
= hammer2_ioctl_inode_get(ip
, &inode
);
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
);
1735 hammer2_bulkfree(struct m_vnode
*vp
)
1737 hammer2_ioc_bulkfree_t bfi
;
1739 size_t usermem_size
= sizeof(usermem
);
1741 bzero(&bfi
, sizeof(bfi
));
1743 if (sysctlbyname("hw.usermem", &usermem
, &usermem_size
, NULL
, 0) == 0)
1744 bfi
.size
= usermem
/ 16;
1747 if (bfi
.size
< 8192 * 1024)
1748 bfi
.size
= 8192 * 1024;
1750 return hammer2_ioctl_bulkfree_scan(VTOI(vp
), &bfi
);
1754 hammer2_destroy_path(struct m_vnode
*dvp
, const char *f
)
1756 hammer2_ioc_destroy_t destroy
;
1757 hammer2_inode_t
*ip
;
1762 assert(strlen(f
) > 0);
1763 o
= p
= name
= strdup(f
);
1765 error
= trim_slash(p
);
1771 while ((p
= strchr(p
, '/')) != NULL
) {
1772 *p
++ = 0; /* NULL terminate name */
1774 error
= hammer2_nresolve(dvp
, &vp
, name
, strlen(name
));
1779 assert(ip
->meta
.type
== HAMMER2_OBJTYPE_DIRECTORY
);
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
);
1795 error
= hammer2_ioctl_destroy(VTOI(dvp
), &destroy
);
1797 printf("%s\n", strerror(error
));
1806 hammer2_destroy_inum(struct m_vnode
*vp
, hammer2_tid_t inum
)
1808 hammer2_ioc_destroy_t destroy
;
1811 bzero(&destroy
, sizeof(destroy
));
1812 destroy
.cmd
= HAMMER2_DELETE_INUM
;
1813 destroy
.inum
= inum
;
1815 printf("%jd\t", (intmax_t)destroy
.inum
);
1818 error
= hammer2_ioctl_destroy(VTOI(vp
), &destroy
);
1820 printf("%s\n", strerror(error
));
1828 hammer2_growfs(struct m_vnode
*vp
, hammer2_off_t size
)
1830 hammer2_ioc_growfs_t growfs
;
1833 bzero(&growfs
, sizeof(growfs
));
1836 error
= hammer2_ioctl_growfs(VTOI(vp
), &growfs
, NULL
);
1838 if (growfs
.modified
)
1839 printf("grown to %016jx\n", (intmax_t)growfs
.size
);
1841 printf("no size change - %016jx\n",
1842 (intmax_t)growfs
.size
);
1849 assert_trim_slash(const char *input
, const char *expected
)
1854 strlcpy(tmp
, input
, sizeof(tmp
));
1855 error
= trim_slash(tmp
);
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
);
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");