1 /*
2 * Copyright (c) 2019 Tomohiro Kusumi <tkusumi@netbsd.org>
3 * Copyright (c) 2019 The DragonFly Project
4 * All rights reserved.
5 *
6 * This code is derived from software contributed to The DragonFly Project
7 * by Matthew Dillon <dillon@dragonflybsd.org>
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
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.
22 *
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.
35 */
36
37 // # gcc -Wall -g -I../../sys -I../hammer2 ../../sys/libkern/icrc32.c ../hammer2/subs.c ../hammer2/ondisk.c ./destroy.c -o destroy
38
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>
50
51 #include <vfs/hammer2/hammer2_disk.h>
52
53 #include "hammer2_subs.h"
54
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);
62
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;
68
69 static int
destroy_blockref(uint8_t type)70 destroy_blockref(uint8_t type)
71 {
72 bool failed = false;
73 int i;
74
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;
80
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;
89 }
90
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;
104 }
105 }
106
107 return failed ? -1 : 0;
108 }
109
110 static int
read_media(const hammer2_blockref_t * bref,hammer2_media_data_t * media,size_t * media_bytes)111 read_media(const hammer2_blockref_t *bref, hammer2_media_data_t *media,
112 size_t *media_bytes)
113 {
114 hammer2_off_t io_off, io_base;
115 size_t bytes, io_bytes, boff;
116 ssize_t ret;
117 int fd;
118
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;
124
125 if (!bytes)
126 return 0;
127
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;
131
132 io_bytes = HAMMER2_LBUFSIZE;
133 while (io_bytes + boff < bytes)
134 io_bytes <<= 1;
135
136 if (io_bytes > sizeof(*media)) {
137 fprintf(stderr, "Bad I/O bytes\n");
138 return -1;
139 }
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;
145 }
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;
153 }
154 if (boff)
155 memmove(media, (char *)media + boff, bytes);
156
157 return 0;
158 }
159
160 static int
write_media(const hammer2_blockref_t * bref,const hammer2_media_data_t * media,size_t media_bytes)161 write_media(const hammer2_blockref_t *bref, const hammer2_media_data_t *media,
162 size_t media_bytes)
163 {
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;
169
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);
175
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;
179
180 io_bytes = HAMMER2_LBUFSIZE;
181 while (io_bytes + boff < bytes)
182 io_bytes <<= 1;
183
184 if (io_bytes > sizeof(buf)) {
185 fprintf(stderr, "Bad I/O bytes\n");
186 return -1;
187 }
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;
193 }
194 if (read(fd, buf, io_bytes) != (ssize_t)io_bytes) {
195 perror("read");
196 return -1;
197 }
198
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;
204 }
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;
212 }
213 if (fsync(fd) == -1) {
214 perror("fsync");
215 return -1;
216 }
217
218 return 0;
219 }
220
221 static int
modify_blockref(const hammer2_volume_data_t * voldata,int bi,hammer2_blockref_t * bref,hammer2_blockref_t * prev_bref)222 modify_blockref(const hammer2_volume_data_t *voldata, int bi,
223 hammer2_blockref_t *bref, hammer2_blockref_t *prev_bref)
224 {
225 hammer2_media_data_t media;
226 hammer2_blockref_t *bscan;
227 int i, bcount, namlen;
228 size_t bytes;
229
230 if (read_media(bref, &media, &bytes) == -1)
231 return -1;
232
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;
241 }
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;
263 }
264 }
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;
274 }
275
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;
281 }
282
283 static int
modify_inode(const hammer2_blockref_t * bref,hammer2_media_data_t * media,size_t media_bytes)284 modify_inode(const hammer2_blockref_t *bref,
285 hammer2_media_data_t *media, size_t media_bytes)
286 {
287 assert(src_inode == media->ipdata.meta.inum);
288
289 if (ForceOpt) {
290 media->ipdata.meta.inum = dst_inode;
291 if (write_media(bref, media, media_bytes) == -1)
292 return -1;
293 }
294
295 printf("%sinode# 0x%016jx -> 0x%016jx\n", ForceOpt ? "Modified " : "",
296 src_inode, dst_inode);
297
298 return 0;
299 }
300
301 static int
modify_dirent_embedded(int bi,hammer2_blockref_t * prev_bref)302 modify_dirent_embedded(int bi, hammer2_blockref_t *prev_bref)
303 {
304 hammer2_media_data_t bscan_media;
305 hammer2_blockref_t *bscan;
306 size_t bytes;
307
308 if (read_media(prev_bref, &bscan_media, &bytes) == -1)
309 return -1;
310 assert(bytes);
311
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;
322 }
323 assert(!memcmp(src_dirent, bscan->check.buf, strlen(src_dirent)));
324
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;
330 }
331
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;
339 }
340
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));
345
346 return 0;
347 }
348
349 static int
modify_dirent(int bi,hammer2_blockref_t * prev_bref,const hammer2_blockref_t * bref,hammer2_media_data_t * media,size_t media_bytes)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)
353 {
354 hammer2_media_data_t bscan_media;
355 hammer2_blockref_t *bscan;
356 size_t bytes;
357
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);
362
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;
373 }
374
375 if (memcmp(bref, bscan, sizeof(*bref))) {
376 fprintf(stderr, "Blockref contents mismatch\n");
377 return -1;
378 }
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;
383 }
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;
389 }
390
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;
404 }
405 }
406
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));
411
412 return 0;
413 }
414
415 static int
init_args(int argc,char ** argv,const char ** devpathp)416 init_args(int argc, char **argv, const char **devpathp)
417 {
418 const char *devpath, *type;
419
420 *devpathp = devpath = argv[0];
421 type = argv[1];
422
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;
429 }
430 if (src_inode == 0) {
431 fprintf(stderr, "Invalid src inode# %ju\n",
432 (uintmax_t)src_inode);
433 return -1;
434 }
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;
440 }
441 if (dst_inode == 0) {
442 fprintf(stderr, "Invalid dst inode# %ju\n",
443 (uintmax_t)dst_inode);
444 return -1;
445 }
446 if (src_inode == dst_inode) {
447 fprintf(stderr, "src equals dst\n");
448 return -1;
449 }
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;
457 }
458 dst_dirent = argv[3];
459 if (strlen(dst_dirent) > HAMMER2_PBUFSIZE) {
460 fprintf(stderr, "dst dirent too long\n");
461 return -1;
462 }
463 if (!strcmp(src_dirent, dst_dirent)) {
464 fprintf(stderr, "src equals dst\n");
465 return -1;
466 }
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;
471 }
472
473 return 0;
474 }
475
476 int
main(int argc,char ** argv)477 main(int argc, char **argv)
478 {
479 int ch;
480 const char *binpath = argv[0];
481 const char *devpath;
482
483 while ((ch = getopt(argc, argv, "f")) != -1) {
484 switch(ch) {
485 case 'f':
486 ForceOpt = true;
487 break;
488 default:
489 break;
490 }
491 }
492 argc -= optind;
493 argv += optind;
494
495 if (argc < 4) {
496 fprintf(stderr, "%s [-f] special type src dst\n", binpath);
497 exit(1);
498 }
499
500 if (init_args(argc, argv, &devpath) == -1)
501 exit(1);
502
503 hammer2_init_volumes(devpath, 0);
504
505 if (destroy_blockref(HAMMER2_BREF_TYPE_VOLUME) == -1)
506 exit(1);
507
508 hammer2_cleanup_volumes();
509
510 return 0;
511 }
512