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