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