usr.sbin/makefs/hammer2: Avoid overlapped snprintf buffer
[dragonfly.git] / usr.sbin / makefs / hammer2.c
blob69ef29b20c2110bb6818d4deca4b9d5d032c72c6
1 /*
2 * SPDX-License-Identifier: BSD-3-Clause
4 * Copyright (c) 2022 Tomohiro Kusumi <tkusumi@netbsd.org>
5 * Copyright (c) 2011-2022 The DragonFly Project. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific, prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
35 #if HAVE_NBTOOL_CONFIG_H
36 #include "nbtool_config.h"
37 #endif
39 #include <sys/param.h>
40 #include <sys/stat.h>
41 #include <sys/sysctl.h>
42 #include <sys/mman.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <stdbool.h>
47 #include <string.h>
48 #include <ctype.h>
49 #include <unistd.h>
50 #include <fcntl.h>
51 #include <time.h>
52 #include <err.h>
53 #include <assert.h>
54 #include <util.h>
56 #include "makefs.h"
57 #include "hammer2.h"
59 #define APRINTF(X, ...) \
60 printf("%s: " X, __func__, ## __VA_ARGS__)
62 static void hammer2_parse_pfs_opts(const char *, fsinfo_t *);
63 static void hammer2_parse_inode_opts(const char *, fsinfo_t *);
64 static void hammer2_dump_fsinfo(fsinfo_t *);
65 static int hammer2_create_image(const char *, fsinfo_t *);
66 static int hammer2_populate_dir(struct m_vnode *, const char *, fsnode *,
67 fsnode *, fsinfo_t *, int);
68 static void hammer2_validate(const char *, fsnode *, fsinfo_t *);
69 static void hammer2_size_dir(fsnode *, fsinfo_t *);
70 static int hammer2_write_file(struct m_vnode *, const char *, fsnode *);
71 static int hammer2_version_get(struct m_vnode *);
72 static int hammer2_pfs_get(struct m_vnode *);
73 static int hammer2_pfs_lookup(struct m_vnode *, const char *);
74 static int hammer2_pfs_create(struct m_vnode *, const char *);
75 static int hammer2_pfs_delete(struct m_vnode *, const char *);
76 static int hammer2_pfs_snapshot(struct m_vnode *, const char *, const char *);
77 static int hammer2_inode_getx(struct m_vnode *, const char *);
78 static int hammer2_inode_setcheck(struct m_vnode *, const char *);
79 static int hammer2_inode_setcomp(struct m_vnode *, const char *);
80 static int hammer2_bulkfree(struct m_vnode *);
81 static int hammer2_destroy_path(struct m_vnode *, const char *);
82 static int hammer2_destroy_inum(struct m_vnode *, hammer2_tid_t);
83 static int hammer2_growfs(struct m_vnode *, hammer2_off_t);
84 static int hammer2_readx(struct m_vnode *, const char *, const char *);
85 static void unittest_trim_slash(void);
87 fsnode *hammer2_curnode;
89 void
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,
107 "emergency mode" },
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" },
114 { .name = NULL },
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 */
124 opt->DebugOpt = 1;
126 fsopts->fs_specific = h2_opt;
127 fsopts->fs_options = copy_opts(hammer2_options);
128 fsopts->sectorsize = DEV_BSIZE;
131 void
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);
139 free(h2_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 */
151 int i;
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));
160 if (i == -1)
161 return 0;
163 if (hammer2_options[i].name == NULL)
164 abort();
166 switch (hammer2_options[i].letter) {
167 case 'b':
168 opt->BootAreaSize = getsize(buf, HAMMER2_NEWFS_ALIGN,
169 HAMMER2_BOOT_MAX_BYTES, 2);
170 break;
171 case 'r':
172 opt->AuxAreaSize = getsize(buf, HAMMER2_NEWFS_ALIGN,
173 HAMMER2_AUX_MAX_BYTES, 2);
174 break;
175 case 'V':
176 if (strlen(buf) == 0) {
177 h2_opt->ioctl_cmd = HAMMER2IOC_VERSION_GET;
178 } else {
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);
186 break;
187 case 'L':
188 h2_opt->label_specified = 1;
189 if (strcasecmp(buf, "none") == 0)
190 break;
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);
199 break;
200 case 'm':
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));
207 break;
208 case 'd':
209 hammer2_debug = strtoll(buf, NULL, 0);
210 break;
211 case 'P':
212 if (strlen(buf) == 0)
213 errx(1, "PFS argument '%s' cannot be 0-length", buf);
214 hammer2_parse_pfs_opts(buf, fsopts);
215 break;
216 case 'I':
217 if (strlen(buf) == 0)
218 errx(1, "Inode argument '%s' cannot be 0-length", buf);
219 hammer2_parse_inode_opts(buf, fsopts);
220 break;
221 case 'B':
222 h2_opt->ioctl_cmd = HAMMER2IOC_BULKFREE_SCAN;
223 break;
224 case 'D':
225 h2_opt->ioctl_cmd = HAMMER2IOC_DESTROY;
226 if (strlen(buf) == 0)
227 errx(1, "Destroy argument '%s' cannot be 0-length", buf);
228 if (buf[0] == '/') {
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);
234 if (errno)
235 err(1, "strtoull");
236 } else {
237 errx(1, "Invalid destroy argument %s", buf);
239 break;
240 case 'G':
241 h2_opt->ioctl_cmd = HAMMER2IOC_GROWFS;
242 break;
243 case 'R':
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));
248 break;
249 default:
250 break;
253 if (hammer2_debug && h2_opt->ioctl_cmd)
254 unittest_trim_slash();
256 return 1;
259 void
260 hammer2_makefs(const char *image, const char *dir, fsnode *root,
261 fsinfo_t *fsopts)
263 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
264 struct mount mp;
265 struct hammer2_mount_info info;
266 struct m_vnode devvp, *vroot;
267 hammer2_inode_t *iroot;
268 struct timeval start;
269 int error;
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",
277 image, dir, root);
279 /* validate tree and options */
280 TIMER_START(start);
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);
287 if (fsopts->fd < 0)
288 err(1, "failed to open `%s'", image);
289 } else {
290 /* create image */
291 TIMER_START(start);
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)
299 putchar('\n');
301 /* vfs init */
302 error = hammer2_vfs_init();
303 if (error)
304 errx(1, "failed to vfs init, error %d", error);
306 /* mount image */
307 memset(&devvp, 0, sizeof(devvp));
308 devvp.fs = fsopts;
309 memset(&mp, 0, sizeof(mp));
310 memset(&info, 0, sizeof(info));
311 error = hammer2_vfs_mount(&devvp, &mp, h2_opt->mount_label, &info);
312 if (error)
313 errx(1, "failed to mount, error %d", error);
314 assert(mp.mnt_data);
316 /* get root vnode */
317 vroot = NULL;
318 error = hammer2_vfs_root(&mp, &vroot);
319 if (error)
320 errx(1, "failed to get root vnode, error %d", error);
321 assert(vroot);
323 iroot = VTOI(vroot);
324 assert(iroot);
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);
334 TIMER_START(start);
335 error = hammer2_version_get(vroot);
336 if (error)
337 errx(1, "version get `%s' failed '%s'", image,
338 strerror(error));
339 TIMER_RESULTS(start, "hammer2_version_get");
340 break;
341 case HAMMER2IOC_PFS_GET:
342 printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image);
343 TIMER_START(start);
344 error = hammer2_pfs_get(vroot);
345 if (error)
346 errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name,
347 image, strerror(error));
348 TIMER_RESULTS(start, "hammer2_pfs_get");
349 break;
350 case HAMMER2IOC_PFS_LOOKUP:
351 printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image);
352 TIMER_START(start);
353 error = hammer2_pfs_lookup(vroot, h2_opt->pfs_name);
354 if (error)
355 errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name,
356 image, strerror(error));
357 TIMER_RESULTS(start, "hammer2_pfs_lookup");
358 break;
359 case HAMMER2IOC_PFS_CREATE:
360 printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image);
361 TIMER_START(start);
362 error = hammer2_pfs_create(vroot, h2_opt->pfs_name);
363 if (error)
364 errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name,
365 image, strerror(error));
366 TIMER_RESULTS(start, "hammer2_pfs_create");
367 break;
368 case HAMMER2IOC_PFS_DELETE:
369 printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image);
370 TIMER_START(start);
371 error = hammer2_pfs_delete(vroot, h2_opt->pfs_name);
372 if (error)
373 errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name,
374 image, strerror(error));
375 TIMER_RESULTS(start, "hammer2_pfs_delete");
376 break;
377 case HAMMER2IOC_PFS_SNAPSHOT:
378 printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image);
379 TIMER_START(start);
380 error = hammer2_pfs_snapshot(vroot, h2_opt->pfs_name,
381 h2_opt->mount_label);
382 if (error)
383 errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name,
384 image, strerror(error));
385 TIMER_RESULTS(start, "hammer2_pfs_snapshot");
386 break;
387 case HAMMER2IOC_INODE_GET:
388 printf("inode %s `%s'\n", h2_opt->inode_cmd_name, image);
389 TIMER_START(start);
390 error = hammer2_inode_getx(vroot, h2_opt->inode_path);
391 if (error)
392 errx(1, "inode %s `%s' failed '%s'",
393 h2_opt->inode_cmd_name, image, strerror(error));
394 TIMER_RESULTS(start, "hammer2_inode_getx");
395 break;
396 case HAMMER2IOC_INODE_SET:
397 printf("inode %s `%s'\n", h2_opt->inode_cmd_name, image);
398 TIMER_START(start);
399 if (!strcmp(h2_opt->inode_cmd_name, "setcheck")) {
400 error = hammer2_inode_setcheck(vroot,
401 h2_opt->inode_path);
402 if (error)
403 errx(1, "inode %s `%s' failed '%s'",
404 h2_opt->inode_cmd_name, image,
405 strerror(error));
406 } else if (!strcmp(h2_opt->inode_cmd_name, "setcomp")) {
407 error = hammer2_inode_setcomp(vroot,
408 h2_opt->inode_path);
409 if (error)
410 errx(1, "inode %s `%s' failed '%s'",
411 h2_opt->inode_cmd_name, image,
412 strerror(error));
413 } else {
414 assert(0);
416 TIMER_RESULTS(start, "hammer2_inode_setx");
417 break;
418 case HAMMER2IOC_BULKFREE_SCAN:
419 printf("bulkfree `%s'\n", image);
420 TIMER_START(start);
421 error = hammer2_bulkfree(vroot);
422 if (error)
423 errx(1, "bulkfree `%s' failed '%s'", image,
424 strerror(error));
425 TIMER_RESULTS(start, "hammer2_bulkfree");
426 break;
427 case HAMMER2IOC_DESTROY:
428 TIMER_START(start);
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);
434 if (error)
435 errx(1, "destroy `%s' in `%s' failed '%s'",
436 h2_opt->destroy_path, image,
437 strerror(error));
438 } else {
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);
443 if (error)
444 errx(1, "destroy %lld in `%s' failed '%s'",
445 (long long)h2_opt->destroy_inum, image,
446 strerror(error));
448 TIMER_RESULTS(start, "hammer2_destroy");
449 break;
450 case HAMMER2IOC_GROWFS:
451 printf("growfs `%s'\n", image);
452 TIMER_START(start);
453 error = hammer2_growfs(vroot, h2_opt->image_size);
454 if (error)
455 errx(1, "growfs `%s' failed '%s'", image,
456 strerror(error));
457 TIMER_RESULTS(start, "hammer2_growfs");
458 break;
459 case HAMMER2IOC_READ:
460 printf("read `%s'\n", image);
461 TIMER_START(start);
462 error = hammer2_readx(vroot, dir, h2_opt->read_path);
463 if (error)
464 errx(1, "read `%s' failed '%s'", image,
465 strerror(error));
466 TIMER_RESULTS(start, "hammer2_readx");
467 break;
468 default:
469 printf("populating `%s'\n", image);
470 TIMER_START(start);
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");
474 break;
477 /* unmount image */
478 error = hammer2_vfs_unmount(&mp, 0);
479 if (error)
480 errx(1, "failed to unmount, error %d", error);
482 /* check leaked resource */
483 if (vnode_count)
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);
487 bcleanup();
489 /* vfs uninit */
490 error = hammer2_vfs_uninit();
491 if (error)
492 errx(1, "failed to vfs uninit, error %d", error);
494 if (close(fsopts->fd) == -1)
495 err(1, "closing `%s'", image);
496 fsopts->fd = -1;
498 printf("image `%s' complete\n", image);
501 /* end of public functions */
503 static void
504 hammer2_parse_pfs_opts(const char *buf, fsinfo_t *fsopts)
506 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
507 char *o, *p;
508 size_t n;
510 o = p = strdup(buf);
511 p = strchr(p, ':');
512 if (p != NULL) {
513 *p++ = 0;
514 n = strlen(p);
515 } else {
516 n = 0;
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")) {
534 if (n > NAME_MAX)
535 errx(1, "invalid PFS name \"%s\"", p);
536 h2_opt->ioctl_cmd = HAMMER2IOC_PFS_SNAPSHOT;
537 } else {
538 errx(1, "invalid PFS command \"%s\"", o);
541 strlcpy(h2_opt->pfs_cmd_name, o, sizeof(h2_opt->pfs_cmd_name));
542 if (n > 0)
543 strlcpy(h2_opt->pfs_name, p, sizeof(h2_opt->pfs_name));
545 free(o);
548 static void
549 hammer2_parse_inode_opts(const char *buf, fsinfo_t *fsopts)
551 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
552 char *o, *p;
553 size_t n;
555 o = p = strdup(buf);
556 p = strchr(p, ':');
557 if (p != NULL) {
558 *p++ = 0;
559 n = strlen(p);
560 } else {
561 n = 0;
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;
576 } else {
577 errx(1, "invalid inode command \"%s\"", o);
580 strlcpy(h2_opt->inode_cmd_name, o, sizeof(h2_opt->inode_cmd_name));
581 if (n > 0)
582 strlcpy(h2_opt->inode_path, p, sizeof(h2_opt->inode_path));
584 free(o);
587 static hammer2_off_t
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;
605 /* add data size */
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);
638 return image_size;
641 static const char *
642 hammer2_label_name(int label_type)
644 switch (label_type) {
645 case HAMMER2_LABEL_NONE:
646 return "NONE";
647 case HAMMER2_LABEL_BOOT:
648 return "BOOT";
649 case HAMMER2_LABEL_ROOT:
650 return "ROOT";
651 case HAMMER2_LABEL_DATA:
652 return "DATA";
653 default:
654 assert(0);
656 return NULL;
659 static void
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;
665 const char *s;
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;
699 else
700 goto done;
703 /* calculate data size */
704 if (fsopts->size != 0)
705 fsopts->size = 0; /* shouldn't reach here to begin with */
706 if (root == NULL)
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);
715 ignore_size_dir:
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));
727 done:
728 if (debug & DEBUG_FS_VALIDATE) {
729 APRINTF("after defaults set:\n");
730 hammer2_dump_fsinfo(fsopts);
734 static void
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;
739 int i;
740 char *s;
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);
776 s = NULL;
777 hammer2_uuid_to_str(&opt->Hammer2_FSType, &s);
778 printf("\tHammer2_FSType \"%s\"\n", s);
779 s = NULL;
780 hammer2_uuid_to_str(&opt->Hammer2_VolFSID, &s);
781 printf("\tHammer2_VolFSID \"%s\"\n", s);
782 s = NULL;
783 hammer2_uuid_to_str(&opt->Hammer2_SupCLID, &s);
784 printf("\tHammer2_SupCLID \"%s\"\n", s);
785 s = NULL;
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]);
791 s = NULL;
792 hammer2_uuid_to_str(&opt->Hammer2_PfsCLID[i], &s);
793 printf("\t Hammer2_PfsCLID[%d] \"%s\"\n", i, s);
794 s = NULL;
795 hammer2_uuid_to_str(&opt->Hammer2_PfsFSID[i], &s);
796 printf("\t Hammer2_PfsFSID[%d] \"%s\"\n", i, s);
799 free(s);
802 static int
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 */
808 char *buf;
809 int i, bufsize, oflags;
810 off_t bufrem;
812 assert(image != NULL);
813 assert(fsopts != NULL);
815 /* create image */
816 oflags = O_RDWR | O_CREAT;
817 if (fsopts->offset == 0)
818 oflags |= O_TRUNC;
819 if ((fsopts->fd = open(image, oflags, 0666)) == -1) {
820 warn("can't open `%s' for writing", image);
821 return -1;
824 /* zero 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");
830 fsopts->sparse = 0;
833 if (fsopts->sparse) {
834 /* File truncated at bufrem. Remaining is 0 */
835 bufrem = 0;
836 buf = NULL;
837 } else {
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) {
847 warn("can't seek");
848 free(buf);
849 return -1;
853 while (bufrem > 0) {
854 i = write(fsopts->fd, buf, MIN(bufsize, bufrem));
855 if (i == -1) {
856 warn("zeroing image, %lld bytes to go",
857 (long long)bufrem);
858 free(buf);
859 return -1;
861 bufrem -= i;
863 if (buf)
864 free(buf);
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 */
871 return fsopts->fd;
874 static off_t
875 hammer2_phys_size(off_t size)
877 off_t radix_size, phys_size = 0;
878 int i;
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;
889 break;
893 return phys_size;
896 /* calculate data size */
897 static void
898 hammer2_size_dir(fsnode *root, fsinfo_t *fsopts)
900 fsnode *node;
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",
916 node->name,
917 (long long)node->inode->st.st_size);
918 fsopts->inodes++;
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);
939 static void
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) {
944 if (1) {
945 int indent = depth * 2;
946 char *type;
947 if (S_ISDIR(node->type))
948 type = "dir";
949 else if (S_ISREG(node->type))
950 type = "reg";
951 else if (S_ISLNK(node->type))
952 type = "lnk";
953 else if (S_ISFIFO(node->type))
954 type = "fifo";
955 else
956 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);
962 } else {
963 char type;
964 if (S_ISDIR(node->type))
965 type = 'd';
966 else if (S_ISREG(node->type))
967 type = 'r';
968 else if (S_ISLNK(node->type))
969 type = 'l';
970 else if (S_ISFIFO(node->type))
971 type = 'f';
972 else
973 type = '?';
974 printf("%c", type);
975 fflush(stdout);
980 static int
981 hammer2_populate_dir(struct m_vnode *dvp, const char *dir, fsnode *root,
982 fsnode *parent, fsinfo_t *fsopts, int depth)
984 fsnode *cur;
985 struct m_vnode *vp;
986 struct stat st;
987 char f[MAXPATHLEN];
988 const char *path;
989 int hardlink;
990 int error;
992 assert(dvp != NULL);
993 assert(dir != NULL);
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;
1017 } else {
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);
1023 } else {
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);
1028 path = f;
1030 if (S_ISLNK(cur->type)) {
1031 if (lstat(path, &st) == -1)
1032 err(1, "no such symlink %s", path);
1033 } else {
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;
1041 if (cur != root)
1042 cur->parent = parent;
1045 /* detect hardlink */
1046 if (cur->inode->flags & FI_WRITTEN) {
1047 assert(!S_ISDIR(cur->type));
1048 hardlink = 1;
1049 } else {
1050 hardlink = 0;
1052 cur->inode->flags |= FI_WRITTEN;
1054 /* make sure it doesn't exist yet */
1055 vp = NULL;
1056 error = hammer2_nresolve(dvp, &vp, cur->name,
1057 strlen(cur->name));
1058 if (!error)
1059 errx(1, "hammer2_nresolve(\"%s\") already exists",
1060 cur->name);
1061 hammer2_print(dvp, vp, cur, depth, "nresolve");
1063 /* if directory, mkdir and recurse */
1064 if (S_ISDIR(cur->type)) {
1065 assert(cur->child);
1067 vp = NULL;
1068 error = hammer2_nmkdir(dvp, &vp, cur->name,
1069 strlen(cur->name), cur->inode->st.st_mode);
1070 if (error)
1071 errx(1, "hammer2_nmkdir(\"%s\") failed: %s",
1072 cur->name, strerror(error));
1073 assert(vp);
1074 hammer2_print(dvp, vp, cur, depth, "nmkdir");
1076 error = hammer2_populate_dir(vp, path, cur->child, cur,
1077 fsopts, depth + 1);
1078 if (error)
1079 errx(1, "failed to populate %s: %s",
1080 path, strerror(error));
1081 cur->inode->param = vp;
1082 continue;
1085 /* if regular file, creat and write its data */
1086 if (S_ISREG(cur->type) && !hardlink) {
1087 assert(cur->child == NULL);
1089 vp = NULL;
1090 error = hammer2_ncreate(dvp, &vp, cur->name,
1091 strlen(cur->name), cur->inode->st.st_mode);
1092 if (error)
1093 errx(1, "hammer2_ncreate(\"%s\") failed: %s",
1094 cur->name, strerror(error));
1095 assert(vp);
1096 hammer2_print(dvp, vp, cur, depth, "ncreate");
1098 error = hammer2_write_file(vp, path, cur);
1099 if (error)
1100 errx(1, "hammer2_write_file(\"%s\") failed: %s",
1101 path, strerror(error));
1102 cur->inode->param = vp;
1103 continue;
1106 /* if symlink, create a symlink against target */
1107 if (S_ISLNK(cur->type)) {
1108 assert(cur->child == NULL);
1110 vp = NULL;
1111 error = hammer2_nsymlink(dvp, &vp, cur->name,
1112 strlen(cur->name), cur->symlink,
1113 cur->inode->st.st_mode);
1114 if (error)
1115 errx(1, "hammer2_nsymlink(\"%s\") failed: %s",
1116 cur->name, strerror(error));
1117 assert(vp);
1118 hammer2_print(dvp, vp, cur, depth, "nsymlink");
1119 cur->inode->param = vp;
1120 continue;
1123 /* if fifo, create a fifo */
1124 if (S_ISFIFO(cur->type) && !hardlink) {
1125 assert(cur->child == NULL);
1127 vp = NULL;
1128 error = hammer2_nmknod(dvp, &vp, cur->name,
1129 strlen(cur->name), VFIFO, cur->inode->st.st_mode);
1130 if (error)
1131 errx(1, "hammer2_nmknod(\"%s\") failed: %s",
1132 cur->name, strerror(error));
1133 assert(vp);
1134 hammer2_print(dvp, vp, cur, depth, "nmknod");
1135 cur->inode->param = vp;
1136 continue;
1139 /* if hardlink, creat a hardlink */
1140 if ((S_ISREG(cur->type) || S_ISFIFO(cur->type)) && hardlink) {
1141 char buf[64];
1142 assert(cur->child == NULL);
1144 /* source vnode must not be NULL */
1145 vp = cur->inode->param;
1146 assert(vp);
1147 /* currently these conditions must be true */
1148 assert(vp->v_data);
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,
1156 strlen(cur->name));
1157 if (error)
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);
1163 continue;
1166 /* other types are unsupported */
1167 printf("ignore %s 0%o\n", path, cur->type);
1170 return 0;
1173 static int
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;
1178 off_t offset;
1179 int fd, error;
1180 char *p;
1182 nsize = st->st_size;
1183 if (nsize == 0)
1184 return 0;
1185 /* check nsize vs maximum file size */
1187 fd = open(path, O_RDONLY);
1188 if (fd < 0)
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);
1194 close(fd);
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);
1200 if (error)
1201 errx(1, "failed to write to %s vnode: %s",
1202 path, strerror(error));
1203 offset += bufsize;
1204 if (bufsize == HAMMER2_PBUFSIZE)
1205 assert((offset & (HAMMER2_PBUFSIZE - 1)) == 0);
1207 munmap(p, nsize);
1209 return 0;
1212 static int
1213 trim_char(char *p, char c)
1215 char *o, tmp[PATH_MAX];
1216 bool prev_was_c;
1217 size_t n;
1218 int i;
1220 strlcpy(tmp, p, sizeof(tmp));
1221 if (strncmp(tmp, p, sizeof(tmp)))
1222 return ENOSPC;
1224 /* trim consecutive */
1225 prev_was_c = false;
1226 o = p;
1227 n = strlen(p);
1229 for (i = 0; i < n; i++) {
1230 if (tmp[i] == c) {
1231 if (!prev_was_c)
1232 *p++ = tmp[i];
1233 prev_was_c = true;
1234 } else {
1235 *p++ = tmp[i];
1236 prev_was_c = false;
1239 *p = 0;
1240 assert(strlen(p) <= strlen(tmp));
1242 /* assert no consecutive */
1243 prev_was_c = false;
1244 p = o;
1245 n = strlen(p);
1247 for (i = 0; i < n; i++) {
1248 if (p[i] == c) {
1249 assert(!prev_was_c);
1250 prev_was_c = true;
1251 } else {
1252 prev_was_c = false;
1256 /* trim leading */
1257 if (*p == c)
1258 memmove(p, p + 1, strlen(p + 1) + 1);
1259 assert(*p != '/');
1261 /* trim trailing */
1262 p += strlen(p);
1263 p--;
1264 if (*p == c)
1265 *p = 0;
1266 assert(p[strlen(p) - 1] != '/');
1268 return 0;
1271 static int
1272 trim_slash(char *p)
1274 return trim_char(p, '/');
1277 static bool
1278 is_supported_link(const char *s)
1280 /* absolute path can't be supported */
1281 if (strlen(s) >= 1 && strncmp(s, "/", 1) == 0)
1282 return false;
1284 /* XXX ".." is currently unsupported */
1285 if (strlen(s) >= 3 && strncmp(s, "../", 3) == 0)
1286 return false;
1288 return true;
1291 static int
1292 hammer2_version_get(struct m_vnode *vp)
1294 hammer2_dev_t *hmp;
1296 hmp = VTOI(vp)->pmp->pfs_hmps[0];
1297 if (hmp == NULL)
1298 return EINVAL;
1300 printf("version: %d\n", hmp->voldata.version);
1302 return 0;
1305 struct pfs_entry {
1306 TAILQ_ENTRY(pfs_entry) entry;
1307 char name[NAME_MAX+1];
1308 char s[NAME_MAX+1];
1311 static int
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;
1317 char *pfs_id_str;
1318 const char *type_str;
1319 int error;
1321 bzero(&pfs, sizeof(pfs));
1322 TAILQ_INIT(&head);
1324 while ((pfs.name_key = pfs.name_next) != (hammer2_key_t)-1) {
1325 error = hammer2_ioctl_pfs_get(VTOI(vp), &pfs);
1326 if (error)
1327 return error;
1329 pfs_id_str = NULL;
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";
1335 else
1336 type_str = hammer2_pfssubtype_to_str(
1337 pfs.pfs_subtype);
1338 } else {
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);
1344 free(pfs_id_str);
1346 p = TAILQ_FIRST(&head);
1347 while (p) {
1348 if (strcmp(e->name, p->name) <= 0) {
1349 TAILQ_INSERT_BEFORE(p, e, entry);
1350 break;
1352 p = TAILQ_NEXT(p, entry);
1354 if (!p)
1355 TAILQ_INSERT_TAIL(&head, e, entry);
1358 printf("Type "
1359 "ClusterId (pfs_clid) "
1360 "Label\n");
1361 while ((p = TAILQ_FIRST(&head)) != NULL) {
1362 printf("%s %s\n", p->s, p->name);
1363 TAILQ_REMOVE(&head, p, entry);
1364 free(p);
1367 return 0;
1370 static int
1371 hammer2_pfs_lookup(struct m_vnode *vp, const char *pfs_name)
1373 hammer2_ioc_pfs_t pfs;
1374 char *pfs_id_str;
1375 int error;
1377 bzero(&pfs, sizeof(pfs));
1378 strlcpy(pfs.name, pfs_name, sizeof(pfs.name));
1380 error = hammer2_ioctl_pfs_lookup(VTOI(vp), &pfs);
1381 if (error == 0) {
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));
1387 pfs_id_str = NULL;
1388 hammer2_uuid_to_str(&pfs.pfs_fsid, &pfs_id_str);
1389 printf("fsid: %s\n", pfs_id_str);
1390 free(pfs_id_str);
1392 pfs_id_str = NULL;
1393 hammer2_uuid_to_str(&pfs.pfs_clid, &pfs_id_str);
1394 printf("clid: %s\n", pfs_id_str);
1395 free(pfs_id_str);
1398 return error;
1401 static int
1402 hammer2_pfs_create(struct m_vnode *vp, const char *pfs_name)
1404 hammer2_ioc_pfs_t pfs;
1405 int error;
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)
1415 fprintf(stderr,
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",
1422 pfs_name);
1424 return error;
1427 static int
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);
1438 static int
1439 hammer2_pfs_snapshot(struct m_vnode *vp, const char *pfs_name,
1440 const char *mount_label)
1442 hammer2_ioc_pfs_t pfs;
1443 struct tm *tp;
1444 time_t t;
1446 bzero(&pfs, sizeof(pfs));
1447 strlcpy(pfs.name, pfs_name, sizeof(pfs.name));
1449 if (strlen(pfs.name) == 0) {
1450 time(&t);
1451 tp = localtime(&t);
1452 snprintf(pfs.name, sizeof(pfs.name),
1453 "%s.%04d%02d%02d.%02d%02d%02d",
1454 mount_label,
1455 tp->tm_year + 1900,
1456 tp->tm_mon + 1,
1457 tp->tm_mday,
1458 tp->tm_hour,
1459 tp->tm_min,
1460 tp->tm_sec);
1463 return hammer2_ioctl_pfs_snapshot(VTOI(vp), &pfs);
1466 static int
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;
1472 struct m_vnode *vp;
1473 char *o, *p, *name, *str = NULL;
1474 char tmp[PATH_MAX];
1475 int error;
1476 uuid_t uuid;
1478 assert(strlen(f) > 0);
1479 o = p = name = strdup(f);
1481 error = trim_slash(p);
1482 if (error)
1483 return error;
1484 if (strlen(p) == 0)
1485 return EINVAL;
1487 while ((p = strchr(p, '/')) != NULL) {
1488 *p++ = 0; /* NULL terminate name */
1489 if (!strcmp(name, ".")) {
1490 name = p;
1491 continue;
1493 vp = NULL;
1494 error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1495 if (error)
1496 return error;
1498 ip = VTOI(vp);
1499 switch (ip->meta.type) {
1500 case HAMMER2_OBJTYPE_DIRECTORY:
1501 break;
1502 case HAMMER2_OBJTYPE_SOFTLINK:
1503 bzero(tmp, sizeof(tmp));
1504 error = hammer2_readlink(vp, tmp, sizeof(tmp));
1505 if (error)
1506 return error;
1507 if (!is_supported_link(tmp))
1508 return EINVAL;
1509 strlcat(tmp, "/", sizeof(tmp));
1510 strlcat(tmp, p, sizeof(tmp));
1511 error = trim_slash(tmp);
1512 if (error)
1513 return error;
1514 p = name = tmp;
1515 continue;
1516 default:
1517 return EINVAL;
1520 dvp = vp;
1521 name = p;
1524 error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1525 if (error)
1526 return error;
1528 bzero(&inode, sizeof(inode));
1529 error = hammer2_ioctl_inode_get(VTOI(vp), &inode);
1530 if (error)
1531 return error;
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));
1549 uuid = meta->uid;
1550 printf("uid = %s\n", hammer2_uuid_to_str(&uuid, &str));
1551 uuid = meta->gid;
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");
1582 free(o);
1584 return error;
1587 static int
1588 hammer2_inode_setcheck(struct m_vnode *dvp, const char *f)
1590 hammer2_ioc_inode_t inode;
1591 hammer2_inode_t *ip;
1592 struct m_vnode *vp;
1593 char *o, *p, *name, *check_algo_str;
1594 char tmp[PATH_MAX];
1595 const char *checks[] = { "none", "disabled", "crc32", "xxhash64",
1596 "sha192", };
1597 int check_algo_idx, error;
1598 uint8_t check_algo;
1600 assert(strlen(f) > 0);
1601 o = p = strdup(f);
1603 p = strrchr(p, ':');
1604 if (p == NULL)
1605 return EINVAL;
1607 *p++ = 0; /* NULL terminate path */
1608 check_algo_str = p;
1609 name = p = o;
1611 error = trim_slash(p);
1612 if (error)
1613 return error;
1614 if (strlen(p) == 0 || strlen(check_algo_str) == 0)
1615 return EINVAL;
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)
1621 break;
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;
1629 } else {
1630 printf("invalid check_algo_str: %s\n", check_algo_str);
1631 return EINVAL;
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, ".")) {
1640 name = p;
1641 continue;
1643 vp = NULL;
1644 error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1645 if (error)
1646 return error;
1648 ip = VTOI(vp);
1649 switch (ip->meta.type) {
1650 case HAMMER2_OBJTYPE_DIRECTORY:
1651 break;
1652 case HAMMER2_OBJTYPE_SOFTLINK:
1653 bzero(tmp, sizeof(tmp));
1654 error = hammer2_readlink(vp, tmp, sizeof(tmp));
1655 if (error)
1656 return error;
1657 if (!is_supported_link(tmp))
1658 return EINVAL;
1659 strlcat(tmp, "/", sizeof(tmp));
1660 strlcat(tmp, p, sizeof(tmp));
1661 error = trim_slash(tmp);
1662 if (error)
1663 return error;
1664 p = name = tmp;
1665 continue;
1666 default:
1667 return EINVAL;
1670 dvp = vp;
1671 name = p;
1674 error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1675 if (error)
1676 return error;
1677 ip = VTOI(vp);
1679 bzero(&inode, sizeof(inode));
1680 error = hammer2_ioctl_inode_get(ip, &inode);
1681 if (error)
1682 return error;
1684 inode.flags |= HAMMER2IOC_INODE_FLAG_CHECK;
1685 inode.ip_data.meta.check_algo = check_algo;
1686 error = hammer2_ioctl_inode_set(ip, &inode);
1687 if (error)
1688 return error;
1690 free(o);
1692 return error;
1695 static int
1696 hammer2_inode_setcomp(struct m_vnode *dvp, const char *f)
1698 hammer2_ioc_inode_t inode;
1699 hammer2_inode_t *ip;
1700 struct m_vnode *vp;
1701 char *o, *p, *name, *comp_algo_str, *comp_level_str;
1702 char tmp[PATH_MAX];
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);
1708 o = p = strdup(f);
1710 p = strrchr(p, ':');
1711 if (p == NULL)
1712 return EINVAL;
1714 *p++ = 0; /* NULL terminate comp_algo_str */
1715 comp_level_str = p;
1716 p = o;
1718 p = strrchr(p, ':');
1719 if (p == NULL) {
1720 /* comp_level_str not specified */
1721 comp_algo_str = comp_level_str;
1722 comp_level_str = NULL;
1723 } else {
1724 *p++ = 0; /* NULL terminate path */
1725 comp_algo_str = p;
1727 name = p = o;
1729 error = trim_slash(p);
1730 if (error)
1731 return error;
1732 if (strlen(p) == 0 || strlen(comp_algo_str) == 0)
1733 return EINVAL;
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)
1739 break;
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;
1747 } else {
1748 printf("invalid comp_algo_str: %s\n", comp_algo_str);
1749 return EINVAL;
1752 comp_algo = HAMMER2_ENC_ALGO(comp_algo_idx);
1754 /* convert comp_level_str to comp_level_idx */
1755 if (comp_level_str == NULL) {
1756 comp_level_idx = 0;
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) {
1760 comp_level_idx = 0;
1761 } else {
1762 printf("invalid comp_level_str: %s\n", comp_level_str);
1763 return EINVAL;
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);
1771 return EINVAL;
1773 break;
1774 default:
1775 printf("unsupported comp_level %d for %s\n",
1776 comp_level_idx, comp_algo_str);
1777 return EINVAL;
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, ".")) {
1787 name = p;
1788 continue;
1790 vp = NULL;
1791 error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1792 if (error)
1793 return error;
1795 ip = VTOI(vp);
1796 switch (ip->meta.type) {
1797 case HAMMER2_OBJTYPE_DIRECTORY:
1798 break;
1799 case HAMMER2_OBJTYPE_SOFTLINK:
1800 bzero(tmp, sizeof(tmp));
1801 error = hammer2_readlink(vp, tmp, sizeof(tmp));
1802 if (error)
1803 return error;
1804 if (!is_supported_link(tmp))
1805 return EINVAL;
1806 strlcat(tmp, "/", sizeof(tmp));
1807 strlcat(tmp, p, sizeof(tmp));
1808 error = trim_slash(tmp);
1809 if (error)
1810 return error;
1811 p = name = tmp;
1812 continue;
1813 default:
1814 return EINVAL;
1817 dvp = vp;
1818 name = p;
1821 error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1822 if (error)
1823 return error;
1824 ip = VTOI(vp);
1826 bzero(&inode, sizeof(inode));
1827 error = hammer2_ioctl_inode_get(ip, &inode);
1828 if (error)
1829 return error;
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);
1834 if (error)
1835 return error;
1837 free(o);
1839 return error;
1842 static int
1843 hammer2_bulkfree(struct m_vnode *vp)
1845 hammer2_ioc_bulkfree_t bfi;
1846 size_t usermem;
1847 size_t usermem_size = sizeof(usermem);
1849 bzero(&bfi, sizeof(bfi));
1850 usermem = 0;
1851 if (sysctlbyname("hw.usermem", &usermem, &usermem_size, NULL, 0) == 0)
1852 bfi.size = usermem / 16;
1853 else
1854 bfi.size = 0;
1855 if (bfi.size < 8192 * 1024)
1856 bfi.size = 8192 * 1024;
1858 return hammer2_ioctl_bulkfree_scan(VTOI(vp), &bfi);
1861 static int
1862 hammer2_destroy_path(struct m_vnode *dvp, const char *f)
1864 hammer2_ioc_destroy_t destroy;
1865 hammer2_inode_t *ip;
1866 struct m_vnode *vp;
1867 char *o, *p, *name;
1868 char tmp[PATH_MAX];
1869 int error;
1871 assert(strlen(f) > 0);
1872 o = p = name = strdup(f);
1874 error = trim_slash(p);
1875 if (error)
1876 return error;
1877 if (strlen(p) == 0)
1878 return EINVAL;
1880 while ((p = strchr(p, '/')) != NULL) {
1881 *p++ = 0; /* NULL terminate name */
1882 if (!strcmp(name, ".")) {
1883 name = p;
1884 continue;
1886 vp = NULL;
1887 error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1888 if (error)
1889 return error;
1891 ip = VTOI(vp);
1892 switch (ip->meta.type) {
1893 case HAMMER2_OBJTYPE_DIRECTORY:
1894 break;
1895 case HAMMER2_OBJTYPE_SOFTLINK:
1896 bzero(tmp, sizeof(tmp));
1897 error = hammer2_readlink(vp, tmp, sizeof(tmp));
1898 if (error)
1899 return error;
1900 if (!is_supported_link(tmp))
1901 return EINVAL;
1902 strlcat(tmp, "/", sizeof(tmp));
1903 strlcat(tmp, p, sizeof(tmp));
1904 error = trim_slash(tmp);
1905 if (error)
1906 return error;
1907 p = name = tmp;
1908 continue;
1909 default:
1910 return EINVAL;
1913 dvp = vp;
1914 name = p;
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);
1924 printf("%s\t", f);
1925 fflush(stdout);
1927 error = hammer2_ioctl_destroy(VTOI(dvp), &destroy);
1928 if (error)
1929 printf("%s\n", strerror(error));
1930 else
1931 printf("ok\n");
1932 free(o);
1934 return error;
1937 static int
1938 hammer2_destroy_inum(struct m_vnode *vp, hammer2_tid_t inum)
1940 hammer2_ioc_destroy_t destroy;
1941 int error;
1943 bzero(&destroy, sizeof(destroy));
1944 destroy.cmd = HAMMER2_DELETE_INUM;
1945 destroy.inum = inum;
1947 printf("%jd\t", (intmax_t)destroy.inum);
1948 fflush(stdout);
1950 error = hammer2_ioctl_destroy(VTOI(vp), &destroy);
1951 if (error)
1952 printf("%s\n", strerror(error));
1953 else
1954 printf("ok\n");
1956 return error;
1959 static int
1960 hammer2_growfs(struct m_vnode *vp, hammer2_off_t size)
1962 hammer2_ioc_growfs_t growfs;
1963 int error;
1965 bzero(&growfs, sizeof(growfs));
1966 growfs.size = size;
1968 error = hammer2_ioctl_growfs(VTOI(vp), &growfs, NULL);
1969 if (!error) {
1970 if (growfs.modified)
1971 printf("grown to %016jx\n", (intmax_t)growfs.size);
1972 else
1973 printf("no size change - %016jx\n",
1974 (intmax_t)growfs.size);
1977 return error;
1980 static int
1981 hammer2_readx(struct m_vnode *dvp, const char *dir, const char *f)
1983 hammer2_inode_t *ip;
1984 struct m_vnode *vp;
1985 char *o, *p, *name, *buf;
1986 char tmp[PATH_MAX], out[PATH_MAX];
1987 size_t resid, n;
1988 off_t offset;
1989 int fd, error;
1991 if (dir == NULL)
1992 return EINVAL;
1994 assert(strlen(f) > 0);
1995 o = p = name = strdup(f);
1997 error = trim_slash(p);
1998 if (error)
1999 return error;
2000 if (strlen(p) == 0)
2001 return EINVAL;
2003 while ((p = strchr(p, '/')) != NULL) {
2004 *p++ = 0; /* NULL terminate name */
2005 if (!strcmp(name, ".")) {
2006 name = p;
2007 continue;
2009 vp = NULL;
2010 error = hammer2_nresolve(dvp, &vp, name, strlen(name));
2011 if (error)
2012 return error;
2014 ip = VTOI(vp);
2015 switch (ip->meta.type) {
2016 case HAMMER2_OBJTYPE_DIRECTORY:
2017 break;
2018 case HAMMER2_OBJTYPE_SOFTLINK:
2019 bzero(tmp, sizeof(tmp));
2020 error = hammer2_readlink(vp, tmp, sizeof(tmp));
2021 if (error)
2022 return error;
2023 if (!is_supported_link(tmp))
2024 return EINVAL;
2025 strlcat(tmp, "/", sizeof(tmp));
2026 strlcat(tmp, p, sizeof(tmp));
2027 error = trim_slash(tmp);
2028 if (error)
2029 return error;
2030 p = name = tmp;
2031 continue;
2032 default:
2033 return EINVAL;
2036 dvp = vp;
2037 name = p;
2040 error = hammer2_nresolve(dvp, &vp, name, strlen(name));
2041 if (error)
2042 return error;
2043 ip = VTOI(vp);
2045 snprintf(out, sizeof(out), "%s/%s", dir, name);
2046 fd = open(out, O_WRONLY | O_CREAT | O_TRUNC, 0666);
2047 if (fd == -1)
2048 err(1, "failed to create %s", out);
2050 buf = calloc(1, HAMMER2_PBUFSIZE);
2051 resid = ip->meta.size;
2052 offset = 0;
2054 while (resid > 0) {
2055 bzero(buf, HAMMER2_PBUFSIZE);
2056 error = hammer2_read(vp, buf, HAMMER2_PBUFSIZE, offset);
2057 if (error)
2058 errx(1, "failed to read from %s", name);
2060 n = resid >= HAMMER2_PBUFSIZE ? HAMMER2_PBUFSIZE : resid;
2061 error = write(fd, buf, n);
2062 if (error == -1)
2063 err(1, "failed to write to %s", out);
2064 else if (error != n)
2065 return EINVAL;
2067 resid -= n;
2068 offset += HAMMER2_PBUFSIZE;
2070 fsync(fd);
2071 close(fd);
2073 free(buf);
2074 free(o);
2076 return 0;
2079 static void
2080 assert_trim_slash(const char *input, const char *expected)
2082 char tmp[PATH_MAX];
2083 int error;
2085 strlcpy(tmp, input, sizeof(tmp));
2086 error = trim_slash(tmp);
2087 if (error)
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);
2095 static void
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");