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 int hammer2_readx(struct m_vnode
*, const char *, const char *);
85 static void unittest_trim_slash(void);
87 fsnode
*hammer2_curnode
;
90 hammer2_prep_opts(fsinfo_t
*fsopts
)
92 hammer2_makefs_options_t
*h2_opt
= ecalloc(1, sizeof(*h2_opt
));
93 hammer2_mkfs_options_t
*opt
= &h2_opt
->mkfs_options
;
95 const option_t hammer2_options
[] = {
96 /* newfs_hammer2(8) compatible options */
97 { 'b', "BootAreaSize", NULL
, OPT_STRBUF
, 0, 0, "boot area size" },
98 { 'r', "AuxAreaSize", NULL
, OPT_STRBUF
, 0, 0, "aux area size" },
99 { 'V', "Hammer2Version", NULL
, OPT_STRBUF
, 0, 0, "file system version" },
100 { 'L', "Label", NULL
, OPT_STRBUF
, 0, 0, "PFS label" },
101 /* makefs(8) specific options */
102 { 'm', "MountLabel", NULL
, OPT_STRBUF
, 0, 0, "destination PFS label" },
103 { 'v', "NumVolhdr", &h2_opt
->num_volhdr
, OPT_INT32
,
104 1, HAMMER2_NUM_VOLHDRS
, "number of volume headers" },
105 { 'd', "Hammer2Debug", NULL
, OPT_STRBUF
, 0, 0, "debug tunable" },
106 { 'E', "EmergencyMode", &h2_opt
->emergency_mode
, OPT_BOOL
, 0, 0,
108 { 'P', "PFS", NULL
, OPT_STRBUF
, 0, 0, "offline PFS" },
109 { 'I', "Inode", NULL
, OPT_STRBUF
, 0, 0, "offline inode" },
110 { 'B', "Bulkfree", NULL
, OPT_STRBUF
, 0, 0, "offline bulkfree" },
111 { 'D', "Destroy", NULL
, OPT_STRBUF
, 0, 0, "offline destroy" },
112 { 'G', "Growfs", NULL
, OPT_STRBUF
, 0, 0, "offline growfs" },
113 { 'R', "Read", NULL
, OPT_STRBUF
, 0, 0, "offline read" },
117 hammer2_mkfs_init(opt
);
119 /* make this tunable ? */
120 assert(opt
->CompType
== HAMMER2_COMP_NEWFS_DEFAULT
);
121 assert(opt
->CheckType
== HAMMER2_CHECK_XXHASH64
);
123 /* force debug mode for mkfs */
126 fsopts
->fs_specific
= h2_opt
;
127 fsopts
->fs_options
= copy_opts(hammer2_options
);
128 fsopts
->sectorsize
= DEV_BSIZE
;
132 hammer2_cleanup_opts(fsinfo_t
*fsopts
)
134 hammer2_makefs_options_t
*h2_opt
= fsopts
->fs_specific
;
135 hammer2_mkfs_options_t
*opt
= &h2_opt
->mkfs_options
;
137 hammer2_mkfs_cleanup(opt
);
140 free(fsopts
->fs_options
);
144 hammer2_parse_opts(const char *option
, fsinfo_t
*fsopts
)
146 hammer2_makefs_options_t
*h2_opt
= fsopts
->fs_specific
;
147 hammer2_mkfs_options_t
*opt
= &h2_opt
->mkfs_options
;
149 option_t
*hammer2_options
= fsopts
->fs_options
;
150 char buf
[1024]; /* > HAMMER2_INODE_MAXNAME */
153 assert(option
!= NULL
);
154 assert(fsopts
!= NULL
);
156 if (debug
& DEBUG_FS_PARSE_OPTS
)
157 APRINTF("got `%s'\n", option
);
159 i
= set_option(hammer2_options
, option
, buf
, sizeof(buf
));
163 if (hammer2_options
[i
].name
== NULL
)
166 switch (hammer2_options
[i
].letter
) {
168 opt
->BootAreaSize
= getsize(buf
, HAMMER2_NEWFS_ALIGN
,
169 HAMMER2_BOOT_MAX_BYTES
, 2);
172 opt
->AuxAreaSize
= getsize(buf
, HAMMER2_NEWFS_ALIGN
,
173 HAMMER2_AUX_MAX_BYTES
, 2);
176 if (strlen(buf
) == 0) {
177 h2_opt
->ioctl_cmd
= HAMMER2IOC_VERSION_GET
;
179 opt
->Hammer2Version
= strtol(buf
, NULL
, 0);
180 if (opt
->Hammer2Version
< HAMMER2_VOL_VERSION_MIN
||
181 opt
->Hammer2Version
>= HAMMER2_VOL_VERSION_WIP
)
182 errx(1, "I don't understand how to format "
183 "HAMMER2 version %d",
184 opt
->Hammer2Version
);
188 h2_opt
->label_specified
= 1;
189 if (strcasecmp(buf
, "none") == 0)
191 if (opt
->NLabels
>= MAXLABELS
)
192 errx(1, "Limit of %d local labels", MAXLABELS
- 1);
193 if (strlen(buf
) == 0)
194 errx(1, "Volume label '%s' cannot be 0-length", buf
);
195 if (strlen(buf
) >= HAMMER2_INODE_MAXNAME
)
196 errx(1, "Volume label '%s' is too long (%d chars max)",
197 buf
, HAMMER2_INODE_MAXNAME
- 1);
198 opt
->Label
[opt
->NLabels
++] = strdup(buf
);
201 if (strlen(buf
) == 0)
202 errx(1, "Volume label '%s' cannot be 0-length", buf
);
203 if (strlen(buf
) >= HAMMER2_INODE_MAXNAME
)
204 errx(1, "Volume label '%s' is too long (%d chars max)",
205 buf
, HAMMER2_INODE_MAXNAME
- 1);
206 strlcpy(h2_opt
->mount_label
, buf
, sizeof(h2_opt
->mount_label
));
209 hammer2_debug
= strtoll(buf
, NULL
, 0);
212 if (strlen(buf
) == 0)
213 errx(1, "PFS argument '%s' cannot be 0-length", buf
);
214 hammer2_parse_pfs_opts(buf
, fsopts
);
217 if (strlen(buf
) == 0)
218 errx(1, "Inode argument '%s' cannot be 0-length", buf
);
219 hammer2_parse_inode_opts(buf
, fsopts
);
222 h2_opt
->ioctl_cmd
= HAMMER2IOC_BULKFREE_SCAN
;
225 h2_opt
->ioctl_cmd
= HAMMER2IOC_DESTROY
;
226 if (strlen(buf
) == 0)
227 errx(1, "Destroy argument '%s' cannot be 0-length", buf
);
229 strlcpy(h2_opt
->destroy_path
, buf
,
230 sizeof(h2_opt
->destroy_path
));
231 } else if (strncmp(buf
, "0x", 2) == 0 ||
232 (buf
[0] >= '0' && buf
[0] <= '9')) {
233 h2_opt
->destroy_inum
= strtoull(buf
, NULL
, 0);
237 errx(1, "Invalid destroy argument %s", buf
);
241 h2_opt
->ioctl_cmd
= HAMMER2IOC_GROWFS
;
244 h2_opt
->ioctl_cmd
= HAMMER2IOC_READ
;
245 if (strlen(buf
) == 0)
246 errx(1, "Read argument '%s' cannot be 0-length", buf
);
247 strlcpy(h2_opt
->read_path
, buf
, sizeof(h2_opt
->read_path
));
253 if (hammer2_debug
&& h2_opt
->ioctl_cmd
)
254 unittest_trim_slash();
260 hammer2_makefs(const char *image
, const char *dir
, fsnode
*root
,
263 hammer2_makefs_options_t
*h2_opt
= fsopts
->fs_specific
;
265 struct hammer2_mount_info info
;
266 struct m_vnode devvp
, *vroot
;
267 hammer2_inode_t
*iroot
;
268 struct timeval start
;
271 /* ioctl commands could have NULL dir / root */
272 assert(image
!= NULL
);
273 assert(fsopts
!= NULL
);
275 if (debug
& DEBUG_FS_MAKEFS
)
276 APRINTF("image \"%s\" directory \"%s\" root %p\n",
279 /* validate tree and options */
281 hammer2_validate(dir
, root
, fsopts
);
282 TIMER_RESULTS(start
, "hammer2_validate");
284 if (h2_opt
->ioctl_cmd
) {
285 /* open existing image */
286 fsopts
->fd
= open(image
, O_RDWR
);
288 err(1, "failed to open `%s'", image
);
292 if (hammer2_create_image(image
, fsopts
) == -1)
293 errx(1, "image file `%s' not created", image
);
294 TIMER_RESULTS(start
, "hammer2_create_image");
296 assert(fsopts
->fd
> 0);
298 if (debug
& DEBUG_FS_MAKEFS
)
302 error
= hammer2_vfs_init();
304 errx(1, "failed to vfs init, error %d", error
);
307 memset(&devvp
, 0, sizeof(devvp
));
309 memset(&mp
, 0, sizeof(mp
));
310 memset(&info
, 0, sizeof(info
));
311 error
= hammer2_vfs_mount(&devvp
, &mp
, h2_opt
->mount_label
, &info
);
313 errx(1, "failed to mount, error %d", error
);
318 error
= hammer2_vfs_root(&mp
, &vroot
);
320 errx(1, "failed to get root vnode, error %d", error
);
325 printf("root inode inum %lld, mode 0%o, refs %d\n",
326 (long long)iroot
->meta
.inum
, iroot
->meta
.mode
, iroot
->refs
);
328 if (h2_opt
->emergency_mode
)
329 hammer2_ioctl_emerg_mode(iroot
, 1);
331 switch (h2_opt
->ioctl_cmd
) {
332 case HAMMER2IOC_VERSION_GET
:
333 printf("version get `%s'\n", image
);
335 error
= hammer2_version_get(vroot
);
337 errx(1, "version get `%s' failed '%s'", image
,
339 TIMER_RESULTS(start
, "hammer2_version_get");
341 case HAMMER2IOC_PFS_GET
:
342 printf("PFS %s `%s'\n", h2_opt
->pfs_cmd_name
, image
);
344 error
= hammer2_pfs_get(vroot
);
346 errx(1, "PFS %s`%s' failed '%s'", h2_opt
->pfs_cmd_name
,
347 image
, strerror(error
));
348 TIMER_RESULTS(start
, "hammer2_pfs_get");
350 case HAMMER2IOC_PFS_LOOKUP
:
351 printf("PFS %s `%s'\n", h2_opt
->pfs_cmd_name
, image
);
353 error
= hammer2_pfs_lookup(vroot
, h2_opt
->pfs_name
);
355 errx(1, "PFS %s`%s' failed '%s'", h2_opt
->pfs_cmd_name
,
356 image
, strerror(error
));
357 TIMER_RESULTS(start
, "hammer2_pfs_lookup");
359 case HAMMER2IOC_PFS_CREATE
:
360 printf("PFS %s `%s'\n", h2_opt
->pfs_cmd_name
, image
);
362 error
= hammer2_pfs_create(vroot
, h2_opt
->pfs_name
);
364 errx(1, "PFS %s`%s' failed '%s'", h2_opt
->pfs_cmd_name
,
365 image
, strerror(error
));
366 TIMER_RESULTS(start
, "hammer2_pfs_create");
368 case HAMMER2IOC_PFS_DELETE
:
369 printf("PFS %s `%s'\n", h2_opt
->pfs_cmd_name
, image
);
371 error
= hammer2_pfs_delete(vroot
, h2_opt
->pfs_name
);
373 errx(1, "PFS %s`%s' failed '%s'", h2_opt
->pfs_cmd_name
,
374 image
, strerror(error
));
375 TIMER_RESULTS(start
, "hammer2_pfs_delete");
377 case HAMMER2IOC_PFS_SNAPSHOT
:
378 printf("PFS %s `%s'\n", h2_opt
->pfs_cmd_name
, image
);
380 error
= hammer2_pfs_snapshot(vroot
, h2_opt
->pfs_name
,
381 h2_opt
->mount_label
);
383 errx(1, "PFS %s`%s' failed '%s'", h2_opt
->pfs_cmd_name
,
384 image
, strerror(error
));
385 TIMER_RESULTS(start
, "hammer2_pfs_snapshot");
387 case HAMMER2IOC_INODE_GET
:
388 printf("inode %s `%s'\n", h2_opt
->inode_cmd_name
, image
);
390 error
= hammer2_inode_getx(vroot
, h2_opt
->inode_path
);
392 errx(1, "inode %s `%s' failed '%s'",
393 h2_opt
->inode_cmd_name
, image
, strerror(error
));
394 TIMER_RESULTS(start
, "hammer2_inode_getx");
396 case HAMMER2IOC_INODE_SET
:
397 printf("inode %s `%s'\n", h2_opt
->inode_cmd_name
, image
);
399 if (!strcmp(h2_opt
->inode_cmd_name
, "setcheck")) {
400 error
= hammer2_inode_setcheck(vroot
,
403 errx(1, "inode %s `%s' failed '%s'",
404 h2_opt
->inode_cmd_name
, image
,
406 } else if (!strcmp(h2_opt
->inode_cmd_name
, "setcomp")) {
407 error
= hammer2_inode_setcomp(vroot
,
410 errx(1, "inode %s `%s' failed '%s'",
411 h2_opt
->inode_cmd_name
, image
,
416 TIMER_RESULTS(start
, "hammer2_inode_setx");
418 case HAMMER2IOC_BULKFREE_SCAN
:
419 printf("bulkfree `%s'\n", image
);
421 error
= hammer2_bulkfree(vroot
);
423 errx(1, "bulkfree `%s' failed '%s'", image
,
425 TIMER_RESULTS(start
, "hammer2_bulkfree");
427 case HAMMER2IOC_DESTROY
:
429 if (strlen(h2_opt
->destroy_path
)) {
430 printf("destroy `%s' in `%s'\n",
431 h2_opt
->destroy_path
, image
);
432 error
= hammer2_destroy_path(vroot
,
433 h2_opt
->destroy_path
);
435 errx(1, "destroy `%s' in `%s' failed '%s'",
436 h2_opt
->destroy_path
, image
,
439 printf("destroy %lld in `%s'\n",
440 (long long)h2_opt
->destroy_inum
, image
);
441 error
= hammer2_destroy_inum(vroot
,
442 h2_opt
->destroy_inum
);
444 errx(1, "destroy %lld in `%s' failed '%s'",
445 (long long)h2_opt
->destroy_inum
, image
,
448 TIMER_RESULTS(start
, "hammer2_destroy");
450 case HAMMER2IOC_GROWFS
:
451 printf("growfs `%s'\n", image
);
453 error
= hammer2_growfs(vroot
, h2_opt
->image_size
);
455 errx(1, "growfs `%s' failed '%s'", image
,
457 TIMER_RESULTS(start
, "hammer2_growfs");
459 case HAMMER2IOC_READ
:
460 printf("read `%s'\n", image
);
462 error
= hammer2_readx(vroot
, dir
, h2_opt
->read_path
);
464 errx(1, "read `%s' failed '%s'", image
,
466 TIMER_RESULTS(start
, "hammer2_readx");
469 printf("populating `%s'\n", image
);
471 if (hammer2_populate_dir(vroot
, dir
, root
, root
, fsopts
, 0))
472 errx(1, "image file `%s' not populated", image
);
473 TIMER_RESULTS(start
, "hammer2_populate_dir");
478 error
= hammer2_vfs_unmount(&mp
, 0);
480 errx(1, "failed to unmount, error %d", error
);
482 /* check leaked resource */
484 printf("XXX %lld vnode left\n", (long long)vnode_count
);
485 if (hammer2_chain_allocs
)
486 printf("XXX %ld chain left\n", hammer2_chain_allocs
);
490 error
= hammer2_vfs_uninit();
492 errx(1, "failed to vfs uninit, error %d", error
);
494 if (close(fsopts
->fd
) == -1)
495 err(1, "closing `%s'", image
);
498 printf("image `%s' complete\n", image
);
501 /* end of public functions */
504 hammer2_parse_pfs_opts(const char *buf
, fsinfo_t
*fsopts
)
506 hammer2_makefs_options_t
*h2_opt
= fsopts
->fs_specific
;
519 if (!strcmp(o
, "get") || !strcmp(o
, "list")) {
520 h2_opt
->ioctl_cmd
= HAMMER2IOC_PFS_GET
;
521 } else if (!strcmp(o
, "lookup")) {
522 if (n
== 0 || n
> NAME_MAX
)
523 errx(1, "invalid PFS name \"%s\"", p
);
524 h2_opt
->ioctl_cmd
= HAMMER2IOC_PFS_LOOKUP
;
525 } else if (!strcmp(o
, "create")) {
526 if (n
== 0 || n
> NAME_MAX
)
527 errx(1, "invalid PFS name \"%s\"", p
);
528 h2_opt
->ioctl_cmd
= HAMMER2IOC_PFS_CREATE
;
529 } else if (!strcmp(o
, "delete")) {
530 if (n
== 0 || n
> NAME_MAX
)
531 errx(1, "invalid PFS name \"%s\"", p
);
532 h2_opt
->ioctl_cmd
= HAMMER2IOC_PFS_DELETE
;
533 } else if (!strcmp(o
, "snapshot")) {
535 errx(1, "invalid PFS name \"%s\"", p
);
536 h2_opt
->ioctl_cmd
= HAMMER2IOC_PFS_SNAPSHOT
;
538 errx(1, "invalid PFS command \"%s\"", o
);
541 strlcpy(h2_opt
->pfs_cmd_name
, o
, sizeof(h2_opt
->pfs_cmd_name
));
543 strlcpy(h2_opt
->pfs_name
, p
, sizeof(h2_opt
->pfs_name
));
549 hammer2_parse_inode_opts(const char *buf
, fsinfo_t
*fsopts
)
551 hammer2_makefs_options_t
*h2_opt
= fsopts
->fs_specific
;
564 if (!strcmp(o
, "get")) {
565 if (n
== 0 || n
> PATH_MAX
)
566 errx(1, "invalid file path \"%s\"", p
);
567 h2_opt
->ioctl_cmd
= HAMMER2IOC_INODE_GET
;
568 } else if (!strcmp(o
, "setcheck")) {
569 if (n
== 0 || n
> PATH_MAX
- 10)
570 errx(1, "invalid argument \"%s\"", p
);
571 h2_opt
->ioctl_cmd
= HAMMER2IOC_INODE_SET
;
572 } else if (!strcmp(o
, "setcomp")) {
573 if (n
== 0 || n
> PATH_MAX
- 10)
574 errx(1, "invalid argument \"%s\"", p
);
575 h2_opt
->ioctl_cmd
= HAMMER2IOC_INODE_SET
;
577 errx(1, "invalid inode command \"%s\"", o
);
580 strlcpy(h2_opt
->inode_cmd_name
, o
, sizeof(h2_opt
->inode_cmd_name
));
582 strlcpy(h2_opt
->inode_path
, p
, sizeof(h2_opt
->inode_path
));
588 hammer2_image_size(fsinfo_t
*fsopts
)
590 hammer2_makefs_options_t
*h2_opt
= fsopts
->fs_specific
;
591 hammer2_off_t image_size
, used_size
= 0;
592 int num_level1
, delta_num_level1
;
594 /* use 4 volume headers by default */
595 num_level1
= h2_opt
->num_volhdr
* 2; /* default 4 x 2 */
596 assert(num_level1
!= 0);
597 assert(num_level1
<= 8);
599 /* add 4MiB segment for each level1 */
600 used_size
+= HAMMER2_ZONE_SEG64
* num_level1
;
602 /* add boot/aux area, but exact size unknown at this point */
603 used_size
+= HAMMER2_BOOT_NOM_BYTES
+ HAMMER2_AUX_NOM_BYTES
;
606 used_size
+= fsopts
->size
;
608 /* XXX add extra level1 for meta data and indirect blocks */
609 used_size
+= HAMMER2_FREEMAP_LEVEL1_SIZE
;
611 /* XXX add extra level1 for safety */
612 if (used_size
> HAMMER2_FREEMAP_LEVEL1_SIZE
* 10)
613 used_size
+= HAMMER2_FREEMAP_LEVEL1_SIZE
;
615 /* use 8GiB image size by default */
616 image_size
= HAMMER2_FREEMAP_LEVEL1_SIZE
* num_level1
;
617 printf("trying default image size %s\n", sizetostr(image_size
));
619 /* adjust if image size isn't large enough */
620 if (used_size
> image_size
) {
621 /* determine extra level1 needed */
622 delta_num_level1
= howmany(used_size
- image_size
,
623 HAMMER2_FREEMAP_LEVEL1_SIZE
);
625 /* adjust used size with 4MiB segment for each extra level1 */
626 used_size
+= HAMMER2_ZONE_SEG64
* delta_num_level1
;
628 /* adjust image size with extra level1 */
629 image_size
+= HAMMER2_FREEMAP_LEVEL1_SIZE
* delta_num_level1
;
630 printf("trying adjusted image size %s\n",
631 sizetostr(image_size
));
633 if (used_size
> image_size
)
634 errx(1, "invalid used_size %lld > image_size %lld",
635 (long long)used_size
, (long long)image_size
);
642 hammer2_label_name(int label_type
)
644 switch (label_type
) {
645 case HAMMER2_LABEL_NONE
:
647 case HAMMER2_LABEL_BOOT
:
649 case HAMMER2_LABEL_ROOT
:
651 case HAMMER2_LABEL_DATA
:
660 hammer2_validate(const char *dir
, fsnode
*root
, fsinfo_t
*fsopts
)
662 hammer2_makefs_options_t
*h2_opt
= fsopts
->fs_specific
;
663 hammer2_mkfs_options_t
*opt
= &h2_opt
->mkfs_options
;
664 hammer2_off_t image_size
= 0, minsize
, maxsize
;
667 /* ioctl commands could have NULL dir / root */
668 assert(fsopts
!= NULL
);
670 if (debug
& DEBUG_FS_VALIDATE
) {
671 APRINTF("before defaults set:\n");
672 hammer2_dump_fsinfo(fsopts
);
675 /* makefs only supports "DATA" for default PFS label */
676 if (!h2_opt
->label_specified
) {
677 opt
->DefaultLabelType
= HAMMER2_LABEL_DATA
;
678 s
= hammer2_label_name(opt
->DefaultLabelType
);
679 printf("using default label \"%s\"\n", s
);
682 /* set default mount PFS label */
683 if (!strcmp(h2_opt
->mount_label
, "")) {
684 s
= hammer2_label_name(HAMMER2_LABEL_DATA
);
685 strlcpy(h2_opt
->mount_label
, s
, sizeof(h2_opt
->mount_label
));
686 printf("using default mount label \"%s\"\n", s
);
689 /* set default number of volume headers */
690 if (!h2_opt
->num_volhdr
) {
691 h2_opt
->num_volhdr
= HAMMER2_NUM_VOLHDRS
;
692 printf("using default %d volume headers\n", h2_opt
->num_volhdr
);
695 /* done if ioctl commands */
696 if (h2_opt
->ioctl_cmd
) {
697 if (h2_opt
->ioctl_cmd
== HAMMER2IOC_GROWFS
)
698 goto ignore_size_dir
;
703 /* calculate data size */
704 if (fsopts
->size
!= 0)
705 fsopts
->size
= 0; /* shouldn't reach here to begin with */
707 errx(1, "fsnode tree not constructed");
708 hammer2_size_dir(root
, fsopts
);
709 printf("estimated data size %s from %lld inode\n",
710 sizetostr(fsopts
->size
), (long long)fsopts
->inodes
);
712 /* determine image size from data size */
713 image_size
= hammer2_image_size(fsopts
);
714 assert((image_size
& HAMMER2_FREEMAP_LEVEL1_MASK
) == 0);
716 minsize
= roundup(fsopts
->minsize
, HAMMER2_FREEMAP_LEVEL1_SIZE
);
717 maxsize
= roundup(fsopts
->maxsize
, HAMMER2_FREEMAP_LEVEL1_SIZE
);
718 if (image_size
< minsize
)
719 image_size
= minsize
;
720 else if (maxsize
> 0 && image_size
> maxsize
)
721 errx(1, "`%s' size of %lld is larger than the maxsize of %lld",
722 dir
, (long long)image_size
, (long long)maxsize
);
724 assert((image_size
& HAMMER2_FREEMAP_LEVEL1_MASK
) == 0);
725 h2_opt
->image_size
= image_size
;
726 printf("using %s image size\n", sizetostr(h2_opt
->image_size
));
728 if (debug
& DEBUG_FS_VALIDATE
) {
729 APRINTF("after defaults set:\n");
730 hammer2_dump_fsinfo(fsopts
);
735 hammer2_dump_fsinfo(fsinfo_t
*fsopts
)
737 hammer2_makefs_options_t
*h2_opt
= fsopts
->fs_specific
;
738 hammer2_mkfs_options_t
*opt
= &h2_opt
->mkfs_options
;
742 assert(fsopts
!= NULL
);
744 APRINTF("fsinfo_t at %p\n", fsopts
);
746 printf("\tinodes %lld\n", (long long)fsopts
->inodes
);
747 printf("\tsize %lld, minsize %lld, maxsize %lld\n",
748 (long long)fsopts
->size
,
749 (long long)fsopts
->minsize
,
750 (long long)fsopts
->maxsize
);
752 printf("\thammer2_debug 0x%x\n", hammer2_debug
);
754 printf("\tlabel_specified %d\n", h2_opt
->label_specified
);
755 printf("\tmount_label \"%s\"\n", h2_opt
->mount_label
);
756 printf("\tnum_volhdr %d\n", h2_opt
->num_volhdr
);
757 printf("\tioctl_cmd %ld\n", h2_opt
->ioctl_cmd
);
758 printf("\temergency_mode %d\n", h2_opt
->emergency_mode
);
759 printf("\tpfs_cmd_name \"%s\"\n", h2_opt
->pfs_cmd_name
);
760 printf("\tpfs_name \"%s\"\n", h2_opt
->pfs_name
);
761 printf("\tinode_cmd_name \"%s\"\n", h2_opt
->inode_cmd_name
);
762 printf("\tinode_path \"%s\"\n", h2_opt
->inode_path
);
763 printf("\tdestroy_path \"%s\"\n", h2_opt
->destroy_path
);
764 printf("\tdestroy_inum %lld\n", (long long)h2_opt
->destroy_inum
);
765 printf("\timage_size 0x%llx\n", (long long)h2_opt
->image_size
);
767 printf("\tHammer2Version %d\n", opt
->Hammer2Version
);
768 printf("\tBootAreaSize 0x%jx\n", opt
->BootAreaSize
);
769 printf("\tAuxAreaSize 0x%jx\n", opt
->AuxAreaSize
);
770 printf("\tNLabels %d\n", opt
->NLabels
);
771 printf("\tCompType %d\n", opt
->CompType
);
772 printf("\tCheckType %d\n", opt
->CheckType
);
773 printf("\tDefaultLabelType %d\n", opt
->DefaultLabelType
);
774 printf("\tDebugOpt %d\n", opt
->DebugOpt
);
777 hammer2_uuid_to_str(&opt
->Hammer2_FSType
, &s
);
778 printf("\tHammer2_FSType \"%s\"\n", s
);
780 hammer2_uuid_to_str(&opt
->Hammer2_VolFSID
, &s
);
781 printf("\tHammer2_VolFSID \"%s\"\n", s
);
783 hammer2_uuid_to_str(&opt
->Hammer2_SupCLID
, &s
);
784 printf("\tHammer2_SupCLID \"%s\"\n", s
);
786 hammer2_uuid_to_str(&opt
->Hammer2_SupFSID
, &s
);
787 printf("\tHammer2_SupFSID \"%s\"\n", s
);
789 for (i
= 0; i
< opt
->NLabels
; i
++) {
790 printf("\tLabel[%d] \"%s\"\n", i
, opt
->Label
[i
]);
792 hammer2_uuid_to_str(&opt
->Hammer2_PfsCLID
[i
], &s
);
793 printf("\t Hammer2_PfsCLID[%d] \"%s\"\n", i
, s
);
795 hammer2_uuid_to_str(&opt
->Hammer2_PfsFSID
[i
], &s
);
796 printf("\t Hammer2_PfsFSID[%d] \"%s\"\n", i
, s
);
803 hammer2_create_image(const char *image
, fsinfo_t
*fsopts
)
805 hammer2_makefs_options_t
*h2_opt
= fsopts
->fs_specific
;
806 hammer2_mkfs_options_t
*opt
= &h2_opt
->mkfs_options
;
807 char *av
[] = { (char *)image
, }; /* XXX support multi-volumes */
809 int i
, bufsize
, oflags
;
812 assert(image
!= NULL
);
813 assert(fsopts
!= NULL
);
816 oflags
= O_RDWR
| O_CREAT
;
817 if (fsopts
->offset
== 0)
819 if ((fsopts
->fd
= open(image
, oflags
, 0666)) == -1) {
820 warn("can't open `%s' for writing", image
);
825 bufsize
= HAMMER2_PBUFSIZE
;
826 bufrem
= h2_opt
->image_size
;
827 if (fsopts
->sparse
) {
828 if (ftruncate(fsopts
->fd
, bufrem
) == -1) {
829 warn("sparse option disabled");
833 if (fsopts
->sparse
) {
834 /* File truncated at bufrem. Remaining is 0 */
838 if (debug
& DEBUG_FS_CREATE_IMAGE
)
839 APRINTF("zero-ing image `%s', %lld sectors, "
840 "using %d byte chunks\n",
841 image
, (long long)bufrem
, bufsize
);
842 buf
= ecalloc(1, bufsize
);
845 if (fsopts
->offset
!= 0) {
846 if (lseek(fsopts
->fd
, fsopts
->offset
, SEEK_SET
) == -1) {
854 i
= write(fsopts
->fd
, buf
, MIN(bufsize
, bufrem
));
856 warn("zeroing image, %lld bytes to go",
866 /* make the file system */
867 if (debug
& DEBUG_FS_CREATE_IMAGE
)
868 APRINTF("calling mkfs(\"%s\", ...)\n", image
);
869 hammer2_mkfs(1, av
, opt
); /* success if returned */
875 hammer2_phys_size(off_t size
)
877 off_t radix_size
, phys_size
= 0;
880 if (size
> HAMMER2_PBUFSIZE
) {
881 phys_size
+= rounddown(size
, HAMMER2_PBUFSIZE
);
882 size
= size
% HAMMER2_PBUFSIZE
;
885 for (i
= HAMMER2_RADIX_MIN
; i
<= HAMMER2_RADIX_MAX
; i
++) {
886 radix_size
= 1UL << i
;
887 if (radix_size
>= size
) {
888 phys_size
+= radix_size
;
896 /* calculate data size */
898 hammer2_size_dir(fsnode
*root
, fsinfo_t
*fsopts
)
902 assert(fsopts
!= NULL
);
904 if (debug
& DEBUG_FS_SIZE_DIR
)
905 APRINTF("entry: bytes %lld inodes %lld\n",
906 (long long)fsopts
->size
, (long long)fsopts
->inodes
);
908 for (node
= root
; node
!= NULL
; node
= node
->next
) {
909 if (node
== root
) { /* we're at "." */
910 assert(strcmp(node
->name
, ".") == 0);
911 } else if ((node
->inode
->flags
& FI_SIZED
) == 0) {
912 /* don't count duplicate names */
913 node
->inode
->flags
|= FI_SIZED
;
914 if (debug
& DEBUG_FS_SIZE_DIR_NODE
)
915 APRINTF("`%s' size %lld\n",
917 (long long)node
->inode
->st
.st_size
);
919 fsopts
->size
+= sizeof(hammer2_inode_data_t
);
920 if (node
->type
== S_IFREG
) {
921 size_t st_size
= node
->inode
->st
.st_size
;
922 if (st_size
> HAMMER2_EMBEDDED_BYTES
)
923 fsopts
->size
+= hammer2_phys_size(st_size
);
924 } else if (node
->type
== S_IFLNK
) {
925 size_t nlen
= strlen(node
->symlink
);
926 if (nlen
> HAMMER2_EMBEDDED_BYTES
)
927 fsopts
->size
+= hammer2_phys_size(nlen
);
930 if (node
->type
== S_IFDIR
)
931 hammer2_size_dir(node
->child
, fsopts
);
934 if (debug
& DEBUG_FS_SIZE_DIR
)
935 APRINTF("exit: size %lld inodes %lld\n",
936 (long long)fsopts
->size
, (long long)fsopts
->inodes
);
940 hammer2_print(const struct m_vnode
*dvp
, const struct m_vnode
*vp
,
941 const fsnode
*node
, int depth
, const char *msg
)
943 if (debug
& DEBUG_FS_POPULATE
) {
945 int indent
= depth
* 2;
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
))
957 printf("%*.*s", indent
, indent
, "");
958 printf("dvp=%p/%d vp=%p/%d \"%s\" %s %s\n",
959 dvp
, dvp
? VTOI(dvp
)->refs
: 0,
960 vp
, vp
? VTOI(vp
)->refs
: 0,
961 node
->name
, type
, msg
);
964 if (S_ISDIR(node
->type
))
966 else if (S_ISREG(node
->type
))
968 else if (S_ISLNK(node
->type
))
970 else if (S_ISFIFO(node
->type
))
981 hammer2_populate_dir(struct m_vnode
*dvp
, const char *dir
, fsnode
*root
,
982 fsnode
*parent
, fsinfo_t
*fsopts
, int depth
)
994 assert(root
!= NULL
);
995 assert(parent
!= NULL
);
996 assert(fsopts
!= NULL
);
998 /* assert root directory */
999 assert(S_ISDIR(root
->type
));
1000 assert(!strcmp(root
->name
, "."));
1001 assert(!root
->child
);
1002 assert(!root
->parent
|| root
->parent
->child
== root
);
1004 hammer2_print(dvp
, NULL
, root
, depth
, "enter");
1005 if (stat(dir
, &st
) == -1)
1006 err(1, "no such path %s", dir
);
1007 if (!S_ISDIR(st
.st_mode
))
1008 errx(1, "no such dir %s", dir
);
1010 for (cur
= root
->next
; cur
!= NULL
; cur
= cur
->next
) {
1011 /* global variable for HAMMER2 vnops */
1012 hammer2_curnode
= cur
;
1014 /* construct source path */
1015 if (cur
->contents
) {
1016 path
= cur
->contents
;
1018 if (S_ISDIR(cur
->type
)) {
1019 /* this should be same as root/path/name */
1020 if (snprintf(f
, sizeof(f
), "%s/%s",
1021 dir
, cur
->name
) >= (int)sizeof(f
))
1022 errx(1, "path %s too long", f
);
1024 if (snprintf(f
, sizeof(f
), "%s/%s/%s",
1025 cur
->root
, cur
->path
, cur
->name
) >= (int)sizeof(f
))
1026 errx(1, "path %s too long", f
);
1030 if (S_ISLNK(cur
->type
)) {
1031 if (lstat(path
, &st
) == -1)
1032 err(1, "no such symlink %s", path
);
1034 if (stat(path
, &st
) == -1)
1035 err(1, "no such path %s", path
);
1038 /* update node state */
1039 if ((cur
->inode
->flags
& FI_ALLOCATED
) == 0) {
1040 cur
->inode
->flags
|= FI_ALLOCATED
;
1042 cur
->parent
= parent
;
1045 /* detect hardlink */
1046 if (cur
->inode
->flags
& FI_WRITTEN
) {
1047 assert(!S_ISDIR(cur
->type
));
1052 cur
->inode
->flags
|= FI_WRITTEN
;
1054 /* make sure it doesn't exist yet */
1056 error
= hammer2_nresolve(dvp
, &vp
, cur
->name
,
1059 errx(1, "hammer2_nresolve(\"%s\") already exists",
1061 hammer2_print(dvp
, vp
, cur
, depth
, "nresolve");
1063 /* if directory, mkdir and recurse */
1064 if (S_ISDIR(cur
->type
)) {
1068 error
= hammer2_nmkdir(dvp
, &vp
, cur
->name
,
1069 strlen(cur
->name
), cur
->inode
->st
.st_mode
);
1071 errx(1, "hammer2_nmkdir(\"%s\") failed: %s",
1072 cur
->name
, strerror(error
));
1074 hammer2_print(dvp
, vp
, cur
, depth
, "nmkdir");
1076 error
= hammer2_populate_dir(vp
, path
, cur
->child
, cur
,
1079 errx(1, "failed to populate %s: %s",
1080 path
, strerror(error
));
1081 cur
->inode
->param
= vp
;
1085 /* if regular file, creat and write its data */
1086 if (S_ISREG(cur
->type
) && !hardlink
) {
1087 assert(cur
->child
== NULL
);
1090 error
= hammer2_ncreate(dvp
, &vp
, cur
->name
,
1091 strlen(cur
->name
), cur
->inode
->st
.st_mode
);
1093 errx(1, "hammer2_ncreate(\"%s\") failed: %s",
1094 cur
->name
, strerror(error
));
1096 hammer2_print(dvp
, vp
, cur
, depth
, "ncreate");
1098 error
= hammer2_write_file(vp
, path
, cur
);
1100 errx(1, "hammer2_write_file(\"%s\") failed: %s",
1101 path
, strerror(error
));
1102 cur
->inode
->param
= vp
;
1106 /* if symlink, create a symlink against target */
1107 if (S_ISLNK(cur
->type
)) {
1108 assert(cur
->child
== NULL
);
1111 error
= hammer2_nsymlink(dvp
, &vp
, cur
->name
,
1112 strlen(cur
->name
), cur
->symlink
,
1113 cur
->inode
->st
.st_mode
);
1115 errx(1, "hammer2_nsymlink(\"%s\") failed: %s",
1116 cur
->name
, strerror(error
));
1118 hammer2_print(dvp
, vp
, cur
, depth
, "nsymlink");
1119 cur
->inode
->param
= vp
;
1123 /* if fifo, create a fifo */
1124 if (S_ISFIFO(cur
->type
) && !hardlink
) {
1125 assert(cur
->child
== NULL
);
1128 error
= hammer2_nmknod(dvp
, &vp
, cur
->name
,
1129 strlen(cur
->name
), VFIFO
, cur
->inode
->st
.st_mode
);
1131 errx(1, "hammer2_nmknod(\"%s\") failed: %s",
1132 cur
->name
, strerror(error
));
1134 hammer2_print(dvp
, vp
, cur
, depth
, "nmknod");
1135 cur
->inode
->param
= vp
;
1139 /* if hardlink, creat a hardlink */
1140 if ((S_ISREG(cur
->type
) || S_ISFIFO(cur
->type
)) && hardlink
) {
1142 assert(cur
->child
== NULL
);
1144 /* source vnode must not be NULL */
1145 vp
= cur
->inode
->param
;
1147 /* currently these conditions must be true */
1149 assert(vp
->v_type
== VREG
|| vp
->v_type
== VFIFO
);
1150 assert(vp
->v_logical
);
1151 assert(!vp
->v_vflushed
);
1152 assert(vp
->v_malloced
);
1153 assert(VTOI(vp
)->refs
> 0);
1155 error
= hammer2_nlink(dvp
, vp
, cur
->name
,
1158 errx(1, "hammer2_nlink(\"%s\") failed: %s",
1159 cur
->name
, strerror(error
));
1160 snprintf(buf
, sizeof(buf
), "nlink=%lld",
1161 (long long)VTOI(vp
)->meta
.nlinks
);
1162 hammer2_print(dvp
, vp
, cur
, depth
, buf
);
1166 /* other types are unsupported */
1167 printf("ignore %s 0%o\n", path
, cur
->type
);
1174 hammer2_write_file(struct m_vnode
*vp
, const char *path
, fsnode
*node
)
1176 struct stat
*st
= &node
->inode
->st
;
1177 size_t nsize
, bufsize
;
1182 nsize
= st
->st_size
;
1185 /* check nsize vs maximum file size */
1187 fd
= open(path
, O_RDONLY
);
1189 err(1, "failed to open %s", path
);
1191 p
= mmap(0, nsize
, PROT_READ
, MAP_FILE
|MAP_PRIVATE
, fd
, 0);
1192 if (p
== MAP_FAILED
)
1193 err(1, "failed to mmap %s", path
);
1196 for (offset
= 0; offset
< nsize
; ) {
1197 bufsize
= MIN(nsize
- offset
, HAMMER2_PBUFSIZE
);
1198 assert(bufsize
<= HAMMER2_PBUFSIZE
);
1199 error
= hammer2_write(vp
, p
+ offset
, bufsize
, offset
);
1201 errx(1, "failed to write to %s vnode: %s",
1202 path
, strerror(error
));
1204 if (bufsize
== HAMMER2_PBUFSIZE
)
1205 assert((offset
& (HAMMER2_PBUFSIZE
- 1)) == 0);
1213 trim_char(char *p
, char c
)
1215 char *o
, tmp
[PATH_MAX
];
1220 strlcpy(tmp
, p
, sizeof(tmp
));
1221 if (strncmp(tmp
, p
, sizeof(tmp
)))
1224 /* trim consecutive */
1229 for (i
= 0; i
< n
; i
++) {
1240 assert(strlen(p
) <= strlen(tmp
));
1242 /* assert no consecutive */
1247 for (i
= 0; i
< n
; i
++) {
1249 assert(!prev_was_c
);
1258 memmove(p
, p
+ 1, strlen(p
+ 1) + 1);
1266 assert(p
[strlen(p
) - 1] != '/');
1274 return trim_char(p
, '/');
1278 is_supported_link(const char *s
)
1280 /* absolute path can't be supported */
1281 if (strlen(s
) >= 1 && strncmp(s
, "/", 1) == 0)
1284 /* XXX ".." is currently unsupported */
1285 if (strlen(s
) >= 3 && strncmp(s
, "../", 3) == 0)
1292 hammer2_version_get(struct m_vnode
*vp
)
1296 hmp
= VTOI(vp
)->pmp
->pfs_hmps
[0];
1300 printf("version: %d\n", hmp
->voldata
.version
);
1306 TAILQ_ENTRY(pfs_entry
) entry
;
1307 char name
[NAME_MAX
+1];
1312 hammer2_pfs_get(struct m_vnode
*vp
)
1314 hammer2_ioc_pfs_t pfs
;
1315 TAILQ_HEAD(, pfs_entry
) head
;
1316 struct pfs_entry
*p
, *e
;
1318 const char *type_str
;
1321 bzero(&pfs
, sizeof(pfs
));
1324 while ((pfs
.name_key
= pfs
.name_next
) != (hammer2_key_t
)-1) {
1325 error
= hammer2_ioctl_pfs_get(VTOI(vp
), &pfs
);
1330 hammer2_uuid_to_str(&pfs
.pfs_clid
, &pfs_id_str
);
1332 if (pfs
.pfs_type
== HAMMER2_PFSTYPE_MASTER
) {
1333 if (pfs
.pfs_subtype
== HAMMER2_PFSSUBTYPE_NONE
)
1334 type_str
= "MASTER";
1336 type_str
= hammer2_pfssubtype_to_str(
1339 type_str
= hammer2_pfstype_to_str(pfs
.pfs_type
);
1341 e
= ecalloc(1, sizeof(*e
));
1342 snprintf(e
->name
, sizeof(e
->name
), "%s", pfs
.name
);
1343 snprintf(e
->s
, sizeof(e
->s
), "%-11s %s", type_str
, pfs_id_str
);
1346 p
= TAILQ_FIRST(&head
);
1348 if (strcmp(e
->name
, p
->name
) <= 0) {
1349 TAILQ_INSERT_BEFORE(p
, e
, entry
);
1352 p
= TAILQ_NEXT(p
, entry
);
1355 TAILQ_INSERT_TAIL(&head
, e
, entry
);
1359 "ClusterId (pfs_clid) "
1361 while ((p
= TAILQ_FIRST(&head
)) != NULL
) {
1362 printf("%s %s\n", p
->s
, p
->name
);
1363 TAILQ_REMOVE(&head
, p
, entry
);
1371 hammer2_pfs_lookup(struct m_vnode
*vp
, const char *pfs_name
)
1373 hammer2_ioc_pfs_t pfs
;
1377 bzero(&pfs
, sizeof(pfs
));
1378 strlcpy(pfs
.name
, pfs_name
, sizeof(pfs
.name
));
1380 error
= hammer2_ioctl_pfs_lookup(VTOI(vp
), &pfs
);
1382 printf("name: %s\n", pfs
.name
);
1383 printf("type: %s\n", hammer2_pfstype_to_str(pfs
.pfs_type
));
1384 printf("subtype: %s\n",
1385 hammer2_pfssubtype_to_str(pfs
.pfs_subtype
));
1388 hammer2_uuid_to_str(&pfs
.pfs_fsid
, &pfs_id_str
);
1389 printf("fsid: %s\n", pfs_id_str
);
1393 hammer2_uuid_to_str(&pfs
.pfs_clid
, &pfs_id_str
);
1394 printf("clid: %s\n", pfs_id_str
);
1402 hammer2_pfs_create(struct m_vnode
*vp
, const char *pfs_name
)
1404 hammer2_ioc_pfs_t pfs
;
1407 bzero(&pfs
, sizeof(pfs
));
1408 strlcpy(pfs
.name
, pfs_name
, sizeof(pfs
.name
));
1409 pfs
.pfs_type
= HAMMER2_PFSTYPE_MASTER
;
1410 uuid_create(&pfs
.pfs_clid
, NULL
);
1411 uuid_create(&pfs
.pfs_fsid
, NULL
);
1413 error
= hammer2_ioctl_pfs_create(VTOI(vp
), &pfs
);
1414 if (error
== EEXIST
)
1416 "NOTE: Typically the same name is "
1417 "used for cluster elements on "
1418 "different mounts,\n"
1419 " but cluster elements on the "
1420 "same mount require unique names.\n"
1421 "hammer2: pfs_create(%s): already present\n",
1428 hammer2_pfs_delete(struct m_vnode
*vp
, const char *pfs_name
)
1430 hammer2_ioc_pfs_t pfs
;
1432 bzero(&pfs
, sizeof(pfs
));
1433 strlcpy(pfs
.name
, pfs_name
, sizeof(pfs
.name
));
1435 return hammer2_ioctl_pfs_delete(VTOI(vp
), &pfs
);
1439 hammer2_pfs_snapshot(struct m_vnode
*vp
, const char *pfs_name
,
1440 const char *mount_label
)
1442 hammer2_ioc_pfs_t pfs
;
1446 bzero(&pfs
, sizeof(pfs
));
1447 strlcpy(pfs
.name
, pfs_name
, sizeof(pfs
.name
));
1449 if (strlen(pfs
.name
) == 0) {
1452 snprintf(pfs
.name
, sizeof(pfs
.name
),
1453 "%s.%04d%02d%02d.%02d%02d%02d",
1463 return hammer2_ioctl_pfs_snapshot(VTOI(vp
), &pfs
);
1467 hammer2_inode_getx(struct m_vnode
*dvp
, const char *f
)
1469 hammer2_ioc_inode_t inode
;
1470 hammer2_inode_t
*ip
;
1471 hammer2_inode_meta_t
*meta
;
1473 char *o
, *p
, *name
, *str
= NULL
;
1478 assert(strlen(f
) > 0);
1479 o
= p
= name
= strdup(f
);
1481 error
= trim_slash(p
);
1487 while ((p
= strchr(p
, '/')) != NULL
) {
1488 *p
++ = 0; /* NULL terminate name */
1489 if (!strcmp(name
, ".")) {
1494 error
= hammer2_nresolve(dvp
, &vp
, name
, strlen(name
));
1499 switch (ip
->meta
.type
) {
1500 case HAMMER2_OBJTYPE_DIRECTORY
:
1502 case HAMMER2_OBJTYPE_SOFTLINK
:
1503 bzero(tmp
, sizeof(tmp
));
1504 error
= hammer2_readlink(vp
, tmp
, sizeof(tmp
));
1507 if (!is_supported_link(tmp
))
1509 strlcat(tmp
, "/", sizeof(tmp
));
1510 strlcat(tmp
, p
, sizeof(tmp
));
1511 error
= trim_slash(tmp
);
1524 error
= hammer2_nresolve(dvp
, &vp
, name
, strlen(name
));
1528 bzero(&inode
, sizeof(inode
));
1529 error
= hammer2_ioctl_inode_get(VTOI(vp
), &inode
);
1533 meta
= &inode
.ip_data
.meta
;
1534 printf("--------------------\n");
1535 printf("flags = 0x%x\n", inode
.flags
);
1536 printf("data_count = %ju\n", (uintmax_t)inode
.data_count
);
1537 printf("inode_count = %ju\n", (uintmax_t)inode
.inode_count
);
1538 printf("--------------------\n");
1539 printf("version = %u\n", meta
->version
);
1540 printf("pfs_subtype = %u (%s)\n", meta
->pfs_subtype
,
1541 hammer2_pfssubtype_to_str(meta
->pfs_subtype
));
1542 printf("uflags = 0x%x\n", (unsigned int)meta
->uflags
);
1543 printf("rmajor = %u\n", meta
->rmajor
);
1544 printf("rminor = %u\n", meta
->rminor
);
1545 printf("ctime = %s\n", hammer2_time64_to_str(meta
->ctime
, &str
));
1546 printf("mtime = %s\n", hammer2_time64_to_str(meta
->mtime
, &str
));
1547 printf("atime = %s\n", hammer2_time64_to_str(meta
->atime
, &str
));
1548 printf("btime = %s\n", hammer2_time64_to_str(meta
->btime
, &str
));
1550 printf("uid = %s\n", hammer2_uuid_to_str(&uuid
, &str
));
1552 printf("gid = %s\n", hammer2_uuid_to_str(&uuid
, &str
));
1553 printf("type = %u (%s)\n", meta
->type
,
1554 hammer2_iptype_to_str(meta
->type
));
1555 printf("op_flags = 0x%x\n", meta
->op_flags
);
1556 printf("cap_flags = 0x%x\n", meta
->cap_flags
);
1557 printf("mode = 0%o\n", meta
->mode
);
1558 printf("inum = 0x%jx\n", (uintmax_t)meta
->inum
);
1559 printf("size = %ju\n", (uintmax_t)meta
->size
);
1560 printf("nlinks = %ju\n", (uintmax_t)meta
->nlinks
);
1561 printf("iparent = 0x%jx\n", (uintmax_t)meta
->iparent
);
1562 printf("name_key = 0x%jx\n", (uintmax_t)meta
->name_key
);
1563 printf("name_len = %u\n", meta
->name_len
);
1564 printf("ncopies = %u\n", meta
->ncopies
);
1565 printf("comp_algo = %u\n", meta
->comp_algo
);
1566 printf("target_type = %u\n", meta
->target_type
);
1567 printf("check_algo = %u\n", meta
->check_algo
);
1568 printf("pfs_nmasters = %u\n", meta
->pfs_nmasters
);
1569 printf("pfs_type = %u (%s)\n", meta
->pfs_type
,
1570 hammer2_pfstype_to_str(meta
->pfs_type
));
1571 printf("pfs_inum = 0x%jx\n", (uintmax_t)meta
->pfs_inum
);
1572 uuid
= meta
->pfs_clid
;
1573 printf("pfs_clid = %s\n", hammer2_uuid_to_str(&uuid
, &str
));
1574 uuid
= meta
->pfs_fsid
;
1575 printf("pfs_fsid = %s\n", hammer2_uuid_to_str(&uuid
, &str
));
1576 printf("data_quota = 0x%jx\n", (uintmax_t)meta
->data_quota
);
1577 printf("inode_quota = 0x%jx\n", (uintmax_t)meta
->inode_quota
);
1578 printf("pfs_lsnap_tid = 0x%jx\n", (uintmax_t)meta
->pfs_lsnap_tid
);
1579 printf("decrypt_check = 0x%jx\n", (uintmax_t)meta
->decrypt_check
);
1580 printf("--------------------\n");
1588 hammer2_inode_setcheck(struct m_vnode
*dvp
, const char *f
)
1590 hammer2_ioc_inode_t inode
;
1591 hammer2_inode_t
*ip
;
1593 char *o
, *p
, *name
, *check_algo_str
;
1595 const char *checks
[] = { "none", "disabled", "crc32", "xxhash64",
1597 int check_algo_idx
, error
;
1600 assert(strlen(f
) > 0);
1603 p
= strrchr(p
, ':');
1607 *p
++ = 0; /* NULL terminate path */
1611 error
= trim_slash(p
);
1614 if (strlen(p
) == 0 || strlen(check_algo_str
) == 0)
1617 /* convert check_algo_str to check_algo_idx */
1618 check_algo_idx
= nitems(checks
);
1619 while (--check_algo_idx
>= 0)
1620 if (strcasecmp(check_algo_str
, checks
[check_algo_idx
]) == 0)
1622 if (check_algo_idx
< 0) {
1623 if (strcasecmp(check_algo_str
, "default") == 0) {
1624 check_algo_str
= "xxhash64";
1625 check_algo_idx
= HAMMER2_CHECK_XXHASH64
;
1626 } else if (strcasecmp(check_algo_str
, "disabled") == 0) {
1627 check_algo_str
= "disabled";
1628 check_algo_idx
= HAMMER2_CHECK_DISABLED
;
1630 printf("invalid check_algo_str: %s\n", check_algo_str
);
1634 check_algo
= HAMMER2_ENC_ALGO(check_algo_idx
);
1635 printf("change %s to algo %d (%s)\n", p
, check_algo
, check_algo_str
);
1637 while ((p
= strchr(p
, '/')) != NULL
) {
1638 *p
++ = 0; /* NULL terminate name */
1639 if (!strcmp(name
, ".")) {
1644 error
= hammer2_nresolve(dvp
, &vp
, name
, strlen(name
));
1649 switch (ip
->meta
.type
) {
1650 case HAMMER2_OBJTYPE_DIRECTORY
:
1652 case HAMMER2_OBJTYPE_SOFTLINK
:
1653 bzero(tmp
, sizeof(tmp
));
1654 error
= hammer2_readlink(vp
, tmp
, sizeof(tmp
));
1657 if (!is_supported_link(tmp
))
1659 strlcat(tmp
, "/", sizeof(tmp
));
1660 strlcat(tmp
, p
, sizeof(tmp
));
1661 error
= trim_slash(tmp
);
1674 error
= hammer2_nresolve(dvp
, &vp
, name
, strlen(name
));
1679 bzero(&inode
, sizeof(inode
));
1680 error
= hammer2_ioctl_inode_get(ip
, &inode
);
1684 inode
.flags
|= HAMMER2IOC_INODE_FLAG_CHECK
;
1685 inode
.ip_data
.meta
.check_algo
= check_algo
;
1686 error
= hammer2_ioctl_inode_set(ip
, &inode
);
1696 hammer2_inode_setcomp(struct m_vnode
*dvp
, const char *f
)
1698 hammer2_ioc_inode_t inode
;
1699 hammer2_inode_t
*ip
;
1701 char *o
, *p
, *name
, *comp_algo_str
, *comp_level_str
;
1703 const char *comps
[] = { "none", "autozero", "lz4", "zlib", };
1704 int comp_algo_idx
, comp_level_idx
, error
;
1705 uint8_t comp_algo
, comp_level
;
1707 assert(strlen(f
) > 0);
1710 p
= strrchr(p
, ':');
1714 *p
++ = 0; /* NULL terminate comp_algo_str */
1718 p
= strrchr(p
, ':');
1720 /* comp_level_str not specified */
1721 comp_algo_str
= comp_level_str
;
1722 comp_level_str
= NULL
;
1724 *p
++ = 0; /* NULL terminate path */
1729 error
= trim_slash(p
);
1732 if (strlen(p
) == 0 || strlen(comp_algo_str
) == 0)
1735 /* convert comp_algo_str to comp_algo_idx */
1736 comp_algo_idx
= nitems(comps
);
1737 while (--comp_algo_idx
>= 0)
1738 if (strcasecmp(comp_algo_str
, comps
[comp_algo_idx
]) == 0)
1740 if (comp_algo_idx
< 0) {
1741 if (strcasecmp(comp_algo_str
, "default") == 0) {
1742 comp_algo_str
= "lz4";
1743 comp_algo_idx
= HAMMER2_COMP_LZ4
;
1744 } else if (strcasecmp(comp_algo_str
, "disabled") == 0) {
1745 comp_algo_str
= "autozero";
1746 comp_algo_idx
= HAMMER2_COMP_AUTOZERO
;
1748 printf("invalid comp_algo_str: %s\n", comp_algo_str
);
1752 comp_algo
= HAMMER2_ENC_ALGO(comp_algo_idx
);
1754 /* convert comp_level_str to comp_level_idx */
1755 if (comp_level_str
== NULL
) {
1757 } else if (isdigit((int)comp_level_str
[0])) {
1758 comp_level_idx
= strtol(comp_level_str
, NULL
, 0);
1759 } else if (strcasecmp(comp_level_str
, "default") == 0) {
1762 printf("invalid comp_level_str: %s\n", comp_level_str
);
1765 if (comp_level_idx
) {
1766 switch (comp_algo
) {
1767 case HAMMER2_COMP_ZLIB
:
1768 if (comp_level_idx
< 6 || comp_level_idx
> 9) {
1769 printf("unsupported comp_level %d for %s\n",
1770 comp_level_idx
, comp_algo_str
);
1775 printf("unsupported comp_level %d for %s\n",
1776 comp_level_idx
, comp_algo_str
);
1780 comp_level
= HAMMER2_ENC_LEVEL(comp_level_idx
);
1781 printf("change %s to algo %d (%s) level %d\n",
1782 p
, comp_algo
, comp_algo_str
, comp_level_idx
);
1784 while ((p
= strchr(p
, '/')) != NULL
) {
1785 *p
++ = 0; /* NULL terminate name */
1786 if (!strcmp(name
, ".")) {
1791 error
= hammer2_nresolve(dvp
, &vp
, name
, strlen(name
));
1796 switch (ip
->meta
.type
) {
1797 case HAMMER2_OBJTYPE_DIRECTORY
:
1799 case HAMMER2_OBJTYPE_SOFTLINK
:
1800 bzero(tmp
, sizeof(tmp
));
1801 error
= hammer2_readlink(vp
, tmp
, sizeof(tmp
));
1804 if (!is_supported_link(tmp
))
1806 strlcat(tmp
, "/", sizeof(tmp
));
1807 strlcat(tmp
, p
, sizeof(tmp
));
1808 error
= trim_slash(tmp
);
1821 error
= hammer2_nresolve(dvp
, &vp
, name
, strlen(name
));
1826 bzero(&inode
, sizeof(inode
));
1827 error
= hammer2_ioctl_inode_get(ip
, &inode
);
1831 inode
.flags
|= HAMMER2IOC_INODE_FLAG_COMP
;
1832 inode
.ip_data
.meta
.comp_algo
= comp_algo
| comp_level
;
1833 error
= hammer2_ioctl_inode_set(ip
, &inode
);
1843 hammer2_bulkfree(struct m_vnode
*vp
)
1845 hammer2_ioc_bulkfree_t bfi
;
1847 size_t usermem_size
= sizeof(usermem
);
1849 bzero(&bfi
, sizeof(bfi
));
1851 if (sysctlbyname("hw.usermem", &usermem
, &usermem_size
, NULL
, 0) == 0)
1852 bfi
.size
= usermem
/ 16;
1855 if (bfi
.size
< 8192 * 1024)
1856 bfi
.size
= 8192 * 1024;
1858 return hammer2_ioctl_bulkfree_scan(VTOI(vp
), &bfi
);
1862 hammer2_destroy_path(struct m_vnode
*dvp
, const char *f
)
1864 hammer2_ioc_destroy_t destroy
;
1865 hammer2_inode_t
*ip
;
1871 assert(strlen(f
) > 0);
1872 o
= p
= name
= strdup(f
);
1874 error
= trim_slash(p
);
1880 while ((p
= strchr(p
, '/')) != NULL
) {
1881 *p
++ = 0; /* NULL terminate name */
1882 if (!strcmp(name
, ".")) {
1887 error
= hammer2_nresolve(dvp
, &vp
, name
, strlen(name
));
1892 switch (ip
->meta
.type
) {
1893 case HAMMER2_OBJTYPE_DIRECTORY
:
1895 case HAMMER2_OBJTYPE_SOFTLINK
:
1896 bzero(tmp
, sizeof(tmp
));
1897 error
= hammer2_readlink(vp
, tmp
, sizeof(tmp
));
1900 if (!is_supported_link(tmp
))
1902 strlcat(tmp
, "/", sizeof(tmp
));
1903 strlcat(tmp
, p
, sizeof(tmp
));
1904 error
= trim_slash(tmp
);
1917 /* XXX When does (or why does not) ioctl modify this inode ? */
1918 hammer2_inode_modify(VTOI(dvp
));
1920 bzero(&destroy
, sizeof(destroy
));
1921 destroy
.cmd
= HAMMER2_DELETE_FILE
;
1922 snprintf(destroy
.path
, sizeof(destroy
.path
), "%s", name
);
1927 error
= hammer2_ioctl_destroy(VTOI(dvp
), &destroy
);
1929 printf("%s\n", strerror(error
));
1938 hammer2_destroy_inum(struct m_vnode
*vp
, hammer2_tid_t inum
)
1940 hammer2_ioc_destroy_t destroy
;
1943 bzero(&destroy
, sizeof(destroy
));
1944 destroy
.cmd
= HAMMER2_DELETE_INUM
;
1945 destroy
.inum
= inum
;
1947 printf("%jd\t", (intmax_t)destroy
.inum
);
1950 error
= hammer2_ioctl_destroy(VTOI(vp
), &destroy
);
1952 printf("%s\n", strerror(error
));
1960 hammer2_growfs(struct m_vnode
*vp
, hammer2_off_t size
)
1962 hammer2_ioc_growfs_t growfs
;
1965 bzero(&growfs
, sizeof(growfs
));
1968 error
= hammer2_ioctl_growfs(VTOI(vp
), &growfs
, NULL
);
1970 if (growfs
.modified
)
1971 printf("grown to %016jx\n", (intmax_t)growfs
.size
);
1973 printf("no size change - %016jx\n",
1974 (intmax_t)growfs
.size
);
1981 hammer2_readx(struct m_vnode
*dvp
, const char *dir
, const char *f
)
1983 hammer2_inode_t
*ip
;
1985 char *o
, *p
, *name
, *buf
;
1986 char tmp
[PATH_MAX
], out
[PATH_MAX
];
1994 assert(strlen(f
) > 0);
1995 o
= p
= name
= strdup(f
);
1997 error
= trim_slash(p
);
2003 while ((p
= strchr(p
, '/')) != NULL
) {
2004 *p
++ = 0; /* NULL terminate name */
2005 if (!strcmp(name
, ".")) {
2010 error
= hammer2_nresolve(dvp
, &vp
, name
, strlen(name
));
2015 switch (ip
->meta
.type
) {
2016 case HAMMER2_OBJTYPE_DIRECTORY
:
2018 case HAMMER2_OBJTYPE_SOFTLINK
:
2019 bzero(tmp
, sizeof(tmp
));
2020 error
= hammer2_readlink(vp
, tmp
, sizeof(tmp
));
2023 if (!is_supported_link(tmp
))
2025 strlcat(tmp
, "/", sizeof(tmp
));
2026 strlcat(tmp
, p
, sizeof(tmp
));
2027 error
= trim_slash(tmp
);
2040 error
= hammer2_nresolve(dvp
, &vp
, name
, strlen(name
));
2045 snprintf(out
, sizeof(out
), "%s/%s", dir
, name
);
2046 fd
= open(out
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0666);
2048 err(1, "failed to create %s", out
);
2050 buf
= calloc(1, HAMMER2_PBUFSIZE
);
2051 resid
= ip
->meta
.size
;
2055 bzero(buf
, HAMMER2_PBUFSIZE
);
2056 error
= hammer2_read(vp
, buf
, HAMMER2_PBUFSIZE
, offset
);
2058 errx(1, "failed to read from %s", name
);
2060 n
= resid
>= HAMMER2_PBUFSIZE
? HAMMER2_PBUFSIZE
: resid
;
2061 error
= write(fd
, buf
, n
);
2063 err(1, "failed to write to %s", out
);
2064 else if (error
!= n
)
2068 offset
+= HAMMER2_PBUFSIZE
;
2080 assert_trim_slash(const char *input
, const char *expected
)
2085 strlcpy(tmp
, input
, sizeof(tmp
));
2086 error
= trim_slash(tmp
);
2088 errx(1, "input \"%s\" error %d", input
, error
);
2090 if (strncmp(tmp
, expected
, sizeof(tmp
)))
2091 errx(1, "input \"%s\" result \"%s\" vs expected \"%s\"",
2092 input
, tmp
, expected
);
2096 unittest_trim_slash(void)
2098 assert_trim_slash("", "");
2099 assert_trim_slash("/", "");
2100 assert_trim_slash("//", "");
2101 assert_trim_slash("///", "");
2103 assert_trim_slash("makefs", "makefs");
2104 assert_trim_slash("/makefs", "makefs");
2105 assert_trim_slash("//makefs", "makefs");
2106 assert_trim_slash("makefs/", "makefs");
2107 assert_trim_slash("makefs//", "makefs");
2108 assert_trim_slash("/makefs/", "makefs");
2109 assert_trim_slash("//makefs//", "makefs");
2111 assert_trim_slash("sys/vfs/hammer2", "sys/vfs/hammer2");
2112 assert_trim_slash("/sys/vfs/hammer2", "sys/vfs/hammer2");
2113 assert_trim_slash("//sys/vfs/hammer2", "sys/vfs/hammer2");
2114 assert_trim_slash("///sys/vfs/hammer2", "sys/vfs/hammer2");
2115 assert_trim_slash("sys/vfs/hammer2/", "sys/vfs/hammer2");
2116 assert_trim_slash("sys/vfs/hammer2//", "sys/vfs/hammer2");
2117 assert_trim_slash("sys/vfs/hammer2///", "sys/vfs/hammer2");
2118 assert_trim_slash("/sys/vfs/hammer2/", "sys/vfs/hammer2");
2119 assert_trim_slash("//sys//vfs//hammer2//", "sys/vfs/hammer2");
2120 assert_trim_slash("///sys///vfs///hammer2///", "sys/vfs/hammer2");
2122 APRINTF("success\n");