usr.sbin/makefs: Add offline HAMMER2IOC_VERSION_GET support
[dragonfly.git] / usr.sbin / makefs / hammer2.c
blob9eda343726723e290e5cf1bb24e06c5987b49a20
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 <unistd.h>
49 #include <fcntl.h>
50 #include <time.h>
51 #include <err.h>
52 #include <assert.h>
53 #include <util.h>
55 #include "makefs.h"
56 #include "hammer2.h"
58 #define APRINTF(X, ...) \
59 printf("%s: " X, __func__, ## __VA_ARGS__)
61 static void hammer2_parse_pfs_opts(const char *, fsinfo_t *);
62 static void hammer2_dump_fsinfo(fsinfo_t *);
63 static int hammer2_create_image(const char *, fsinfo_t *);
64 static int hammer2_populate_dir(struct m_vnode *, const char *, fsnode *,
65 fsnode *, fsinfo_t *, int);
66 static void hammer2_validate(const char *, fsnode *, fsinfo_t *);
67 static void hammer2_size_dir(fsnode *, fsinfo_t *);
68 static int hammer2_write_file(struct m_vnode *, const char *, fsnode *);
69 static int hammer2_version_get(struct m_vnode *);
70 static int hammer2_pfs_get(struct m_vnode *);
71 static int hammer2_pfs_lookup(struct m_vnode *, const char *);
72 static int hammer2_pfs_create(struct m_vnode *, const char *);
73 static int hammer2_pfs_delete(struct m_vnode *, const char *);
74 static int hammer2_pfs_snapshot(struct m_vnode *, const char *, const char *);
75 static int hammer2_bulkfree(struct m_vnode *);
76 static int hammer2_destroy_path(struct m_vnode *, const char *);
77 static int hammer2_destroy_inum(struct m_vnode *, hammer2_tid_t);
78 static int hammer2_growfs(struct m_vnode *, hammer2_off_t);
80 fsnode *hammer2_curnode;
82 void
83 hammer2_prep_opts(fsinfo_t *fsopts)
85 hammer2_makefs_options_t *h2_opt = ecalloc(1, sizeof(*h2_opt));
86 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
88 const option_t hammer2_options[] = {
89 /* newfs_hammer2(8) compatible options */
90 { 'b', "BootAreaSize", NULL, OPT_STRBUF, 0, 0, "boot area size" },
91 { 'r', "AuxAreaSize", NULL, OPT_STRBUF, 0, 0, "aux area size" },
92 { 'V', "Hammer2Version", NULL, OPT_STRBUF, 0, 0, "file system version" },
93 { 'L', "Label", NULL, OPT_STRBUF, 0, 0, "PFS label" },
94 /* makefs(8) specific options */
95 { 'm', "MountLabel", NULL, OPT_STRBUF, 0, 0, "destination PFS label" },
96 { 'v', "NumVolhdr", &h2_opt->num_volhdr, OPT_INT32,
97 1, HAMMER2_NUM_VOLHDRS, "number of volume headers" },
98 { 'd', "Hammer2Debug", NULL, OPT_STRBUF, 0, 0, "debug tunable" },
99 { 'E', "EmergencyMode", &h2_opt->emergency_mode, OPT_BOOL, 0, 0,
100 "emergency mode" },
101 { 'P', "PFS", NULL, OPT_STRBUF, 0, 0, "offline PFS" },
102 { 'B', "Bulkfree", NULL, OPT_STRBUF, 0, 0, "offline bulkfree" },
103 { 'D', "Destroy", NULL, OPT_STRBUF, 0, 0, "offline destroy" },
104 { 'G', "Growfs", NULL, OPT_STRBUF, 0, 0, "offline growfs" },
105 { .name = NULL },
108 hammer2_mkfs_init(opt);
110 /* make this tunable ? */
111 assert(opt->CompType == HAMMER2_COMP_NEWFS_DEFAULT);
112 assert(opt->CheckType == HAMMER2_CHECK_XXHASH64);
114 /* force debug mode for mkfs */
115 opt->DebugOpt = 1;
117 fsopts->fs_specific = h2_opt;
118 fsopts->fs_options = copy_opts(hammer2_options);
119 fsopts->sectorsize = DEV_BSIZE;
122 void
123 hammer2_cleanup_opts(fsinfo_t *fsopts)
125 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
126 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
128 hammer2_mkfs_cleanup(opt);
130 free(h2_opt);
131 free(fsopts->fs_options);
135 hammer2_parse_opts(const char *option, fsinfo_t *fsopts)
137 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
138 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
140 option_t *hammer2_options = fsopts->fs_options;
141 char buf[1024]; /* > HAMMER2_INODE_MAXNAME */
142 int i;
144 assert(option != NULL);
145 assert(fsopts != NULL);
147 if (debug & DEBUG_FS_PARSE_OPTS)
148 APRINTF("got `%s'\n", option);
150 i = set_option(hammer2_options, option, buf, sizeof(buf));
151 if (i == -1)
152 return 0;
154 if (hammer2_options[i].name == NULL)
155 abort();
157 switch (hammer2_options[i].letter) {
158 case 'b':
159 opt->BootAreaSize = getsize(buf, HAMMER2_NEWFS_ALIGN,
160 HAMMER2_BOOT_MAX_BYTES, 2);
161 break;
162 case 'r':
163 opt->AuxAreaSize = getsize(buf, HAMMER2_NEWFS_ALIGN,
164 HAMMER2_AUX_MAX_BYTES, 2);
165 break;
166 case 'V':
167 if (strlen(buf) == 0) {
168 h2_opt->ioctl_cmd = HAMMER2IOC_VERSION_GET;
169 } else {
170 opt->Hammer2Version = strtol(buf, NULL, 0);
171 if (opt->Hammer2Version < HAMMER2_VOL_VERSION_MIN ||
172 opt->Hammer2Version >= HAMMER2_VOL_VERSION_WIP)
173 errx(1, "I don't understand how to format "
174 "HAMMER2 version %d",
175 opt->Hammer2Version);
177 break;
178 case 'L':
179 h2_opt->label_specified = 1;
180 if (strcasecmp(buf, "none") == 0)
181 break;
182 if (opt->NLabels >= MAXLABELS)
183 errx(1, "Limit of %d local labels", MAXLABELS - 1);
184 if (strlen(buf) == 0)
185 errx(1, "Volume label '%s' cannot be 0-length", buf);
186 if (strlen(buf) >= HAMMER2_INODE_MAXNAME)
187 errx(1, "Volume label '%s' is too long (%d chars max)",
188 buf, HAMMER2_INODE_MAXNAME - 1);
189 opt->Label[opt->NLabels++] = strdup(buf);
190 break;
191 case 'm':
192 if (strlen(buf) == 0)
193 errx(1, "Volume label '%s' cannot be 0-length", buf);
194 if (strlen(buf) >= HAMMER2_INODE_MAXNAME)
195 errx(1, "Volume label '%s' is too long (%d chars max)",
196 buf, HAMMER2_INODE_MAXNAME - 1);
197 strlcpy(h2_opt->mount_label, buf, sizeof(h2_opt->mount_label));
198 break;
199 case 'd':
200 hammer2_debug = strtoll(buf, NULL, 0);
201 break;
202 case 'P':
203 if (strlen(buf) == 0)
204 errx(1, "PFS argument '%s' cannot be 0-length", buf);
205 hammer2_parse_pfs_opts(buf, fsopts);
206 break;
207 case 'B':
208 h2_opt->ioctl_cmd = HAMMER2IOC_BULKFREE_SCAN;
209 break;
210 case 'D':
211 h2_opt->ioctl_cmd = HAMMER2IOC_DESTROY;
212 if (strlen(buf) == 0)
213 errx(1, "Destroy argument '%s' cannot be 0-length", buf);
214 if (buf[0] == '/') {
215 strlcpy(h2_opt->destroy_path, buf + 1,
216 sizeof(h2_opt->destroy_path));
217 } else if (strncmp(buf, "0x", 2) == 0 ||
218 (buf[0] >= '0' && buf[0] <= '9')) {
219 h2_opt->destroy_inum = strtoull(buf, NULL, 0);
220 if (errno)
221 err(1, "strtoull");
222 } else {
223 errx(1, "Invalid destroy argument %s", buf);
225 break;
226 case 'G':
227 h2_opt->ioctl_cmd = HAMMER2IOC_GROWFS;
228 break;
229 default:
230 break;
233 return 1;
236 void
237 hammer2_makefs(const char *image, const char *dir, fsnode *root,
238 fsinfo_t *fsopts)
240 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
241 struct mount mp;
242 struct hammer2_mount_info info;
243 struct m_vnode devvp, *vroot;
244 hammer2_inode_t *iroot;
245 struct timeval start;
246 int error;
248 /* ioctl commands could have NULL root */
249 assert(image != NULL);
250 assert(dir != NULL);
251 assert(fsopts != NULL);
253 if (debug & DEBUG_FS_MAKEFS)
254 APRINTF("image \"%s\" directory \"%s\" root %p\n",
255 image, dir, root);
257 /* validate tree and options */
258 TIMER_START(start);
259 hammer2_validate(dir, root, fsopts);
260 TIMER_RESULTS(start, "hammer2_validate");
262 if (h2_opt->ioctl_cmd) {
263 /* open existing image */
264 fsopts->fd = open(image, O_RDWR);
265 if (fsopts->fd < 0)
266 err(1, "failed to open `%s'", image);
267 } else {
268 /* create image */
269 TIMER_START(start);
270 if (hammer2_create_image(image, fsopts) == -1)
271 errx(1, "image file `%s' not created", image);
272 TIMER_RESULTS(start, "hammer2_create_image");
274 assert(fsopts->fd > 0);
276 if (debug & DEBUG_FS_MAKEFS)
277 putchar('\n');
279 /* vfs init */
280 error = hammer2_vfs_init();
281 if (error)
282 errx(1, "failed to vfs init, error %d", error);
284 /* mount image */
285 memset(&devvp, 0, sizeof(devvp));
286 devvp.fs = fsopts;
287 memset(&mp, 0, sizeof(mp));
288 memset(&info, 0, sizeof(info));
289 error = hammer2_vfs_mount(&devvp, &mp, h2_opt->mount_label, &info);
290 if (error)
291 errx(1, "failed to mount, error %d", error);
292 assert(mp.mnt_data);
294 /* get root vnode */
295 vroot = NULL;
296 error = hammer2_vfs_root(&mp, &vroot);
297 if (error)
298 errx(1, "failed to get root vnode, error %d", error);
299 assert(vroot);
301 iroot = VTOI(vroot);
302 assert(iroot);
303 printf("root inode inum %lld, mode 0%o, refs %d\n",
304 (long long)iroot->meta.inum, iroot->meta.mode, iroot->refs);
306 if (h2_opt->emergency_mode)
307 hammer2_ioctl_emerg_mode(iroot, 1);
309 switch (h2_opt->ioctl_cmd) {
310 case HAMMER2IOC_VERSION_GET:
311 printf("version get `%s'\n", image);
312 TIMER_START(start);
313 error = hammer2_version_get(vroot);
314 if (error)
315 errx(1, "version get `%s' failed '%s'", image,
316 strerror(error));
317 TIMER_RESULTS(start, "hammer2_version_get");
318 break;
319 case HAMMER2IOC_PFS_GET:
320 printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image);
321 TIMER_START(start);
322 error = hammer2_pfs_get(vroot);
323 if (error)
324 errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name,
325 image, strerror(error));
326 TIMER_RESULTS(start, "hammer2_pfs_get");
327 break;
328 case HAMMER2IOC_PFS_LOOKUP:
329 printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image);
330 TIMER_START(start);
331 error = hammer2_pfs_lookup(vroot, h2_opt->pfs_name);
332 if (error)
333 errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name,
334 image, strerror(error));
335 TIMER_RESULTS(start, "hammer2_pfs_lookup");
336 break;
337 case HAMMER2IOC_PFS_CREATE:
338 printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image);
339 TIMER_START(start);
340 error = hammer2_pfs_create(vroot, h2_opt->pfs_name);
341 if (error)
342 errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name,
343 image, strerror(error));
344 TIMER_RESULTS(start, "hammer2_pfs_create");
345 break;
346 case HAMMER2IOC_PFS_DELETE:
347 printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image);
348 TIMER_START(start);
349 error = hammer2_pfs_delete(vroot, h2_opt->pfs_name);
350 if (error)
351 errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name,
352 image, strerror(error));
353 TIMER_RESULTS(start, "hammer2_pfs_delete");
354 break;
355 case HAMMER2IOC_PFS_SNAPSHOT:
356 printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image);
357 TIMER_START(start);
358 error = hammer2_pfs_snapshot(vroot, h2_opt->pfs_name,
359 h2_opt->mount_label);
360 if (error)
361 errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name,
362 image, strerror(error));
363 TIMER_RESULTS(start, "hammer2_pfs_snapshot");
364 break;
365 case HAMMER2IOC_BULKFREE_SCAN:
366 printf("bulkfree `%s'\n", image);
367 TIMER_START(start);
368 error = hammer2_bulkfree(vroot);
369 if (error)
370 errx(1, "bulkfree `%s' failed '%s'", image,
371 strerror(error));
372 TIMER_RESULTS(start, "hammer2_bulkfree");
373 break;
374 case HAMMER2IOC_DESTROY:
375 TIMER_START(start);
376 if (strlen(h2_opt->destroy_path)) {
377 printf("destroy `%s' in `%s'\n",
378 h2_opt->destroy_path, image);
379 error = hammer2_destroy_path(vroot,
380 h2_opt->destroy_path);
381 if (error)
382 errx(1, "destroy `%s' in `%s' failed '%s'",
383 h2_opt->destroy_path, image,
384 strerror(error));
385 } else {
386 printf("destroy %lld in `%s'\n",
387 (long long)h2_opt->destroy_inum, image);
388 error = hammer2_destroy_inum(vroot,
389 h2_opt->destroy_inum);
390 if (error)
391 errx(1, "destroy %lld in `%s' failed '%s'",
392 (long long)h2_opt->destroy_inum, image,
393 strerror(error));
395 TIMER_RESULTS(start, "hammer2_destroy");
396 break;
397 case HAMMER2IOC_GROWFS:
398 printf("growfs `%s'\n", image);
399 TIMER_START(start);
400 error = hammer2_growfs(vroot, h2_opt->image_size);
401 if (error)
402 errx(1, "growfs `%s' failed '%s'", image,
403 strerror(error));
404 TIMER_RESULTS(start, "hammer2_growfs");
405 break;
406 default:
407 printf("populating `%s'\n", image);
408 TIMER_START(start);
409 if (hammer2_populate_dir(vroot, dir, root, root, fsopts, 0))
410 errx(1, "image file `%s' not populated", image);
411 TIMER_RESULTS(start, "hammer2_populate_dir");
412 break;
415 /* unmount image */
416 error = hammer2_vfs_unmount(&mp, 0);
417 if (error)
418 errx(1, "failed to unmount, error %d", error);
420 /* check leaked resource */
421 if (vnode_count)
422 printf("XXX %lld vnode left\n", (long long)vnode_count);
423 if (hammer2_chain_allocs)
424 printf("XXX %ld chain left\n", hammer2_chain_allocs);
425 bcleanup();
427 /* vfs uninit */
428 error = hammer2_vfs_uninit();
429 if (error)
430 errx(1, "failed to vfs uninit, error %d", error);
432 if (close(fsopts->fd) == -1)
433 err(1, "closing `%s'", image);
434 fsopts->fd = -1;
436 printf("image `%s' complete\n", image);
439 /* end of public functions */
441 static void
442 hammer2_parse_pfs_opts(const char *buf, fsinfo_t *fsopts)
444 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
445 char *o, *p;
446 size_t n;
448 o = p = strdup(buf);
449 p = strchr(p, ':');
450 if (p != NULL) {
451 *p++ = 0;
452 n = strlen(p);
453 } else {
454 n = 0;
457 if (!strcmp(o, "get") || !strcmp(o, "list")) {
458 h2_opt->ioctl_cmd = HAMMER2IOC_PFS_GET;
459 } else if (!strcmp(o, "lookup")) {
460 if (n == 0 || n > NAME_MAX)
461 errx(1, "invalid PFS name \"%s\"", p);
462 h2_opt->ioctl_cmd = HAMMER2IOC_PFS_LOOKUP;
463 } else if (!strcmp(o, "create")) {
464 if (n == 0 || n > NAME_MAX)
465 errx(1, "invalid PFS name \"%s\"", p);
466 h2_opt->ioctl_cmd = HAMMER2IOC_PFS_CREATE;
467 } else if (!strcmp(o, "delete")) {
468 if (n == 0 || n > NAME_MAX)
469 errx(1, "invalid PFS name \"%s\"", p);
470 h2_opt->ioctl_cmd = HAMMER2IOC_PFS_DELETE;
471 } else if (!strcmp(o, "snapshot")) {
472 if (n > NAME_MAX)
473 errx(1, "invalid PFS name \"%s\"", p);
474 h2_opt->ioctl_cmd = HAMMER2IOC_PFS_SNAPSHOT;
475 } else {
476 errx(1, "invalid PFS command \"%s\"", o);
479 strlcpy(h2_opt->pfs_cmd_name, o, sizeof(h2_opt->pfs_cmd_name));
480 if (n > 0)
481 strlcpy(h2_opt->pfs_name, p, sizeof(h2_opt->pfs_name));
483 free(o);
486 static hammer2_off_t
487 hammer2_image_size(fsinfo_t *fsopts)
489 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
490 hammer2_off_t image_size, used_size = 0;
491 int num_level1, delta_num_level1;
493 /* use 4 volume headers by default */
494 num_level1 = h2_opt->num_volhdr * 2; /* default 4 x 2 */
495 assert(num_level1 != 0);
496 assert(num_level1 <= 8);
498 /* add 4MiB segment for each level1 */
499 used_size += HAMMER2_ZONE_SEG64 * num_level1;
501 /* add boot/aux area, but exact size unknown at this point */
502 used_size += HAMMER2_BOOT_NOM_BYTES + HAMMER2_AUX_NOM_BYTES;
504 /* add data size */
505 used_size += fsopts->size;
507 /* XXX add extra level1 for meta data and indirect blocks */
508 used_size += HAMMER2_FREEMAP_LEVEL1_SIZE;
510 /* XXX add extra level1 for safety */
511 if (used_size > HAMMER2_FREEMAP_LEVEL1_SIZE * 10)
512 used_size += HAMMER2_FREEMAP_LEVEL1_SIZE;
514 /* use 8GiB image size by default */
515 image_size = HAMMER2_FREEMAP_LEVEL1_SIZE * num_level1;
516 printf("trying default image size %s\n", sizetostr(image_size));
518 /* adjust if image size isn't large enough */
519 if (used_size > image_size) {
520 /* determine extra level1 needed */
521 delta_num_level1 = howmany(used_size - image_size,
522 HAMMER2_FREEMAP_LEVEL1_SIZE);
524 /* adjust used size with 4MiB segment for each extra level1 */
525 used_size += HAMMER2_ZONE_SEG64 * delta_num_level1;
527 /* adjust image size with extra level1 */
528 image_size += HAMMER2_FREEMAP_LEVEL1_SIZE * delta_num_level1;
529 printf("trying adjusted image size %s\n",
530 sizetostr(image_size));
532 if (used_size > image_size)
533 errx(1, "invalid used_size %lld > image_size %lld",
534 (long long)used_size, (long long)image_size);
537 return image_size;
540 static const char *
541 hammer2_label_name(int label_type)
543 switch (label_type) {
544 case HAMMER2_LABEL_NONE:
545 return "NONE";
546 case HAMMER2_LABEL_BOOT:
547 return "BOOT";
548 case HAMMER2_LABEL_ROOT:
549 return "ROOT";
550 case HAMMER2_LABEL_DATA:
551 return "DATA";
552 default:
553 assert(0);
555 return NULL;
558 static void
559 hammer2_validate(const char *dir, fsnode *root, fsinfo_t *fsopts)
561 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
562 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
563 hammer2_off_t image_size = 0, minsize, maxsize;
564 const char *s;
566 assert(dir != NULL);
567 assert(fsopts != NULL);
569 if (debug & DEBUG_FS_VALIDATE) {
570 APRINTF("before defaults set:\n");
571 hammer2_dump_fsinfo(fsopts);
574 /* makefs only supports "DATA" for default PFS label */
575 if (!h2_opt->label_specified) {
576 opt->DefaultLabelType = HAMMER2_LABEL_DATA;
577 s = hammer2_label_name(opt->DefaultLabelType);
578 printf("using default label \"%s\"\n", s);
581 /* set default mount PFS label */
582 if (!strcmp(h2_opt->mount_label, "")) {
583 s = hammer2_label_name(HAMMER2_LABEL_DATA);
584 strlcpy(h2_opt->mount_label, s, sizeof(h2_opt->mount_label));
585 printf("using default mount label \"%s\"\n", s);
588 /* set default number of volume headers */
589 if (!h2_opt->num_volhdr) {
590 h2_opt->num_volhdr = HAMMER2_NUM_VOLHDRS;
591 printf("using default %d volume headers\n", h2_opt->num_volhdr);
594 /* done if ioctl commands */
595 if (h2_opt->ioctl_cmd) {
596 if (h2_opt->ioctl_cmd == HAMMER2IOC_GROWFS)
597 goto ignore_size_dir;
598 else
599 goto done;
602 /* calculate data size */
603 if (fsopts->size != 0)
604 fsopts->size = 0; /* shouldn't reach here to begin with */
605 if (root == NULL)
606 errx(1, "fsnode tree not constructed");
607 hammer2_size_dir(root, fsopts);
608 printf("estimated data size %s from %lld inode\n",
609 sizetostr(fsopts->size), (long long)fsopts->inodes);
611 /* determine image size from data size */
612 image_size = hammer2_image_size(fsopts);
613 assert((image_size & HAMMER2_FREEMAP_LEVEL1_MASK) == 0);
614 ignore_size_dir:
615 minsize = roundup(fsopts->minsize, HAMMER2_FREEMAP_LEVEL1_SIZE);
616 maxsize = roundup(fsopts->maxsize, HAMMER2_FREEMAP_LEVEL1_SIZE);
617 if (image_size < minsize)
618 image_size = minsize;
619 else if (maxsize > 0 && image_size > maxsize)
620 errx(1, "`%s' size of %lld is larger than the maxsize of %lld",
621 dir, (long long)image_size, (long long)maxsize);
623 assert((image_size & HAMMER2_FREEMAP_LEVEL1_MASK) == 0);
624 h2_opt->image_size = image_size;
625 printf("using %s image size\n", sizetostr(h2_opt->image_size));
626 done:
627 if (debug & DEBUG_FS_VALIDATE) {
628 APRINTF("after defaults set:\n");
629 hammer2_dump_fsinfo(fsopts);
633 static void
634 hammer2_dump_fsinfo(fsinfo_t *fsopts)
636 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
637 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
638 int i;
639 char *s;
641 assert(fsopts != NULL);
643 APRINTF("fsinfo_t at %p\n", fsopts);
645 printf("\tinodes %lld\n", (long long)fsopts->inodes);
646 printf("\tsize %lld, minsize %lld, maxsize %lld\n",
647 (long long)fsopts->size,
648 (long long)fsopts->minsize,
649 (long long)fsopts->maxsize);
651 printf("\thammer2_debug 0x%x\n", hammer2_debug);
653 printf("\tlabel_specified %d\n", h2_opt->label_specified);
654 printf("\tmount_label \"%s\"\n", h2_opt->mount_label);
655 printf("\tnum_volhdr %d\n", h2_opt->num_volhdr);
656 printf("\tioctl_cmd %ld\n", h2_opt->ioctl_cmd);
657 printf("\temergency_mode %d\n", h2_opt->emergency_mode);
658 printf("\tpfs_cmd_name \"%s\"\n", h2_opt->pfs_cmd_name);
659 printf("\tpfs_name \"%s\"\n", h2_opt->pfs_name);
660 printf("\tdestroy_path \"%s\"\n", h2_opt->destroy_path);
661 printf("\tdestroy_inum %lld\n", (long long)h2_opt->destroy_inum);
662 printf("\timage_size 0x%llx\n", (long long)h2_opt->image_size);
664 printf("\tHammer2Version %d\n", opt->Hammer2Version);
665 printf("\tBootAreaSize 0x%jx\n", opt->BootAreaSize);
666 printf("\tAuxAreaSize 0x%jx\n", opt->AuxAreaSize);
667 printf("\tNLabels %d\n", opt->NLabels);
668 printf("\tCompType %d\n", opt->CompType);
669 printf("\tCheckType %d\n", opt->CheckType);
670 printf("\tDefaultLabelType %d\n", opt->DefaultLabelType);
671 printf("\tDebugOpt %d\n", opt->DebugOpt);
673 s = NULL;
674 hammer2_uuid_to_str(&opt->Hammer2_FSType, &s);
675 printf("\tHammer2_FSType \"%s\"\n", s);
676 s = NULL;
677 hammer2_uuid_to_str(&opt->Hammer2_VolFSID, &s);
678 printf("\tHammer2_VolFSID \"%s\"\n", s);
679 s = NULL;
680 hammer2_uuid_to_str(&opt->Hammer2_SupCLID, &s);
681 printf("\tHammer2_SupCLID \"%s\"\n", s);
682 s = NULL;
683 hammer2_uuid_to_str(&opt->Hammer2_SupFSID, &s);
684 printf("\tHammer2_SupFSID \"%s\"\n", s);
686 for (i = 0; i < opt->NLabels; i++) {
687 printf("\tLabel[%d] \"%s\"\n", i, opt->Label[i]);
688 s = NULL;
689 hammer2_uuid_to_str(&opt->Hammer2_PfsCLID[i], &s);
690 printf("\t Hammer2_PfsCLID[%d] \"%s\"\n", i, s);
691 s = NULL;
692 hammer2_uuid_to_str(&opt->Hammer2_PfsFSID[i], &s);
693 printf("\t Hammer2_PfsFSID[%d] \"%s\"\n", i, s);
696 free(s);
699 static int
700 hammer2_create_image(const char *image, fsinfo_t *fsopts)
702 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
703 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
704 char *av[] = { (char *)image, }; /* XXX support multi-volumes */
705 char *buf;
706 int i, bufsize, oflags;
707 off_t bufrem;
709 assert(image != NULL);
710 assert(fsopts != NULL);
712 /* create image */
713 oflags = O_RDWR | O_CREAT;
714 if (fsopts->offset == 0)
715 oflags |= O_TRUNC;
716 if ((fsopts->fd = open(image, oflags, 0666)) == -1) {
717 warn("can't open `%s' for writing", image);
718 return -1;
721 /* zero image */
722 bufsize = HAMMER2_PBUFSIZE;
723 bufrem = h2_opt->image_size;
724 if (fsopts->sparse) {
725 if (ftruncate(fsopts->fd, bufrem) == -1) {
726 warn("sparse option disabled");
727 fsopts->sparse = 0;
730 if (fsopts->sparse) {
731 /* File truncated at bufrem. Remaining is 0 */
732 bufrem = 0;
733 buf = NULL;
734 } else {
735 if (debug & DEBUG_FS_CREATE_IMAGE)
736 APRINTF("zero-ing image `%s', %lld sectors, "
737 "using %d byte chunks\n",
738 image, (long long)bufrem, bufsize);
739 buf = ecalloc(1, bufsize);
742 if (fsopts->offset != 0) {
743 if (lseek(fsopts->fd, fsopts->offset, SEEK_SET) == -1) {
744 warn("can't seek");
745 free(buf);
746 return -1;
750 while (bufrem > 0) {
751 i = write(fsopts->fd, buf, MIN(bufsize, bufrem));
752 if (i == -1) {
753 warn("zeroing image, %lld bytes to go",
754 (long long)bufrem);
755 free(buf);
756 return -1;
758 bufrem -= i;
760 if (buf)
761 free(buf);
763 /* make the file system */
764 if (debug & DEBUG_FS_CREATE_IMAGE)
765 APRINTF("calling mkfs(\"%s\", ...)\n", image);
766 hammer2_mkfs(1, av, opt); /* success if returned */
768 return fsopts->fd;
771 static off_t
772 hammer2_phys_size(off_t size)
774 off_t radix_size, phys_size = 0;
775 int i;
777 if (size > HAMMER2_PBUFSIZE) {
778 phys_size += rounddown(size, HAMMER2_PBUFSIZE);
779 size = size % HAMMER2_PBUFSIZE;
782 for (i = HAMMER2_RADIX_MIN; i <= HAMMER2_RADIX_MAX; i++) {
783 radix_size = 1UL << i;
784 if (radix_size >= size) {
785 phys_size += radix_size;
786 break;
790 return phys_size;
793 /* calculate data size */
794 static void
795 hammer2_size_dir(fsnode *root, fsinfo_t *fsopts)
797 fsnode *node;
799 assert(fsopts != NULL);
801 if (debug & DEBUG_FS_SIZE_DIR)
802 APRINTF("entry: bytes %lld inodes %lld\n",
803 (long long)fsopts->size, (long long)fsopts->inodes);
805 for (node = root; node != NULL; node = node->next) {
806 if (node == root) { /* we're at "." */
807 assert(strcmp(node->name, ".") == 0);
808 } else if ((node->inode->flags & FI_SIZED) == 0) {
809 /* don't count duplicate names */
810 node->inode->flags |= FI_SIZED;
811 if (debug & DEBUG_FS_SIZE_DIR_NODE)
812 APRINTF("`%s' size %lld\n",
813 node->name,
814 (long long)node->inode->st.st_size);
815 fsopts->inodes++;
816 fsopts->size += sizeof(hammer2_inode_data_t);
817 if (node->type == S_IFREG) {
818 size_t st_size = node->inode->st.st_size;
819 if (st_size > HAMMER2_EMBEDDED_BYTES)
820 fsopts->size += hammer2_phys_size(st_size);
821 } else if (node->type == S_IFLNK) {
822 size_t nlen = strlen(node->symlink);
823 if (nlen > HAMMER2_EMBEDDED_BYTES)
824 fsopts->size += hammer2_phys_size(nlen);
827 if (node->type == S_IFDIR)
828 hammer2_size_dir(node->child, fsopts);
831 if (debug & DEBUG_FS_SIZE_DIR)
832 APRINTF("exit: size %lld inodes %lld\n",
833 (long long)fsopts->size, (long long)fsopts->inodes);
836 static void
837 hammer2_print(const struct m_vnode *dvp, const struct m_vnode *vp,
838 const fsnode *node, int depth, const char *msg)
840 if (debug & DEBUG_FS_POPULATE) {
841 if (1) {
842 int indent = depth * 2;
843 char *type;
844 if (S_ISDIR(node->type))
845 type = "dir";
846 else if (S_ISREG(node->type))
847 type = "reg";
848 else if (S_ISLNK(node->type))
849 type = "lnk";
850 else if (S_ISFIFO(node->type))
851 type = "fifo";
852 else
853 type = "???";
854 printf("%*.*s", indent, indent, "");
855 printf("dvp=%p/%d vp=%p/%d \"%s\" %s %s\n",
856 dvp, dvp ? VTOI(dvp)->refs : 0,
857 vp, vp ? VTOI(vp)->refs : 0,
858 node->name, type, msg);
859 } else {
860 char type;
861 if (S_ISDIR(node->type))
862 type = 'd';
863 else if (S_ISREG(node->type))
864 type = 'r';
865 else if (S_ISLNK(node->type))
866 type = 'l';
867 else if (S_ISFIFO(node->type))
868 type = 'f';
869 else
870 type = '?';
871 printf("%c", type);
872 fflush(stdout);
877 static int
878 hammer2_populate_dir(struct m_vnode *dvp, const char *dir, fsnode *root,
879 fsnode *parent, fsinfo_t *fsopts, int depth)
881 fsnode *cur;
882 struct m_vnode *vp;
883 struct stat st;
884 char f[MAXPATHLEN];
885 const char *path;
886 int hardlink;
887 int error;
889 assert(dvp != NULL);
890 assert(dir != NULL);
891 assert(root != NULL);
892 assert(parent != NULL);
893 assert(fsopts != NULL);
895 /* assert root directory */
896 assert(S_ISDIR(root->type));
897 assert(!strcmp(root->name, "."));
898 assert(!root->child);
899 assert(!root->parent || root->parent->child == root);
901 hammer2_print(dvp, NULL, root, depth, "enter");
902 if (stat(dir, &st) == -1)
903 err(1, "no such path %s", dir);
904 if (!S_ISDIR(st.st_mode))
905 errx(1, "no such dir %s", dir);
907 for (cur = root->next; cur != NULL; cur = cur->next) {
908 /* global variable for HAMMER2 vnops */
909 hammer2_curnode = cur;
911 /* construct source path */
912 if (cur->contents) {
913 path = cur->contents;
914 } else {
915 if (S_ISDIR(cur->type)) {
916 /* this should be same as root/path/name */
917 if (snprintf(f, sizeof(f), "%s/%s",
918 dir, cur->name) >= (int)sizeof(f))
919 errx(1, "path %s too long", f);
920 } else {
921 if (snprintf(f, sizeof(f), "%s/%s/%s",
922 cur->root, cur->path, cur->name) >= (int)sizeof(f))
923 errx(1, "path %s too long", f);
925 path = f;
927 if (stat(path, &st) == -1)
928 err(1, "no such file %s", f);
930 /* update node state */
931 if ((cur->inode->flags & FI_ALLOCATED) == 0) {
932 cur->inode->flags |= FI_ALLOCATED;
933 if (cur != root)
934 cur->parent = parent;
937 /* detect hardlink */
938 if (cur->inode->flags & FI_WRITTEN) {
939 assert(!S_ISDIR(cur->type));
940 hardlink = 1;
941 } else {
942 hardlink = 0;
944 cur->inode->flags |= FI_WRITTEN;
946 /* make sure it doesn't exist yet */
947 vp = NULL;
948 error = hammer2_nresolve(dvp, &vp, cur->name,
949 strlen(cur->name));
950 if (!error)
951 errx(1, "hammer2_nresolve(\"%s\") already exists",
952 cur->name);
953 hammer2_print(dvp, vp, cur, depth, "nresolve");
955 /* if directory, mkdir and recurse */
956 if (S_ISDIR(cur->type)) {
957 assert(cur->child);
959 vp = NULL;
960 error = hammer2_nmkdir(dvp, &vp, cur->name,
961 strlen(cur->name), cur->inode->st.st_mode);
962 if (error)
963 errx(1, "hammer2_nmkdir(\"%s\") failed: %s",
964 cur->name, strerror(error));
965 assert(vp);
966 hammer2_print(dvp, vp, cur, depth, "nmkdir");
968 error = hammer2_populate_dir(vp, path, cur->child, cur,
969 fsopts, depth + 1);
970 if (error)
971 errx(1, "failed to populate %s: %s",
972 path, strerror(error));
973 cur->inode->param = vp;
974 continue;
977 /* if regular file, creat and write its data */
978 if (S_ISREG(cur->type) && !hardlink) {
979 assert(cur->child == NULL);
981 vp = NULL;
982 error = hammer2_ncreate(dvp, &vp, cur->name,
983 strlen(cur->name), cur->inode->st.st_mode);
984 if (error)
985 errx(1, "hammer2_ncreate(\"%s\") failed: %s",
986 cur->name, strerror(error));
987 assert(vp);
988 hammer2_print(dvp, vp, cur, depth, "ncreate");
990 error = hammer2_write_file(vp, path, cur);
991 if (error)
992 errx(1, "hammer2_write_file(\"%s\") failed: %s",
993 path, strerror(error));
994 cur->inode->param = vp;
995 continue;
998 /* if symlink, create a symlink against target */
999 if (S_ISLNK(cur->type)) {
1000 assert(cur->child == NULL);
1002 vp = NULL;
1003 error = hammer2_nsymlink(dvp, &vp, cur->name,
1004 strlen(cur->name), cur->symlink,
1005 cur->inode->st.st_mode);
1006 if (error)
1007 errx(1, "hammer2_nsymlink(\"%s\") failed: %s",
1008 cur->name, strerror(error));
1009 assert(vp);
1010 hammer2_print(dvp, vp, cur, depth, "nsymlink");
1011 cur->inode->param = vp;
1012 continue;
1015 /* if fifo, create a fifo */
1016 if (S_ISFIFO(cur->type) && !hardlink) {
1017 assert(cur->child == NULL);
1019 vp = NULL;
1020 error = hammer2_nmknod(dvp, &vp, cur->name,
1021 strlen(cur->name), VFIFO, cur->inode->st.st_mode);
1022 if (error)
1023 errx(1, "hammer2_nmknod(\"%s\") failed: %s",
1024 cur->name, strerror(error));
1025 assert(vp);
1026 hammer2_print(dvp, vp, cur, depth, "nmknod");
1027 cur->inode->param = vp;
1028 continue;
1031 /* if hardlink, creat a hardlink */
1032 if ((S_ISREG(cur->type) || S_ISFIFO(cur->type)) && hardlink) {
1033 char buf[64];
1034 assert(cur->child == NULL);
1036 /* source vnode must not be NULL */
1037 vp = cur->inode->param;
1038 assert(vp);
1039 /* currently these conditions must be true */
1040 assert(vp->v_data);
1041 assert(vp->v_type == VREG || vp->v_type == VFIFO);
1042 assert(vp->v_logical);
1043 assert(!vp->v_vflushed);
1044 assert(vp->v_malloced);
1045 assert(VTOI(vp)->refs > 0);
1047 error = hammer2_nlink(dvp, vp, cur->name,
1048 strlen(cur->name));
1049 if (error)
1050 errx(1, "hammer2_nlink(\"%s\") failed: %s",
1051 cur->name, strerror(error));
1052 snprintf(buf, sizeof(buf), "nlink=%lld",
1053 (long long)VTOI(vp)->meta.nlinks);
1054 hammer2_print(dvp, vp, cur, depth, buf);
1055 continue;
1058 /* other types are unsupported */
1059 printf("ignore %s 0%o\n", path, cur->type);
1062 return 0;
1065 static int
1066 hammer2_write_file(struct m_vnode *vp, const char *path, fsnode *node)
1068 struct stat *st = &node->inode->st;
1069 size_t nsize, bufsize;
1070 off_t offset;
1071 int fd, error;
1072 char *p;
1074 nsize = st->st_size;
1075 if (nsize == 0)
1076 return 0;
1077 /* check nsize vs maximum file size */
1079 fd = open(path, O_RDONLY);
1080 if (fd < 0)
1081 err(1, "failed to open %s", path);
1083 p = mmap(0, nsize, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0);
1084 if (p == MAP_FAILED)
1085 err(1, "failed to mmap %s", path);
1086 close(fd);
1088 for (offset = 0; offset < nsize; ) {
1089 bufsize = MIN(nsize - offset, HAMMER2_PBUFSIZE);
1090 assert(bufsize <= HAMMER2_PBUFSIZE);
1091 error = hammer2_write(vp, p + offset, bufsize, offset);
1092 if (error)
1093 errx(1, "failed to write to %s vnode: %s",
1094 path, strerror(error));
1095 offset += bufsize;
1096 if (bufsize == HAMMER2_PBUFSIZE)
1097 assert((offset & (HAMMER2_PBUFSIZE - 1)) == 0);
1099 munmap(p, nsize);
1101 return 0;
1104 static int
1105 hammer2_version_get(struct m_vnode *vp)
1107 hammer2_dev_t *hmp;
1109 hmp = VTOI(vp)->pmp->pfs_hmps[0];
1110 if (hmp == NULL)
1111 return EINVAL;
1113 printf("version: %d\n", hmp->voldata.version);
1115 return 0;
1118 struct pfs_entry {
1119 TAILQ_ENTRY(pfs_entry) entry;
1120 char name[NAME_MAX+1];
1121 char s[NAME_MAX+1];
1124 static int
1125 hammer2_pfs_get(struct m_vnode *vp)
1127 hammer2_ioc_pfs_t pfs;
1128 TAILQ_HEAD(, pfs_entry) head;
1129 struct pfs_entry *p, *e;
1130 char *pfs_id_str;
1131 const char *type_str;
1132 int error;
1134 bzero(&pfs, sizeof(pfs));
1135 TAILQ_INIT(&head);
1137 while ((pfs.name_key = pfs.name_next) != (hammer2_key_t)-1) {
1138 error = hammer2_ioctl_pfs_get(VTOI(vp), &pfs);
1139 if (error)
1140 return error;
1142 pfs_id_str = NULL;
1143 hammer2_uuid_to_str(&pfs.pfs_clid, &pfs_id_str);
1145 if (pfs.pfs_type == HAMMER2_PFSTYPE_MASTER) {
1146 if (pfs.pfs_subtype == HAMMER2_PFSSUBTYPE_NONE)
1147 type_str = "MASTER";
1148 else
1149 type_str = hammer2_pfssubtype_to_str(
1150 pfs.pfs_subtype);
1151 } else {
1152 type_str = hammer2_pfstype_to_str(pfs.pfs_type);
1154 e = ecalloc(1, sizeof(*e));
1155 snprintf(e->name, sizeof(e->name), "%s", pfs.name);
1156 snprintf(e->s, sizeof(e->s), "%-11s %s", type_str, pfs_id_str);
1157 free(pfs_id_str);
1159 p = TAILQ_FIRST(&head);
1160 while (p) {
1161 if (strcmp(e->name, p->name) <= 0) {
1162 TAILQ_INSERT_BEFORE(p, e, entry);
1163 break;
1165 p = TAILQ_NEXT(p, entry);
1167 if (!p)
1168 TAILQ_INSERT_TAIL(&head, e, entry);
1171 printf("Type "
1172 "ClusterId (pfs_clid) "
1173 "Label\n");
1174 while ((p = TAILQ_FIRST(&head)) != NULL) {
1175 printf("%s %s\n", p->s, p->name);
1176 TAILQ_REMOVE(&head, p, entry);
1177 free(p);
1180 return 0;
1183 static int
1184 hammer2_pfs_lookup(struct m_vnode *vp, const char *pfs_name)
1186 hammer2_ioc_pfs_t pfs;
1187 char *pfs_id_str;
1188 int error;
1190 bzero(&pfs, sizeof(pfs));
1191 strlcpy(pfs.name, pfs_name, sizeof(pfs.name));
1193 error = hammer2_ioctl_pfs_lookup(VTOI(vp), &pfs);
1194 if (error == 0) {
1195 printf("name: %s\n", pfs.name);
1196 printf("type: %s\n", hammer2_pfstype_to_str(pfs.pfs_type));
1197 printf("subtype: %s\n",
1198 hammer2_pfssubtype_to_str(pfs.pfs_subtype));
1200 pfs_id_str = NULL;
1201 hammer2_uuid_to_str(&pfs.pfs_fsid, &pfs_id_str);
1202 printf("fsid: %s\n", pfs_id_str);
1203 free(pfs_id_str);
1205 pfs_id_str = NULL;
1206 hammer2_uuid_to_str(&pfs.pfs_clid, &pfs_id_str);
1207 printf("clid: %s\n", pfs_id_str);
1208 free(pfs_id_str);
1211 return error;
1214 static int
1215 hammer2_pfs_create(struct m_vnode *vp, const char *pfs_name)
1217 hammer2_ioc_pfs_t pfs;
1218 int error;
1220 bzero(&pfs, sizeof(pfs));
1221 strlcpy(pfs.name, pfs_name, sizeof(pfs.name));
1222 pfs.pfs_type = HAMMER2_PFSTYPE_MASTER;
1223 uuid_create(&pfs.pfs_clid, NULL);
1224 uuid_create(&pfs.pfs_fsid, NULL);
1226 error = hammer2_ioctl_pfs_create(VTOI(vp), &pfs);
1227 if (error == EEXIST)
1228 fprintf(stderr,
1229 "NOTE: Typically the same name is "
1230 "used for cluster elements on "
1231 "different mounts,\n"
1232 " but cluster elements on the "
1233 "same mount require unique names.\n"
1234 "hammer2: pfs_create(%s): already present\n",
1235 pfs_name);
1237 return error;
1240 static int
1241 hammer2_pfs_delete(struct m_vnode *vp, const char *pfs_name)
1243 hammer2_ioc_pfs_t pfs;
1245 bzero(&pfs, sizeof(pfs));
1246 strlcpy(pfs.name, pfs_name, sizeof(pfs.name));
1248 return hammer2_ioctl_pfs_delete(VTOI(vp), &pfs);
1251 static int
1252 hammer2_pfs_snapshot(struct m_vnode *vp, const char *pfs_name,
1253 const char *mount_label)
1255 hammer2_ioc_pfs_t pfs;
1256 struct tm *tp;
1257 time_t t;
1259 bzero(&pfs, sizeof(pfs));
1260 strlcpy(pfs.name, pfs_name, sizeof(pfs.name));
1262 if (strlen(pfs.name) == 0) {
1263 time(&t);
1264 tp = localtime(&t);
1265 snprintf(pfs.name, sizeof(pfs.name),
1266 "%s.%04d%02d%02d.%02d%02d%02d",
1267 mount_label,
1268 tp->tm_year + 1900,
1269 tp->tm_mon + 1,
1270 tp->tm_mday,
1271 tp->tm_hour,
1272 tp->tm_min,
1273 tp->tm_sec);
1276 return hammer2_ioctl_pfs_snapshot(VTOI(vp), &pfs);
1279 static int
1280 hammer2_bulkfree(struct m_vnode *vp)
1282 hammer2_ioc_bulkfree_t bfi;
1283 size_t usermem;
1284 size_t usermem_size = sizeof(usermem);
1286 bzero(&bfi, sizeof(bfi));
1287 usermem = 0;
1288 if (sysctlbyname("hw.usermem", &usermem, &usermem_size, NULL, 0) == 0)
1289 bfi.size = usermem / 16;
1290 else
1291 bfi.size = 0;
1292 if (bfi.size < 8192 * 1024)
1293 bfi.size = 8192 * 1024;
1295 return hammer2_ioctl_bulkfree_scan(VTOI(vp), &bfi);
1298 static int
1299 hammer2_destroy_path(struct m_vnode *dvp, const char *f)
1301 hammer2_ioc_destroy_t destroy;
1302 hammer2_inode_t *ip;
1303 struct m_vnode *vp;
1304 char *o, *p, *name;
1305 int error;
1307 assert(strlen(f) > 0);
1308 o = p = strdup(f);
1310 /* Trim trailing '/'. */
1311 p += strlen(p);
1312 p--;
1313 while (p >= o && *p == '/')
1314 p--;
1315 *++p = 0;
1317 name = p = o;
1318 if (strlen(p) == 0)
1319 return EINVAL;
1321 while ((p = strchr(p, '/')) != NULL) {
1322 *p++ = 0; /* NULL terminate name */
1323 vp = NULL;
1324 error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1325 if (error)
1326 return error;
1328 ip = VTOI(vp);
1329 assert(ip->meta.type == HAMMER2_OBJTYPE_DIRECTORY);
1330 /* XXX When does (or why does not) ioctl modify this inode ? */
1331 hammer2_inode_modify(ip);
1333 dvp = vp;
1334 name = p;
1337 bzero(&destroy, sizeof(destroy));
1338 destroy.cmd = HAMMER2_DELETE_FILE;
1339 snprintf(destroy.path, sizeof(destroy.path), "%s", name);
1341 printf("%s\t", f);
1342 fflush(stdout);
1344 error = hammer2_ioctl_destroy(VTOI(dvp), &destroy);
1345 if (error)
1346 printf("%s\n", strerror(error));
1347 else
1348 printf("ok\n");
1349 free(o);
1351 return error;
1354 static int
1355 hammer2_destroy_inum(struct m_vnode *vp, hammer2_tid_t inum)
1357 hammer2_ioc_destroy_t destroy;
1358 int error;
1360 bzero(&destroy, sizeof(destroy));
1361 destroy.cmd = HAMMER2_DELETE_INUM;
1362 destroy.inum = inum;
1364 printf("%jd\t", (intmax_t)destroy.inum);
1365 fflush(stdout);
1367 error = hammer2_ioctl_destroy(VTOI(vp), &destroy);
1368 if (error)
1369 printf("%s\n", strerror(error));
1370 else
1371 printf("ok\n");
1373 return error;
1376 static int
1377 hammer2_growfs(struct m_vnode *vp, hammer2_off_t size)
1379 hammer2_ioc_growfs_t growfs;
1380 int error;
1382 bzero(&growfs, sizeof(growfs));
1383 growfs.size = size;
1385 error = hammer2_ioctl_growfs(VTOI(vp), &growfs, NULL);
1386 if (!error) {
1387 if (growfs.modified)
1388 printf("grown to %016jx\n", (intmax_t)growfs.size);
1389 else
1390 printf("no size change - %016jx\n",
1391 (intmax_t)growfs.size);
1394 return error;