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 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_PBUFSIZE); 93 if (ret == HAMMER2_PBUFSIZE) { 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 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 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 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 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 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((const unsigned char*)dst_dirent, 337 strlen(dst_dirent)); 338 if (write_media(prev_bref, &bscan_media, bytes) == -1) 339 return -1; 340 } 341 342 printf("%sembedded dirent %s (%d bytes) -> %s (%d bytes)\n", 343 ForceOpt ? "Modified " : "", 344 src_dirent, (int)strlen(src_dirent), 345 dst_dirent, (int)strlen(dst_dirent)); 346 347 return 0; 348 } 349 350 static int 351 modify_dirent(int bi, hammer2_blockref_t *prev_bref, 352 const hammer2_blockref_t *bref, hammer2_media_data_t *media, 353 size_t media_bytes) 354 { 355 hammer2_media_data_t bscan_media; 356 hammer2_blockref_t *bscan; 357 size_t bytes; 358 359 assert(!memcmp(src_dirent, media->buf, strlen(src_dirent))); 360 if (read_media(prev_bref, &bscan_media, &bytes) == -1) 361 return -1; 362 assert(bytes); 363 364 switch (prev_bref->type) { 365 case HAMMER2_BREF_TYPE_INODE: 366 bscan = &bscan_media.ipdata.u.blockset.blockref[bi]; 367 break; 368 case HAMMER2_BREF_TYPE_INDIRECT: 369 bscan = &bscan_media.npdata[bi]; 370 break; 371 default: 372 assert(0); 373 break; 374 } 375 376 if (memcmp(bref, bscan, sizeof(*bref))) { 377 fprintf(stderr, "Blockref contents mismatch\n"); 378 return -1; 379 } 380 if (strlen(dst_dirent) > sizeof(media->buf)) { 381 fprintf(stderr, "dirent %s (%d bytes) can't exceed %lu bytes\n", 382 dst_dirent, (int)strlen(dst_dirent), sizeof(media->buf)); 383 return -1; 384 } 385 if (strlen(dst_dirent) <= sizeof(bscan->check.buf)) { 386 fprintf(stderr, "dirent %s (%d bytes) must exceed %lu bytes\n", 387 dst_dirent, (int)strlen(dst_dirent), 388 sizeof(bscan->check.buf)); 389 return -1; 390 } 391 392 if (ForceOpt) { 393 memset(media->buf, 0, sizeof(media->buf)); 394 memcpy(media->buf, dst_dirent, strlen(dst_dirent)); 395 bscan->embed.dirent.namlen = strlen(dst_dirent); 396 bscan->key = dirhash((const unsigned char*)dst_dirent, 397 strlen(dst_dirent)); 398 if (write_media(bref, media, media_bytes) == -1) 399 return -1; 400 if (write_media(prev_bref, &bscan_media, bytes) == -1) { 401 memset(media->buf, 0, sizeof(media->buf)); 402 memcpy(media->buf, src_dirent, strlen(src_dirent)); 403 if (write_media(bref, media, media_bytes) == -1) 404 return -1; 405 return -1; 406 } 407 } 408 409 printf("%sdirent %s (%d bytes) -> %s (%d bytes)\n", 410 ForceOpt ? "Modified " : "", 411 src_dirent, (int)strlen(src_dirent), 412 dst_dirent, (int)strlen(dst_dirent)); 413 414 return 0; 415 } 416 417 static int 418 init_args(int argc, char **argv, const char **devpathp) 419 { 420 const char *devpath, *type; 421 422 *devpathp = devpath = argv[0]; 423 type = argv[1]; 424 425 if (!strcmp(type, "inode")) { 426 errno = 0; 427 src_inode = strtoull(argv[2], NULL, 16); 428 if (errno == ERANGE && src_inode == ULLONG_MAX) { 429 perror("strtoull"); 430 return -1; 431 } 432 if (src_inode == 0) { 433 fprintf(stderr, "Invalid src inode# %ju\n", 434 (uintmax_t)src_inode); 435 return -1; 436 } 437 errno = 0; 438 dst_inode = strtoull(argv[3], NULL, 16); 439 if (errno == ERANGE && dst_inode == ULLONG_MAX) { 440 perror("strtoull"); 441 return -1; 442 } 443 if (dst_inode == 0) { 444 fprintf(stderr, "Invalid dst inode# %ju\n", 445 (uintmax_t)dst_inode); 446 return -1; 447 } 448 if (src_inode == dst_inode) { 449 fprintf(stderr, "src equals dst\n"); 450 return -1; 451 } 452 printf("%s 0x%016jx 0x%016jx\n", devpath, (uintmax_t)src_inode, 453 (uintmax_t)dst_inode); 454 } else if (!strcmp(type, "dirent")) { 455 src_dirent = argv[2]; 456 if (strlen(src_dirent) > HAMMER2_PBUFSIZE) { 457 fprintf(stderr, "src dirent too long\n"); 458 return -1; 459 } 460 dst_dirent = argv[3]; 461 if (strlen(dst_dirent) > HAMMER2_PBUFSIZE) { 462 fprintf(stderr, "dst dirent too long\n"); 463 return -1; 464 } 465 if (!strcmp(src_dirent, dst_dirent)) { 466 fprintf(stderr, "src equals dst\n"); 467 return -1; 468 } 469 printf("%s %s %s\n", devpath, src_dirent, dst_dirent); 470 } else { 471 fprintf(stderr, "Invalid blockref type %s\n", type); 472 return -1; 473 } 474 475 return 0; 476 } 477 478 int 479 main(int argc, char **argv) 480 { 481 int ch; 482 const char *binpath = argv[0]; 483 const char *devpath; 484 485 while ((ch = getopt(argc, argv, "f")) != -1) { 486 switch(ch) { 487 case 'f': 488 ForceOpt = true; 489 break; 490 default: 491 break; 492 } 493 } 494 argc -= optind; 495 argv += optind; 496 497 if (argc < 4) { 498 fprintf(stderr, "%s [-f] special type src dst\n", binpath); 499 exit(1); 500 } 501 502 if (init_args(argc, argv, &devpath) == -1) 503 exit(1); 504 505 hammer2_init_volumes(devpath, 0); 506 507 if (destroy_blockref(HAMMER2_BREF_TYPE_VOLUME) == -1) 508 exit(1); 509 510 hammer2_cleanup_volumes(); 511 512 return 0; 513 } 514