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