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