xref: /dragonfly/sbin/fsck_hammer2/destroy.c (revision 73da171931e10cdde2aceb0593a5ff8ad3ecb333)
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