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