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 38 // ../../sys/libkern/icrc32.c ../hammer2/subs.c ./destroy.c -o destroy 39 40 #include <sys/types.h> 41 #include <sys/stat.h> 42 #include <unistd.h> 43 #include <fcntl.h> 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <stdbool.h> 47 #include <string.h> 48 #include <limits.h> 49 #include <errno.h> 50 #include <assert.h> 51 52 #include <vfs/hammer2/hammer2_disk.h> 53 54 #include "hammer2_subs.h" 55 56 static int modify_blockref(int, const hammer2_volume_data_t *, int, 57 hammer2_blockref_t *, hammer2_blockref_t *); 58 static int modify_inode(int, const hammer2_blockref_t *, 59 hammer2_media_data_t *, size_t); 60 static int modify_dirent_embedded(int, int, hammer2_blockref_t *); 61 static int modify_dirent(int, int, hammer2_blockref_t *, 62 const hammer2_blockref_t *, hammer2_media_data_t *, size_t); 63 64 static hammer2_tid_t src_inode = 0; 65 static hammer2_tid_t dst_inode = 0; 66 static const char *src_dirent = NULL; 67 static const char *dst_dirent = NULL; 68 static bool ForceOpt = false; 69 70 static int 71 destroy_blockref(int fd, uint8_t type) 72 { 73 bool failed = false; 74 int i; 75 76 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) { 77 hammer2_volume_data_t voldata; 78 hammer2_blockref_t broot; 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 if (lseek(fd, broot.data_off & ~HAMMER2_OFF_MASK_RADIX, 85 SEEK_SET) == -1) { 86 perror("lseek"); 87 return -1; 88 } 89 90 ret = read(fd, &voldata, HAMMER2_PBUFSIZE); 91 if (ret == HAMMER2_PBUFSIZE) { 92 fprintf(stdout, "zone.%d %016jx\n", 93 i, (uintmax_t)broot.data_off); 94 if (modify_blockref(fd, &voldata, -1, &broot, NULL) 95 == -1) 96 failed = true; 97 } else if (ret == -1) { 98 perror("read"); 99 return -1; 100 } else { 101 fprintf(stderr, "Failed to read volume header\n"); 102 return -1; 103 } 104 } 105 106 return failed ? -1 : 0; 107 } 108 109 static int 110 read_media(int fd, const hammer2_blockref_t *bref, hammer2_media_data_t *media, 111 size_t *media_bytes) 112 { 113 hammer2_off_t io_off, io_base; 114 size_t bytes, io_bytes, boff; 115 ssize_t ret; 116 117 bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX); 118 if (bytes) 119 bytes = (size_t)1 << bytes; 120 if (media_bytes) 121 *media_bytes = bytes; 122 123 if (!bytes) 124 return 0; 125 126 io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX; 127 io_base = io_off & ~(hammer2_off_t)(HAMMER2_MINIOSIZE - 1); 128 boff = io_off - io_base; 129 130 io_bytes = HAMMER2_MINIOSIZE; 131 while (io_bytes + boff < bytes) 132 io_bytes <<= 1; 133 134 if (io_bytes > sizeof(*media)) { 135 fprintf(stderr, "Bad I/O bytes\n"); 136 return -1; 137 } 138 if (lseek(fd, io_base, SEEK_SET) == -1) { 139 perror("lseek"); 140 return -1; 141 } 142 ret = read(fd, media, io_bytes); 143 if (ret == -1) { 144 perror("read"); 145 return -1; 146 } else if (ret != (ssize_t)io_bytes) { 147 fprintf(stderr, "Failed to read media\n"); 148 return -1; 149 } 150 if (boff) 151 memmove(media, (char *)media + boff, bytes); 152 153 return 0; 154 } 155 156 static int 157 write_media(int fd, const hammer2_blockref_t *bref, 158 const hammer2_media_data_t *media, size_t media_bytes) 159 { 160 hammer2_off_t io_off, io_base; 161 char buf[HAMMER2_PBUFSIZE]; 162 size_t bytes, io_bytes, boff; 163 ssize_t ret; 164 165 bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX); 166 if (bytes) 167 bytes = (size_t)1 << bytes; 168 assert(bytes != 0); 169 assert(bytes == media_bytes); 170 171 io_off = bref->data_off & ~HAMMER2_OFF_MASK_RADIX; 172 io_base = io_off & ~(hammer2_off_t)(HAMMER2_MINIOSIZE - 1); 173 boff = io_off - io_base; 174 175 io_bytes = HAMMER2_MINIOSIZE; 176 while (io_bytes + boff < bytes) 177 io_bytes <<= 1; 178 179 if (io_bytes > sizeof(buf)) { 180 fprintf(stderr, "Bad I/O bytes\n"); 181 return -1; 182 } 183 if (lseek(fd, io_base, SEEK_SET) == -1) { 184 perror("lseek"); 185 return -1; 186 } 187 if (read(fd, buf, io_bytes) != (ssize_t)io_bytes) { 188 perror("read"); 189 return -1; 190 } 191 192 memcpy(buf + boff, media, media_bytes); 193 if (lseek(fd, io_base, SEEK_SET) == -1) { 194 perror("lseek"); 195 return -1; 196 } 197 ret = write(fd, buf, io_bytes); 198 if (ret == -1) { 199 perror("write"); 200 return -1; 201 } else if (ret != (ssize_t)io_bytes) { 202 fprintf(stderr, "Failed to write media\n"); 203 return -1; 204 } 205 if (fsync(fd) == -1) { 206 perror("fsync"); 207 return -1; 208 } 209 210 return 0; 211 } 212 213 static int 214 modify_blockref(int fd, const hammer2_volume_data_t *voldata, int bi, 215 hammer2_blockref_t *bref, hammer2_blockref_t *prev_bref) 216 { 217 hammer2_media_data_t media; 218 hammer2_blockref_t *bscan; 219 int i, bcount, namlen; 220 size_t bytes; 221 222 if (read_media(fd, bref, &media, &bytes) == -1) 223 return -1; 224 225 switch (bref->type) { 226 case HAMMER2_BREF_TYPE_INODE: 227 if (!(media.ipdata.meta.op_flags & HAMMER2_OPFLAG_DIRECTDATA)) { 228 bscan = &media.ipdata.u.blockset.blockref[0]; 229 bcount = HAMMER2_SET_COUNT; 230 } else { 231 bscan = NULL; 232 bcount = 0; 233 } 234 if (src_inode && media.ipdata.meta.inum == src_inode) 235 if (modify_inode(fd, bref, &media, bytes) == -1) 236 return -1; 237 break; 238 case HAMMER2_BREF_TYPE_INDIRECT: 239 bscan = &media.npdata[0]; 240 bcount = bytes / sizeof(hammer2_blockref_t); 241 break; 242 case HAMMER2_BREF_TYPE_DIRENT: 243 bscan = NULL; 244 bcount = 0; 245 namlen = bref->embed.dirent.namlen; 246 if (src_dirent && namlen == strlen(src_dirent)) { 247 if (namlen <= sizeof(bref->check.buf) && 248 !memcmp(bref->check.buf, src_dirent, namlen)) { 249 if (modify_dirent_embedded(fd, bi, prev_bref) 250 == -1) 251 return -1; 252 } else if (!memcmp(media.buf, src_dirent, namlen)) { 253 if (modify_dirent(fd, bi, prev_bref, bref, 254 &media, bytes) == -1) 255 return -1; 256 } 257 } 258 break; 259 case HAMMER2_BREF_TYPE_VOLUME: 260 bscan = &media.voldata.sroot_blockset.blockref[0]; 261 bcount = HAMMER2_SET_COUNT; 262 break; 263 default: 264 bscan = NULL; 265 bcount = 0; 266 break; 267 } 268 269 for (i = 0; i < bcount; ++i) 270 if (bscan[i].type != HAMMER2_BREF_TYPE_EMPTY) 271 if (modify_blockref(fd, voldata, i, &bscan[i], bref) 272 == -1) 273 return -1; 274 return 0; 275 } 276 277 static int 278 modify_inode(int fd, const hammer2_blockref_t *bref, 279 hammer2_media_data_t *media, size_t media_bytes) 280 { 281 assert(src_inode == media->ipdata.meta.inum); 282 283 if (ForceOpt) { 284 media->ipdata.meta.inum = dst_inode; 285 if (write_media(fd, bref, media, media_bytes) == -1) 286 return -1; 287 } 288 289 printf("%sinode# 0x%016jx -> 0x%016jx\n", ForceOpt ? "Modified " : "", 290 src_inode, dst_inode); 291 292 return 0; 293 } 294 295 static int 296 modify_dirent_embedded(int fd, int bi, hammer2_blockref_t *prev_bref) 297 { 298 hammer2_media_data_t bscan_media; 299 hammer2_blockref_t *bscan; 300 size_t bytes; 301 302 if (read_media(fd, prev_bref, &bscan_media, &bytes) == -1) 303 return -1; 304 assert(bytes); 305 306 switch (prev_bref->type) { 307 case HAMMER2_BREF_TYPE_INODE: 308 bscan = &bscan_media.ipdata.u.blockset.blockref[bi]; 309 break; 310 case HAMMER2_BREF_TYPE_INDIRECT: 311 bscan = &bscan_media.npdata[bi]; 312 break; 313 default: 314 assert(0); 315 break; 316 } 317 assert(!memcmp(src_dirent, bscan->check.buf, strlen(src_dirent))); 318 319 if (strlen(dst_dirent) > sizeof(bscan->check.buf)) { 320 fprintf(stderr, "embedded dirent %s (%d bytes) can't exceed " 321 "%lu bytes\n", dst_dirent, (int)strlen(dst_dirent), 322 sizeof(bscan->check.buf)); 323 return -1; 324 } 325 326 if (ForceOpt) { 327 memset(bscan->check.buf, 0, sizeof(bscan->check.buf)); 328 memcpy(bscan->check.buf, dst_dirent, strlen(dst_dirent)); 329 bscan->embed.dirent.namlen = strlen(dst_dirent); 330 bscan->key = dirhash((const unsigned char*)dst_dirent, 331 strlen(dst_dirent)); 332 if (write_media(fd, prev_bref, &bscan_media, bytes) == -1) 333 return -1; 334 } 335 336 printf("%sembedded dirent %s (%d bytes) -> %s (%d bytes)\n", 337 ForceOpt ? "Modified " : "", 338 src_dirent, (int)strlen(src_dirent), 339 dst_dirent, (int)strlen(dst_dirent)); 340 341 return 0; 342 } 343 344 static int 345 modify_dirent(int fd, int bi, hammer2_blockref_t *prev_bref, 346 const hammer2_blockref_t *bref, hammer2_media_data_t *media, 347 size_t media_bytes) 348 { 349 hammer2_media_data_t bscan_media; 350 hammer2_blockref_t *bscan; 351 size_t bytes; 352 353 assert(!memcmp(src_dirent, media->buf, strlen(src_dirent))); 354 if (read_media(fd, prev_bref, &bscan_media, &bytes) == -1) 355 return -1; 356 assert(bytes); 357 358 switch (prev_bref->type) { 359 case HAMMER2_BREF_TYPE_INODE: 360 bscan = &bscan_media.ipdata.u.blockset.blockref[bi]; 361 break; 362 case HAMMER2_BREF_TYPE_INDIRECT: 363 bscan = &bscan_media.npdata[bi]; 364 break; 365 default: 366 assert(0); 367 break; 368 } 369 370 if (memcmp(bref, bscan, sizeof(*bref))) { 371 fprintf(stderr, "Blockref contents mismatch\n"); 372 return -1; 373 } 374 if (strlen(dst_dirent) > sizeof(media->buf)) { 375 fprintf(stderr, "dirent %s (%d bytes) can't exceed %lu bytes\n", 376 dst_dirent, (int)strlen(dst_dirent), sizeof(media->buf)); 377 return -1; 378 } 379 if (strlen(dst_dirent) <= sizeof(bscan->check.buf)) { 380 fprintf(stderr, "dirent %s (%d bytes) must exceed %lu bytes\n", 381 dst_dirent, (int)strlen(dst_dirent), 382 sizeof(bscan->check.buf)); 383 return -1; 384 } 385 386 if (ForceOpt) { 387 memset(media->buf, 0, sizeof(media->buf)); 388 memcpy(media->buf, dst_dirent, strlen(dst_dirent)); 389 bscan->embed.dirent.namlen = strlen(dst_dirent); 390 bscan->key = dirhash((const unsigned char*)dst_dirent, 391 strlen(dst_dirent)); 392 if (write_media(fd, bref, media, media_bytes) == -1) 393 return -1; 394 if (write_media(fd, prev_bref, &bscan_media, bytes) == -1) { 395 memset(media->buf, 0, sizeof(media->buf)); 396 memcpy(media->buf, src_dirent, strlen(src_dirent)); 397 if (write_media(fd, bref, media, media_bytes) == -1) 398 return -1; 399 return -1; 400 } 401 } 402 403 printf("%sdirent %s (%d bytes) -> %s (%d bytes)\n", 404 ForceOpt ? "Modified " : "", 405 src_dirent, (int)strlen(src_dirent), 406 dst_dirent, (int)strlen(dst_dirent)); 407 408 return 0; 409 } 410 411 static int 412 init_args(int argc, char **argv, const char **devpathp) 413 { 414 const char *devpath, *type; 415 416 *devpathp = devpath = argv[0]; 417 type = argv[1]; 418 419 if (!strcmp(type, "inode")) { 420 errno = 0; 421 src_inode = strtoull(argv[2], NULL, 16); 422 if (errno == ERANGE && src_inode == ULLONG_MAX) { 423 perror("strtoull"); 424 return -1; 425 } 426 if (src_inode == 0) { 427 fprintf(stderr, "Invalid src inode# %ju\n", 428 (uintmax_t)src_inode); 429 return -1; 430 } 431 errno = 0; 432 dst_inode = strtoull(argv[3], NULL, 16); 433 if (errno == ERANGE && dst_inode == ULLONG_MAX) { 434 perror("strtoull"); 435 return -1; 436 } 437 if (dst_inode == 0) { 438 fprintf(stderr, "Invalid dst inode# %ju\n", 439 (uintmax_t)dst_inode); 440 return -1; 441 } 442 if (src_inode == dst_inode) { 443 fprintf(stderr, "src equals dst\n"); 444 return -1; 445 } 446 printf("%s 0x%016jx 0x%016jx\n", devpath, (uintmax_t)src_inode, 447 (uintmax_t)dst_inode); 448 } else if (!strcmp(type, "dirent")) { 449 src_dirent = argv[2]; 450 if (strlen(src_dirent) > HAMMER2_PBUFSIZE) { 451 fprintf(stderr, "src dirent too long\n"); 452 return -1; 453 } 454 dst_dirent = argv[3]; 455 if (strlen(dst_dirent) > HAMMER2_PBUFSIZE) { 456 fprintf(stderr, "dst dirent too long\n"); 457 return -1; 458 } 459 if (!strcmp(src_dirent, dst_dirent)) { 460 fprintf(stderr, "src equals dst\n"); 461 return -1; 462 } 463 printf("%s %s %s\n", devpath, src_dirent, dst_dirent); 464 } else { 465 fprintf(stderr, "Invalid blockref type %s\n", type); 466 return -1; 467 } 468 469 return 0; 470 } 471 472 int 473 main(int argc, char **argv) 474 { 475 struct stat st; 476 int ch, fd; 477 const char *binpath = argv[0]; 478 const char *devpath; 479 480 while ((ch = getopt(argc, argv, "f")) != -1) { 481 switch(ch) { 482 case 'f': 483 ForceOpt = true; 484 break; 485 default: 486 break; 487 } 488 } 489 argc -= optind; 490 argv += optind; 491 492 if (argc < 4) { 493 fprintf(stderr, "%s [-f] special type src dst\n", binpath); 494 exit(1); 495 } 496 497 if (init_args(argc, argv, &devpath) == -1) 498 exit(1); 499 500 fd = open(devpath, O_RDWR); 501 if (fd == -1) { 502 perror("open"); 503 exit(1); 504 } 505 506 if (fstat(fd, &st) == -1) { 507 perror("fstat"); 508 exit(1); 509 } 510 if (!S_ISCHR(st.st_mode) && !S_ISREG(st.st_mode)) { 511 fprintf(stderr, "Unsupported file type\n"); 512 exit(1); 513 } 514 515 if (destroy_blockref(fd, HAMMER2_BREF_TYPE_VOLUME) == -1) 516 exit(1); 517 518 close(fd); 519 520 return 0; 521 } 522