1 /*
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 2022 Tomohiro Kusumi <tkusumi@netbsd.org>
5 * Copyright (c) 2011-2022 The DragonFly Project. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
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.
20 *
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.
33 */
34
35 #if HAVE_NBTOOL_CONFIG_H
36 #include "nbtool_config.h"
37 #endif
38
39 #include <sys/param.h>
40 #include <sys/stat.h>
41 #include <sys/sysctl.h>
42 #include <sys/mman.h>
43 #include <sys/time.h>
44 #include <sys/dirent.h>
45
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <stdbool.h>
49 #include <string.h>
50 #include <ctype.h>
51 #include <unistd.h>
52 #include <fcntl.h>
53 #include <time.h>
54 #include <err.h>
55 #include <assert.h>
56 #include <util.h>
57
58 #include "makefs.h"
59 #include "hammer2.h"
60
61 #define APRINTF(X, ...) \
62 printf("%s: " X, __func__, ## __VA_ARGS__)
63
64 static void hammer2_parse_pfs_opts(const char *, fsinfo_t *);
65 static void hammer2_parse_inode_opts(const char *, fsinfo_t *);
66 static void hammer2_dump_fsinfo(fsinfo_t *);
67 static int hammer2_create_image(const char *, fsinfo_t *);
68 static int hammer2_populate_dir(struct m_vnode *, const char *, fsnode *,
69 fsnode *, fsinfo_t *, int);
70 static void hammer2_validate(const char *, fsnode *, fsinfo_t *);
71 static void hammer2_size_dir(fsnode *, fsinfo_t *);
72 static int hammer2_write_file(struct m_vnode *, const char *, fsnode *);
73 static int hammer2_version_get(struct m_vnode *);
74 static int hammer2_pfs_get(struct m_vnode *);
75 static int hammer2_pfs_lookup(struct m_vnode *, const char *);
76 static int hammer2_pfs_create(struct m_vnode *, const char *);
77 static int hammer2_pfs_delete(struct m_vnode *, const char *);
78 static int hammer2_pfs_snapshot(struct m_vnode *, const char *, const char *);
79 static int hammer2_inode_getx(struct m_vnode *, const char *);
80 static int hammer2_inode_setcheck(struct m_vnode *, const char *);
81 static int hammer2_inode_setcomp(struct m_vnode *, const char *);
82 static int hammer2_bulkfree(struct m_vnode *);
83 static int hammer2_destroy_path(struct m_vnode *, const char *);
84 static int hammer2_destroy_inum(struct m_vnode *, hammer2_tid_t);
85 static int hammer2_growfs(struct m_vnode *, hammer2_off_t);
86 struct hammer2_linkq;
87 static int hammer2_readx_handle(struct m_vnode *, const char *, const char *,
88 struct hammer2_linkq *);
89 static int hammer2_readx(struct m_vnode *, const char *, const char *);
90 static void unittest_trim_slash(void);
91
92 fsnode *hammer2_curnode;
93
94 void
hammer2_prep_opts(fsinfo_t * fsopts)95 hammer2_prep_opts(fsinfo_t *fsopts)
96 {
97 hammer2_makefs_options_t *h2_opt = ecalloc(1, sizeof(*h2_opt));
98 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
99
100 const option_t hammer2_options[] = {
101 /* newfs_hammer2(8) compatible options */
102 { 'b', "BootAreaSize", NULL, OPT_STRBUF, 0, 0, "boot area size" },
103 { 'r', "AuxAreaSize", NULL, OPT_STRBUF, 0, 0, "aux area size" },
104 { 'V', "Hammer2Version", NULL, OPT_STRBUF, 0, 0, "file system version" },
105 { 'L', "Label", NULL, OPT_STRBUF, 0, 0, "PFS label" },
106 /* makefs(8) specific options */
107 { 'm', "MountLabel", NULL, OPT_STRBUF, 0, 0, "destination PFS label" },
108 { 'v', "NumVolhdr", &h2_opt->num_volhdr, OPT_INT32,
109 1, HAMMER2_NUM_VOLHDRS, "number of volume headers" },
110 { 'c', "CompressionType", NULL, OPT_STRBUF, 0, 0, "compression type" },
111 { 'C', "CheckType", NULL, OPT_STRBUF, 0, 0, "check type" },
112 { 'd', "Hammer2Debug", NULL, OPT_STRBUF, 0, 0, "debug tunable" },
113 { 'E', "EmergencyMode", &h2_opt->emergency_mode, OPT_BOOL, 0, 0,
114 "emergency mode" },
115 { 'P', "PFS", NULL, OPT_STRBUF, 0, 0, "offline PFS" },
116 { 'I', "Inode", NULL, OPT_STRBUF, 0, 0, "offline inode" },
117 { 'B', "Bulkfree", NULL, OPT_STRBUF, 0, 0, "offline bulkfree" },
118 { 'D', "Destroy", NULL, OPT_STRBUF, 0, 0, "offline destroy" },
119 { 'G', "Growfs", NULL, OPT_STRBUF, 0, 0, "offline growfs" },
120 { 'R', "Read", NULL, OPT_STRBUF, 0, 0, "offline read" },
121 { .name = NULL },
122 };
123
124 hammer2_mkfs_init(opt);
125
126 assert(opt->CompType == HAMMER2_COMP_DEFAULT);
127 assert(opt->CheckType == HAMMER2_CHECK_DEFAULT);
128
129 /* force debug mode for mkfs */
130 opt->DebugOpt = 1;
131
132 fsopts->fs_specific = h2_opt;
133 fsopts->fs_options = copy_opts(hammer2_options);
134 fsopts->sectorsize = DEV_BSIZE;
135 }
136
137 void
hammer2_cleanup_opts(fsinfo_t * fsopts)138 hammer2_cleanup_opts(fsinfo_t *fsopts)
139 {
140 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
141 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
142
143 hammer2_mkfs_cleanup(opt);
144
145 free(h2_opt);
146 free(fsopts->fs_options);
147 }
148
149 int
hammer2_parse_opts(const char * option,fsinfo_t * fsopts)150 hammer2_parse_opts(const char *option, fsinfo_t *fsopts)
151 {
152 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
153 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
154
155 option_t *hammer2_options = fsopts->fs_options;
156 char buf[1024]; /* > HAMMER2_INODE_MAXNAME */
157 int i;
158
159 assert(option != NULL);
160 assert(fsopts != NULL);
161
162 if (debug & DEBUG_FS_PARSE_OPTS)
163 APRINTF("got `%s'\n", option);
164
165 i = set_option(hammer2_options, option, buf, sizeof(buf));
166 if (i == -1)
167 return 0;
168
169 if (hammer2_options[i].name == NULL)
170 abort();
171
172 switch (hammer2_options[i].letter) {
173 case 'b':
174 opt->BootAreaSize = getsize(buf, HAMMER2_NEWFS_ALIGN,
175 HAMMER2_BOOT_MAX_BYTES, 2);
176 break;
177 case 'r':
178 opt->AuxAreaSize = getsize(buf, HAMMER2_NEWFS_ALIGN,
179 HAMMER2_AUX_MAX_BYTES, 2);
180 break;
181 case 'V':
182 if (strlen(buf) == 0) {
183 h2_opt->ioctl_cmd = HAMMER2IOC_VERSION_GET;
184 } else {
185 opt->Hammer2Version = strtol(buf, NULL, 0);
186 if (opt->Hammer2Version < HAMMER2_VOL_VERSION_MIN ||
187 opt->Hammer2Version >= HAMMER2_VOL_VERSION_WIP)
188 errx(1, "I don't understand how to format "
189 "HAMMER2 version %d",
190 opt->Hammer2Version);
191 }
192 break;
193 case 'L':
194 h2_opt->label_specified = 1;
195 if (strcasecmp(buf, "none") == 0)
196 break;
197 if (opt->NLabels >= MAXLABELS)
198 errx(1, "Limit of %d local labels", MAXLABELS - 1);
199 if (strlen(buf) == 0)
200 errx(1, "Volume label '%s' cannot be 0-length", buf);
201 if (strlen(buf) >= HAMMER2_INODE_MAXNAME)
202 errx(1, "Volume label '%s' is too long (%d chars max)",
203 buf, HAMMER2_INODE_MAXNAME - 1);
204 opt->Label[opt->NLabels++] = strdup(buf);
205 break;
206 case 'm':
207 if (strlen(buf) == 0)
208 errx(1, "Volume label '%s' cannot be 0-length", buf);
209 if (strlen(buf) >= HAMMER2_INODE_MAXNAME)
210 errx(1, "Volume label '%s' is too long (%d chars max)",
211 buf, HAMMER2_INODE_MAXNAME - 1);
212 strlcpy(h2_opt->mount_label, buf, sizeof(h2_opt->mount_label));
213 break;
214 case 'c':
215 if (strlen(buf) == 0)
216 errx(1, "Compression type '%s' cannot be 0-length", buf);
217 if (strcasecmp(buf, "none") == 0)
218 opt->CompType = HAMMER2_COMP_NONE;
219 else if (strcasecmp(buf, "autozero") == 0)
220 opt->CompType = HAMMER2_COMP_AUTOZERO;
221 else if (strcasecmp(buf, "lz4") == 0)
222 opt->CompType = HAMMER2_COMP_LZ4;
223 else if (strcasecmp(buf, "zlib") == 0)
224 opt->CompType = HAMMER2_COMP_ZLIB;
225 else
226 errx(1, "Invalid compression type '%s'", buf);
227 break;
228 case 'C':
229 if (strlen(buf) == 0)
230 errx(1, "Check type '%s' cannot be 0-length", buf);
231 if (strcasecmp(buf, "none") == 0)
232 opt->CheckType = HAMMER2_CHECK_NONE;
233 else if (strcasecmp(buf, "disabled") == 0)
234 opt->CheckType = HAMMER2_CHECK_DISABLED;
235 else if (strcasecmp(buf, "iscsi32") == 0)
236 opt->CheckType = HAMMER2_CHECK_ISCSI32;
237 else if (strcasecmp(buf, "xxhash64") == 0)
238 opt->CheckType = HAMMER2_CHECK_XXHASH64;
239 else if (strcasecmp(buf, "sha192") == 0)
240 opt->CheckType = HAMMER2_CHECK_SHA192;
241 else if (strcasecmp(buf, "freemap") == 0)
242 opt->CheckType = HAMMER2_CHECK_FREEMAP;
243 else
244 errx(1, "Invalid check type '%s'", buf);
245 break;
246 case 'd':
247 hammer2_debug = strtoll(buf, NULL, 0);
248 break;
249 case 'P':
250 if (strlen(buf) == 0)
251 errx(1, "PFS argument '%s' cannot be 0-length", buf);
252 hammer2_parse_pfs_opts(buf, fsopts);
253 break;
254 case 'I':
255 if (strlen(buf) == 0)
256 errx(1, "Inode argument '%s' cannot be 0-length", buf);
257 hammer2_parse_inode_opts(buf, fsopts);
258 break;
259 case 'B':
260 h2_opt->ioctl_cmd = HAMMER2IOC_BULKFREE_SCAN;
261 break;
262 case 'D':
263 h2_opt->ioctl_cmd = HAMMER2IOC_DESTROY;
264 if (strlen(buf) == 0)
265 errx(1, "Destroy argument '%s' cannot be 0-length", buf);
266 if (buf[0] == '/') {
267 strlcpy(h2_opt->destroy_path, buf,
268 sizeof(h2_opt->destroy_path));
269 } else if (strncmp(buf, "0x", 2) == 0 ||
270 (buf[0] >= '0' && buf[0] <= '9')) {
271 h2_opt->destroy_inum = strtoull(buf, NULL, 0);
272 if (errno)
273 err(1, "strtoull");
274 } else {
275 errx(1, "Invalid destroy argument %s", buf);
276 }
277 break;
278 case 'G':
279 h2_opt->ioctl_cmd = HAMMER2IOC_GROWFS;
280 break;
281 case 'R':
282 h2_opt->ioctl_cmd = HAMMER2IOC_READ;
283 if (strlen(buf) == 0)
284 errx(1, "Read argument '%s' cannot be 0-length", buf);
285 strlcpy(h2_opt->read_path, buf, sizeof(h2_opt->read_path));
286 break;
287 default:
288 break;
289 }
290
291 if (hammer2_debug && h2_opt->ioctl_cmd)
292 unittest_trim_slash();
293
294 return 1;
295 }
296
297 void
hammer2_makefs(const char * image,const char * dir,fsnode * root,fsinfo_t * fsopts)298 hammer2_makefs(const char *image, const char *dir, fsnode *root,
299 fsinfo_t *fsopts)
300 {
301 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
302 struct mount mp;
303 struct hammer2_mount_info info;
304 struct m_vnode devvp, *vroot;
305 hammer2_inode_t *iroot;
306 struct timeval start;
307 int error;
308
309 /* ioctl commands could have NULL dir / root */
310 assert(image != NULL);
311 assert(fsopts != NULL);
312
313 if (debug & DEBUG_FS_MAKEFS)
314 APRINTF("image \"%s\" directory \"%s\" root %p\n",
315 image, dir, root);
316
317 /* validate tree and options */
318 TIMER_START(start);
319 hammer2_validate(dir, root, fsopts);
320 TIMER_RESULTS(start, "hammer2_validate");
321
322 if (h2_opt->ioctl_cmd) {
323 /* open existing image */
324 fsopts->fd = open(image, O_RDWR);
325 if (fsopts->fd < 0)
326 err(1, "failed to open `%s'", image);
327 } else {
328 /* create image */
329 TIMER_START(start);
330 if (hammer2_create_image(image, fsopts) == -1)
331 errx(1, "image file `%s' not created", image);
332 TIMER_RESULTS(start, "hammer2_create_image");
333 }
334 assert(fsopts->fd > 0);
335
336 if (debug & DEBUG_FS_MAKEFS)
337 putchar('\n');
338
339 /* vfs init */
340 error = hammer2_vfs_init();
341 if (error)
342 errx(1, "failed to vfs init, error %d", error);
343
344 /* mount image */
345 memset(&devvp, 0, sizeof(devvp));
346 devvp.fs = fsopts;
347 memset(&mp, 0, sizeof(mp));
348 memset(&info, 0, sizeof(info));
349 error = hammer2_vfs_mount(&devvp, &mp, h2_opt->mount_label, &info);
350 if (error)
351 errx(1, "failed to mount, error %d", error);
352 assert(mp.mnt_data);
353
354 /* get root vnode */
355 vroot = NULL;
356 error = hammer2_vfs_root(&mp, &vroot);
357 if (error)
358 errx(1, "failed to get root vnode, error %d", error);
359 assert(vroot);
360
361 iroot = VTOI(vroot);
362 assert(iroot);
363 printf("root inode inum %lld, mode 0%o, refs %d\n",
364 (long long)iroot->meta.inum, iroot->meta.mode, iroot->refs);
365
366 if (h2_opt->emergency_mode)
367 hammer2_ioctl_emerg_mode(iroot, 1);
368
369 switch (h2_opt->ioctl_cmd) {
370 case HAMMER2IOC_VERSION_GET:
371 printf("version get `%s'\n", image);
372 TIMER_START(start);
373 error = hammer2_version_get(vroot);
374 if (error)
375 errx(1, "version get `%s' failed '%s'", image,
376 strerror(error));
377 TIMER_RESULTS(start, "hammer2_version_get");
378 break;
379 case HAMMER2IOC_PFS_GET:
380 printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image);
381 TIMER_START(start);
382 error = hammer2_pfs_get(vroot);
383 if (error)
384 errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name,
385 image, strerror(error));
386 TIMER_RESULTS(start, "hammer2_pfs_get");
387 break;
388 case HAMMER2IOC_PFS_LOOKUP:
389 printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image);
390 TIMER_START(start);
391 error = hammer2_pfs_lookup(vroot, h2_opt->pfs_name);
392 if (error)
393 errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name,
394 image, strerror(error));
395 TIMER_RESULTS(start, "hammer2_pfs_lookup");
396 break;
397 case HAMMER2IOC_PFS_CREATE:
398 printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image);
399 TIMER_START(start);
400 error = hammer2_pfs_create(vroot, h2_opt->pfs_name);
401 if (error)
402 errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name,
403 image, strerror(error));
404 TIMER_RESULTS(start, "hammer2_pfs_create");
405 break;
406 case HAMMER2IOC_PFS_DELETE:
407 printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image);
408 TIMER_START(start);
409 error = hammer2_pfs_delete(vroot, h2_opt->pfs_name);
410 if (error)
411 errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name,
412 image, strerror(error));
413 TIMER_RESULTS(start, "hammer2_pfs_delete");
414 break;
415 case HAMMER2IOC_PFS_SNAPSHOT:
416 printf("PFS %s `%s'\n", h2_opt->pfs_cmd_name, image);
417 TIMER_START(start);
418 error = hammer2_pfs_snapshot(vroot, h2_opt->pfs_name,
419 h2_opt->mount_label);
420 if (error)
421 errx(1, "PFS %s`%s' failed '%s'", h2_opt->pfs_cmd_name,
422 image, strerror(error));
423 TIMER_RESULTS(start, "hammer2_pfs_snapshot");
424 break;
425 case HAMMER2IOC_INODE_GET:
426 printf("inode %s `%s'\n", h2_opt->inode_cmd_name, image);
427 TIMER_START(start);
428 error = hammer2_inode_getx(vroot, h2_opt->inode_path);
429 if (error)
430 errx(1, "inode %s `%s' failed '%s'",
431 h2_opt->inode_cmd_name, image, strerror(error));
432 TIMER_RESULTS(start, "hammer2_inode_getx");
433 break;
434 case HAMMER2IOC_INODE_SET:
435 printf("inode %s `%s'\n", h2_opt->inode_cmd_name, image);
436 TIMER_START(start);
437 if (!strcmp(h2_opt->inode_cmd_name, "setcheck")) {
438 error = hammer2_inode_setcheck(vroot,
439 h2_opt->inode_path);
440 if (error)
441 errx(1, "inode %s `%s' failed '%s'",
442 h2_opt->inode_cmd_name, image,
443 strerror(error));
444 } else if (!strcmp(h2_opt->inode_cmd_name, "setcomp")) {
445 error = hammer2_inode_setcomp(vroot,
446 h2_opt->inode_path);
447 if (error)
448 errx(1, "inode %s `%s' failed '%s'",
449 h2_opt->inode_cmd_name, image,
450 strerror(error));
451 } else {
452 assert(0);
453 }
454 TIMER_RESULTS(start, "hammer2_inode_setx");
455 break;
456 case HAMMER2IOC_BULKFREE_SCAN:
457 printf("bulkfree `%s'\n", image);
458 TIMER_START(start);
459 error = hammer2_bulkfree(vroot);
460 if (error)
461 errx(1, "bulkfree `%s' failed '%s'", image,
462 strerror(error));
463 TIMER_RESULTS(start, "hammer2_bulkfree");
464 break;
465 case HAMMER2IOC_DESTROY:
466 TIMER_START(start);
467 if (strlen(h2_opt->destroy_path)) {
468 printf("destroy `%s' in `%s'\n",
469 h2_opt->destroy_path, image);
470 error = hammer2_destroy_path(vroot,
471 h2_opt->destroy_path);
472 if (error)
473 errx(1, "destroy `%s' in `%s' failed '%s'",
474 h2_opt->destroy_path, image,
475 strerror(error));
476 } else {
477 printf("destroy %lld in `%s'\n",
478 (long long)h2_opt->destroy_inum, image);
479 error = hammer2_destroy_inum(vroot,
480 h2_opt->destroy_inum);
481 if (error)
482 errx(1, "destroy %lld in `%s' failed '%s'",
483 (long long)h2_opt->destroy_inum, image,
484 strerror(error));
485 }
486 TIMER_RESULTS(start, "hammer2_destroy");
487 break;
488 case HAMMER2IOC_GROWFS:
489 printf("growfs `%s'\n", image);
490 TIMER_START(start);
491 error = hammer2_growfs(vroot, h2_opt->image_size);
492 if (error)
493 errx(1, "growfs `%s' failed '%s'", image,
494 strerror(error));
495 TIMER_RESULTS(start, "hammer2_growfs");
496 break;
497 case HAMMER2IOC_READ:
498 printf("read `%s'\n", image);
499 TIMER_START(start);
500 error = hammer2_readx(vroot, dir, h2_opt->read_path);
501 if (error)
502 errx(1, "read `%s' failed '%s'", image,
503 strerror(error));
504 TIMER_RESULTS(start, "hammer2_readx");
505 break;
506 default:
507 printf("populating `%s'\n", image);
508 TIMER_START(start);
509 if (hammer2_populate_dir(vroot, dir, root, root, fsopts, 0))
510 errx(1, "image file `%s' not populated", image);
511 TIMER_RESULTS(start, "hammer2_populate_dir");
512 break;
513 }
514
515 /* unmount image */
516 error = hammer2_vfs_unmount(&mp, 0);
517 if (error)
518 errx(1, "failed to unmount, error %d", error);
519
520 /* check leaked resource */
521 if (vnode_count)
522 printf("XXX %lld vnode left\n", (long long)vnode_count);
523 if (hammer2_chain_allocs)
524 printf("XXX %ld chain left\n", hammer2_chain_allocs);
525 bcleanup();
526
527 /* vfs uninit */
528 error = hammer2_vfs_uninit();
529 if (error)
530 errx(1, "failed to vfs uninit, error %d", error);
531
532 if (close(fsopts->fd) == -1)
533 err(1, "closing `%s'", image);
534 fsopts->fd = -1;
535
536 printf("image `%s' complete\n", image);
537 }
538
539 /* end of public functions */
540
541 static void
hammer2_parse_pfs_opts(const char * buf,fsinfo_t * fsopts)542 hammer2_parse_pfs_opts(const char *buf, fsinfo_t *fsopts)
543 {
544 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
545 char *o, *p;
546 size_t n;
547
548 o = p = strdup(buf);
549 p = strchr(p, ':');
550 if (p != NULL) {
551 *p++ = 0;
552 n = strlen(p);
553 } else {
554 n = 0;
555 }
556
557 if (!strcmp(o, "get") || !strcmp(o, "list")) {
558 h2_opt->ioctl_cmd = HAMMER2IOC_PFS_GET;
559 } else if (!strcmp(o, "lookup")) {
560 if (n == 0 || n > NAME_MAX)
561 errx(1, "invalid PFS name \"%s\"", p);
562 h2_opt->ioctl_cmd = HAMMER2IOC_PFS_LOOKUP;
563 } else if (!strcmp(o, "create")) {
564 if (n == 0 || n > NAME_MAX)
565 errx(1, "invalid PFS name \"%s\"", p);
566 h2_opt->ioctl_cmd = HAMMER2IOC_PFS_CREATE;
567 } else if (!strcmp(o, "delete")) {
568 if (n == 0 || n > NAME_MAX)
569 errx(1, "invalid PFS name \"%s\"", p);
570 h2_opt->ioctl_cmd = HAMMER2IOC_PFS_DELETE;
571 } else if (!strcmp(o, "snapshot")) {
572 if (n > NAME_MAX)
573 errx(1, "invalid PFS name \"%s\"", p);
574 h2_opt->ioctl_cmd = HAMMER2IOC_PFS_SNAPSHOT;
575 } else {
576 errx(1, "invalid PFS command \"%s\"", o);
577 }
578
579 strlcpy(h2_opt->pfs_cmd_name, o, sizeof(h2_opt->pfs_cmd_name));
580 if (n > 0)
581 strlcpy(h2_opt->pfs_name, p, sizeof(h2_opt->pfs_name));
582
583 free(o);
584 }
585
586 static void
hammer2_parse_inode_opts(const char * buf,fsinfo_t * fsopts)587 hammer2_parse_inode_opts(const char *buf, fsinfo_t *fsopts)
588 {
589 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
590 char *o, *p;
591 size_t n;
592
593 o = p = strdup(buf);
594 p = strchr(p, ':');
595 if (p != NULL) {
596 *p++ = 0;
597 n = strlen(p);
598 } else {
599 n = 0;
600 }
601
602 if (!strcmp(o, "get")) {
603 if (n == 0 || n > PATH_MAX)
604 errx(1, "invalid file path \"%s\"", p);
605 h2_opt->ioctl_cmd = HAMMER2IOC_INODE_GET;
606 } else if (!strcmp(o, "setcheck")) {
607 if (n == 0 || n > PATH_MAX - 10)
608 errx(1, "invalid argument \"%s\"", p);
609 h2_opt->ioctl_cmd = HAMMER2IOC_INODE_SET;
610 } else if (!strcmp(o, "setcomp")) {
611 if (n == 0 || n > PATH_MAX - 10)
612 errx(1, "invalid argument \"%s\"", p);
613 h2_opt->ioctl_cmd = HAMMER2IOC_INODE_SET;
614 } else {
615 errx(1, "invalid inode command \"%s\"", o);
616 }
617
618 strlcpy(h2_opt->inode_cmd_name, o, sizeof(h2_opt->inode_cmd_name));
619 if (n > 0)
620 strlcpy(h2_opt->inode_path, p, sizeof(h2_opt->inode_path));
621
622 free(o);
623 }
624
625 static hammer2_off_t
hammer2_image_size(fsinfo_t * fsopts)626 hammer2_image_size(fsinfo_t *fsopts)
627 {
628 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
629 hammer2_off_t image_size, used_size = 0;
630 int num_level1, delta_num_level1;
631
632 /* use 4 volume headers by default */
633 num_level1 = h2_opt->num_volhdr * 2; /* default 4 x 2 */
634 assert(num_level1 != 0);
635 assert(num_level1 <= 8);
636
637 /* add 4MiB segment for each level1 */
638 used_size += HAMMER2_ZONE_SEG64 * num_level1;
639
640 /* add boot/aux area, but exact size unknown at this point */
641 used_size += HAMMER2_BOOT_NOM_BYTES + HAMMER2_AUX_NOM_BYTES;
642
643 /* add data size */
644 used_size += fsopts->size;
645
646 /* XXX add extra level1 for meta data and indirect blocks */
647 used_size += HAMMER2_FREEMAP_LEVEL1_SIZE;
648
649 /* XXX add extra level1 for safety */
650 if (used_size > HAMMER2_FREEMAP_LEVEL1_SIZE * 10)
651 used_size += HAMMER2_FREEMAP_LEVEL1_SIZE;
652
653 /* use 8GiB image size by default */
654 image_size = HAMMER2_FREEMAP_LEVEL1_SIZE * num_level1;
655 printf("trying default image size %s\n", sizetostr(image_size));
656
657 /* adjust if image size isn't large enough */
658 if (used_size > image_size) {
659 /* determine extra level1 needed */
660 delta_num_level1 = howmany(used_size - image_size,
661 HAMMER2_FREEMAP_LEVEL1_SIZE);
662
663 /* adjust used size with 4MiB segment for each extra level1 */
664 used_size += HAMMER2_ZONE_SEG64 * delta_num_level1;
665
666 /* adjust image size with extra level1 */
667 image_size += HAMMER2_FREEMAP_LEVEL1_SIZE * delta_num_level1;
668 printf("trying adjusted image size %s\n",
669 sizetostr(image_size));
670
671 if (used_size > image_size)
672 errx(1, "invalid used_size %lld > image_size %lld",
673 (long long)used_size, (long long)image_size);
674 }
675
676 return image_size;
677 }
678
679 static const char *
hammer2_label_name(int label_type)680 hammer2_label_name(int label_type)
681 {
682 switch (label_type) {
683 case HAMMER2_LABEL_NONE:
684 return "NONE";
685 case HAMMER2_LABEL_BOOT:
686 return "BOOT";
687 case HAMMER2_LABEL_ROOT:
688 return "ROOT";
689 case HAMMER2_LABEL_DATA:
690 return "DATA";
691 default:
692 assert(0);
693 }
694 return NULL;
695 }
696
697 static void
hammer2_validate(const char * dir,fsnode * root,fsinfo_t * fsopts)698 hammer2_validate(const char *dir, fsnode *root, fsinfo_t *fsopts)
699 {
700 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
701 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
702 hammer2_off_t image_size = 0, minsize, maxsize;
703 const char *s;
704
705 /* ioctl commands could have NULL dir / root */
706 assert(fsopts != NULL);
707
708 if (debug & DEBUG_FS_VALIDATE) {
709 APRINTF("before defaults set:\n");
710 hammer2_dump_fsinfo(fsopts);
711 }
712
713 /* makefs only supports "DATA" for default PFS label */
714 if (!h2_opt->label_specified) {
715 opt->DefaultLabelType = HAMMER2_LABEL_DATA;
716 s = hammer2_label_name(opt->DefaultLabelType);
717 printf("using default label \"%s\"\n", s);
718 }
719
720 /* set default mount PFS label */
721 if (!strcmp(h2_opt->mount_label, "")) {
722 s = hammer2_label_name(HAMMER2_LABEL_DATA);
723 strlcpy(h2_opt->mount_label, s, sizeof(h2_opt->mount_label));
724 printf("using default mount label \"%s\"\n", s);
725 }
726
727 /* set default number of volume headers */
728 if (!h2_opt->num_volhdr) {
729 h2_opt->num_volhdr = HAMMER2_NUM_VOLHDRS;
730 printf("using default %d volume headers\n", h2_opt->num_volhdr);
731 }
732
733 /* done if ioctl commands */
734 if (h2_opt->ioctl_cmd) {
735 if (h2_opt->ioctl_cmd == HAMMER2IOC_GROWFS)
736 goto ignore_size_dir;
737 else
738 goto done;
739 }
740
741 /* calculate data size */
742 if (fsopts->size != 0)
743 fsopts->size = 0; /* shouldn't reach here to begin with */
744 if (root == NULL)
745 errx(1, "fsnode tree not constructed");
746 hammer2_size_dir(root, fsopts);
747 printf("estimated data size %s from %lld inode\n",
748 sizetostr(fsopts->size), (long long)fsopts->inodes);
749
750 /* determine image size from data size */
751 image_size = hammer2_image_size(fsopts);
752 assert((image_size & HAMMER2_FREEMAP_LEVEL1_MASK) == 0);
753 ignore_size_dir:
754 minsize = roundup(fsopts->minsize, HAMMER2_FREEMAP_LEVEL1_SIZE);
755 maxsize = roundup(fsopts->maxsize, HAMMER2_FREEMAP_LEVEL1_SIZE);
756 if (image_size < minsize)
757 image_size = minsize;
758 else if (maxsize > 0 && image_size > maxsize)
759 errx(1, "`%s' size of %lld is larger than the maxsize of %lld",
760 dir, (long long)image_size, (long long)maxsize);
761
762 assert((image_size & HAMMER2_FREEMAP_LEVEL1_MASK) == 0);
763 h2_opt->image_size = image_size;
764 printf("using %s image size\n", sizetostr(h2_opt->image_size));
765 done:
766 if (debug & DEBUG_FS_VALIDATE) {
767 APRINTF("after defaults set:\n");
768 hammer2_dump_fsinfo(fsopts);
769 }
770 }
771
772 static void
hammer2_dump_fsinfo(fsinfo_t * fsopts)773 hammer2_dump_fsinfo(fsinfo_t *fsopts)
774 {
775 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
776 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
777 int i;
778 char *s;
779
780 assert(fsopts != NULL);
781
782 APRINTF("fsinfo_t at %p\n", fsopts);
783
784 printf("\tinodes %lld\n", (long long)fsopts->inodes);
785 printf("\tsize %lld, minsize %lld, maxsize %lld\n",
786 (long long)fsopts->size,
787 (long long)fsopts->minsize,
788 (long long)fsopts->maxsize);
789
790 printf("\thammer2_debug 0x%x\n", hammer2_debug);
791
792 printf("\tlabel_specified %d\n", h2_opt->label_specified);
793 printf("\tmount_label \"%s\"\n", h2_opt->mount_label);
794 printf("\tnum_volhdr %d\n", h2_opt->num_volhdr);
795 printf("\tioctl_cmd %ld\n", h2_opt->ioctl_cmd);
796 printf("\temergency_mode %d\n", h2_opt->emergency_mode);
797 printf("\tpfs_cmd_name \"%s\"\n", h2_opt->pfs_cmd_name);
798 printf("\tpfs_name \"%s\"\n", h2_opt->pfs_name);
799 printf("\tinode_cmd_name \"%s\"\n", h2_opt->inode_cmd_name);
800 printf("\tinode_path \"%s\"\n", h2_opt->inode_path);
801 printf("\tdestroy_path \"%s\"\n", h2_opt->destroy_path);
802 printf("\tdestroy_inum %lld\n", (long long)h2_opt->destroy_inum);
803 printf("\tread_path \"%s\"\n", h2_opt->read_path);
804 printf("\timage_size 0x%llx\n", (long long)h2_opt->image_size);
805
806 printf("\tHammer2Version %d\n", opt->Hammer2Version);
807 printf("\tBootAreaSize 0x%jx\n", opt->BootAreaSize);
808 printf("\tAuxAreaSize 0x%jx\n", opt->AuxAreaSize);
809 printf("\tNLabels %d\n", opt->NLabels);
810 printf("\tCompType %d\n", opt->CompType);
811 printf("\tCheckType %d\n", opt->CheckType);
812 printf("\tDefaultLabelType %d\n", opt->DefaultLabelType);
813 printf("\tDebugOpt %d\n", opt->DebugOpt);
814
815 s = NULL;
816 hammer2_uuid_to_str(&opt->Hammer2_FSType, &s);
817 printf("\tHammer2_FSType \"%s\"\n", s);
818 s = NULL;
819 hammer2_uuid_to_str(&opt->Hammer2_VolFSID, &s);
820 printf("\tHammer2_VolFSID \"%s\"\n", s);
821 s = NULL;
822 hammer2_uuid_to_str(&opt->Hammer2_SupCLID, &s);
823 printf("\tHammer2_SupCLID \"%s\"\n", s);
824 s = NULL;
825 hammer2_uuid_to_str(&opt->Hammer2_SupFSID, &s);
826 printf("\tHammer2_SupFSID \"%s\"\n", s);
827
828 for (i = 0; i < opt->NLabels; i++) {
829 printf("\tLabel[%d] \"%s\"\n", i, opt->Label[i]);
830 s = NULL;
831 hammer2_uuid_to_str(&opt->Hammer2_PfsCLID[i], &s);
832 printf("\t Hammer2_PfsCLID[%d] \"%s\"\n", i, s);
833 s = NULL;
834 hammer2_uuid_to_str(&opt->Hammer2_PfsFSID[i], &s);
835 printf("\t Hammer2_PfsFSID[%d] \"%s\"\n", i, s);
836 }
837
838 free(s);
839 }
840
841 static int
hammer2_setup_blkdev(const char * image,fsinfo_t * fsopts)842 hammer2_setup_blkdev(const char *image, fsinfo_t *fsopts)
843 {
844 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
845 hammer2_off_t size;
846
847 if ((fsopts->fd = open(image, O_RDWR)) == -1) {
848 warn("can't open `%s' for writing", image);
849 return -1;
850 }
851
852 size = check_volume(fsopts->fd);
853 if (h2_opt->image_size > size) {
854 warnx("image size %lld exceeds %s size %lld",
855 (long long)h2_opt->image_size, image, (long long)size);
856 return -1;
857 }
858
859 return 0;
860 }
861
862 static int
hammer2_create_image(const char * image,fsinfo_t * fsopts)863 hammer2_create_image(const char *image, fsinfo_t *fsopts)
864 {
865 hammer2_makefs_options_t *h2_opt = fsopts->fs_specific;
866 hammer2_mkfs_options_t *opt = &h2_opt->mkfs_options;
867 char *av[] = { (char *)image, }; /* XXX support multi-volumes */
868 char *buf;
869 int i, bufsize, oflags;
870 off_t bufrem;
871 struct stat st;
872
873 assert(image != NULL);
874 assert(fsopts != NULL);
875
876 /* check if image is blk or chr */
877 if (stat(image, &st) == 0) {
878 if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode)) {
879 if (hammer2_setup_blkdev(image, fsopts))
880 return -1;
881 goto done;
882 }
883 }
884
885 /* create image */
886 oflags = O_RDWR | O_CREAT;
887 if (fsopts->offset == 0)
888 oflags |= O_TRUNC;
889 if ((fsopts->fd = open(image, oflags, 0666)) == -1) {
890 warn("can't open `%s' for writing", image);
891 return -1;
892 }
893
894 /* zero image */
895 bufsize = HAMMER2_PBUFSIZE;
896 bufrem = h2_opt->image_size;
897 if (fsopts->sparse) {
898 if (ftruncate(fsopts->fd, bufrem) == -1) {
899 warn("sparse option disabled");
900 fsopts->sparse = 0;
901 }
902 }
903 if (fsopts->sparse) {
904 /* File truncated at bufrem. Remaining is 0 */
905 bufrem = 0;
906 buf = NULL;
907 } else {
908 if (debug & DEBUG_FS_CREATE_IMAGE)
909 APRINTF("zero-ing image `%s', %lld sectors, "
910 "using %d byte chunks\n",
911 image, (long long)bufrem, bufsize);
912 buf = ecalloc(1, bufsize);
913 }
914
915 if (fsopts->offset != 0) {
916 if (lseek(fsopts->fd, fsopts->offset, SEEK_SET) == -1) {
917 warn("can't seek");
918 free(buf);
919 return -1;
920 }
921 }
922
923 while (bufrem > 0) {
924 i = write(fsopts->fd, buf, MIN(bufsize, bufrem));
925 if (i == -1) {
926 warn("zeroing image, %lld bytes to go",
927 (long long)bufrem);
928 free(buf);
929 return -1;
930 }
931 bufrem -= i;
932 }
933 if (buf)
934 free(buf);
935 done:
936 /* make the file system */
937 if (debug & DEBUG_FS_CREATE_IMAGE)
938 APRINTF("calling mkfs(\"%s\", ...)\n", image);
939 hammer2_mkfs(1, av, opt); /* success if returned */
940
941 return fsopts->fd;
942 }
943
944 static off_t
hammer2_phys_size(off_t size)945 hammer2_phys_size(off_t size)
946 {
947 off_t radix_size, phys_size = 0;
948 int i;
949
950 if (size > HAMMER2_PBUFSIZE) {
951 phys_size += rounddown(size, HAMMER2_PBUFSIZE);
952 size = size % HAMMER2_PBUFSIZE;
953 }
954
955 for (i = HAMMER2_RADIX_MIN; i <= HAMMER2_RADIX_MAX; i++) {
956 radix_size = 1UL << i;
957 if (radix_size >= size) {
958 phys_size += radix_size;
959 break;
960 }
961 }
962
963 return phys_size;
964 }
965
966 /* calculate data size */
967 static void
hammer2_size_dir(fsnode * root,fsinfo_t * fsopts)968 hammer2_size_dir(fsnode *root, fsinfo_t *fsopts)
969 {
970 fsnode *node;
971
972 assert(fsopts != NULL);
973
974 if (debug & DEBUG_FS_SIZE_DIR)
975 APRINTF("entry: bytes %lld inodes %lld\n",
976 (long long)fsopts->size, (long long)fsopts->inodes);
977
978 for (node = root; node != NULL; node = node->next) {
979 if (node == root) { /* we're at "." */
980 assert(strcmp(node->name, ".") == 0);
981 } else if ((node->inode->flags & FI_SIZED) == 0) {
982 /* don't count duplicate names */
983 node->inode->flags |= FI_SIZED;
984 if (debug & DEBUG_FS_SIZE_DIR_NODE)
985 APRINTF("`%s' size %lld\n",
986 node->name,
987 (long long)node->inode->st.st_size);
988 fsopts->inodes++;
989 fsopts->size += sizeof(hammer2_inode_data_t);
990 if (node->type == S_IFREG) {
991 size_t st_size = node->inode->st.st_size;
992 if (st_size > HAMMER2_EMBEDDED_BYTES)
993 fsopts->size += hammer2_phys_size(st_size);
994 } else if (node->type == S_IFLNK) {
995 size_t nlen = strlen(node->symlink);
996 if (nlen > HAMMER2_EMBEDDED_BYTES)
997 fsopts->size += hammer2_phys_size(nlen);
998 }
999 }
1000 if (node->type == S_IFDIR)
1001 hammer2_size_dir(node->child, fsopts);
1002 }
1003
1004 if (debug & DEBUG_FS_SIZE_DIR)
1005 APRINTF("exit: size %lld inodes %lld\n",
1006 (long long)fsopts->size, (long long)fsopts->inodes);
1007 }
1008
1009 static void
hammer2_print(const struct m_vnode * dvp,const struct m_vnode * vp,const fsnode * node,int depth,const char * msg)1010 hammer2_print(const struct m_vnode *dvp, const struct m_vnode *vp,
1011 const fsnode *node, int depth, const char *msg)
1012 {
1013 if (debug & DEBUG_FS_POPULATE) {
1014 if (1) {
1015 int indent = depth * 2;
1016 char *type;
1017 if (S_ISDIR(node->type))
1018 type = "dir";
1019 else if (S_ISREG(node->type))
1020 type = "reg";
1021 else if (S_ISLNK(node->type))
1022 type = "lnk";
1023 else if (S_ISFIFO(node->type))
1024 type = "fifo";
1025 else
1026 type = "???";
1027 printf("%*.*s", indent, indent, "");
1028 printf("dvp=%p/%d vp=%p/%d \"%s\" %s %s\n",
1029 dvp, dvp ? VTOI(dvp)->refs : 0,
1030 vp, vp ? VTOI(vp)->refs : 0,
1031 node->name, type, msg);
1032 } else {
1033 char type;
1034 if (S_ISDIR(node->type))
1035 type = 'd';
1036 else if (S_ISREG(node->type))
1037 type = 'r';
1038 else if (S_ISLNK(node->type))
1039 type = 'l';
1040 else if (S_ISFIFO(node->type))
1041 type = 'f';
1042 else
1043 type = '?';
1044 printf("%c", type);
1045 fflush(stdout);
1046 }
1047 }
1048 }
1049
1050 static int
hammer2_populate_dir(struct m_vnode * dvp,const char * dir,fsnode * root,fsnode * parent,fsinfo_t * fsopts,int depth)1051 hammer2_populate_dir(struct m_vnode *dvp, const char *dir, fsnode *root,
1052 fsnode *parent, fsinfo_t *fsopts, int depth)
1053 {
1054 fsnode *cur;
1055 struct m_vnode *vp;
1056 struct stat st;
1057 char f[MAXPATHLEN];
1058 const char *path;
1059 int hardlink;
1060 int error;
1061
1062 assert(dvp != NULL);
1063 assert(dir != NULL);
1064 assert(root != NULL);
1065 assert(parent != NULL);
1066 assert(fsopts != NULL);
1067
1068 /* assert root directory */
1069 assert(S_ISDIR(root->type));
1070 assert(!strcmp(root->name, "."));
1071 assert(!root->child);
1072 assert(!root->parent || root->parent->child == root);
1073
1074 hammer2_print(dvp, NULL, root, depth, "enter");
1075 if (stat(dir, &st) == -1)
1076 err(1, "no such path %s", dir);
1077 if (!S_ISDIR(st.st_mode))
1078 errx(1, "no such dir %s", dir);
1079
1080 for (cur = root->next; cur != NULL; cur = cur->next) {
1081 /* global variable for HAMMER2 vnops */
1082 hammer2_curnode = cur;
1083
1084 /* construct source path */
1085 if (cur->contents) {
1086 path = cur->contents;
1087 } else {
1088 if (snprintf(f, sizeof(f), "%s/%s/%s",
1089 cur->root, cur->path, cur->name) >= (int)sizeof(f))
1090 errx(1, "path %s too long", f);
1091 path = f;
1092 }
1093 if (S_ISLNK(cur->type)) {
1094 if (lstat(path, &st) == -1)
1095 err(1, "no such symlink %s", path);
1096 } else {
1097 if (stat(path, &st) == -1)
1098 err(1, "no such path %s", path);
1099 }
1100
1101 /* update node state */
1102 if ((cur->inode->flags & FI_ALLOCATED) == 0) {
1103 cur->inode->flags |= FI_ALLOCATED;
1104 if (cur != root)
1105 cur->parent = parent;
1106 }
1107
1108 /* detect hardlink */
1109 if (cur->inode->flags & FI_WRITTEN) {
1110 assert(!S_ISDIR(cur->type));
1111 hardlink = 1;
1112 } else {
1113 hardlink = 0;
1114 }
1115 cur->inode->flags |= FI_WRITTEN;
1116
1117 /* make sure it doesn't exist yet */
1118 vp = NULL;
1119 error = hammer2_nresolve(dvp, &vp, cur->name,
1120 strlen(cur->name));
1121 if (!error)
1122 errx(1, "hammer2_nresolve(\"%s\") already exists",
1123 cur->name);
1124 hammer2_print(dvp, vp, cur, depth, "nresolve");
1125
1126 /* if directory, mkdir and recurse */
1127 if (S_ISDIR(cur->type)) {
1128 assert(cur->child);
1129
1130 vp = NULL;
1131 error = hammer2_nmkdir(dvp, &vp, cur->name,
1132 strlen(cur->name), cur->inode->st.st_mode);
1133 if (error)
1134 errx(1, "hammer2_nmkdir(\"%s\") failed: %s",
1135 cur->name, strerror(error));
1136 assert(vp);
1137 hammer2_print(dvp, vp, cur, depth, "nmkdir");
1138
1139 error = hammer2_populate_dir(vp, path, cur->child, cur,
1140 fsopts, depth + 1);
1141 if (error)
1142 errx(1, "failed to populate %s: %s",
1143 path, strerror(error));
1144 cur->inode->param = vp;
1145 continue;
1146 }
1147
1148 /* if regular file, creat and write its data */
1149 if (S_ISREG(cur->type) && !hardlink) {
1150 assert(cur->child == NULL);
1151
1152 vp = NULL;
1153 error = hammer2_ncreate(dvp, &vp, cur->name,
1154 strlen(cur->name), cur->inode->st.st_mode);
1155 if (error)
1156 errx(1, "hammer2_ncreate(\"%s\") failed: %s",
1157 cur->name, strerror(error));
1158 assert(vp);
1159 hammer2_print(dvp, vp, cur, depth, "ncreate");
1160
1161 error = hammer2_write_file(vp, path, cur);
1162 if (error)
1163 errx(1, "hammer2_write_file(\"%s\") failed: %s",
1164 path, strerror(error));
1165 cur->inode->param = vp;
1166 continue;
1167 }
1168
1169 /* if symlink, create a symlink against target */
1170 if (S_ISLNK(cur->type)) {
1171 assert(cur->child == NULL);
1172
1173 vp = NULL;
1174 error = hammer2_nsymlink(dvp, &vp, cur->name,
1175 strlen(cur->name), cur->symlink,
1176 cur->inode->st.st_mode);
1177 if (error)
1178 errx(1, "hammer2_nsymlink(\"%s\") failed: %s",
1179 cur->name, strerror(error));
1180 assert(vp);
1181 hammer2_print(dvp, vp, cur, depth, "nsymlink");
1182 cur->inode->param = vp;
1183 continue;
1184 }
1185
1186 /* if fifo, create a fifo */
1187 if (S_ISFIFO(cur->type) && !hardlink) {
1188 assert(cur->child == NULL);
1189
1190 vp = NULL;
1191 error = hammer2_nmknod(dvp, &vp, cur->name,
1192 strlen(cur->name), VFIFO, cur->inode->st.st_mode);
1193 if (error)
1194 errx(1, "hammer2_nmknod(\"%s\") failed: %s",
1195 cur->name, strerror(error));
1196 assert(vp);
1197 hammer2_print(dvp, vp, cur, depth, "nmknod");
1198 cur->inode->param = vp;
1199 continue;
1200 }
1201
1202 /* if hardlink, creat a hardlink */
1203 if ((S_ISREG(cur->type) || S_ISFIFO(cur->type)) && hardlink) {
1204 char buf[64];
1205 assert(cur->child == NULL);
1206
1207 /* source vnode must not be NULL */
1208 vp = cur->inode->param;
1209 assert(vp);
1210 /* currently these conditions must be true */
1211 assert(vp->v_data);
1212 assert(vp->v_type == VREG || vp->v_type == VFIFO);
1213 assert(vp->v_logical);
1214 assert(!vp->v_vflushed);
1215 assert(vp->v_malloced);
1216 assert(VTOI(vp)->refs > 0);
1217
1218 error = hammer2_nlink(dvp, vp, cur->name,
1219 strlen(cur->name));
1220 if (error)
1221 errx(1, "hammer2_nlink(\"%s\") failed: %s",
1222 cur->name, strerror(error));
1223 snprintf(buf, sizeof(buf), "nlink=%lld",
1224 (long long)VTOI(vp)->meta.nlinks);
1225 hammer2_print(dvp, vp, cur, depth, buf);
1226 continue;
1227 }
1228
1229 /* other types are unsupported */
1230 printf("ignore %s 0%o\n", path, cur->type);
1231 }
1232
1233 return 0;
1234 }
1235
1236 static int
hammer2_write_file(struct m_vnode * vp,const char * path,fsnode * node)1237 hammer2_write_file(struct m_vnode *vp, const char *path, fsnode *node)
1238 {
1239 struct stat *st = &node->inode->st;
1240 size_t nsize, bufsize;
1241 off_t offset;
1242 int fd, error;
1243 char *p;
1244
1245 nsize = st->st_size;
1246 if (nsize == 0)
1247 return 0;
1248 /* check nsize vs maximum file size */
1249
1250 fd = open(path, O_RDONLY);
1251 if (fd < 0)
1252 err(1, "failed to open %s", path);
1253
1254 p = mmap(0, nsize, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0);
1255 if (p == MAP_FAILED)
1256 err(1, "failed to mmap %s", path);
1257 close(fd);
1258
1259 for (offset = 0; offset < nsize; ) {
1260 bufsize = MIN(nsize - offset, HAMMER2_PBUFSIZE);
1261 assert(bufsize <= HAMMER2_PBUFSIZE);
1262 error = hammer2_write(vp, p + offset, bufsize, offset);
1263 if (error)
1264 errx(1, "failed to write to %s vnode: %s",
1265 path, strerror(error));
1266 offset += bufsize;
1267 if (bufsize == HAMMER2_PBUFSIZE)
1268 assert((offset & (HAMMER2_PBUFSIZE - 1)) == 0);
1269 }
1270 munmap(p, nsize);
1271
1272 return 0;
1273 }
1274
1275 static int
trim_char(char * p,char c)1276 trim_char(char *p, char c)
1277 {
1278 char *o, tmp[PATH_MAX];
1279 bool prev_was_c;
1280 size_t n;
1281 int i;
1282
1283 assert(p);
1284 /* nothing to do */
1285 if (strlen(p) == 0)
1286 return 0;
1287
1288 strlcpy(tmp, p, sizeof(tmp));
1289 if (strncmp(tmp, p, sizeof(tmp)))
1290 return ENOSPC;
1291
1292 /* trim consecutive */
1293 prev_was_c = false;
1294 o = p;
1295 n = strlen(p);
1296
1297 for (i = 0; i < n; i++) {
1298 if (tmp[i] == c) {
1299 if (!prev_was_c)
1300 *p++ = tmp[i];
1301 prev_was_c = true;
1302 } else {
1303 *p++ = tmp[i];
1304 prev_was_c = false;
1305 }
1306 }
1307 *p = 0;
1308 assert(strlen(p) <= strlen(tmp));
1309
1310 /* assert no consecutive */
1311 prev_was_c = false;
1312 p = o;
1313 n = strlen(p);
1314
1315 for (i = 0; i < n; i++) {
1316 if (p[i] == c) {
1317 assert(!prev_was_c);
1318 prev_was_c = true;
1319 } else {
1320 prev_was_c = false;
1321 }
1322 }
1323
1324 /* trim leading */
1325 if (*p == c)
1326 memmove(p, p + 1, strlen(p + 1) + 1);
1327 assert(*p != '/');
1328
1329 /* trim trailing */
1330 p += strlen(p);
1331 p--;
1332 if (*p == c)
1333 *p = 0;
1334 assert(p[strlen(p) - 1] != '/');
1335
1336 return 0;
1337 }
1338
1339 static int
trim_slash(char * p)1340 trim_slash(char *p)
1341 {
1342 return trim_char(p, '/');
1343 }
1344
1345 static bool
is_supported_link(const char * s)1346 is_supported_link(const char *s)
1347 {
1348 /* absolute path can't be supported */
1349 if (strlen(s) >= 1 && strncmp(s, "/", 1) == 0)
1350 return false;
1351
1352 /* XXX ".." is currently unsupported */
1353 if (strlen(s) >= 3 && strncmp(s, "../", 3) == 0)
1354 return false;
1355
1356 return true;
1357 }
1358
1359 static int
hammer2_version_get(struct m_vnode * vp)1360 hammer2_version_get(struct m_vnode *vp)
1361 {
1362 hammer2_dev_t *hmp;
1363
1364 hmp = VTOI(vp)->pmp->pfs_hmps[0];
1365 if (hmp == NULL)
1366 return EINVAL;
1367
1368 printf("version: %d\n", hmp->voldata.version);
1369
1370 return 0;
1371 }
1372
1373 struct pfs_entry {
1374 TAILQ_ENTRY(pfs_entry) entry;
1375 char name[NAME_MAX+1];
1376 char s[NAME_MAX+1];
1377 };
1378
1379 static int
hammer2_pfs_get(struct m_vnode * vp)1380 hammer2_pfs_get(struct m_vnode *vp)
1381 {
1382 hammer2_ioc_pfs_t pfs;
1383 TAILQ_HEAD(, pfs_entry) head;
1384 struct pfs_entry *p, *e;
1385 char *pfs_id_str;
1386 const char *type_str;
1387 int error;
1388
1389 bzero(&pfs, sizeof(pfs));
1390 TAILQ_INIT(&head);
1391
1392 while ((pfs.name_key = pfs.name_next) != (hammer2_key_t)-1) {
1393 error = hammer2_ioctl_pfs_get(VTOI(vp), &pfs);
1394 if (error)
1395 return error;
1396
1397 pfs_id_str = NULL;
1398 hammer2_uuid_to_str(&pfs.pfs_clid, &pfs_id_str);
1399
1400 if (pfs.pfs_type == HAMMER2_PFSTYPE_MASTER) {
1401 if (pfs.pfs_subtype == HAMMER2_PFSSUBTYPE_NONE)
1402 type_str = "MASTER";
1403 else
1404 type_str = hammer2_pfssubtype_to_str(
1405 pfs.pfs_subtype);
1406 } else {
1407 type_str = hammer2_pfstype_to_str(pfs.pfs_type);
1408 }
1409 e = ecalloc(1, sizeof(*e));
1410 snprintf(e->name, sizeof(e->name), "%s", pfs.name);
1411 snprintf(e->s, sizeof(e->s), "%-11s %s", type_str, pfs_id_str);
1412 free(pfs_id_str);
1413
1414 p = TAILQ_FIRST(&head);
1415 while (p) {
1416 if (strcmp(e->name, p->name) <= 0) {
1417 TAILQ_INSERT_BEFORE(p, e, entry);
1418 break;
1419 }
1420 p = TAILQ_NEXT(p, entry);
1421 }
1422 if (!p)
1423 TAILQ_INSERT_TAIL(&head, e, entry);
1424 }
1425
1426 printf("Type "
1427 "ClusterId (pfs_clid) "
1428 "Label\n");
1429 while ((p = TAILQ_FIRST(&head)) != NULL) {
1430 printf("%s %s\n", p->s, p->name);
1431 TAILQ_REMOVE(&head, p, entry);
1432 free(p);
1433 }
1434
1435 return 0;
1436 }
1437
1438 static int
hammer2_pfs_lookup(struct m_vnode * vp,const char * pfs_name)1439 hammer2_pfs_lookup(struct m_vnode *vp, const char *pfs_name)
1440 {
1441 hammer2_ioc_pfs_t pfs;
1442 char *pfs_id_str;
1443 int error;
1444
1445 bzero(&pfs, sizeof(pfs));
1446 strlcpy(pfs.name, pfs_name, sizeof(pfs.name));
1447
1448 error = hammer2_ioctl_pfs_lookup(VTOI(vp), &pfs);
1449 if (error == 0) {
1450 printf("name: %s\n", pfs.name);
1451 printf("type: %s\n", hammer2_pfstype_to_str(pfs.pfs_type));
1452 printf("subtype: %s\n",
1453 hammer2_pfssubtype_to_str(pfs.pfs_subtype));
1454
1455 pfs_id_str = NULL;
1456 hammer2_uuid_to_str(&pfs.pfs_fsid, &pfs_id_str);
1457 printf("fsid: %s\n", pfs_id_str);
1458 free(pfs_id_str);
1459
1460 pfs_id_str = NULL;
1461 hammer2_uuid_to_str(&pfs.pfs_clid, &pfs_id_str);
1462 printf("clid: %s\n", pfs_id_str);
1463 free(pfs_id_str);
1464 }
1465
1466 return error;
1467 }
1468
1469 static int
hammer2_pfs_create(struct m_vnode * vp,const char * pfs_name)1470 hammer2_pfs_create(struct m_vnode *vp, const char *pfs_name)
1471 {
1472 hammer2_ioc_pfs_t pfs;
1473 int error;
1474
1475 bzero(&pfs, sizeof(pfs));
1476 strlcpy(pfs.name, pfs_name, sizeof(pfs.name));
1477 pfs.pfs_type = HAMMER2_PFSTYPE_MASTER;
1478 uuid_create(&pfs.pfs_clid, NULL);
1479 uuid_create(&pfs.pfs_fsid, NULL);
1480
1481 error = hammer2_ioctl_pfs_create(VTOI(vp), &pfs);
1482 if (error == EEXIST)
1483 fprintf(stderr,
1484 "NOTE: Typically the same name is "
1485 "used for cluster elements on "
1486 "different mounts,\n"
1487 " but cluster elements on the "
1488 "same mount require unique names.\n"
1489 "hammer2: pfs_create(%s): already present\n",
1490 pfs_name);
1491
1492 return error;
1493 }
1494
1495 static int
hammer2_pfs_delete(struct m_vnode * vp,const char * pfs_name)1496 hammer2_pfs_delete(struct m_vnode *vp, const char *pfs_name)
1497 {
1498 hammer2_ioc_pfs_t pfs;
1499
1500 bzero(&pfs, sizeof(pfs));
1501 strlcpy(pfs.name, pfs_name, sizeof(pfs.name));
1502
1503 return hammer2_ioctl_pfs_delete(VTOI(vp), &pfs);
1504 }
1505
1506 static int
hammer2_pfs_snapshot(struct m_vnode * vp,const char * pfs_name,const char * mount_label)1507 hammer2_pfs_snapshot(struct m_vnode *vp, const char *pfs_name,
1508 const char *mount_label)
1509 {
1510 hammer2_ioc_pfs_t pfs;
1511 struct tm *tp;
1512 time_t t;
1513
1514 bzero(&pfs, sizeof(pfs));
1515 strlcpy(pfs.name, pfs_name, sizeof(pfs.name));
1516
1517 if (strlen(pfs.name) == 0) {
1518 time(&t);
1519 tp = localtime(&t);
1520 snprintf(pfs.name, sizeof(pfs.name),
1521 "%s.%04d%02d%02d.%02d%02d%02d",
1522 mount_label,
1523 tp->tm_year + 1900,
1524 tp->tm_mon + 1,
1525 tp->tm_mday,
1526 tp->tm_hour,
1527 tp->tm_min,
1528 tp->tm_sec);
1529 }
1530
1531 return hammer2_ioctl_pfs_snapshot(VTOI(vp), &pfs);
1532 }
1533
1534 static int
hammer2_inode_getx(struct m_vnode * dvp,const char * f)1535 hammer2_inode_getx(struct m_vnode *dvp, const char *f)
1536 {
1537 hammer2_ioc_inode_t inode;
1538 hammer2_inode_t *ip;
1539 hammer2_inode_meta_t *meta;
1540 struct m_vnode *vp;
1541 char *o, *p, *name, *str = NULL;
1542 char tmp[PATH_MAX];
1543 int error;
1544 uuid_t uuid;
1545
1546 assert(strlen(f) > 0);
1547 o = p = name = strdup(f);
1548
1549 error = trim_slash(p);
1550 if (error)
1551 return error;
1552 if (strlen(p) == 0) {
1553 vp = dvp;
1554 goto start_ioctl;
1555 }
1556
1557 while ((p = strchr(p, '/')) != NULL) {
1558 *p++ = 0; /* NULL terminate name */
1559 if (!strcmp(name, ".")) {
1560 name = p;
1561 continue;
1562 }
1563 vp = NULL;
1564 error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1565 if (error)
1566 return error;
1567
1568 ip = VTOI(vp);
1569 switch (ip->meta.type) {
1570 case HAMMER2_OBJTYPE_DIRECTORY:
1571 break;
1572 case HAMMER2_OBJTYPE_SOFTLINK:
1573 bzero(tmp, sizeof(tmp));
1574 error = hammer2_readlink(vp, tmp, sizeof(tmp));
1575 if (error)
1576 return error;
1577 if (!is_supported_link(tmp))
1578 return EINVAL;
1579 strlcat(tmp, "/", sizeof(tmp));
1580 strlcat(tmp, p, sizeof(tmp));
1581 error = trim_slash(tmp);
1582 if (error)
1583 return error;
1584 p = name = tmp;
1585 continue;
1586 default:
1587 return EINVAL;
1588 }
1589
1590 dvp = vp;
1591 name = p;
1592 }
1593
1594 error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1595 if (error)
1596 return error;
1597 start_ioctl:
1598 bzero(&inode, sizeof(inode));
1599 error = hammer2_ioctl_inode_get(VTOI(vp), &inode);
1600 if (error)
1601 return error;
1602
1603 meta = &inode.ip_data.meta;
1604 printf("--------------------\n");
1605 printf("flags = 0x%x\n", inode.flags);
1606 printf("data_count = %ju\n", (uintmax_t)inode.data_count);
1607 printf("inode_count = %ju\n", (uintmax_t)inode.inode_count);
1608 printf("--------------------\n");
1609 printf("version = %u\n", meta->version);
1610 printf("pfs_subtype = %u (%s)\n", meta->pfs_subtype,
1611 hammer2_pfssubtype_to_str(meta->pfs_subtype));
1612 printf("uflags = 0x%x\n", (unsigned int)meta->uflags);
1613 printf("rmajor = %u\n", meta->rmajor);
1614 printf("rminor = %u\n", meta->rminor);
1615 printf("ctime = %s\n", hammer2_time64_to_str(meta->ctime, &str));
1616 printf("mtime = %s\n", hammer2_time64_to_str(meta->mtime, &str));
1617 printf("atime = %s\n", hammer2_time64_to_str(meta->atime, &str));
1618 printf("btime = %s\n", hammer2_time64_to_str(meta->btime, &str));
1619 uuid = meta->uid;
1620 printf("uid = %s\n", hammer2_uuid_to_str(&uuid, &str));
1621 uuid = meta->gid;
1622 printf("gid = %s\n", hammer2_uuid_to_str(&uuid, &str));
1623 printf("type = %u (%s)\n", meta->type,
1624 hammer2_iptype_to_str(meta->type));
1625 printf("op_flags = 0x%x\n", meta->op_flags);
1626 printf("cap_flags = 0x%x\n", meta->cap_flags);
1627 printf("mode = 0%o\n", meta->mode);
1628 printf("inum = 0x%jx\n", (uintmax_t)meta->inum);
1629 printf("size = %ju\n", (uintmax_t)meta->size);
1630 printf("nlinks = %ju\n", (uintmax_t)meta->nlinks);
1631 printf("iparent = 0x%jx\n", (uintmax_t)meta->iparent);
1632 printf("name_key = 0x%jx\n", (uintmax_t)meta->name_key);
1633 printf("name_len = %u\n", meta->name_len);
1634 printf("ncopies = %u\n", meta->ncopies);
1635 printf("comp_algo = 0x%jx\n", (uintmax_t)meta->comp_algo);
1636 printf("check_algo = %u\n", meta->check_algo);
1637 printf("pfs_nmasters = %u\n", meta->pfs_nmasters);
1638 printf("pfs_type = %u (%s)\n", meta->pfs_type,
1639 hammer2_pfstype_to_str(meta->pfs_type));
1640 printf("pfs_inum = 0x%jx\n", (uintmax_t)meta->pfs_inum);
1641 uuid = meta->pfs_clid;
1642 printf("pfs_clid = %s\n", hammer2_uuid_to_str(&uuid, &str));
1643 uuid = meta->pfs_fsid;
1644 printf("pfs_fsid = %s\n", hammer2_uuid_to_str(&uuid, &str));
1645 printf("data_quota = 0x%jx\n", (uintmax_t)meta->data_quota);
1646 printf("inode_quota = 0x%jx\n", (uintmax_t)meta->inode_quota);
1647 printf("pfs_lsnap_tid = 0x%jx\n", (uintmax_t)meta->pfs_lsnap_tid);
1648 printf("decrypt_check = 0x%jx\n", (uintmax_t)meta->decrypt_check);
1649 printf("--------------------\n");
1650
1651 free(o);
1652
1653 return error;
1654 }
1655
1656 static int
hammer2_inode_setcheck(struct m_vnode * dvp,const char * f)1657 hammer2_inode_setcheck(struct m_vnode *dvp, const char *f)
1658 {
1659 hammer2_ioc_inode_t inode;
1660 hammer2_inode_t *ip;
1661 struct m_vnode *vp;
1662 char *o, *p, *name, *check_algo_str;
1663 char tmp[PATH_MAX];
1664 const char *checks[] = { "none", "disabled", "crc32", "xxhash64",
1665 "sha192", };
1666 int check_algo_idx, error;
1667 uint8_t check_algo;
1668
1669 assert(strlen(f) > 0);
1670 o = p = strdup(f);
1671
1672 p = strrchr(p, ':');
1673 if (p == NULL)
1674 return EINVAL;
1675
1676 *p++ = 0; /* NULL terminate path */
1677 check_algo_str = p;
1678 name = p = o;
1679
1680 /* fail if already empty before trim */
1681 if (strlen(p) == 0)
1682 return EINVAL;
1683
1684 error = trim_slash(p);
1685 if (error)
1686 return error;
1687 if (strlen(check_algo_str) == 0)
1688 return EINVAL;
1689
1690 /* convert check_algo_str to check_algo_idx */
1691 check_algo_idx = nitems(checks);
1692 while (--check_algo_idx >= 0)
1693 if (strcasecmp(check_algo_str, checks[check_algo_idx]) == 0)
1694 break;
1695 if (check_algo_idx < 0) {
1696 if (strcasecmp(check_algo_str, "default") == 0) {
1697 check_algo_str = "xxhash64";
1698 check_algo_idx = HAMMER2_CHECK_XXHASH64;
1699 } else if (strcasecmp(check_algo_str, "disabled") == 0) {
1700 check_algo_str = "disabled";
1701 check_algo_idx = HAMMER2_CHECK_DISABLED;
1702 } else {
1703 printf("invalid check_algo_str: %s\n", check_algo_str);
1704 return EINVAL;
1705 }
1706 }
1707 check_algo = HAMMER2_ENC_ALGO(check_algo_idx);
1708 printf("change %s to algo %d (%s)\n", p, check_algo, check_algo_str);
1709
1710 if (strlen(p) == 0) {
1711 vp = dvp;
1712 goto start_ioctl;
1713 }
1714
1715 while ((p = strchr(p, '/')) != NULL) {
1716 *p++ = 0; /* NULL terminate name */
1717 if (!strcmp(name, ".")) {
1718 name = p;
1719 continue;
1720 }
1721 vp = NULL;
1722 error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1723 if (error)
1724 return error;
1725
1726 ip = VTOI(vp);
1727 switch (ip->meta.type) {
1728 case HAMMER2_OBJTYPE_DIRECTORY:
1729 break;
1730 case HAMMER2_OBJTYPE_SOFTLINK:
1731 bzero(tmp, sizeof(tmp));
1732 error = hammer2_readlink(vp, tmp, sizeof(tmp));
1733 if (error)
1734 return error;
1735 if (!is_supported_link(tmp))
1736 return EINVAL;
1737 strlcat(tmp, "/", sizeof(tmp));
1738 strlcat(tmp, p, sizeof(tmp));
1739 error = trim_slash(tmp);
1740 if (error)
1741 return error;
1742 p = name = tmp;
1743 continue;
1744 default:
1745 return EINVAL;
1746 }
1747
1748 dvp = vp;
1749 name = p;
1750 }
1751
1752 error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1753 if (error)
1754 return error;
1755 start_ioctl:
1756 ip = VTOI(vp);
1757
1758 bzero(&inode, sizeof(inode));
1759 error = hammer2_ioctl_inode_get(ip, &inode);
1760 if (error)
1761 return error;
1762
1763 inode.flags |= HAMMER2IOC_INODE_FLAG_CHECK;
1764 inode.ip_data.meta.check_algo = check_algo;
1765 error = hammer2_ioctl_inode_set(ip, &inode);
1766 if (error)
1767 return error;
1768
1769 free(o);
1770
1771 return error;
1772 }
1773
1774 static int
hammer2_inode_setcomp(struct m_vnode * dvp,const char * f)1775 hammer2_inode_setcomp(struct m_vnode *dvp, const char *f)
1776 {
1777 hammer2_ioc_inode_t inode;
1778 hammer2_inode_t *ip;
1779 struct m_vnode *vp;
1780 char *o, *p, *name, *comp_algo_str, *comp_level_str;
1781 char tmp[PATH_MAX];
1782 const char *comps[] = { "none", "autozero", "lz4", "zlib", };
1783 int comp_algo_idx, comp_level_idx, error;
1784 uint8_t comp_algo, comp_level;
1785
1786 assert(strlen(f) > 0);
1787 o = p = strdup(f);
1788
1789 p = strrchr(p, ':');
1790 if (p == NULL)
1791 return EINVAL;
1792
1793 *p++ = 0; /* NULL terminate comp_algo_str */
1794 comp_level_str = p;
1795 p = o;
1796
1797 p = strrchr(p, ':');
1798 if (p == NULL) {
1799 /* comp_level_str not specified */
1800 comp_algo_str = comp_level_str;
1801 comp_level_str = NULL;
1802 } else {
1803 *p++ = 0; /* NULL terminate path */
1804 comp_algo_str = p;
1805 }
1806 name = p = o;
1807
1808 /* fail if already empty before trim */
1809 if (strlen(p) == 0)
1810 return EINVAL;
1811
1812 error = trim_slash(p);
1813 if (error)
1814 return error;
1815 if (strlen(comp_algo_str) == 0)
1816 return EINVAL;
1817
1818 /* convert comp_algo_str to comp_algo_idx */
1819 comp_algo_idx = nitems(comps);
1820 while (--comp_algo_idx >= 0)
1821 if (strcasecmp(comp_algo_str, comps[comp_algo_idx]) == 0)
1822 break;
1823 if (comp_algo_idx < 0) {
1824 if (strcasecmp(comp_algo_str, "default") == 0) {
1825 comp_algo_str = "lz4";
1826 comp_algo_idx = HAMMER2_COMP_LZ4;
1827 } else if (strcasecmp(comp_algo_str, "disabled") == 0) {
1828 comp_algo_str = "autozero";
1829 comp_algo_idx = HAMMER2_COMP_AUTOZERO;
1830 } else {
1831 printf("invalid comp_algo_str: %s\n", comp_algo_str);
1832 return EINVAL;
1833 }
1834 }
1835 comp_algo = HAMMER2_ENC_ALGO(comp_algo_idx);
1836
1837 /* convert comp_level_str to comp_level_idx */
1838 if (comp_level_str == NULL) {
1839 comp_level_idx = 0;
1840 } else if (isdigit((int)comp_level_str[0])) {
1841 comp_level_idx = strtol(comp_level_str, NULL, 0);
1842 } else if (strcasecmp(comp_level_str, "default") == 0) {
1843 comp_level_idx = 0;
1844 } else {
1845 printf("invalid comp_level_str: %s\n", comp_level_str);
1846 return EINVAL;
1847 }
1848 if (comp_level_idx) {
1849 switch (comp_algo) {
1850 case HAMMER2_COMP_ZLIB:
1851 if (comp_level_idx < 6 || comp_level_idx > 9) {
1852 printf("unsupported comp_level %d for %s\n",
1853 comp_level_idx, comp_algo_str);
1854 return EINVAL;
1855 }
1856 break;
1857 default:
1858 printf("unsupported comp_level %d for %s\n",
1859 comp_level_idx, comp_algo_str);
1860 return EINVAL;
1861 }
1862 }
1863 comp_level = HAMMER2_ENC_LEVEL(comp_level_idx);
1864 printf("change %s to algo %d (%s) level %d\n",
1865 p, comp_algo, comp_algo_str, comp_level_idx);
1866
1867 if (strlen(p) == 0) {
1868 vp = dvp;
1869 goto start_ioctl;
1870 }
1871
1872 while ((p = strchr(p, '/')) != NULL) {
1873 *p++ = 0; /* NULL terminate name */
1874 if (!strcmp(name, ".")) {
1875 name = p;
1876 continue;
1877 }
1878 vp = NULL;
1879 error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1880 if (error)
1881 return error;
1882
1883 ip = VTOI(vp);
1884 switch (ip->meta.type) {
1885 case HAMMER2_OBJTYPE_DIRECTORY:
1886 break;
1887 case HAMMER2_OBJTYPE_SOFTLINK:
1888 bzero(tmp, sizeof(tmp));
1889 error = hammer2_readlink(vp, tmp, sizeof(tmp));
1890 if (error)
1891 return error;
1892 if (!is_supported_link(tmp))
1893 return EINVAL;
1894 strlcat(tmp, "/", sizeof(tmp));
1895 strlcat(tmp, p, sizeof(tmp));
1896 error = trim_slash(tmp);
1897 if (error)
1898 return error;
1899 p = name = tmp;
1900 continue;
1901 default:
1902 return EINVAL;
1903 }
1904
1905 dvp = vp;
1906 name = p;
1907 }
1908
1909 error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1910 if (error)
1911 return error;
1912 start_ioctl:
1913 ip = VTOI(vp);
1914
1915 bzero(&inode, sizeof(inode));
1916 error = hammer2_ioctl_inode_get(ip, &inode);
1917 if (error)
1918 return error;
1919
1920 inode.flags |= HAMMER2IOC_INODE_FLAG_COMP;
1921 inode.ip_data.meta.comp_algo = comp_algo | comp_level;
1922 error = hammer2_ioctl_inode_set(ip, &inode);
1923 if (error)
1924 return error;
1925
1926 free(o);
1927
1928 return error;
1929 }
1930
1931 static int
hammer2_bulkfree(struct m_vnode * vp)1932 hammer2_bulkfree(struct m_vnode *vp)
1933 {
1934 hammer2_ioc_bulkfree_t bfi;
1935 size_t usermem;
1936 size_t usermem_size = sizeof(usermem);
1937
1938 bzero(&bfi, sizeof(bfi));
1939 usermem = 0;
1940 if (sysctlbyname("hw.usermem", &usermem, &usermem_size, NULL, 0) == 0)
1941 bfi.size = usermem / 16;
1942 else
1943 bfi.size = 0;
1944 if (bfi.size < 8192 * 1024)
1945 bfi.size = 8192 * 1024;
1946
1947 return hammer2_ioctl_bulkfree_scan(VTOI(vp), &bfi);
1948 }
1949
1950 static int
hammer2_destroy_path(struct m_vnode * dvp,const char * f)1951 hammer2_destroy_path(struct m_vnode *dvp, const char *f)
1952 {
1953 hammer2_ioc_destroy_t destroy;
1954 hammer2_inode_t *ip;
1955 struct m_vnode *vp;
1956 char *o, *p, *name;
1957 char tmp[PATH_MAX];
1958 int error;
1959
1960 assert(strlen(f) > 0);
1961 o = p = name = strdup(f);
1962
1963 error = trim_slash(p);
1964 if (error)
1965 return error;
1966 if (strlen(p) == 0)
1967 return EINVAL;
1968
1969 while ((p = strchr(p, '/')) != NULL) {
1970 *p++ = 0; /* NULL terminate name */
1971 if (!strcmp(name, ".")) {
1972 name = p;
1973 continue;
1974 }
1975 vp = NULL;
1976 error = hammer2_nresolve(dvp, &vp, name, strlen(name));
1977 if (error)
1978 return error;
1979
1980 ip = VTOI(vp);
1981 switch (ip->meta.type) {
1982 case HAMMER2_OBJTYPE_DIRECTORY:
1983 break;
1984 case HAMMER2_OBJTYPE_SOFTLINK:
1985 bzero(tmp, sizeof(tmp));
1986 error = hammer2_readlink(vp, tmp, sizeof(tmp));
1987 if (error)
1988 return error;
1989 if (!is_supported_link(tmp))
1990 return EINVAL;
1991 strlcat(tmp, "/", sizeof(tmp));
1992 strlcat(tmp, p, sizeof(tmp));
1993 error = trim_slash(tmp);
1994 if (error)
1995 return error;
1996 p = name = tmp;
1997 continue;
1998 default:
1999 return EINVAL;
2000 }
2001
2002 dvp = vp;
2003 name = p;
2004 }
2005
2006 /* XXX When does (or why does not) ioctl modify this inode ? */
2007 hammer2_inode_modify(VTOI(dvp));
2008
2009 bzero(&destroy, sizeof(destroy));
2010 destroy.cmd = HAMMER2_DELETE_FILE;
2011 snprintf(destroy.path, sizeof(destroy.path), "%s", name);
2012
2013 printf("%s\t", f);
2014 fflush(stdout);
2015
2016 error = hammer2_ioctl_destroy(VTOI(dvp), &destroy);
2017 if (error)
2018 printf("%s\n", strerror(error));
2019 else
2020 printf("ok\n");
2021 free(o);
2022
2023 return error;
2024 }
2025
2026 static int
hammer2_destroy_inum(struct m_vnode * vp,hammer2_tid_t inum)2027 hammer2_destroy_inum(struct m_vnode *vp, hammer2_tid_t inum)
2028 {
2029 hammer2_ioc_destroy_t destroy;
2030 int error;
2031
2032 bzero(&destroy, sizeof(destroy));
2033 destroy.cmd = HAMMER2_DELETE_INUM;
2034 destroy.inum = inum;
2035
2036 printf("%jd\t", (intmax_t)destroy.inum);
2037 fflush(stdout);
2038
2039 error = hammer2_ioctl_destroy(VTOI(vp), &destroy);
2040 if (error)
2041 printf("%s\n", strerror(error));
2042 else
2043 printf("ok\n");
2044
2045 return error;
2046 }
2047
2048 static int
hammer2_growfs(struct m_vnode * vp,hammer2_off_t size)2049 hammer2_growfs(struct m_vnode *vp, hammer2_off_t size)
2050 {
2051 hammer2_ioc_growfs_t growfs;
2052 int error;
2053
2054 bzero(&growfs, sizeof(growfs));
2055 growfs.size = size;
2056
2057 error = hammer2_ioctl_growfs(VTOI(vp), &growfs, NULL);
2058 if (!error) {
2059 if (growfs.modified)
2060 printf("grown to %016jx\n", (intmax_t)growfs.size);
2061 else
2062 printf("no size change - %016jx\n",
2063 (intmax_t)growfs.size);
2064 }
2065
2066 return error;
2067 }
2068
2069 struct hammer2_link {
2070 TAILQ_ENTRY(hammer2_link) entry;
2071 hammer2_tid_t inum;
2072 uint64_t nlinks;
2073 char path[PATH_MAX];
2074 };
2075
2076 TAILQ_HEAD(hammer2_linkq, hammer2_link);
2077
2078 static void
hammer2_linkq_init(struct hammer2_linkq * linkq)2079 hammer2_linkq_init(struct hammer2_linkq *linkq)
2080 {
2081 TAILQ_INIT(linkq);
2082 }
2083
2084 static void
hammer2_linkq_cleanup(struct hammer2_linkq * linkq,bool is_root)2085 hammer2_linkq_cleanup(struct hammer2_linkq *linkq, bool is_root)
2086 {
2087 struct hammer2_link *e;
2088 int count = 0;
2089
2090 /*
2091 * If is_root is true, linkq must be empty, or link count is broken.
2092 * Note that if an image was made by makefs, hardlinks in the source
2093 * directory became hardlinks in the image only if >1 links existed under
2094 * that directory, as makefs doesn't determine hardlink via link count.
2095 */
2096 while ((e = TAILQ_FIRST(linkq)) != NULL) {
2097 count++;
2098 TAILQ_REMOVE(linkq, e, entry);
2099 free(e);
2100 }
2101 assert(TAILQ_EMPTY(linkq));
2102
2103 if (count && is_root)
2104 errx(1, "%d link entries remained", count);
2105 }
2106
2107 static void
hammer2_linkq_add(struct hammer2_linkq * linkq,hammer2_tid_t inum,uint64_t nlinks,const char * path)2108 hammer2_linkq_add(struct hammer2_linkq *linkq, hammer2_tid_t inum,
2109 uint64_t nlinks, const char *path)
2110 {
2111 struct hammer2_link *e;
2112 int count = 0;
2113
2114 e = ecalloc(1, sizeof(*e));
2115 e->inum = inum;
2116 e->nlinks = nlinks;
2117 strlcpy(e->path, path, sizeof(e->path));
2118 TAILQ_INSERT_TAIL(linkq, e, entry);
2119
2120 TAILQ_FOREACH(e, linkq, entry)
2121 if (e->inum == inum)
2122 count++;
2123 if (count > 1)
2124 errx(1, "%d link entries exist for inum %jd",
2125 count, (intmax_t)inum);
2126 }
2127
2128 static void
hammer2_linkq_del(struct hammer2_linkq * linkq,hammer2_tid_t inum)2129 hammer2_linkq_del(struct hammer2_linkq *linkq, hammer2_tid_t inum)
2130 {
2131 struct hammer2_link *e, *next;
2132
2133 TAILQ_FOREACH_MUTABLE(e, linkq, entry, next)
2134 if (e->inum == inum) {
2135 e->nlinks--;
2136 if (e->nlinks == 1) {
2137 TAILQ_REMOVE(linkq, e, entry);
2138 free(e);
2139 }
2140 }
2141 }
2142
2143 static void
hammer2_utimes(struct m_vnode * vp,const char * f)2144 hammer2_utimes(struct m_vnode *vp, const char *f)
2145 {
2146 hammer2_inode_t *ip = VTOI(vp);
2147 struct timeval tv[2];
2148
2149 hammer2_time_to_timeval(ip->meta.atime, &tv[0]);
2150 hammer2_time_to_timeval(ip->meta.mtime, &tv[1]);
2151
2152 utimes(f, tv); /* ignore failure */
2153 }
2154
2155 static int
hammer2_readx_directory(struct m_vnode * dvp,const char * dir,const char * name,struct hammer2_linkq * linkq)2156 hammer2_readx_directory(struct m_vnode *dvp, const char *dir, const char *name,
2157 struct hammer2_linkq *linkq)
2158 {
2159 struct m_vnode *vp;
2160 struct dirent *dp;
2161 struct stat st;
2162 char *buf, tmp[PATH_MAX];
2163 off_t offset = 0;
2164 int ndirent = 0;
2165 int eofflag = 0;
2166 int i, error;
2167
2168 snprintf(tmp, sizeof(tmp), "%s/%s", dir, name);
2169 if (stat(tmp, &st) == -1 && mkdir(tmp, 0666) == -1)
2170 err(1, "failed to mkdir %s", tmp);
2171
2172 buf = ecalloc(1, HAMMER2_PBUFSIZE);
2173
2174 while (!eofflag) {
2175 error = hammer2_readdir(dvp, buf, HAMMER2_PBUFSIZE, &offset,
2176 &ndirent, &eofflag);
2177 if (error)
2178 errx(1, "failed to readdir");
2179 dp = (void *)buf;
2180
2181 for (i = 0; i < ndirent; i++) {
2182 if (strcmp(dp->d_name, ".") &&
2183 strcmp(dp->d_name, "..")) {
2184 error = hammer2_nresolve(dvp, &vp, dp->d_name,
2185 strlen(dp->d_name));
2186 if (error)
2187 return error;
2188 error = hammer2_readx_handle(vp, tmp,
2189 dp->d_name, linkq);
2190 if (error)
2191 return error;
2192 }
2193 dp = (void *)((char *)dp +
2194 _DIRENT_RECLEN(dp->d_namlen));
2195 }
2196 }
2197
2198 free(buf);
2199 hammer2_utimes(dvp, tmp);
2200
2201 return 0;
2202 }
2203
2204 static int
hammer2_readx_link(struct m_vnode * vp,const char * src,const char * lnk,struct hammer2_linkq * linkq)2205 hammer2_readx_link(struct m_vnode *vp, const char *src, const char *lnk,
2206 struct hammer2_linkq *linkq)
2207 {
2208 hammer2_inode_t *ip = VTOI(vp);
2209 struct stat st;
2210 int error;
2211
2212 if (!stat(lnk, &st)) {
2213 error = unlink(lnk);
2214 if (error)
2215 return error;
2216 }
2217
2218 error = link(src, lnk);
2219 if (error)
2220 return error;
2221
2222 hammer2_linkq_del(linkq, ip->meta.inum);
2223
2224 return 0;
2225 }
2226
2227 static int
hammer2_readx_regfile(struct m_vnode * vp,const char * dir,const char * name,struct hammer2_linkq * linkq)2228 hammer2_readx_regfile(struct m_vnode *vp, const char *dir, const char *name,
2229 struct hammer2_linkq *linkq)
2230 {
2231 hammer2_inode_t *ip = VTOI(vp);
2232 struct hammer2_link *e;
2233 char *buf, out[PATH_MAX];
2234 size_t resid, n;
2235 off_t offset;
2236 int fd, error;
2237 bool found = false;
2238
2239 snprintf(out, sizeof(out), "%s/%s", dir, name);
2240
2241 if (ip->meta.nlinks > 1) {
2242 TAILQ_FOREACH(e, linkq, entry)
2243 if (e->inum == ip->meta.inum) {
2244 found = true;
2245 error = hammer2_readx_link(vp, e->path, out,
2246 linkq);
2247 if (error == 0)
2248 return 0;
2249 /* ignore failure */
2250 }
2251 if (!found)
2252 hammer2_linkq_add(linkq, ip->meta.inum, ip->meta.nlinks,
2253 out);
2254 }
2255
2256 fd = open(out, O_WRONLY | O_CREAT | O_TRUNC, 0666);
2257 if (fd == -1)
2258 err(1, "failed to create %s", out);
2259
2260 buf = ecalloc(1, HAMMER2_PBUFSIZE);
2261 resid = ip->meta.size;
2262 offset = 0;
2263
2264 while (resid > 0) {
2265 bzero(buf, HAMMER2_PBUFSIZE);
2266 error = hammer2_read(vp, buf, HAMMER2_PBUFSIZE, offset);
2267 if (error)
2268 errx(1, "failed to read from %s", name);
2269
2270 n = resid >= HAMMER2_PBUFSIZE ? HAMMER2_PBUFSIZE : resid;
2271 error = write(fd, buf, n);
2272 if (error == -1)
2273 err(1, "failed to write to %s", out);
2274 else if (error != n)
2275 return EINVAL;
2276
2277 resid -= n;
2278 offset += HAMMER2_PBUFSIZE;
2279 }
2280 fsync(fd);
2281 close(fd);
2282
2283 free(buf);
2284 hammer2_utimes(vp, out);
2285
2286 return 0;
2287 }
2288
2289 static int
hammer2_readx_handle(struct m_vnode * vp,const char * dir,const char * name,struct hammer2_linkq * linkq)2290 hammer2_readx_handle(struct m_vnode *vp, const char *dir, const char *name,
2291 struct hammer2_linkq *linkq)
2292 {
2293 hammer2_inode_t *ip = VTOI(vp);
2294
2295 switch (ip->meta.type) {
2296 case HAMMER2_OBJTYPE_DIRECTORY:
2297 return hammer2_readx_directory(vp, dir, name, linkq);
2298 case HAMMER2_OBJTYPE_REGFILE:
2299 return hammer2_readx_regfile(vp, dir, name, linkq);
2300 default:
2301 /* XXX */
2302 printf("ignore inode %jd %s \"%s\"\n",
2303 (intmax_t)ip->meta.inum,
2304 hammer2_iptype_to_str(ip->meta.type),
2305 name);
2306 return 0;
2307 }
2308 return EINVAL;
2309 }
2310
2311 static int
hammer2_readx(struct m_vnode * dvp,const char * dir,const char * f)2312 hammer2_readx(struct m_vnode *dvp, const char *dir, const char *f)
2313 {
2314 hammer2_inode_t *ip;
2315 struct hammer2_linkq linkq;
2316 struct m_vnode *vp, *ovp = dvp;
2317 char *o, *p, *name;
2318 char tmp[PATH_MAX];
2319 int error;
2320
2321 if (dir == NULL)
2322 return EINVAL;
2323
2324 assert(strlen(f) > 0);
2325 o = p = name = strdup(f);
2326
2327 error = trim_slash(p);
2328 if (error)
2329 return error;
2330 if (strlen(p) == 0) {
2331 vp = dvp;
2332 goto start_read;
2333 }
2334
2335 while ((p = strchr(p, '/')) != NULL) {
2336 *p++ = 0; /* NULL terminate name */
2337 if (!strcmp(name, ".")) {
2338 name = p;
2339 continue;
2340 }
2341 vp = NULL;
2342 error = hammer2_nresolve(dvp, &vp, name, strlen(name));
2343 if (error)
2344 return error;
2345
2346 ip = VTOI(vp);
2347 switch (ip->meta.type) {
2348 case HAMMER2_OBJTYPE_DIRECTORY:
2349 break;
2350 case HAMMER2_OBJTYPE_SOFTLINK:
2351 bzero(tmp, sizeof(tmp));
2352 error = hammer2_readlink(vp, tmp, sizeof(tmp));
2353 if (error)
2354 return error;
2355 if (!is_supported_link(tmp))
2356 return EINVAL;
2357 strlcat(tmp, "/", sizeof(tmp));
2358 strlcat(tmp, p, sizeof(tmp));
2359 error = trim_slash(tmp);
2360 if (error)
2361 return error;
2362 p = name = tmp;
2363 continue;
2364 default:
2365 return EINVAL;
2366 }
2367
2368 dvp = vp;
2369 name = p;
2370 }
2371
2372 error = hammer2_nresolve(dvp, &vp, name, strlen(name));
2373 if (error)
2374 return error;
2375 start_read:
2376 hammer2_linkq_init(&linkq);
2377 error = hammer2_readx_handle(vp, dir, name, &linkq);
2378 hammer2_linkq_cleanup(&linkq, vp == ovp);
2379 if (error)
2380 return error;
2381
2382 free(o);
2383
2384 return 0;
2385 }
2386
2387 static void
assert_trim_slash(const char * input,const char * expected)2388 assert_trim_slash(const char *input, const char *expected)
2389 {
2390 char tmp[PATH_MAX];
2391 int error;
2392
2393 strlcpy(tmp, input, sizeof(tmp));
2394 error = trim_slash(tmp);
2395 if (error)
2396 errx(1, "input \"%s\" error %d", input, error);
2397
2398 if (strncmp(tmp, expected, sizeof(tmp)))
2399 errx(1, "input \"%s\" result \"%s\" vs expected \"%s\"",
2400 input, tmp, expected);
2401 }
2402
2403 static void
unittest_trim_slash(void)2404 unittest_trim_slash(void)
2405 {
2406 assert_trim_slash("", "");
2407 assert_trim_slash("/", "");
2408 assert_trim_slash("//", "");
2409 assert_trim_slash("///", "");
2410
2411 assert_trim_slash("makefs", "makefs");
2412 assert_trim_slash("/makefs", "makefs");
2413 assert_trim_slash("//makefs", "makefs");
2414 assert_trim_slash("makefs/", "makefs");
2415 assert_trim_slash("makefs//", "makefs");
2416 assert_trim_slash("/makefs/", "makefs");
2417 assert_trim_slash("//makefs//", "makefs");
2418
2419 assert_trim_slash("sys/vfs/hammer2", "sys/vfs/hammer2");
2420 assert_trim_slash("/sys/vfs/hammer2", "sys/vfs/hammer2");
2421 assert_trim_slash("//sys/vfs/hammer2", "sys/vfs/hammer2");
2422 assert_trim_slash("///sys/vfs/hammer2", "sys/vfs/hammer2");
2423 assert_trim_slash("sys/vfs/hammer2/", "sys/vfs/hammer2");
2424 assert_trim_slash("sys/vfs/hammer2//", "sys/vfs/hammer2");
2425 assert_trim_slash("sys/vfs/hammer2///", "sys/vfs/hammer2");
2426 assert_trim_slash("/sys/vfs/hammer2/", "sys/vfs/hammer2");
2427 assert_trim_slash("//sys//vfs//hammer2//", "sys/vfs/hammer2");
2428 assert_trim_slash("///sys///vfs///hammer2///", "sys/vfs/hammer2");
2429
2430 APRINTF("success\n");
2431 }
2432