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