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>
44 #include <sys/dirent.h>
61 #define APRINTF(X, ...) \
62 printf("%s: " X, __func__, ## __VA_ARGS__)
64 static void hammer2_parse_pfs_opts(const char *, fsinfo_t
*);
65 static void hammer2_parse_inode_opts(const char *, fsinfo_t
*);
66 static void hammer2_dump_fsinfo(fsinfo_t
*);
67 static int hammer2_create_image(const char *, fsinfo_t
*);
68 static int hammer2_populate_dir(struct m_vnode
*, const char *, fsnode
*,
69 fsnode
*, fsinfo_t
*, int);
70 static void hammer2_validate(const char *, fsnode
*, fsinfo_t
*);
71 static void hammer2_size_dir(fsnode
*, fsinfo_t
*);
72 static int hammer2_write_file(struct m_vnode
*, const char *, fsnode
*);
73 static int hammer2_version_get(struct m_vnode
*);
74 static int hammer2_pfs_get(struct m_vnode
*);
75 static int hammer2_pfs_lookup(struct m_vnode
*, const char *);
76 static int hammer2_pfs_create(struct m_vnode
*, const char *);
77 static int hammer2_pfs_delete(struct m_vnode
*, const char *);
78 static int hammer2_pfs_snapshot(struct m_vnode
*, const char *, const char *);
79 static int hammer2_inode_getx(struct m_vnode
*, const char *);
80 static int hammer2_inode_setcheck(struct m_vnode
*, const char *);
81 static int hammer2_inode_setcomp(struct m_vnode
*, const char *);
82 static int hammer2_bulkfree(struct m_vnode
*);
83 static int hammer2_destroy_path(struct m_vnode
*, const char *);
84 static int hammer2_destroy_inum(struct m_vnode
*, hammer2_tid_t
);
85 static int hammer2_growfs(struct m_vnode
*, hammer2_off_t
);
87 static int hammer2_readx_handle(struct m_vnode
*, const char *, const char *,
88 struct hammer2_linkq
*);
89 static int hammer2_readx(struct m_vnode
*, const char *, const char *);
90 static void unittest_trim_slash(void);
92 fsnode
*hammer2_curnode
;
95 hammer2_prep_opts(fsinfo_t
*fsopts
)
97 hammer2_makefs_options_t
*h2_opt
= ecalloc(1, sizeof(*h2_opt
));
98 hammer2_mkfs_options_t
*opt
= &h2_opt
->mkfs_options
;
100 const option_t hammer2_options
[] = {
101 /* newfs_hammer2(8) compatible options */
102 { 'b', "BootAreaSize", NULL
, OPT_STRBUF
, 0, 0, "boot area size" },
103 { 'r', "AuxAreaSize", NULL
, OPT_STRBUF
, 0, 0, "aux area size" },
104 { 'V', "Hammer2Version", NULL
, OPT_STRBUF
, 0, 0, "file system version" },
105 { 'L', "Label", NULL
, OPT_STRBUF
, 0, 0, "PFS label" },
106 /* makefs(8) specific options */
107 { 'm', "MountLabel", NULL
, OPT_STRBUF
, 0, 0, "destination PFS label" },
108 { 'v', "NumVolhdr", &h2_opt
->num_volhdr
, OPT_INT32
,
109 1, HAMMER2_NUM_VOLHDRS
, "number of volume headers" },
110 { 'c', "CompressionType", NULL
, OPT_STRBUF
, 0, 0, "compression type" },
111 { 'C', "CheckType", NULL
, OPT_STRBUF
, 0, 0, "check type" },
112 { 'd', "Hammer2Debug", NULL
, OPT_STRBUF
, 0, 0, "debug tunable" },
113 { 'E', "EmergencyMode", &h2_opt
->emergency_mode
, OPT_BOOL
, 0, 0,
115 { 'P', "PFS", NULL
, OPT_STRBUF
, 0, 0, "offline PFS" },
116 { 'I', "Inode", NULL
, OPT_STRBUF
, 0, 0, "offline inode" },
117 { 'B', "Bulkfree", NULL
, OPT_STRBUF
, 0, 0, "offline bulkfree" },
118 { 'D', "Destroy", NULL
, OPT_STRBUF
, 0, 0, "offline destroy" },
119 { 'G', "Growfs", NULL
, OPT_STRBUF
, 0, 0, "offline growfs" },
120 { 'R', "Read", NULL
, OPT_STRBUF
, 0, 0, "offline read" },
124 hammer2_mkfs_init(opt
);
126 assert(opt
->CompType
== HAMMER2_COMP_NEWFS_DEFAULT
);
127 assert(opt
->CheckType
== HAMMER2_CHECK_XXHASH64
);
129 /* force debug mode for mkfs */
132 fsopts
->fs_specific
= h2_opt
;
133 fsopts
->fs_options
= copy_opts(hammer2_options
);
134 fsopts
->sectorsize
= DEV_BSIZE
;
138 hammer2_cleanup_opts(fsinfo_t
*fsopts
)
140 hammer2_makefs_options_t
*h2_opt
= fsopts
->fs_specific
;
141 hammer2_mkfs_options_t
*opt
= &h2_opt
->mkfs_options
;
143 hammer2_mkfs_cleanup(opt
);
146 free(fsopts
->fs_options
);
150 hammer2_parse_opts(const char *option
, fsinfo_t
*fsopts
)
152 hammer2_makefs_options_t
*h2_opt
= fsopts
->fs_specific
;
153 hammer2_mkfs_options_t
*opt
= &h2_opt
->mkfs_options
;
155 option_t
*hammer2_options
= fsopts
->fs_options
;
156 char buf
[1024]; /* > HAMMER2_INODE_MAXNAME */
159 assert(option
!= NULL
);
160 assert(fsopts
!= NULL
);
162 if (debug
& DEBUG_FS_PARSE_OPTS
)
163 APRINTF("got `%s'\n", option
);
165 i
= set_option(hammer2_options
, option
, buf
, sizeof(buf
));
169 if (hammer2_options
[i
].name
== NULL
)
172 switch (hammer2_options
[i
].letter
) {
174 opt
->BootAreaSize
= getsize(buf
, HAMMER2_NEWFS_ALIGN
,
175 HAMMER2_BOOT_MAX_BYTES
, 2);
178 opt
->AuxAreaSize
= getsize(buf
, HAMMER2_NEWFS_ALIGN
,
179 HAMMER2_AUX_MAX_BYTES
, 2);
182 if (strlen(buf
) == 0) {
183 h2_opt
->ioctl_cmd
= HAMMER2IOC_VERSION_GET
;
185 opt
->Hammer2Version
= strtol(buf
, NULL
, 0);
186 if (opt
->Hammer2Version
< HAMMER2_VOL_VERSION_MIN
||
187 opt
->Hammer2Version
>= HAMMER2_VOL_VERSION_WIP
)
188 errx(1, "I don't understand how to format "
189 "HAMMER2 version %d",
190 opt
->Hammer2Version
);
194 h2_opt
->label_specified
= 1;
195 if (strcasecmp(buf
, "none") == 0)
197 if (opt
->NLabels
>= MAXLABELS
)
198 errx(1, "Limit of %d local labels", MAXLABELS
- 1);
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 opt
->Label
[opt
->NLabels
++] = strdup(buf
);
207 if (strlen(buf
) == 0)
208 errx(1, "Volume label '%s' cannot be 0-length", buf
);
209 if (strlen(buf
) >= HAMMER2_INODE_MAXNAME
)
210 errx(1, "Volume label '%s' is too long (%d chars max)",
211 buf
, HAMMER2_INODE_MAXNAME
- 1);
212 strlcpy(h2_opt
->mount_label
, buf
, sizeof(h2_opt
->mount_label
));
215 if (strlen(buf
) == 0)
216 errx(1, "Compression type '%s' cannot be 0-length", buf
);
217 if (strcasecmp(buf
, "none") == 0)
218 opt
->CompType
= HAMMER2_COMP_NONE
;
219 else if (strcasecmp(buf
, "autozero") == 0)
220 opt
->CompType
= HAMMER2_COMP_AUTOZERO
;
221 else if (strcasecmp(buf
, "lz4") == 0)
222 opt
->CompType
= HAMMER2_COMP_LZ4
;
223 else if (strcasecmp(buf
, "zlib") == 0)
224 opt
->CompType
= HAMMER2_COMP_ZLIB
;
226 errx(1, "Invalid compression type '%s'", buf
);
229 if (strlen(buf
) == 0)
230 errx(1, "Check type '%s' cannot be 0-length", buf
);
231 if (strcasecmp(buf
, "none") == 0)
232 opt
->CheckType
= HAMMER2_CHECK_NONE
;
233 else if (strcasecmp(buf
, "disabled") == 0)
234 opt
->CheckType
= HAMMER2_CHECK_DISABLED
;
235 else if (strcasecmp(buf
, "iscsi32") == 0)
236 opt
->CheckType
= HAMMER2_CHECK_ISCSI32
;
237 else if (strcasecmp(buf
, "xxhash64") == 0)
238 opt
->CheckType
= HAMMER2_CHECK_XXHASH64
;
239 else if (strcasecmp(buf
, "sha192") == 0)
240 opt
->CheckType
= HAMMER2_CHECK_SHA192
;
241 else if (strcasecmp(buf
, "freemap") == 0)
242 opt
->CheckType
= HAMMER2_CHECK_FREEMAP
;
244 errx(1, "Invalid check type '%s'", buf
);
247 hammer2_debug
= strtoll(buf
, NULL
, 0);
250 if (strlen(buf
) == 0)
251 errx(1, "PFS argument '%s' cannot be 0-length", buf
);
252 hammer2_parse_pfs_opts(buf
, fsopts
);
255 if (strlen(buf
) == 0)
256 errx(1, "Inode argument '%s' cannot be 0-length", buf
);
257 hammer2_parse_inode_opts(buf
, fsopts
);
260 h2_opt
->ioctl_cmd
= HAMMER2IOC_BULKFREE_SCAN
;
263 h2_opt
->ioctl_cmd
= HAMMER2IOC_DESTROY
;
264 if (strlen(buf
) == 0)
265 errx(1, "Destroy argument '%s' cannot be 0-length", buf
);
267 strlcpy(h2_opt
->destroy_path
, buf
,
268 sizeof(h2_opt
->destroy_path
));
269 } else if (strncmp(buf
, "0x", 2) == 0 ||
270 (buf
[0] >= '0' && buf
[0] <= '9')) {
271 h2_opt
->destroy_inum
= strtoull(buf
, NULL
, 0);
275 errx(1, "Invalid destroy argument %s", buf
);
279 h2_opt
->ioctl_cmd
= HAMMER2IOC_GROWFS
;
282 h2_opt
->ioctl_cmd
= HAMMER2IOC_READ
;
283 if (strlen(buf
) == 0)
284 errx(1, "Read argument '%s' cannot be 0-length", buf
);
285 strlcpy(h2_opt
->read_path
, buf
, sizeof(h2_opt
->read_path
));
291 if (hammer2_debug
&& h2_opt
->ioctl_cmd
)
292 unittest_trim_slash();
298 hammer2_makefs(const char *image
, const char *dir
, fsnode
*root
,
301 hammer2_makefs_options_t
*h2_opt
= fsopts
->fs_specific
;
303 struct hammer2_mount_info info
;
304 struct m_vnode devvp
, *vroot
;
305 hammer2_inode_t
*iroot
;
306 struct timeval start
;
309 /* ioctl commands could have NULL dir / root */
310 assert(image
!= NULL
);
311 assert(fsopts
!= NULL
);
313 if (debug
& DEBUG_FS_MAKEFS
)
314 APRINTF("image \"%s\" directory \"%s\" root %p\n",
317 /* validate tree and options */
319 hammer2_validate(dir
, root
, fsopts
);
320 TIMER_RESULTS(start
, "hammer2_validate");
322 if (h2_opt
->ioctl_cmd
) {
323 /* open existing image */
324 fsopts
->fd
= open(image
, O_RDWR
);
326 err(1, "failed to open `%s'", image
);
330 if (hammer2_create_image(image
, fsopts
) == -1)
331 errx(1, "image file `%s' not created", image
);
332 TIMER_RESULTS(start
, "hammer2_create_image");
334 assert(fsopts
->fd
> 0);
336 if (debug
& DEBUG_FS_MAKEFS
)
340 error
= hammer2_vfs_init();
342 errx(1, "failed to vfs init, error %d", error
);
345 memset(&devvp
, 0, sizeof(devvp
));
347 memset(&mp
, 0, sizeof(mp
));
348 memset(&info
, 0, sizeof(info
));
349 error
= hammer2_vfs_mount(&devvp
, &mp
, h2_opt
->mount_label
, &info
);
351 errx(1, "failed to mount, error %d", error
);
356 error
= hammer2_vfs_root(&mp
, &vroot
);
358 errx(1, "failed to get root vnode, error %d", error
);
363 printf("root inode inum %lld, mode 0%o, refs %d\n",
364 (long long)iroot
->meta
.inum
, iroot
->meta
.mode
, iroot
->refs
);
366 if (h2_opt
->emergency_mode
)
367 hammer2_ioctl_emerg_mode(iroot
, 1);
369 switch (h2_opt
->ioctl_cmd
) {
370 case HAMMER2IOC_VERSION_GET
:
371 printf("version get `%s'\n", image
);
373 error
= hammer2_version_get(vroot
);
375 errx(1, "version get `%s' failed '%s'", image
,
377 TIMER_RESULTS(start
, "hammer2_version_get");
379 case HAMMER2IOC_PFS_GET
:
380 printf("PFS %s `%s'\n", h2_opt
->pfs_cmd_name
, image
);
382 error
= hammer2_pfs_get(vroot
);
384 errx(1, "PFS %s`%s' failed '%s'", h2_opt
->pfs_cmd_name
,
385 image
, strerror(error
));
386 TIMER_RESULTS(start
, "hammer2_pfs_get");
388 case HAMMER2IOC_PFS_LOOKUP
:
389 printf("PFS %s `%s'\n", h2_opt
->pfs_cmd_name
, image
);
391 error
= hammer2_pfs_lookup(vroot
, h2_opt
->pfs_name
);
393 errx(1, "PFS %s`%s' failed '%s'", h2_opt
->pfs_cmd_name
,
394 image
, strerror(error
));
395 TIMER_RESULTS(start
, "hammer2_pfs_lookup");
397 case HAMMER2IOC_PFS_CREATE
:
398 printf("PFS %s `%s'\n", h2_opt
->pfs_cmd_name
, image
);
400 error
= hammer2_pfs_create(vroot
, h2_opt
->pfs_name
);
402 errx(1, "PFS %s`%s' failed '%s'", h2_opt
->pfs_cmd_name
,
403 image
, strerror(error
));
404 TIMER_RESULTS(start
, "hammer2_pfs_create");
406 case HAMMER2IOC_PFS_DELETE
:
407 printf("PFS %s `%s'\n", h2_opt
->pfs_cmd_name
, image
);
409 error
= hammer2_pfs_delete(vroot
, h2_opt
->pfs_name
);
411 errx(1, "PFS %s`%s' failed '%s'", h2_opt
->pfs_cmd_name
,
412 image
, strerror(error
));
413 TIMER_RESULTS(start
, "hammer2_pfs_delete");
415 case HAMMER2IOC_PFS_SNAPSHOT
:
416 printf("PFS %s `%s'\n", h2_opt
->pfs_cmd_name
, image
);
418 error
= hammer2_pfs_snapshot(vroot
, h2_opt
->pfs_name
,
419 h2_opt
->mount_label
);
421 errx(1, "PFS %s`%s' failed '%s'", h2_opt
->pfs_cmd_name
,
422 image
, strerror(error
));
423 TIMER_RESULTS(start
, "hammer2_pfs_snapshot");
425 case HAMMER2IOC_INODE_GET
:
426 printf("inode %s `%s'\n", h2_opt
->inode_cmd_name
, image
);
428 error
= hammer2_inode_getx(vroot
, h2_opt
->inode_path
);
430 errx(1, "inode %s `%s' failed '%s'",
431 h2_opt
->inode_cmd_name
, image
, strerror(error
));
432 TIMER_RESULTS(start
, "hammer2_inode_getx");
434 case HAMMER2IOC_INODE_SET
:
435 printf("inode %s `%s'\n", h2_opt
->inode_cmd_name
, image
);
437 if (!strcmp(h2_opt
->inode_cmd_name
, "setcheck")) {
438 error
= hammer2_inode_setcheck(vroot
,
441 errx(1, "inode %s `%s' failed '%s'",
442 h2_opt
->inode_cmd_name
, image
,
444 } else if (!strcmp(h2_opt
->inode_cmd_name
, "setcomp")) {
445 error
= hammer2_inode_setcomp(vroot
,
448 errx(1, "inode %s `%s' failed '%s'",
449 h2_opt
->inode_cmd_name
, image
,
454 TIMER_RESULTS(start
, "hammer2_inode_setx");
456 case HAMMER2IOC_BULKFREE_SCAN
:
457 printf("bulkfree `%s'\n", image
);
459 error
= hammer2_bulkfree(vroot
);
461 errx(1, "bulkfree `%s' failed '%s'", image
,
463 TIMER_RESULTS(start
, "hammer2_bulkfree");
465 case HAMMER2IOC_DESTROY
:
467 if (strlen(h2_opt
->destroy_path
)) {
468 printf("destroy `%s' in `%s'\n",
469 h2_opt
->destroy_path
, image
);
470 error
= hammer2_destroy_path(vroot
,
471 h2_opt
->destroy_path
);
473 errx(1, "destroy `%s' in `%s' failed '%s'",
474 h2_opt
->destroy_path
, image
,
477 printf("destroy %lld in `%s'\n",
478 (long long)h2_opt
->destroy_inum
, image
);
479 error
= hammer2_destroy_inum(vroot
,
480 h2_opt
->destroy_inum
);
482 errx(1, "destroy %lld in `%s' failed '%s'",
483 (long long)h2_opt
->destroy_inum
, image
,
486 TIMER_RESULTS(start
, "hammer2_destroy");
488 case HAMMER2IOC_GROWFS
:
489 printf("growfs `%s'\n", image
);
491 error
= hammer2_growfs(vroot
, h2_opt
->image_size
);
493 errx(1, "growfs `%s' failed '%s'", image
,
495 TIMER_RESULTS(start
, "hammer2_growfs");
497 case HAMMER2IOC_READ
:
498 printf("read `%s'\n", image
);
500 error
= hammer2_readx(vroot
, dir
, h2_opt
->read_path
);
502 errx(1, "read `%s' failed '%s'", image
,
504 TIMER_RESULTS(start
, "hammer2_readx");
507 printf("populating `%s'\n", image
);
509 if (hammer2_populate_dir(vroot
, dir
, root
, root
, fsopts
, 0))
510 errx(1, "image file `%s' not populated", image
);
511 TIMER_RESULTS(start
, "hammer2_populate_dir");
516 error
= hammer2_vfs_unmount(&mp
, 0);
518 errx(1, "failed to unmount, error %d", error
);
520 /* check leaked resource */
522 printf("XXX %lld vnode left\n", (long long)vnode_count
);
523 if (hammer2_chain_allocs
)
524 printf("XXX %ld chain left\n", hammer2_chain_allocs
);
528 error
= hammer2_vfs_uninit();
530 errx(1, "failed to vfs uninit, error %d", error
);
532 if (close(fsopts
->fd
) == -1)
533 err(1, "closing `%s'", image
);
536 printf("image `%s' complete\n", image
);
539 /* end of public functions */
542 hammer2_parse_pfs_opts(const char *buf
, fsinfo_t
*fsopts
)
544 hammer2_makefs_options_t
*h2_opt
= fsopts
->fs_specific
;
557 if (!strcmp(o
, "get") || !strcmp(o
, "list")) {
558 h2_opt
->ioctl_cmd
= HAMMER2IOC_PFS_GET
;
559 } else if (!strcmp(o
, "lookup")) {
560 if (n
== 0 || n
> NAME_MAX
)
561 errx(1, "invalid PFS name \"%s\"", p
);
562 h2_opt
->ioctl_cmd
= HAMMER2IOC_PFS_LOOKUP
;
563 } else if (!strcmp(o
, "create")) {
564 if (n
== 0 || n
> NAME_MAX
)
565 errx(1, "invalid PFS name \"%s\"", p
);
566 h2_opt
->ioctl_cmd
= HAMMER2IOC_PFS_CREATE
;
567 } else if (!strcmp(o
, "delete")) {
568 if (n
== 0 || n
> NAME_MAX
)
569 errx(1, "invalid PFS name \"%s\"", p
);
570 h2_opt
->ioctl_cmd
= HAMMER2IOC_PFS_DELETE
;
571 } else if (!strcmp(o
, "snapshot")) {
573 errx(1, "invalid PFS name \"%s\"", p
);
574 h2_opt
->ioctl_cmd
= HAMMER2IOC_PFS_SNAPSHOT
;
576 errx(1, "invalid PFS command \"%s\"", o
);
579 strlcpy(h2_opt
->pfs_cmd_name
, o
, sizeof(h2_opt
->pfs_cmd_name
));
581 strlcpy(h2_opt
->pfs_name
, p
, sizeof(h2_opt
->pfs_name
));
587 hammer2_parse_inode_opts(const char *buf
, fsinfo_t
*fsopts
)
589 hammer2_makefs_options_t
*h2_opt
= fsopts
->fs_specific
;
602 if (!strcmp(o
, "get")) {
603 if (n
== 0 || n
> PATH_MAX
)
604 errx(1, "invalid file path \"%s\"", p
);
605 h2_opt
->ioctl_cmd
= HAMMER2IOC_INODE_GET
;
606 } else if (!strcmp(o
, "setcheck")) {
607 if (n
== 0 || n
> PATH_MAX
- 10)
608 errx(1, "invalid argument \"%s\"", p
);
609 h2_opt
->ioctl_cmd
= HAMMER2IOC_INODE_SET
;
610 } else if (!strcmp(o
, "setcomp")) {
611 if (n
== 0 || n
> PATH_MAX
- 10)
612 errx(1, "invalid argument \"%s\"", p
);
613 h2_opt
->ioctl_cmd
= HAMMER2IOC_INODE_SET
;
615 errx(1, "invalid inode command \"%s\"", o
);
618 strlcpy(h2_opt
->inode_cmd_name
, o
, sizeof(h2_opt
->inode_cmd_name
));
620 strlcpy(h2_opt
->inode_path
, p
, sizeof(h2_opt
->inode_path
));
626 hammer2_image_size(fsinfo_t
*fsopts
)
628 hammer2_makefs_options_t
*h2_opt
= fsopts
->fs_specific
;
629 hammer2_off_t image_size
, used_size
= 0;
630 int num_level1
, delta_num_level1
;
632 /* use 4 volume headers by default */
633 num_level1
= h2_opt
->num_volhdr
* 2; /* default 4 x 2 */
634 assert(num_level1
!= 0);
635 assert(num_level1
<= 8);
637 /* add 4MiB segment for each level1 */
638 used_size
+= HAMMER2_ZONE_SEG64
* num_level1
;
640 /* add boot/aux area, but exact size unknown at this point */
641 used_size
+= HAMMER2_BOOT_NOM_BYTES
+ HAMMER2_AUX_NOM_BYTES
;
644 used_size
+= fsopts
->size
;
646 /* XXX add extra level1 for meta data and indirect blocks */
647 used_size
+= HAMMER2_FREEMAP_LEVEL1_SIZE
;
649 /* XXX add extra level1 for safety */
650 if (used_size
> HAMMER2_FREEMAP_LEVEL1_SIZE
* 10)
651 used_size
+= HAMMER2_FREEMAP_LEVEL1_SIZE
;
653 /* use 8GiB image size by default */
654 image_size
= HAMMER2_FREEMAP_LEVEL1_SIZE
* num_level1
;
655 printf("trying default image size %s\n", sizetostr(image_size
));
657 /* adjust if image size isn't large enough */
658 if (used_size
> image_size
) {
659 /* determine extra level1 needed */
660 delta_num_level1
= howmany(used_size
- image_size
,
661 HAMMER2_FREEMAP_LEVEL1_SIZE
);
663 /* adjust used size with 4MiB segment for each extra level1 */
664 used_size
+= HAMMER2_ZONE_SEG64
* delta_num_level1
;
666 /* adjust image size with extra level1 */
667 image_size
+= HAMMER2_FREEMAP_LEVEL1_SIZE
* delta_num_level1
;
668 printf("trying adjusted image size %s\n",
669 sizetostr(image_size
));
671 if (used_size
> image_size
)
672 errx(1, "invalid used_size %lld > image_size %lld",
673 (long long)used_size
, (long long)image_size
);
680 hammer2_label_name(int label_type
)
682 switch (label_type
) {
683 case HAMMER2_LABEL_NONE
:
685 case HAMMER2_LABEL_BOOT
:
687 case HAMMER2_LABEL_ROOT
:
689 case HAMMER2_LABEL_DATA
:
698 hammer2_validate(const char *dir
, fsnode
*root
, fsinfo_t
*fsopts
)
700 hammer2_makefs_options_t
*h2_opt
= fsopts
->fs_specific
;
701 hammer2_mkfs_options_t
*opt
= &h2_opt
->mkfs_options
;
702 hammer2_off_t image_size
= 0, minsize
, maxsize
;
705 /* ioctl commands could have NULL dir / root */
706 assert(fsopts
!= NULL
);
708 if (debug
& DEBUG_FS_VALIDATE
) {
709 APRINTF("before defaults set:\n");
710 hammer2_dump_fsinfo(fsopts
);
713 /* makefs only supports "DATA" for default PFS label */
714 if (!h2_opt
->label_specified
) {
715 opt
->DefaultLabelType
= HAMMER2_LABEL_DATA
;
716 s
= hammer2_label_name(opt
->DefaultLabelType
);
717 printf("using default label \"%s\"\n", s
);
720 /* set default mount PFS label */
721 if (!strcmp(h2_opt
->mount_label
, "")) {
722 s
= hammer2_label_name(HAMMER2_LABEL_DATA
);
723 strlcpy(h2_opt
->mount_label
, s
, sizeof(h2_opt
->mount_label
));
724 printf("using default mount label \"%s\"\n", s
);
727 /* set default number of volume headers */
728 if (!h2_opt
->num_volhdr
) {
729 h2_opt
->num_volhdr
= HAMMER2_NUM_VOLHDRS
;
730 printf("using default %d volume headers\n", h2_opt
->num_volhdr
);
733 /* done if ioctl commands */
734 if (h2_opt
->ioctl_cmd
) {
735 if (h2_opt
->ioctl_cmd
== HAMMER2IOC_GROWFS
)
736 goto ignore_size_dir
;
741 /* calculate data size */
742 if (fsopts
->size
!= 0)
743 fsopts
->size
= 0; /* shouldn't reach here to begin with */
745 errx(1, "fsnode tree not constructed");
746 hammer2_size_dir(root
, fsopts
);
747 printf("estimated data size %s from %lld inode\n",
748 sizetostr(fsopts
->size
), (long long)fsopts
->inodes
);
750 /* determine image size from data size */
751 image_size
= hammer2_image_size(fsopts
);
752 assert((image_size
& HAMMER2_FREEMAP_LEVEL1_MASK
) == 0);
754 minsize
= roundup(fsopts
->minsize
, HAMMER2_FREEMAP_LEVEL1_SIZE
);
755 maxsize
= roundup(fsopts
->maxsize
, HAMMER2_FREEMAP_LEVEL1_SIZE
);
756 if (image_size
< minsize
)
757 image_size
= minsize
;
758 else if (maxsize
> 0 && image_size
> maxsize
)
759 errx(1, "`%s' size of %lld is larger than the maxsize of %lld",
760 dir
, (long long)image_size
, (long long)maxsize
);
762 assert((image_size
& HAMMER2_FREEMAP_LEVEL1_MASK
) == 0);
763 h2_opt
->image_size
= image_size
;
764 printf("using %s image size\n", sizetostr(h2_opt
->image_size
));
766 if (debug
& DEBUG_FS_VALIDATE
) {
767 APRINTF("after defaults set:\n");
768 hammer2_dump_fsinfo(fsopts
);
773 hammer2_dump_fsinfo(fsinfo_t
*fsopts
)
775 hammer2_makefs_options_t
*h2_opt
= fsopts
->fs_specific
;
776 hammer2_mkfs_options_t
*opt
= &h2_opt
->mkfs_options
;
780 assert(fsopts
!= NULL
);
782 APRINTF("fsinfo_t at %p\n", fsopts
);
784 printf("\tinodes %lld\n", (long long)fsopts
->inodes
);
785 printf("\tsize %lld, minsize %lld, maxsize %lld\n",
786 (long long)fsopts
->size
,
787 (long long)fsopts
->minsize
,
788 (long long)fsopts
->maxsize
);
790 printf("\thammer2_debug 0x%x\n", hammer2_debug
);
792 printf("\tlabel_specified %d\n", h2_opt
->label_specified
);
793 printf("\tmount_label \"%s\"\n", h2_opt
->mount_label
);
794 printf("\tnum_volhdr %d\n", h2_opt
->num_volhdr
);
795 printf("\tioctl_cmd %ld\n", h2_opt
->ioctl_cmd
);
796 printf("\temergency_mode %d\n", h2_opt
->emergency_mode
);
797 printf("\tpfs_cmd_name \"%s\"\n", h2_opt
->pfs_cmd_name
);
798 printf("\tpfs_name \"%s\"\n", h2_opt
->pfs_name
);
799 printf("\tinode_cmd_name \"%s\"\n", h2_opt
->inode_cmd_name
);
800 printf("\tinode_path \"%s\"\n", h2_opt
->inode_path
);
801 printf("\tdestroy_path \"%s\"\n", h2_opt
->destroy_path
);
802 printf("\tdestroy_inum %lld\n", (long long)h2_opt
->destroy_inum
);
803 printf("\tread_path \"%s\"\n", h2_opt
->read_path
);
804 printf("\timage_size 0x%llx\n", (long long)h2_opt
->image_size
);
806 printf("\tHammer2Version %d\n", opt
->Hammer2Version
);
807 printf("\tBootAreaSize 0x%jx\n", opt
->BootAreaSize
);
808 printf("\tAuxAreaSize 0x%jx\n", opt
->AuxAreaSize
);
809 printf("\tNLabels %d\n", opt
->NLabels
);
810 printf("\tCompType %d\n", opt
->CompType
);
811 printf("\tCheckType %d\n", opt
->CheckType
);
812 printf("\tDefaultLabelType %d\n", opt
->DefaultLabelType
);
813 printf("\tDebugOpt %d\n", opt
->DebugOpt
);
816 hammer2_uuid_to_str(&opt
->Hammer2_FSType
, &s
);
817 printf("\tHammer2_FSType \"%s\"\n", s
);
819 hammer2_uuid_to_str(&opt
->Hammer2_VolFSID
, &s
);
820 printf("\tHammer2_VolFSID \"%s\"\n", s
);
822 hammer2_uuid_to_str(&opt
->Hammer2_SupCLID
, &s
);
823 printf("\tHammer2_SupCLID \"%s\"\n", s
);
825 hammer2_uuid_to_str(&opt
->Hammer2_SupFSID
, &s
);
826 printf("\tHammer2_SupFSID \"%s\"\n", s
);
828 for (i
= 0; i
< opt
->NLabels
; i
++) {
829 printf("\tLabel[%d] \"%s\"\n", i
, opt
->Label
[i
]);
831 hammer2_uuid_to_str(&opt
->Hammer2_PfsCLID
[i
], &s
);
832 printf("\t Hammer2_PfsCLID[%d] \"%s\"\n", i
, s
);
834 hammer2_uuid_to_str(&opt
->Hammer2_PfsFSID
[i
], &s
);
835 printf("\t Hammer2_PfsFSID[%d] \"%s\"\n", i
, s
);
842 hammer2_create_image(const char *image
, fsinfo_t
*fsopts
)
844 hammer2_makefs_options_t
*h2_opt
= fsopts
->fs_specific
;
845 hammer2_mkfs_options_t
*opt
= &h2_opt
->mkfs_options
;
846 char *av
[] = { (char *)image
, }; /* XXX support multi-volumes */
848 int i
, bufsize
, oflags
;
851 assert(image
!= NULL
);
852 assert(fsopts
!= NULL
);
855 oflags
= O_RDWR
| O_CREAT
;
856 if (fsopts
->offset
== 0)
858 if ((fsopts
->fd
= open(image
, oflags
, 0666)) == -1) {
859 warn("can't open `%s' for writing", image
);
864 bufsize
= HAMMER2_PBUFSIZE
;
865 bufrem
= h2_opt
->image_size
;
866 if (fsopts
->sparse
) {
867 if (ftruncate(fsopts
->fd
, bufrem
) == -1) {
868 warn("sparse option disabled");
872 if (fsopts
->sparse
) {
873 /* File truncated at bufrem. Remaining is 0 */
877 if (debug
& DEBUG_FS_CREATE_IMAGE
)
878 APRINTF("zero-ing image `%s', %lld sectors, "
879 "using %d byte chunks\n",
880 image
, (long long)bufrem
, bufsize
);
881 buf
= ecalloc(1, bufsize
);
884 if (fsopts
->offset
!= 0) {
885 if (lseek(fsopts
->fd
, fsopts
->offset
, SEEK_SET
) == -1) {
893 i
= write(fsopts
->fd
, buf
, MIN(bufsize
, bufrem
));
895 warn("zeroing image, %lld bytes to go",
905 /* make the file system */
906 if (debug
& DEBUG_FS_CREATE_IMAGE
)
907 APRINTF("calling mkfs(\"%s\", ...)\n", image
);
908 hammer2_mkfs(1, av
, opt
); /* success if returned */
914 hammer2_phys_size(off_t size
)
916 off_t radix_size
, phys_size
= 0;
919 if (size
> HAMMER2_PBUFSIZE
) {
920 phys_size
+= rounddown(size
, HAMMER2_PBUFSIZE
);
921 size
= size
% HAMMER2_PBUFSIZE
;
924 for (i
= HAMMER2_RADIX_MIN
; i
<= HAMMER2_RADIX_MAX
; i
++) {
925 radix_size
= 1UL << i
;
926 if (radix_size
>= size
) {
927 phys_size
+= radix_size
;
935 /* calculate data size */
937 hammer2_size_dir(fsnode
*root
, fsinfo_t
*fsopts
)
941 assert(fsopts
!= NULL
);
943 if (debug
& DEBUG_FS_SIZE_DIR
)
944 APRINTF("entry: bytes %lld inodes %lld\n",
945 (long long)fsopts
->size
, (long long)fsopts
->inodes
);
947 for (node
= root
; node
!= NULL
; node
= node
->next
) {
948 if (node
== root
) { /* we're at "." */
949 assert(strcmp(node
->name
, ".") == 0);
950 } else if ((node
->inode
->flags
& FI_SIZED
) == 0) {
951 /* don't count duplicate names */
952 node
->inode
->flags
|= FI_SIZED
;
953 if (debug
& DEBUG_FS_SIZE_DIR_NODE
)
954 APRINTF("`%s' size %lld\n",
956 (long long)node
->inode
->st
.st_size
);
958 fsopts
->size
+= sizeof(hammer2_inode_data_t
);
959 if (node
->type
== S_IFREG
) {
960 size_t st_size
= node
->inode
->st
.st_size
;
961 if (st_size
> HAMMER2_EMBEDDED_BYTES
)
962 fsopts
->size
+= hammer2_phys_size(st_size
);
963 } else if (node
->type
== S_IFLNK
) {
964 size_t nlen
= strlen(node
->symlink
);
965 if (nlen
> HAMMER2_EMBEDDED_BYTES
)
966 fsopts
->size
+= hammer2_phys_size(nlen
);
969 if (node
->type
== S_IFDIR
)
970 hammer2_size_dir(node
->child
, fsopts
);
973 if (debug
& DEBUG_FS_SIZE_DIR
)
974 APRINTF("exit: size %lld inodes %lld\n",
975 (long long)fsopts
->size
, (long long)fsopts
->inodes
);
979 hammer2_print(const struct m_vnode
*dvp
, const struct m_vnode
*vp
,
980 const fsnode
*node
, int depth
, const char *msg
)
982 if (debug
& DEBUG_FS_POPULATE
) {
984 int indent
= depth
* 2;
986 if (S_ISDIR(node
->type
))
988 else if (S_ISREG(node
->type
))
990 else if (S_ISLNK(node
->type
))
992 else if (S_ISFIFO(node
->type
))
996 printf("%*.*s", indent
, indent
, "");
997 printf("dvp=%p/%d vp=%p/%d \"%s\" %s %s\n",
998 dvp
, dvp
? VTOI(dvp
)->refs
: 0,
999 vp
, vp
? VTOI(vp
)->refs
: 0,
1000 node
->name
, type
, msg
);
1003 if (S_ISDIR(node
->type
))
1005 else if (S_ISREG(node
->type
))
1007 else if (S_ISLNK(node
->type
))
1009 else if (S_ISFIFO(node
->type
))
1020 hammer2_populate_dir(struct m_vnode
*dvp
, const char *dir
, fsnode
*root
,
1021 fsnode
*parent
, fsinfo_t
*fsopts
, int depth
)
1031 assert(dvp
!= NULL
);
1032 assert(dir
!= NULL
);
1033 assert(root
!= NULL
);
1034 assert(parent
!= NULL
);
1035 assert(fsopts
!= NULL
);
1037 /* assert root directory */
1038 assert(S_ISDIR(root
->type
));
1039 assert(!strcmp(root
->name
, "."));
1040 assert(!root
->child
);
1041 assert(!root
->parent
|| root
->parent
->child
== root
);
1043 hammer2_print(dvp
, NULL
, root
, depth
, "enter");
1044 if (stat(dir
, &st
) == -1)
1045 err(1, "no such path %s", dir
);
1046 if (!S_ISDIR(st
.st_mode
))
1047 errx(1, "no such dir %s", dir
);
1049 for (cur
= root
->next
; cur
!= NULL
; cur
= cur
->next
) {
1050 /* global variable for HAMMER2 vnops */
1051 hammer2_curnode
= cur
;
1053 /* construct source path */
1054 if (cur
->contents
) {
1055 path
= cur
->contents
;
1057 if (snprintf(f
, sizeof(f
), "%s/%s/%s",
1058 cur
->root
, cur
->path
, cur
->name
) >= (int)sizeof(f
))
1059 errx(1, "path %s too long", f
);
1062 if (S_ISLNK(cur
->type
)) {
1063 if (lstat(path
, &st
) == -1)
1064 err(1, "no such symlink %s", path
);
1066 if (stat(path
, &st
) == -1)
1067 err(1, "no such path %s", path
);
1070 /* update node state */
1071 if ((cur
->inode
->flags
& FI_ALLOCATED
) == 0) {
1072 cur
->inode
->flags
|= FI_ALLOCATED
;
1074 cur
->parent
= parent
;
1077 /* detect hardlink */
1078 if (cur
->inode
->flags
& FI_WRITTEN
) {
1079 assert(!S_ISDIR(cur
->type
));
1084 cur
->inode
->flags
|= FI_WRITTEN
;
1086 /* make sure it doesn't exist yet */
1088 error
= hammer2_nresolve(dvp
, &vp
, cur
->name
,
1091 errx(1, "hammer2_nresolve(\"%s\") already exists",
1093 hammer2_print(dvp
, vp
, cur
, depth
, "nresolve");
1095 /* if directory, mkdir and recurse */
1096 if (S_ISDIR(cur
->type
)) {
1100 error
= hammer2_nmkdir(dvp
, &vp
, cur
->name
,
1101 strlen(cur
->name
), cur
->inode
->st
.st_mode
);
1103 errx(1, "hammer2_nmkdir(\"%s\") failed: %s",
1104 cur
->name
, strerror(error
));
1106 hammer2_print(dvp
, vp
, cur
, depth
, "nmkdir");
1108 error
= hammer2_populate_dir(vp
, path
, cur
->child
, cur
,
1111 errx(1, "failed to populate %s: %s",
1112 path
, strerror(error
));
1113 cur
->inode
->param
= vp
;
1117 /* if regular file, creat and write its data */
1118 if (S_ISREG(cur
->type
) && !hardlink
) {
1119 assert(cur
->child
== NULL
);
1122 error
= hammer2_ncreate(dvp
, &vp
, cur
->name
,
1123 strlen(cur
->name
), cur
->inode
->st
.st_mode
);
1125 errx(1, "hammer2_ncreate(\"%s\") failed: %s",
1126 cur
->name
, strerror(error
));
1128 hammer2_print(dvp
, vp
, cur
, depth
, "ncreate");
1130 error
= hammer2_write_file(vp
, path
, cur
);
1132 errx(1, "hammer2_write_file(\"%s\") failed: %s",
1133 path
, strerror(error
));
1134 cur
->inode
->param
= vp
;
1138 /* if symlink, create a symlink against target */
1139 if (S_ISLNK(cur
->type
)) {
1140 assert(cur
->child
== NULL
);
1143 error
= hammer2_nsymlink(dvp
, &vp
, cur
->name
,
1144 strlen(cur
->name
), cur
->symlink
,
1145 cur
->inode
->st
.st_mode
);
1147 errx(1, "hammer2_nsymlink(\"%s\") failed: %s",
1148 cur
->name
, strerror(error
));
1150 hammer2_print(dvp
, vp
, cur
, depth
, "nsymlink");
1151 cur
->inode
->param
= vp
;
1155 /* if fifo, create a fifo */
1156 if (S_ISFIFO(cur
->type
) && !hardlink
) {
1157 assert(cur
->child
== NULL
);
1160 error
= hammer2_nmknod(dvp
, &vp
, cur
->name
,
1161 strlen(cur
->name
), VFIFO
, cur
->inode
->st
.st_mode
);
1163 errx(1, "hammer2_nmknod(\"%s\") failed: %s",
1164 cur
->name
, strerror(error
));
1166 hammer2_print(dvp
, vp
, cur
, depth
, "nmknod");
1167 cur
->inode
->param
= vp
;
1171 /* if hardlink, creat a hardlink */
1172 if ((S_ISREG(cur
->type
) || S_ISFIFO(cur
->type
)) && hardlink
) {
1174 assert(cur
->child
== NULL
);
1176 /* source vnode must not be NULL */
1177 vp
= cur
->inode
->param
;
1179 /* currently these conditions must be true */
1181 assert(vp
->v_type
== VREG
|| vp
->v_type
== VFIFO
);
1182 assert(vp
->v_logical
);
1183 assert(!vp
->v_vflushed
);
1184 assert(vp
->v_malloced
);
1185 assert(VTOI(vp
)->refs
> 0);
1187 error
= hammer2_nlink(dvp
, vp
, cur
->name
,
1190 errx(1, "hammer2_nlink(\"%s\") failed: %s",
1191 cur
->name
, strerror(error
));
1192 snprintf(buf
, sizeof(buf
), "nlink=%lld",
1193 (long long)VTOI(vp
)->meta
.nlinks
);
1194 hammer2_print(dvp
, vp
, cur
, depth
, buf
);
1198 /* other types are unsupported */
1199 printf("ignore %s 0%o\n", path
, cur
->type
);
1206 hammer2_write_file(struct m_vnode
*vp
, const char *path
, fsnode
*node
)
1208 struct stat
*st
= &node
->inode
->st
;
1209 size_t nsize
, bufsize
;
1214 nsize
= st
->st_size
;
1217 /* check nsize vs maximum file size */
1219 fd
= open(path
, O_RDONLY
);
1221 err(1, "failed to open %s", path
);
1223 p
= mmap(0, nsize
, PROT_READ
, MAP_FILE
|MAP_PRIVATE
, fd
, 0);
1224 if (p
== MAP_FAILED
)
1225 err(1, "failed to mmap %s", path
);
1228 for (offset
= 0; offset
< nsize
; ) {
1229 bufsize
= MIN(nsize
- offset
, HAMMER2_PBUFSIZE
);
1230 assert(bufsize
<= HAMMER2_PBUFSIZE
);
1231 error
= hammer2_write(vp
, p
+ offset
, bufsize
, offset
);
1233 errx(1, "failed to write to %s vnode: %s",
1234 path
, strerror(error
));
1236 if (bufsize
== HAMMER2_PBUFSIZE
)
1237 assert((offset
& (HAMMER2_PBUFSIZE
- 1)) == 0);
1245 trim_char(char *p
, char c
)
1247 char *o
, tmp
[PATH_MAX
];
1257 strlcpy(tmp
, p
, sizeof(tmp
));
1258 if (strncmp(tmp
, p
, sizeof(tmp
)))
1261 /* trim consecutive */
1266 for (i
= 0; i
< n
; i
++) {
1277 assert(strlen(p
) <= strlen(tmp
));
1279 /* assert no consecutive */
1284 for (i
= 0; i
< n
; i
++) {
1286 assert(!prev_was_c
);
1295 memmove(p
, p
+ 1, strlen(p
+ 1) + 1);
1303 assert(p
[strlen(p
) - 1] != '/');
1311 return trim_char(p
, '/');
1315 is_supported_link(const char *s
)
1317 /* absolute path can't be supported */
1318 if (strlen(s
) >= 1 && strncmp(s
, "/", 1) == 0)
1321 /* XXX ".." is currently unsupported */
1322 if (strlen(s
) >= 3 && strncmp(s
, "../", 3) == 0)
1329 hammer2_version_get(struct m_vnode
*vp
)
1333 hmp
= VTOI(vp
)->pmp
->pfs_hmps
[0];
1337 printf("version: %d\n", hmp
->voldata
.version
);
1343 TAILQ_ENTRY(pfs_entry
) entry
;
1344 char name
[NAME_MAX
+1];
1349 hammer2_pfs_get(struct m_vnode
*vp
)
1351 hammer2_ioc_pfs_t pfs
;
1352 TAILQ_HEAD(, pfs_entry
) head
;
1353 struct pfs_entry
*p
, *e
;
1355 const char *type_str
;
1358 bzero(&pfs
, sizeof(pfs
));
1361 while ((pfs
.name_key
= pfs
.name_next
) != (hammer2_key_t
)-1) {
1362 error
= hammer2_ioctl_pfs_get(VTOI(vp
), &pfs
);
1367 hammer2_uuid_to_str(&pfs
.pfs_clid
, &pfs_id_str
);
1369 if (pfs
.pfs_type
== HAMMER2_PFSTYPE_MASTER
) {
1370 if (pfs
.pfs_subtype
== HAMMER2_PFSSUBTYPE_NONE
)
1371 type_str
= "MASTER";
1373 type_str
= hammer2_pfssubtype_to_str(
1376 type_str
= hammer2_pfstype_to_str(pfs
.pfs_type
);
1378 e
= ecalloc(1, sizeof(*e
));
1379 snprintf(e
->name
, sizeof(e
->name
), "%s", pfs
.name
);
1380 snprintf(e
->s
, sizeof(e
->s
), "%-11s %s", type_str
, pfs_id_str
);
1383 p
= TAILQ_FIRST(&head
);
1385 if (strcmp(e
->name
, p
->name
) <= 0) {
1386 TAILQ_INSERT_BEFORE(p
, e
, entry
);
1389 p
= TAILQ_NEXT(p
, entry
);
1392 TAILQ_INSERT_TAIL(&head
, e
, entry
);
1396 "ClusterId (pfs_clid) "
1398 while ((p
= TAILQ_FIRST(&head
)) != NULL
) {
1399 printf("%s %s\n", p
->s
, p
->name
);
1400 TAILQ_REMOVE(&head
, p
, entry
);
1408 hammer2_pfs_lookup(struct m_vnode
*vp
, const char *pfs_name
)
1410 hammer2_ioc_pfs_t pfs
;
1414 bzero(&pfs
, sizeof(pfs
));
1415 strlcpy(pfs
.name
, pfs_name
, sizeof(pfs
.name
));
1417 error
= hammer2_ioctl_pfs_lookup(VTOI(vp
), &pfs
);
1419 printf("name: %s\n", pfs
.name
);
1420 printf("type: %s\n", hammer2_pfstype_to_str(pfs
.pfs_type
));
1421 printf("subtype: %s\n",
1422 hammer2_pfssubtype_to_str(pfs
.pfs_subtype
));
1425 hammer2_uuid_to_str(&pfs
.pfs_fsid
, &pfs_id_str
);
1426 printf("fsid: %s\n", pfs_id_str
);
1430 hammer2_uuid_to_str(&pfs
.pfs_clid
, &pfs_id_str
);
1431 printf("clid: %s\n", pfs_id_str
);
1439 hammer2_pfs_create(struct m_vnode
*vp
, const char *pfs_name
)
1441 hammer2_ioc_pfs_t pfs
;
1444 bzero(&pfs
, sizeof(pfs
));
1445 strlcpy(pfs
.name
, pfs_name
, sizeof(pfs
.name
));
1446 pfs
.pfs_type
= HAMMER2_PFSTYPE_MASTER
;
1447 uuid_create(&pfs
.pfs_clid
, NULL
);
1448 uuid_create(&pfs
.pfs_fsid
, NULL
);
1450 error
= hammer2_ioctl_pfs_create(VTOI(vp
), &pfs
);
1451 if (error
== EEXIST
)
1453 "NOTE: Typically the same name is "
1454 "used for cluster elements on "
1455 "different mounts,\n"
1456 " but cluster elements on the "
1457 "same mount require unique names.\n"
1458 "hammer2: pfs_create(%s): already present\n",
1465 hammer2_pfs_delete(struct m_vnode
*vp
, const char *pfs_name
)
1467 hammer2_ioc_pfs_t pfs
;
1469 bzero(&pfs
, sizeof(pfs
));
1470 strlcpy(pfs
.name
, pfs_name
, sizeof(pfs
.name
));
1472 return hammer2_ioctl_pfs_delete(VTOI(vp
), &pfs
);
1476 hammer2_pfs_snapshot(struct m_vnode
*vp
, const char *pfs_name
,
1477 const char *mount_label
)
1479 hammer2_ioc_pfs_t pfs
;
1483 bzero(&pfs
, sizeof(pfs
));
1484 strlcpy(pfs
.name
, pfs_name
, sizeof(pfs
.name
));
1486 if (strlen(pfs
.name
) == 0) {
1489 snprintf(pfs
.name
, sizeof(pfs
.name
),
1490 "%s.%04d%02d%02d.%02d%02d%02d",
1500 return hammer2_ioctl_pfs_snapshot(VTOI(vp
), &pfs
);
1504 hammer2_inode_getx(struct m_vnode
*dvp
, const char *f
)
1506 hammer2_ioc_inode_t inode
;
1507 hammer2_inode_t
*ip
;
1508 hammer2_inode_meta_t
*meta
;
1510 char *o
, *p
, *name
, *str
= NULL
;
1515 assert(strlen(f
) > 0);
1516 o
= p
= name
= strdup(f
);
1518 error
= trim_slash(p
);
1521 if (strlen(p
) == 0) {
1526 while ((p
= strchr(p
, '/')) != NULL
) {
1527 *p
++ = 0; /* NULL terminate name */
1528 if (!strcmp(name
, ".")) {
1533 error
= hammer2_nresolve(dvp
, &vp
, name
, strlen(name
));
1538 switch (ip
->meta
.type
) {
1539 case HAMMER2_OBJTYPE_DIRECTORY
:
1541 case HAMMER2_OBJTYPE_SOFTLINK
:
1542 bzero(tmp
, sizeof(tmp
));
1543 error
= hammer2_readlink(vp
, tmp
, sizeof(tmp
));
1546 if (!is_supported_link(tmp
))
1548 strlcat(tmp
, "/", sizeof(tmp
));
1549 strlcat(tmp
, p
, sizeof(tmp
));
1550 error
= trim_slash(tmp
);
1563 error
= hammer2_nresolve(dvp
, &vp
, name
, strlen(name
));
1567 bzero(&inode
, sizeof(inode
));
1568 error
= hammer2_ioctl_inode_get(VTOI(vp
), &inode
);
1572 meta
= &inode
.ip_data
.meta
;
1573 printf("--------------------\n");
1574 printf("flags = 0x%x\n", inode
.flags
);
1575 printf("data_count = %ju\n", (uintmax_t)inode
.data_count
);
1576 printf("inode_count = %ju\n", (uintmax_t)inode
.inode_count
);
1577 printf("--------------------\n");
1578 printf("version = %u\n", meta
->version
);
1579 printf("pfs_subtype = %u (%s)\n", meta
->pfs_subtype
,
1580 hammer2_pfssubtype_to_str(meta
->pfs_subtype
));
1581 printf("uflags = 0x%x\n", (unsigned int)meta
->uflags
);
1582 printf("rmajor = %u\n", meta
->rmajor
);
1583 printf("rminor = %u\n", meta
->rminor
);
1584 printf("ctime = %s\n", hammer2_time64_to_str(meta
->ctime
, &str
));
1585 printf("mtime = %s\n", hammer2_time64_to_str(meta
->mtime
, &str
));
1586 printf("atime = %s\n", hammer2_time64_to_str(meta
->atime
, &str
));
1587 printf("btime = %s\n", hammer2_time64_to_str(meta
->btime
, &str
));
1589 printf("uid = %s\n", hammer2_uuid_to_str(&uuid
, &str
));
1591 printf("gid = %s\n", hammer2_uuid_to_str(&uuid
, &str
));
1592 printf("type = %u (%s)\n", meta
->type
,
1593 hammer2_iptype_to_str(meta
->type
));
1594 printf("op_flags = 0x%x\n", meta
->op_flags
);
1595 printf("cap_flags = 0x%x\n", meta
->cap_flags
);
1596 printf("mode = 0%o\n", meta
->mode
);
1597 printf("inum = 0x%jx\n", (uintmax_t)meta
->inum
);
1598 printf("size = %ju\n", (uintmax_t)meta
->size
);
1599 printf("nlinks = %ju\n", (uintmax_t)meta
->nlinks
);
1600 printf("iparent = 0x%jx\n", (uintmax_t)meta
->iparent
);
1601 printf("name_key = 0x%jx\n", (uintmax_t)meta
->name_key
);
1602 printf("name_len = %u\n", meta
->name_len
);
1603 printf("ncopies = %u\n", meta
->ncopies
);
1604 printf("comp_algo = 0x%jx\n", (uintmax_t)meta
->comp_algo
);
1605 printf("target_type = %u\n", meta
->target_type
);
1606 printf("check_algo = %u\n", meta
->check_algo
);
1607 printf("pfs_nmasters = %u\n", meta
->pfs_nmasters
);
1608 printf("pfs_type = %u (%s)\n", meta
->pfs_type
,
1609 hammer2_pfstype_to_str(meta
->pfs_type
));
1610 printf("pfs_inum = 0x%jx\n", (uintmax_t)meta
->pfs_inum
);
1611 uuid
= meta
->pfs_clid
;
1612 printf("pfs_clid = %s\n", hammer2_uuid_to_str(&uuid
, &str
));
1613 uuid
= meta
->pfs_fsid
;
1614 printf("pfs_fsid = %s\n", hammer2_uuid_to_str(&uuid
, &str
));
1615 printf("data_quota = 0x%jx\n", (uintmax_t)meta
->data_quota
);
1616 printf("inode_quota = 0x%jx\n", (uintmax_t)meta
->inode_quota
);
1617 printf("pfs_lsnap_tid = 0x%jx\n", (uintmax_t)meta
->pfs_lsnap_tid
);
1618 printf("decrypt_check = 0x%jx\n", (uintmax_t)meta
->decrypt_check
);
1619 printf("--------------------\n");
1627 hammer2_inode_setcheck(struct m_vnode
*dvp
, const char *f
)
1629 hammer2_ioc_inode_t inode
;
1630 hammer2_inode_t
*ip
;
1632 char *o
, *p
, *name
, *check_algo_str
;
1634 const char *checks
[] = { "none", "disabled", "crc32", "xxhash64",
1636 int check_algo_idx
, error
;
1639 assert(strlen(f
) > 0);
1642 p
= strrchr(p
, ':');
1646 *p
++ = 0; /* NULL terminate path */
1650 /* fail if already empty before trim */
1654 error
= trim_slash(p
);
1657 if (strlen(check_algo_str
) == 0)
1660 /* convert check_algo_str to check_algo_idx */
1661 check_algo_idx
= nitems(checks
);
1662 while (--check_algo_idx
>= 0)
1663 if (strcasecmp(check_algo_str
, checks
[check_algo_idx
]) == 0)
1665 if (check_algo_idx
< 0) {
1666 if (strcasecmp(check_algo_str
, "default") == 0) {
1667 check_algo_str
= "xxhash64";
1668 check_algo_idx
= HAMMER2_CHECK_XXHASH64
;
1669 } else if (strcasecmp(check_algo_str
, "disabled") == 0) {
1670 check_algo_str
= "disabled";
1671 check_algo_idx
= HAMMER2_CHECK_DISABLED
;
1673 printf("invalid check_algo_str: %s\n", check_algo_str
);
1677 check_algo
= HAMMER2_ENC_ALGO(check_algo_idx
);
1678 printf("change %s to algo %d (%s)\n", p
, check_algo
, check_algo_str
);
1680 if (strlen(p
) == 0) {
1685 while ((p
= strchr(p
, '/')) != NULL
) {
1686 *p
++ = 0; /* NULL terminate name */
1687 if (!strcmp(name
, ".")) {
1692 error
= hammer2_nresolve(dvp
, &vp
, name
, strlen(name
));
1697 switch (ip
->meta
.type
) {
1698 case HAMMER2_OBJTYPE_DIRECTORY
:
1700 case HAMMER2_OBJTYPE_SOFTLINK
:
1701 bzero(tmp
, sizeof(tmp
));
1702 error
= hammer2_readlink(vp
, tmp
, sizeof(tmp
));
1705 if (!is_supported_link(tmp
))
1707 strlcat(tmp
, "/", sizeof(tmp
));
1708 strlcat(tmp
, p
, sizeof(tmp
));
1709 error
= trim_slash(tmp
);
1722 error
= hammer2_nresolve(dvp
, &vp
, name
, strlen(name
));
1728 bzero(&inode
, sizeof(inode
));
1729 error
= hammer2_ioctl_inode_get(ip
, &inode
);
1733 inode
.flags
|= HAMMER2IOC_INODE_FLAG_CHECK
;
1734 inode
.ip_data
.meta
.check_algo
= check_algo
;
1735 error
= hammer2_ioctl_inode_set(ip
, &inode
);
1745 hammer2_inode_setcomp(struct m_vnode
*dvp
, const char *f
)
1747 hammer2_ioc_inode_t inode
;
1748 hammer2_inode_t
*ip
;
1750 char *o
, *p
, *name
, *comp_algo_str
, *comp_level_str
;
1752 const char *comps
[] = { "none", "autozero", "lz4", "zlib", };
1753 int comp_algo_idx
, comp_level_idx
, error
;
1754 uint8_t comp_algo
, comp_level
;
1756 assert(strlen(f
) > 0);
1759 p
= strrchr(p
, ':');
1763 *p
++ = 0; /* NULL terminate comp_algo_str */
1767 p
= strrchr(p
, ':');
1769 /* comp_level_str not specified */
1770 comp_algo_str
= comp_level_str
;
1771 comp_level_str
= NULL
;
1773 *p
++ = 0; /* NULL terminate path */
1778 /* fail if already empty before trim */
1782 error
= trim_slash(p
);
1785 if (strlen(comp_algo_str
) == 0)
1788 /* convert comp_algo_str to comp_algo_idx */
1789 comp_algo_idx
= nitems(comps
);
1790 while (--comp_algo_idx
>= 0)
1791 if (strcasecmp(comp_algo_str
, comps
[comp_algo_idx
]) == 0)
1793 if (comp_algo_idx
< 0) {
1794 if (strcasecmp(comp_algo_str
, "default") == 0) {
1795 comp_algo_str
= "lz4";
1796 comp_algo_idx
= HAMMER2_COMP_LZ4
;
1797 } else if (strcasecmp(comp_algo_str
, "disabled") == 0) {
1798 comp_algo_str
= "autozero";
1799 comp_algo_idx
= HAMMER2_COMP_AUTOZERO
;
1801 printf("invalid comp_algo_str: %s\n", comp_algo_str
);
1805 comp_algo
= HAMMER2_ENC_ALGO(comp_algo_idx
);
1807 /* convert comp_level_str to comp_level_idx */
1808 if (comp_level_str
== NULL
) {
1810 } else if (isdigit((int)comp_level_str
[0])) {
1811 comp_level_idx
= strtol(comp_level_str
, NULL
, 0);
1812 } else if (strcasecmp(comp_level_str
, "default") == 0) {
1815 printf("invalid comp_level_str: %s\n", comp_level_str
);
1818 if (comp_level_idx
) {
1819 switch (comp_algo
) {
1820 case HAMMER2_COMP_ZLIB
:
1821 if (comp_level_idx
< 6 || comp_level_idx
> 9) {
1822 printf("unsupported comp_level %d for %s\n",
1823 comp_level_idx
, comp_algo_str
);
1828 printf("unsupported comp_level %d for %s\n",
1829 comp_level_idx
, comp_algo_str
);
1833 comp_level
= HAMMER2_ENC_LEVEL(comp_level_idx
);
1834 printf("change %s to algo %d (%s) level %d\n",
1835 p
, comp_algo
, comp_algo_str
, comp_level_idx
);
1837 if (strlen(p
) == 0) {
1842 while ((p
= strchr(p
, '/')) != NULL
) {
1843 *p
++ = 0; /* NULL terminate name */
1844 if (!strcmp(name
, ".")) {
1849 error
= hammer2_nresolve(dvp
, &vp
, name
, strlen(name
));
1854 switch (ip
->meta
.type
) {
1855 case HAMMER2_OBJTYPE_DIRECTORY
:
1857 case HAMMER2_OBJTYPE_SOFTLINK
:
1858 bzero(tmp
, sizeof(tmp
));
1859 error
= hammer2_readlink(vp
, tmp
, sizeof(tmp
));
1862 if (!is_supported_link(tmp
))
1864 strlcat(tmp
, "/", sizeof(tmp
));
1865 strlcat(tmp
, p
, sizeof(tmp
));
1866 error
= trim_slash(tmp
);
1879 error
= hammer2_nresolve(dvp
, &vp
, name
, strlen(name
));
1885 bzero(&inode
, sizeof(inode
));
1886 error
= hammer2_ioctl_inode_get(ip
, &inode
);
1890 inode
.flags
|= HAMMER2IOC_INODE_FLAG_COMP
;
1891 inode
.ip_data
.meta
.comp_algo
= comp_algo
| comp_level
;
1892 error
= hammer2_ioctl_inode_set(ip
, &inode
);
1902 hammer2_bulkfree(struct m_vnode
*vp
)
1904 hammer2_ioc_bulkfree_t bfi
;
1906 size_t usermem_size
= sizeof(usermem
);
1908 bzero(&bfi
, sizeof(bfi
));
1910 if (sysctlbyname("hw.usermem", &usermem
, &usermem_size
, NULL
, 0) == 0)
1911 bfi
.size
= usermem
/ 16;
1914 if (bfi
.size
< 8192 * 1024)
1915 bfi
.size
= 8192 * 1024;
1917 return hammer2_ioctl_bulkfree_scan(VTOI(vp
), &bfi
);
1921 hammer2_destroy_path(struct m_vnode
*dvp
, const char *f
)
1923 hammer2_ioc_destroy_t destroy
;
1924 hammer2_inode_t
*ip
;
1930 assert(strlen(f
) > 0);
1931 o
= p
= name
= strdup(f
);
1933 error
= trim_slash(p
);
1939 while ((p
= strchr(p
, '/')) != NULL
) {
1940 *p
++ = 0; /* NULL terminate name */
1941 if (!strcmp(name
, ".")) {
1946 error
= hammer2_nresolve(dvp
, &vp
, name
, strlen(name
));
1951 switch (ip
->meta
.type
) {
1952 case HAMMER2_OBJTYPE_DIRECTORY
:
1954 case HAMMER2_OBJTYPE_SOFTLINK
:
1955 bzero(tmp
, sizeof(tmp
));
1956 error
= hammer2_readlink(vp
, tmp
, sizeof(tmp
));
1959 if (!is_supported_link(tmp
))
1961 strlcat(tmp
, "/", sizeof(tmp
));
1962 strlcat(tmp
, p
, sizeof(tmp
));
1963 error
= trim_slash(tmp
);
1976 /* XXX When does (or why does not) ioctl modify this inode ? */
1977 hammer2_inode_modify(VTOI(dvp
));
1979 bzero(&destroy
, sizeof(destroy
));
1980 destroy
.cmd
= HAMMER2_DELETE_FILE
;
1981 snprintf(destroy
.path
, sizeof(destroy
.path
), "%s", name
);
1986 error
= hammer2_ioctl_destroy(VTOI(dvp
), &destroy
);
1988 printf("%s\n", strerror(error
));
1997 hammer2_destroy_inum(struct m_vnode
*vp
, hammer2_tid_t inum
)
1999 hammer2_ioc_destroy_t destroy
;
2002 bzero(&destroy
, sizeof(destroy
));
2003 destroy
.cmd
= HAMMER2_DELETE_INUM
;
2004 destroy
.inum
= inum
;
2006 printf("%jd\t", (intmax_t)destroy
.inum
);
2009 error
= hammer2_ioctl_destroy(VTOI(vp
), &destroy
);
2011 printf("%s\n", strerror(error
));
2019 hammer2_growfs(struct m_vnode
*vp
, hammer2_off_t size
)
2021 hammer2_ioc_growfs_t growfs
;
2024 bzero(&growfs
, sizeof(growfs
));
2027 error
= hammer2_ioctl_growfs(VTOI(vp
), &growfs
, NULL
);
2029 if (growfs
.modified
)
2030 printf("grown to %016jx\n", (intmax_t)growfs
.size
);
2032 printf("no size change - %016jx\n",
2033 (intmax_t)growfs
.size
);
2039 struct hammer2_link
{
2040 TAILQ_ENTRY(hammer2_link
) entry
;
2043 char path
[PATH_MAX
];
2046 TAILQ_HEAD(hammer2_linkq
, hammer2_link
);
2049 hammer2_linkq_init(struct hammer2_linkq
*linkq
)
2055 hammer2_linkq_cleanup(struct hammer2_linkq
*linkq
, bool is_root
)
2057 struct hammer2_link
*e
;
2061 * If is_root is true, linkq must be empty, or link count is broken.
2062 * Note that if an image was made by makefs, hardlinks in the source
2063 * directory became hardlinks in the image only if >1 links existed under
2064 * that directory, as makefs doesn't determine hardlink via link count.
2066 while ((e
= TAILQ_FIRST(linkq
)) != NULL
) {
2068 TAILQ_REMOVE(linkq
, e
, entry
);
2071 assert(TAILQ_EMPTY(linkq
));
2073 if (count
&& is_root
)
2074 errx(1, "%d link entries remained", count
);
2078 hammer2_linkq_add(struct hammer2_linkq
*linkq
, hammer2_tid_t inum
,
2079 uint64_t nlinks
, const char *path
)
2081 struct hammer2_link
*e
;
2084 e
= ecalloc(1, sizeof(*e
));
2087 strlcpy(e
->path
, path
, sizeof(e
->path
));
2088 TAILQ_INSERT_TAIL(linkq
, e
, entry
);
2090 TAILQ_FOREACH(e
, linkq
, entry
)
2091 if (e
->inum
== inum
)
2094 errx(1, "%d link entries exist for inum %jd",
2095 count
, (intmax_t)inum
);
2099 hammer2_linkq_del(struct hammer2_linkq
*linkq
, hammer2_tid_t inum
)
2101 struct hammer2_link
*e
, *next
;
2103 TAILQ_FOREACH_MUTABLE(e
, linkq
, entry
, next
)
2104 if (e
->inum
== inum
) {
2106 if (e
->nlinks
== 1) {
2107 TAILQ_REMOVE(linkq
, e
, entry
);
2114 hammer2_utimes(struct m_vnode
*vp
, const char *f
)
2116 hammer2_inode_t
*ip
= VTOI(vp
);
2117 struct timeval tv
[2];
2119 hammer2_time_to_timeval(ip
->meta
.atime
, &tv
[0]);
2120 hammer2_time_to_timeval(ip
->meta
.mtime
, &tv
[1]);
2122 utimes(f
, tv
); /* ignore failure */
2126 hammer2_readx_directory(struct m_vnode
*dvp
, const char *dir
, const char *name
,
2127 struct hammer2_linkq
*linkq
)
2132 char *buf
, tmp
[PATH_MAX
];
2138 snprintf(tmp
, sizeof(tmp
), "%s/%s", dir
, name
);
2139 if (stat(tmp
, &st
) == -1 && mkdir(tmp
, 0666) == -1)
2140 err(1, "failed to mkdir %s", tmp
);
2142 buf
= ecalloc(1, HAMMER2_PBUFSIZE
);
2145 error
= hammer2_readdir(dvp
, buf
, HAMMER2_PBUFSIZE
, &offset
,
2146 &ndirent
, &eofflag
);
2148 errx(1, "failed to readdir");
2151 for (i
= 0; i
< ndirent
; i
++) {
2152 if (strcmp(dp
->d_name
, ".") &&
2153 strcmp(dp
->d_name
, "..")) {
2154 error
= hammer2_nresolve(dvp
, &vp
, dp
->d_name
,
2155 strlen(dp
->d_name
));
2158 error
= hammer2_readx_handle(vp
, tmp
,
2163 dp
= (void *)((char *)dp
+
2164 _DIRENT_RECLEN(dp
->d_namlen
));
2169 hammer2_utimes(dvp
, tmp
);
2175 hammer2_readx_link(struct m_vnode
*vp
, const char *src
, const char *lnk
,
2176 struct hammer2_linkq
*linkq
)
2178 hammer2_inode_t
*ip
= VTOI(vp
);
2182 if (!stat(lnk
, &st
)) {
2183 error
= unlink(lnk
);
2188 error
= link(src
, lnk
);
2192 hammer2_linkq_del(linkq
, ip
->meta
.inum
);
2198 hammer2_readx_regfile(struct m_vnode
*vp
, const char *dir
, const char *name
,
2199 struct hammer2_linkq
*linkq
)
2201 hammer2_inode_t
*ip
= VTOI(vp
);
2202 struct hammer2_link
*e
;
2203 char *buf
, out
[PATH_MAX
];
2209 snprintf(out
, sizeof(out
), "%s/%s", dir
, name
);
2211 if (ip
->meta
.nlinks
> 1) {
2212 TAILQ_FOREACH(e
, linkq
, entry
)
2213 if (e
->inum
== ip
->meta
.inum
) {
2215 error
= hammer2_readx_link(vp
, e
->path
, out
,
2219 /* ignore failure */
2222 hammer2_linkq_add(linkq
, ip
->meta
.inum
, ip
->meta
.nlinks
,
2226 fd
= open(out
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0666);
2228 err(1, "failed to create %s", out
);
2230 buf
= ecalloc(1, HAMMER2_PBUFSIZE
);
2231 resid
= ip
->meta
.size
;
2235 bzero(buf
, HAMMER2_PBUFSIZE
);
2236 error
= hammer2_read(vp
, buf
, HAMMER2_PBUFSIZE
, offset
);
2238 errx(1, "failed to read from %s", name
);
2240 n
= resid
>= HAMMER2_PBUFSIZE
? HAMMER2_PBUFSIZE
: resid
;
2241 error
= write(fd
, buf
, n
);
2243 err(1, "failed to write to %s", out
);
2244 else if (error
!= n
)
2248 offset
+= HAMMER2_PBUFSIZE
;
2254 hammer2_utimes(vp
, out
);
2260 hammer2_readx_handle(struct m_vnode
*vp
, const char *dir
, const char *name
,
2261 struct hammer2_linkq
*linkq
)
2263 hammer2_inode_t
*ip
= VTOI(vp
);
2265 switch (ip
->meta
.type
) {
2266 case HAMMER2_OBJTYPE_DIRECTORY
:
2267 return hammer2_readx_directory(vp
, dir
, name
, linkq
);
2268 case HAMMER2_OBJTYPE_REGFILE
:
2269 return hammer2_readx_regfile(vp
, dir
, name
, linkq
);
2272 printf("ignore inode %jd %s \"%s\"\n",
2273 (intmax_t)ip
->meta
.inum
,
2274 hammer2_iptype_to_str(ip
->meta
.type
),
2282 hammer2_readx(struct m_vnode
*dvp
, const char *dir
, const char *f
)
2284 hammer2_inode_t
*ip
;
2285 struct hammer2_linkq linkq
;
2286 struct m_vnode
*vp
, *ovp
= dvp
;
2294 assert(strlen(f
) > 0);
2295 o
= p
= name
= strdup(f
);
2297 error
= trim_slash(p
);
2300 if (strlen(p
) == 0) {
2305 while ((p
= strchr(p
, '/')) != NULL
) {
2306 *p
++ = 0; /* NULL terminate name */
2307 if (!strcmp(name
, ".")) {
2312 error
= hammer2_nresolve(dvp
, &vp
, name
, strlen(name
));
2317 switch (ip
->meta
.type
) {
2318 case HAMMER2_OBJTYPE_DIRECTORY
:
2320 case HAMMER2_OBJTYPE_SOFTLINK
:
2321 bzero(tmp
, sizeof(tmp
));
2322 error
= hammer2_readlink(vp
, tmp
, sizeof(tmp
));
2325 if (!is_supported_link(tmp
))
2327 strlcat(tmp
, "/", sizeof(tmp
));
2328 strlcat(tmp
, p
, sizeof(tmp
));
2329 error
= trim_slash(tmp
);
2342 error
= hammer2_nresolve(dvp
, &vp
, name
, strlen(name
));
2346 hammer2_linkq_init(&linkq
);
2347 error
= hammer2_readx_handle(vp
, dir
, name
, &linkq
);
2348 hammer2_linkq_cleanup(&linkq
, vp
== ovp
);
2358 assert_trim_slash(const char *input
, const char *expected
)
2363 strlcpy(tmp
, input
, sizeof(tmp
));
2364 error
= trim_slash(tmp
);
2366 errx(1, "input \"%s\" error %d", input
, error
);
2368 if (strncmp(tmp
, expected
, sizeof(tmp
)))
2369 errx(1, "input \"%s\" result \"%s\" vs expected \"%s\"",
2370 input
, tmp
, expected
);
2374 unittest_trim_slash(void)
2376 assert_trim_slash("", "");
2377 assert_trim_slash("/", "");
2378 assert_trim_slash("//", "");
2379 assert_trim_slash("///", "");
2381 assert_trim_slash("makefs", "makefs");
2382 assert_trim_slash("/makefs", "makefs");
2383 assert_trim_slash("//makefs", "makefs");
2384 assert_trim_slash("makefs/", "makefs");
2385 assert_trim_slash("makefs//", "makefs");
2386 assert_trim_slash("/makefs/", "makefs");
2387 assert_trim_slash("//makefs//", "makefs");
2389 assert_trim_slash("sys/vfs/hammer2", "sys/vfs/hammer2");
2390 assert_trim_slash("/sys/vfs/hammer2", "sys/vfs/hammer2");
2391 assert_trim_slash("//sys/vfs/hammer2", "sys/vfs/hammer2");
2392 assert_trim_slash("///sys/vfs/hammer2", "sys/vfs/hammer2");
2393 assert_trim_slash("sys/vfs/hammer2/", "sys/vfs/hammer2");
2394 assert_trim_slash("sys/vfs/hammer2//", "sys/vfs/hammer2");
2395 assert_trim_slash("sys/vfs/hammer2///", "sys/vfs/hammer2");
2396 assert_trim_slash("/sys/vfs/hammer2/", "sys/vfs/hammer2");
2397 assert_trim_slash("//sys//vfs//hammer2//", "sys/vfs/hammer2");
2398 assert_trim_slash("///sys///vfs///hammer2///", "sys/vfs/hammer2");
2400 APRINTF("success\n");