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