sys/vfs/hammer2: Use HAMMER2_VOLUME_BYTES for volume header size
[dragonfly.git] / sbin / fsck_hammer2 / destroy.c
blob88ad9d0ac8018931ea89e1085063a1a929a9e1c4
1 /*
2 * Copyright (c) 2019 Tomohiro Kusumi <tkusumi@netbsd.org>
3 * Copyright (c) 2019 The DragonFly Project
4 * All rights reserved.
6 * This code is derived from software contributed to The DragonFly Project
7 * by Matthew Dillon <dillon@dragonflybsd.org>
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in
17 * the documentation and/or other materials provided with the
18 * distribution.
19 * 3. Neither the name of The DragonFly Project nor the names of its
20 * contributors may be used to endorse or promote products derived
21 * from this software without specific, prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
29 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
31 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
37 // # gcc -Wall -g -I../../sys -I../hammer2 ../../sys/libkern/icrc32.c ../hammer2/subs.c ../hammer2/ondisk.c ./destroy.c -o destroy
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <unistd.h>
42 #include <fcntl.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <stdbool.h>
46 #include <string.h>
47 #include <limits.h>
48 #include <errno.h>
49 #include <assert.h>
51 #include <vfs/hammer2/hammer2_disk.h>
53 #include "hammer2_subs.h"
55 static int modify_blockref(const hammer2_volume_data_t *, int,
56 hammer2_blockref_t *, hammer2_blockref_t *);
57 static int modify_inode(const hammer2_blockref_t *,
58 hammer2_media_data_t *, size_t);
59 static int modify_dirent_embedded(int, hammer2_blockref_t *);
60 static int modify_dirent(int, hammer2_blockref_t *, const hammer2_blockref_t *,
61 hammer2_media_data_t *, size_t);
63 static hammer2_tid_t src_inode = 0;
64 static hammer2_tid_t dst_inode = 0;
65 static const char *src_dirent = NULL;
66 static const char *dst_dirent = NULL;
67 static bool ForceOpt = false;
69 static int
70 destroy_blockref(uint8_t type)
72 bool failed = false;
73 int i;
75 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
76 hammer2_volume_data_t voldata;
77 hammer2_blockref_t broot;
78 hammer2_off_t off;
79 ssize_t ret;
81 memset(&broot, 0, sizeof(broot));
82 broot.type = type;
83 broot.data_off = (i * HAMMER2_ZONE_BYTES64) | HAMMER2_PBUFRADIX;
84 off = broot.data_off & ~HAMMER2_OFF_MASK_RADIX;
85 if (lseek(hammer2_get_root_volume_fd(),
86 off - hammer2_get_root_volume_offset(), SEEK_SET) == -1) {
87 perror("lseek");
88 return -1;
91 ret = read(hammer2_get_root_volume_fd(), &voldata,
92 HAMMER2_VOLUME_BYTES);
93 if (ret == HAMMER2_VOLUME_BYTES) {
94 fprintf(stdout, "zone.%d %016jx\n",
95 i, (uintmax_t)broot.data_off);
96 if (modify_blockref(&voldata, -1, &broot, NULL) == -1)
97 failed = true;
98 } else if (ret == -1) {
99 perror("read");
100 return -1;
101 } else {
102 fprintf(stderr, "Failed to read volume header\n");
103 return -1;
107 return failed ? -1 : 0;
110 static int
111 read_media(const hammer2_blockref_t *bref, hammer2_media_data_t *media,
112 size_t *media_bytes)
114 hammer2_off_t io_off, io_base;
115 size_t bytes, io_bytes, boff;
116 ssize_t ret;
117 int fd;
119 bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX);
120 if (bytes)
121 bytes = (size_t)1 << bytes;
122 if (media_bytes)
123 *media_bytes = bytes;
125 if (!bytes)
126 return 0;
128 io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX;
129 io_base = io_off & ~(hammer2_off_t)(HAMMER2_LBUFSIZE - 1);
130 boff = io_off - io_base;
132 io_bytes = HAMMER2_LBUFSIZE;
133 while (io_bytes + boff < bytes)
134 io_bytes <<= 1;
136 if (io_bytes > sizeof(*media)) {
137 fprintf(stderr, "Bad I/O bytes\n");
138 return -1;
140 fd = hammer2_get_volume_fd(io_off);
141 if (lseek(fd, io_base - hammer2_get_volume_offset(io_base), SEEK_SET)
142 == -1) {
143 perror("lseek");
144 return -1;
146 ret = read(fd, media, io_bytes);
147 if (ret == -1) {
148 perror("read");
149 return -1;
150 } else if (ret != (ssize_t)io_bytes) {
151 fprintf(stderr, "Failed to read media\n");
152 return -1;
154 if (boff)
155 memmove(media, (char *)media + boff, bytes);
157 return 0;
160 static int
161 write_media(const hammer2_blockref_t *bref, const hammer2_media_data_t *media,
162 size_t media_bytes)
164 hammer2_off_t io_off, io_base;
165 char buf[HAMMER2_PBUFSIZE];
166 size_t bytes, io_bytes, boff;
167 ssize_t ret;
168 int fd;
170 bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX);
171 if (bytes)
172 bytes = (size_t)1 << bytes;
173 assert(bytes != 0);
174 assert(bytes == media_bytes);
176 io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX;
177 io_base = io_off & ~(hammer2_off_t)(HAMMER2_LBUFSIZE - 1);
178 boff = io_off - io_base;
180 io_bytes = HAMMER2_LBUFSIZE;
181 while (io_bytes + boff < bytes)
182 io_bytes <<= 1;
184 if (io_bytes > sizeof(buf)) {
185 fprintf(stderr, "Bad I/O bytes\n");
186 return -1;
188 fd = hammer2_get_volume_fd(io_off);
189 if (lseek(fd, io_base - hammer2_get_volume_offset(io_base), SEEK_SET)
190 == -1) {
191 perror("lseek");
192 return -1;
194 if (read(fd, buf, io_bytes) != (ssize_t)io_bytes) {
195 perror("read");
196 return -1;
199 memcpy(buf + boff, media, media_bytes);
200 if (lseek(fd, io_base - hammer2_get_volume_offset(io_base), SEEK_SET)
201 == -1) {
202 perror("lseek");
203 return -1;
205 ret = write(fd, buf, io_bytes);
206 if (ret == -1) {
207 perror("write");
208 return -1;
209 } else if (ret != (ssize_t)io_bytes) {
210 fprintf(stderr, "Failed to write media\n");
211 return -1;
213 if (fsync(fd) == -1) {
214 perror("fsync");
215 return -1;
218 return 0;
221 static int
222 modify_blockref(const hammer2_volume_data_t *voldata, int bi,
223 hammer2_blockref_t *bref, hammer2_blockref_t *prev_bref)
225 hammer2_media_data_t media;
226 hammer2_blockref_t *bscan;
227 int i, bcount, namlen;
228 size_t bytes;
230 if (read_media(bref, &media, &bytes) == -1)
231 return -1;
233 switch (bref->type) {
234 case HAMMER2_BREF_TYPE_INODE:
235 if (!(media.ipdata.meta.op_flags & HAMMER2_OPFLAG_DIRECTDATA)) {
236 bscan = &media.ipdata.u.blockset.blockref[0];
237 bcount = HAMMER2_SET_COUNT;
238 } else {
239 bscan = NULL;
240 bcount = 0;
242 if (src_inode && media.ipdata.meta.inum == src_inode)
243 if (modify_inode(bref, &media, bytes) == -1)
244 return -1;
245 break;
246 case HAMMER2_BREF_TYPE_INDIRECT:
247 bscan = &media.npdata[0];
248 bcount = bytes / sizeof(hammer2_blockref_t);
249 break;
250 case HAMMER2_BREF_TYPE_DIRENT:
251 bscan = NULL;
252 bcount = 0;
253 namlen = bref->embed.dirent.namlen;
254 if (src_dirent && namlen == strlen(src_dirent)) {
255 if (namlen <= sizeof(bref->check.buf) &&
256 !memcmp(bref->check.buf, src_dirent, namlen)) {
257 if (modify_dirent_embedded(bi, prev_bref) == -1)
258 return -1;
259 } else if (!memcmp(media.buf, src_dirent, namlen)) {
260 if (modify_dirent(bi, prev_bref, bref, &media,
261 bytes) == -1)
262 return -1;
265 break;
266 case HAMMER2_BREF_TYPE_VOLUME:
267 bscan = &media.voldata.sroot_blockset.blockref[0];
268 bcount = HAMMER2_SET_COUNT;
269 break;
270 default:
271 bscan = NULL;
272 bcount = 0;
273 break;
276 for (i = 0; i < bcount; ++i)
277 if (bscan[i].type != HAMMER2_BREF_TYPE_EMPTY)
278 if (modify_blockref(voldata, i, &bscan[i], bref) == -1)
279 return -1;
280 return 0;
283 static int
284 modify_inode(const hammer2_blockref_t *bref,
285 hammer2_media_data_t *media, size_t media_bytes)
287 assert(src_inode == media->ipdata.meta.inum);
289 if (ForceOpt) {
290 media->ipdata.meta.inum = dst_inode;
291 if (write_media(bref, media, media_bytes) == -1)
292 return -1;
295 printf("%sinode# 0x%016jx -> 0x%016jx\n", ForceOpt ? "Modified " : "",
296 src_inode, dst_inode);
298 return 0;
301 static int
302 modify_dirent_embedded(int bi, hammer2_blockref_t *prev_bref)
304 hammer2_media_data_t bscan_media;
305 hammer2_blockref_t *bscan;
306 size_t bytes;
308 if (read_media(prev_bref, &bscan_media, &bytes) == -1)
309 return -1;
310 assert(bytes);
312 switch (prev_bref->type) {
313 case HAMMER2_BREF_TYPE_INODE:
314 bscan = &bscan_media.ipdata.u.blockset.blockref[bi];
315 break;
316 case HAMMER2_BREF_TYPE_INDIRECT:
317 bscan = &bscan_media.npdata[bi];
318 break;
319 default:
320 assert(0);
321 break;
323 assert(!memcmp(src_dirent, bscan->check.buf, strlen(src_dirent)));
325 if (strlen(dst_dirent) > sizeof(bscan->check.buf)) {
326 fprintf(stderr, "embedded dirent %s (%d bytes) can't exceed "
327 "%lu bytes\n", dst_dirent, (int)strlen(dst_dirent),
328 sizeof(bscan->check.buf));
329 return -1;
332 if (ForceOpt) {
333 memset(bscan->check.buf, 0, sizeof(bscan->check.buf));
334 memcpy(bscan->check.buf, dst_dirent, strlen(dst_dirent));
335 bscan->embed.dirent.namlen = strlen(dst_dirent);
336 bscan->key = dirhash(dst_dirent, strlen(dst_dirent));
337 if (write_media(prev_bref, &bscan_media, bytes) == -1)
338 return -1;
341 printf("%sembedded dirent %s (%d bytes) -> %s (%d bytes)\n",
342 ForceOpt ? "Modified " : "",
343 src_dirent, (int)strlen(src_dirent),
344 dst_dirent, (int)strlen(dst_dirent));
346 return 0;
349 static int
350 modify_dirent(int bi, hammer2_blockref_t *prev_bref,
351 const hammer2_blockref_t *bref, hammer2_media_data_t *media,
352 size_t media_bytes)
354 hammer2_media_data_t bscan_media;
355 hammer2_blockref_t *bscan;
356 size_t bytes;
358 assert(!memcmp(src_dirent, media->buf, strlen(src_dirent)));
359 if (read_media(prev_bref, &bscan_media, &bytes) == -1)
360 return -1;
361 assert(bytes);
363 switch (prev_bref->type) {
364 case HAMMER2_BREF_TYPE_INODE:
365 bscan = &bscan_media.ipdata.u.blockset.blockref[bi];
366 break;
367 case HAMMER2_BREF_TYPE_INDIRECT:
368 bscan = &bscan_media.npdata[bi];
369 break;
370 default:
371 assert(0);
372 break;
375 if (memcmp(bref, bscan, sizeof(*bref))) {
376 fprintf(stderr, "Blockref contents mismatch\n");
377 return -1;
379 if (strlen(dst_dirent) > sizeof(media->buf)) {
380 fprintf(stderr, "dirent %s (%d bytes) can't exceed %lu bytes\n",
381 dst_dirent, (int)strlen(dst_dirent), sizeof(media->buf));
382 return -1;
384 if (strlen(dst_dirent) <= sizeof(bscan->check.buf)) {
385 fprintf(stderr, "dirent %s (%d bytes) must exceed %lu bytes\n",
386 dst_dirent, (int)strlen(dst_dirent),
387 sizeof(bscan->check.buf));
388 return -1;
391 if (ForceOpt) {
392 memset(media->buf, 0, sizeof(media->buf));
393 memcpy(media->buf, dst_dirent, strlen(dst_dirent));
394 bscan->embed.dirent.namlen = strlen(dst_dirent);
395 bscan->key = dirhash(dst_dirent, strlen(dst_dirent));
396 if (write_media(bref, media, media_bytes) == -1)
397 return -1;
398 if (write_media(prev_bref, &bscan_media, bytes) == -1) {
399 memset(media->buf, 0, sizeof(media->buf));
400 memcpy(media->buf, src_dirent, strlen(src_dirent));
401 if (write_media(bref, media, media_bytes) == -1)
402 return -1;
403 return -1;
407 printf("%sdirent %s (%d bytes) -> %s (%d bytes)\n",
408 ForceOpt ? "Modified " : "",
409 src_dirent, (int)strlen(src_dirent),
410 dst_dirent, (int)strlen(dst_dirent));
412 return 0;
415 static int
416 init_args(int argc, char **argv, const char **devpathp)
418 const char *devpath, *type;
420 *devpathp = devpath = argv[0];
421 type = argv[1];
423 if (!strcmp(type, "inode")) {
424 errno = 0;
425 src_inode = strtoull(argv[2], NULL, 16);
426 if (errno == ERANGE && src_inode == ULLONG_MAX) {
427 perror("strtoull");
428 return -1;
430 if (src_inode == 0) {
431 fprintf(stderr, "Invalid src inode# %ju\n",
432 (uintmax_t)src_inode);
433 return -1;
435 errno = 0;
436 dst_inode = strtoull(argv[3], NULL, 16);
437 if (errno == ERANGE && dst_inode == ULLONG_MAX) {
438 perror("strtoull");
439 return -1;
441 if (dst_inode == 0) {
442 fprintf(stderr, "Invalid dst inode# %ju\n",
443 (uintmax_t)dst_inode);
444 return -1;
446 if (src_inode == dst_inode) {
447 fprintf(stderr, "src equals dst\n");
448 return -1;
450 printf("%s 0x%016jx 0x%016jx\n", devpath, (uintmax_t)src_inode,
451 (uintmax_t)dst_inode);
452 } else if (!strcmp(type, "dirent")) {
453 src_dirent = argv[2];
454 if (strlen(src_dirent) > HAMMER2_PBUFSIZE) {
455 fprintf(stderr, "src dirent too long\n");
456 return -1;
458 dst_dirent = argv[3];
459 if (strlen(dst_dirent) > HAMMER2_PBUFSIZE) {
460 fprintf(stderr, "dst dirent too long\n");
461 return -1;
463 if (!strcmp(src_dirent, dst_dirent)) {
464 fprintf(stderr, "src equals dst\n");
465 return -1;
467 printf("%s %s %s\n", devpath, src_dirent, dst_dirent);
468 } else {
469 fprintf(stderr, "Invalid blockref type %s\n", type);
470 return -1;
473 return 0;
477 main(int argc, char **argv)
479 int ch;
480 const char *binpath = argv[0];
481 const char *devpath;
483 while ((ch = getopt(argc, argv, "f")) != -1) {
484 switch(ch) {
485 case 'f':
486 ForceOpt = true;
487 break;
488 default:
489 break;
492 argc -= optind;
493 argv += optind;
495 if (argc < 4) {
496 fprintf(stderr, "%s [-f] special type src dst\n", binpath);
497 exit(1);
500 if (init_args(argc, argv, &devpath) == -1)
501 exit(1);
503 hammer2_init_volumes(devpath, 0);
505 if (destroy_blockref(HAMMER2_BREF_TYPE_VOLUME) == -1)
506 exit(1);
508 hammer2_cleanup_volumes();
510 return 0;