1 /* $NetBSD: scan_ffs.c,v 1.37 2023/01/24 08:05:07 mlelstv Exp $ */ 2 3 /* 4 * Copyright (c) 2005-2007 Juan Romero Pardines 5 * Copyright (c) 1998 Niklas Hallqvist, Tobias Weingartner 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 /* 30 * Currently it can detect FFS and LFS partitions (version 1 or 2) 31 * up to 8192/65536 fragsize/blocksize. 32 */ 33 34 #include <sys/cdefs.h> 35 #ifndef lint 36 __RCSID("$NetBSD: scan_ffs.c,v 1.37 2023/01/24 08:05:07 mlelstv Exp $"); 37 #endif /* not lint */ 38 39 #include <sys/types.h> 40 #include <sys/param.h> 41 #include <sys/disklabel.h> 42 #include <sys/dkio.h> 43 #include <sys/ioctl.h> 44 #include <sys/fcntl.h> 45 #include <sys/mount.h> 46 47 #include <ufs/lfs/lfs.h> 48 #include <ufs/lfs/lfs_accessors.h> 49 #include <ufs/lfs/lfs_extern.h> 50 51 #include <ufs/ufs/dinode.h> 52 #include <ufs/ffs/fs.h> 53 #include <ufs/ffs/ffs_extern.h> 54 55 #include <unistd.h> 56 #include <stdlib.h> 57 #include <stdio.h> 58 #include <string.h> 59 #include <err.h> 60 #include <util.h> 61 #include <paths.h> 62 63 #define BLK_CNT (blk + (n / 512)) 64 65 /* common struct for FFS/LFS */ 66 struct sblockinfo { 67 struct lfs *lfs; 68 struct fs *ffs; 69 uint64_t lfs_off; 70 uint64_t ffs_off; 71 char lfs_path[MAXMNTLEN]; 72 char ffs_path[MAXMNTLEN]; 73 }; 74 75 static daddr_t blk, lastblk; 76 77 static int eflag = 0; 78 static int fflag = 0; 79 static int flags = 0; 80 static int sbaddr = 0; /* counter for the LFS superblocks */ 81 82 static char device[MAXPATHLEN]; 83 static const char *fstypes[] = { "NONE", "FFSv1", "FFSv2" }; 84 85 static sig_atomic_t print_info = 0; 86 87 #define FSTYPE_NONE 0 88 #define FSTYPE_FFSV1 1 89 #define FSTYPE_FFSV2 2 90 91 #define SBCOUNT 128 /* may be changed */ 92 #define SBPASS (SBCOUNT * SBLOCKSIZE / 512) 93 94 /* This is only useful for LFS */ 95 96 /* first sblock address contains the correct offset */ 97 #define FIRST_SBLOCK_ADDRESS 1 98 /* second and third sblock address contain lfs_fsmnt[MAXMNTLEN] */ 99 #define SECOND_SBLOCK_ADDRESS 2 100 /* last sblock address in a LFS partition */ 101 #define MAX_SBLOCK_ADDRESS 10 102 103 enum { NADA=0, VERBOSE=1, LABELS=2, BLOCKS=4 }; 104 105 /* FFS functions */ 106 static void ffs_printpart(struct sblockinfo *, int, size_t, int); 107 static void ffs_scan(struct sblockinfo *, int); 108 static int ffs_checkver(struct sblockinfo *); 109 /* LFS functions */ 110 static void lfs_printpart(struct sblockinfo *, int, int); 111 static void lfs_scan(struct sblockinfo *, int); 112 /* common functions */ 113 static void usage(void) __dead; 114 static int scan_disk(int, daddr_t, daddr_t, int); 115 116 static void 117 got_siginfo(int signo) 118 { 119 120 print_info = 1; 121 } 122 123 static int 124 ffs_checkver(struct sblockinfo *sbi) 125 { 126 switch (sbi->ffs->fs_magic) { 127 case FS_UFS1_MAGIC_SWAPPED: 128 case FS_UFS2_MAGIC_SWAPPED: 129 case FS_UFS2EA_MAGIC_SWAPPED: 130 ffs_sb_swap(sbi->ffs, sbi->ffs); 131 break; 132 } 133 134 switch (sbi->ffs->fs_magic) { 135 case FS_UFS1_MAGIC: 136 case FS_UFS1_MAGIC_SWAPPED: 137 sbi->ffs->fs_size = sbi->ffs->fs_old_size; 138 return FSTYPE_FFSV1; 139 case FS_UFS2_MAGIC: 140 case FS_UFS2EA_MAGIC: 141 case FS_UFS2_MAGIC_SWAPPED: 142 case FS_UFS2EA_MAGIC_SWAPPED: 143 return FSTYPE_FFSV2; 144 default: 145 return FSTYPE_NONE; 146 } 147 } 148 149 static void 150 ffs_printpart(struct sblockinfo *sbi, int flag, size_t ffsize, int n) 151 { 152 int offset, ver; 153 154 switch (flag) { 155 case VERBOSE: 156 switch (ffs_checkver(sbi)) { 157 case FSTYPE_FFSV1: 158 (void)printf("offset: %" PRIu64 " n: %d " 159 "id: %x,%x size: %" PRIu64 "\n", 160 BLK_CNT - (2 * SBLOCKSIZE / 512), n, 161 sbi->ffs->fs_id[0], sbi->ffs->fs_id[1], 162 (uint64_t)sbi->ffs->fs_size * 163 sbi->ffs->fs_fsize / 512); 164 break; 165 case FSTYPE_FFSV2: 166 (void)printf("offset: %" PRIu64 " n: %d " 167 "id: %x,%x size: %" PRIu64 "\n", 168 BLK_CNT - (ffsize * SBLOCKSIZE / 512+128), 169 n, sbi->ffs->fs_id[0], sbi->ffs->fs_id[1], 170 (uint64_t)sbi->ffs->fs_size * 171 sbi->ffs->fs_fsize / 512); 172 break; 173 default: 174 break; 175 } 176 break; 177 case LABELS: 178 (void)printf("X: %9" PRIu64, 179 (uint64_t)(sbi->ffs->fs_size * 180 sbi->ffs->fs_fsize / 512)); 181 switch (ffs_checkver(sbi)) { 182 case FSTYPE_FFSV1: 183 (void)printf(" %9" PRIu64, 184 BLK_CNT - (ffsize * SBLOCKSIZE / 512)); 185 break; 186 case FSTYPE_FFSV2: 187 (void)printf(" %9" PRIu64, 188 BLK_CNT - (ffsize * SBLOCKSIZE / 512 + 128)); 189 break; 190 default: 191 break; 192 } 193 (void)printf(" 4.2BSD %6d %5d %7d # %s [%s]\n", 194 sbi->ffs->fs_fsize, sbi->ffs->fs_bsize, 195 sbi->ffs->fs_old_cpg, 196 sbi->ffs_path, fstypes[ffs_checkver(sbi)]); 197 break; 198 case BLOCKS: 199 default: 200 (void)printf("%s ", fstypes[ffs_checkver(sbi)]); 201 ver = ffs_checkver(sbi); 202 if (ver == FSTYPE_NONE) 203 break; 204 205 offset = 0; 206 if (flag == BLOCKS) 207 (void)printf("sb "); 208 else if (ver == FSTYPE_FFSV1) 209 offset = (2 * SBLOCKSIZE / 512); 210 else if (ver == FSTYPE_FFSV2) 211 offset = (ffsize * SBLOCKSIZE / 512 + 128); 212 213 (void)printf("at %" PRIu64, BLK_CNT - offset); 214 (void)printf(" size %" PRIu64 ", last mounted on %s\n", 215 (uint64_t)(sbi->ffs->fs_size * 216 sbi->ffs->fs_fsize / 512), sbi->ffs_path); 217 break; 218 } 219 } 220 221 static void 222 ffs_scan(struct sblockinfo *sbi, int n) 223 { 224 size_t i = 0; 225 226 if (flags & BLOCKS) { 227 ffs_printpart(sbi, BLOCKS, 0, n); 228 return; 229 } 230 if (flags & VERBOSE) 231 ffs_printpart(sbi, VERBOSE, NADA, n); 232 switch (ffs_checkver(sbi)) { 233 case FSTYPE_FFSV1: 234 /* fsize/bsize > 512/4096 and < 4096/32768. */ 235 if ((BLK_CNT - lastblk) == (SBLOCKSIZE / 512)) { 236 i = 2; 237 /* fsize/bsize 4096/32768. */ 238 } else if ((BLK_CNT - lastblk) == (SBLOCKSIZE / 170)) { 239 i = 4; 240 /* fsize/bsize 8192/65536 */ 241 } else if ((BLK_CNT - lastblk) == (SBLOCKSIZE / 73)) { 242 i = 8; 243 } else 244 break; 245 246 if (flags & LABELS) 247 ffs_printpart(sbi, LABELS, i, n); 248 else 249 ffs_printpart(sbi, NADA, i, n); 250 251 break; 252 case FSTYPE_FFSV2: 253 /* 254 * That checks for FFSv2 partitions with fragsize/blocksize: 255 * 512/4096, 1024/8192, 2048/16384, 4096/32768 and 8192/65536. 256 * Really enough for now. 257 */ 258 for (i = 1; i < 16; i <<= 1) 259 if ((BLK_CNT - lastblk) == (daddr_t)(i * SBLOCKSIZE / 512)) { 260 if (flags & LABELS) 261 ffs_printpart(sbi, LABELS, i, n); 262 else 263 ffs_printpart(sbi, NADA, i, n); 264 } 265 break; 266 } 267 } 268 269 static void 270 lfs_printpart(struct sblockinfo *sbi, int flag, int n) 271 { 272 if (flags & VERBOSE) 273 (void)printf("offset: %" PRIu64 " size %" PRIu64 274 " fsid %" PRIx32 "\n", sbi->lfs_off, 275 lfs_sb_getsize(sbi->lfs), 276 lfs_sb_getident(sbi->lfs)); 277 switch (flag) { 278 case LABELS: 279 (void)printf("X: %9" PRIu64, 280 (lfs_sb_getsize(sbi->lfs) * 281 lfs_sb_getfsize(sbi->lfs) / 512)); 282 (void)printf(" %9" PRIu64, sbi->lfs_off); 283 (void)printf(" 4.4LFS %6d %5d %7d # %s [LFS%d v%d]\n", 284 lfs_sb_getfsize(sbi->lfs), lfs_sb_getbsize(sbi->lfs), 285 lfs_sb_getnseg(sbi->lfs), sbi->lfs_path, 286 sbi->lfs->lfs_is64 ? 64 : 32, 287 lfs_sb_getversion(sbi->lfs)); 288 break; 289 case BLOCKS: 290 (void)printf("LFS%d v%d", sbi->lfs->lfs_is64 ? 64 : 32, 291 lfs_sb_getversion(sbi->lfs)); 292 (void)printf(" sb at %" PRIu64, sbi->lfs_off + btodb(LFS_LABELPAD)); 293 (void)printf(" fsid %" PRIx32, lfs_sb_getident(sbi->lfs)); 294 (void)printf(" size %" PRIu64 ", last mounted on %s\n", 295 (lfs_sb_getsize(sbi->lfs) * 296 lfs_sb_getfsize(sbi->lfs) / 512), sbi->lfs_path); 297 break; 298 default: 299 (void)printf("LFS%d v%d ", sbi->lfs->lfs_is64 ? 64 : 32, 300 lfs_sb_getversion(sbi->lfs)); 301 (void)printf("at %" PRIu64, sbi->lfs_off); 302 (void)printf(" size %" PRIu64 ", last mounted on %s\n", 303 (lfs_sb_getsize(sbi->lfs) * 304 lfs_sb_getfsize(sbi->lfs) / 512), sbi->lfs_path); 305 break; 306 } 307 } 308 309 static void 310 lfs_scan(struct sblockinfo *sbi, int n) 311 { 312 size_t namesize; 313 314 /* Check to see if the sb checksums correctly */ 315 if (lfs_sb_cksum(sbi->lfs) != lfs_sb_getcksum(sbi->lfs)) { 316 if (flags & VERBOSE) 317 printf("LFS bad superblock at %" PRIu64 "\n", 318 BLK_CNT); 319 return; 320 } 321 322 /* backup offset */ 323 lastblk = BLK_CNT - (LFS_SBPAD / 512); 324 /* increment counter */ 325 ++sbaddr; 326 327 if (flags & BLOCKS) { 328 sbi->lfs_off = BLK_CNT - btodb(LFS_LABELPAD); 329 lfs_printpart(sbi, BLOCKS, n); 330 return; 331 } 332 333 switch (sbaddr) { 334 /* 335 * first superblock contains the right offset, but lfs_fsmnt is 336 * empty... fortunately the next superblock address has it. 337 */ 338 case FIRST_SBLOCK_ADDRESS: 339 /* copy partition offset */ 340 if ((daddr_t)sbi->lfs_off != lastblk) 341 sbi->lfs_off = BLK_CNT - (LFS_LABELPAD / 512); 342 break; 343 case SECOND_SBLOCK_ADDRESS: 344 /* copy the path of last mount */ 345 namesize = MIN(sizeof(sbi->lfs_path), MIN( 346 sizeof(sbi->lfs->lfs_dlfs_u.u_32.dlfs_fsmnt), 347 sizeof(sbi->lfs->lfs_dlfs_u.u_64.dlfs_fsmnt))); 348 (void)memcpy(sbi->lfs_path, lfs_sb_getfsmnt(sbi->lfs), 349 namesize); 350 sbi->lfs_path[namesize - 1] = '\0'; 351 /* print now that we have the info */ 352 if (flags & LABELS) 353 lfs_printpart(sbi, LABELS, n); 354 else 355 lfs_printpart(sbi, NADA, n); 356 /* clear our struct */ 357 (void)memset(sbi, 0, sizeof(*sbi)); 358 break; 359 case MAX_SBLOCK_ADDRESS: 360 /* 361 * reset the counter, this is the last superblock address, 362 * the next one will be another partition maybe. 363 */ 364 sbaddr = 0; 365 break; 366 default: 367 break; 368 } 369 } 370 371 static int 372 lfs_checkmagic(struct sblockinfo *sbinfo) 373 { 374 switch (sbinfo->lfs->lfs_dlfs_u.u_32.dlfs_magic) { 375 case LFS_MAGIC: 376 sbinfo->lfs->lfs_is64 = false; 377 sbinfo->lfs->lfs_dobyteswap = false; 378 return 1; 379 case LFS_MAGIC_SWAPPED: 380 sbinfo->lfs->lfs_is64 = false; 381 sbinfo->lfs->lfs_dobyteswap = true; 382 return 1; 383 case LFS64_MAGIC: 384 sbinfo->lfs->lfs_is64 = true; 385 sbinfo->lfs->lfs_dobyteswap = false; 386 return 1; 387 case LFS64_MAGIC_SWAPPED: 388 sbinfo->lfs->lfs_is64 = true; 389 sbinfo->lfs->lfs_dobyteswap = true; 390 return 1; 391 default: 392 return 0; 393 } 394 } 395 396 static void 397 show_status(uintmax_t beg, uintmax_t total) 398 { 399 static int ttyfd = -2; 400 char buf[2048]; 401 const uintmax_t done = blk - beg; 402 const double pcent = (100.0 * done) / total; 403 int n; 404 405 if (ttyfd == -2) 406 ttyfd = open(_PATH_TTY, O_RDWR | O_CLOEXEC); 407 408 if (ttyfd == -1) 409 return; 410 411 n = snprintf(buf, sizeof(buf), "%s: done %ju of %ju blocks (%3.2f%%)\n", 412 getprogname(), done, total, pcent); 413 414 if (n <= 0) 415 return; 416 write(ttyfd, buf, (size_t)n); 417 } 418 419 static int 420 scan_disk(int fd, daddr_t beg, daddr_t end, int fflags) 421 { 422 struct sblockinfo sbinfo; 423 uint8_t buf[SBLOCKSIZE * SBCOUNT]; 424 int n; 425 426 n = 0; 427 lastblk = -1; 428 429 /* clear our struct before using it */ 430 (void)memset(&sbinfo, 0, sizeof(sbinfo)); 431 432 if (fflags & LABELS) 433 (void)printf( 434 "# size offset fstype [fsize bsize cpg/sgs]\n"); 435 436 const daddr_t total = end - beg; 437 for (blk = beg; blk <= end; blk += SBPASS) { 438 if (print_info) { 439 show_status(beg, total); 440 441 print_info = 0; 442 } 443 if (pread(fd, buf, sizeof(buf), blk * 512) == -1) { 444 if (fflag && fd >= 0) 445 (void)close(fd); 446 err(1, "pread"); 447 } 448 449 for (n = 0; n < (SBLOCKSIZE * SBCOUNT); n += 512) { 450 sbinfo.ffs = (struct fs *)&buf[n]; 451 sbinfo.lfs = (struct lfs *)&buf[n]; 452 453 switch (ffs_checkver(&sbinfo)) { 454 case FSTYPE_FFSV1: 455 case FSTYPE_FFSV2: 456 ffs_scan(&sbinfo, n); 457 lastblk = BLK_CNT; 458 (void)memcpy(sbinfo.ffs_path, 459 sbinfo.ffs->fs_fsmnt, MAXMNTLEN); 460 break; 461 case FSTYPE_NONE: 462 /* maybe LFS? */ 463 if (lfs_checkmagic(&sbinfo)) 464 lfs_scan(&sbinfo, n); 465 break; 466 default: 467 break; 468 } 469 } 470 } 471 472 if (fflag && fd >= 0) 473 (void)close(fd); 474 475 return EXIT_SUCCESS; 476 } 477 478 479 static void 480 usage(void) 481 { 482 (void)fprintf(stderr, 483 "Usage: %s [-blv] [-e end] [-F file] [-s start] " 484 "device\n", getprogname()); 485 exit(EXIT_FAILURE); 486 } 487 488 489 int 490 main(int argc, char **argv) 491 { 492 int ch, fd; 493 const char *fpath; 494 daddr_t end = -1, beg = 0; 495 struct disklabel dl; 496 497 fpath = NULL; 498 499 setprogname(*argv); 500 while ((ch = getopt(argc, argv, "be:F:ls:v")) != -1) 501 switch(ch) { 502 case 'b': 503 flags |= BLOCKS; 504 flags &= ~LABELS; 505 break; 506 case 'e': 507 eflag = 1; 508 end = atoi(optarg); 509 break; 510 case 'F': 511 fflag = 1; 512 fpath = optarg; 513 break; 514 case 'l': 515 flags |= LABELS; 516 flags &= ~BLOCKS; 517 break; 518 case 's': 519 beg = atoi(optarg); 520 break; 521 case 'v': 522 flags |= VERBOSE; 523 break; 524 default: 525 usage(); 526 /* NOTREACHED */ 527 } 528 529 argc -= optind; 530 argv += optind; 531 532 signal(SIGINFO, got_siginfo); 533 534 if (fflag) { 535 struct stat stp; 536 537 if (stat(fpath, &stp)) 538 err(1, "Cannot stat `%s'", fpath); 539 540 if (!eflag) 541 end = (uint64_t)stp.st_size; 542 543 (void)printf("Total file size: %" PRIu64 "\n\n", 544 (uint64_t)stp.st_size); 545 546 fd = open(fpath, O_RDONLY | O_DIRECT); 547 } else { 548 if (argc != 1) 549 usage(); 550 551 fd = opendisk(argv[0], O_RDONLY, device, sizeof(device), 0); 552 553 if (ioctl(fd, DIOCGDINFO, &dl) == -1) { 554 warn("Couldn't retrieve disklabel"); 555 (void)memset(&dl, 0, sizeof(dl)); 556 dl.d_secperunit = 0x7fffffff; 557 } else { 558 (void)printf("Disk: %s\n", dl.d_typename); 559 (void)printf("Total sectors on disk: %" PRIu32 "\n\n", 560 dl.d_secperunit); 561 } 562 } 563 564 if (!eflag && !fflag) 565 end = dl.d_secperunit; /* default to max sectors */ 566 567 if (fd == -1) 568 err(1, "Cannot open `%s'", device); 569 /* NOTREACHED */ 570 571 return scan_disk(fd, beg, end, flags); 572 } 573