xref: /netbsd-src/sbin/scan_ffs/scan_ffs.c (revision b1c86f5f087524e68db12794ee9c3e3da1ab17a0)
1 /* $NetBSD: scan_ffs.c,v 1.21 2009/04/11 06:52:59 lukem 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.21 2009/04/11 06:52:59 lukem 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 */
102 struct 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 
111 static daddr_t	blk, lastblk;
112 
113 static int	eflag = 0;
114 static int	fflag = 0;
115 static int	flags = 0;
116 static int	sbaddr = 0; /* counter for the LFS superblocks */
117 
118 static char	device[MAXPATHLEN];
119 static 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 
137 enum { NADA=0, VERBOSE=1, LABELS=2, BLOCKS=4 };
138 
139 /* FFS functions */
140 static void	ffs_printpart(struct sblockinfo *, int, size_t, int);
141 static void	ffs_scan(struct sblockinfo *, int);
142 static int	ffs_checkver(struct sblockinfo *);
143 /* LFS functions */
144 static void	lfs_printpart(struct sblockinfo *, int, int);
145 static void	lfs_scan(struct sblockinfo *, int);
146 /* common functions */
147 static void	usage(void) __dead;
148 static int	scan_disk(int, daddr_t, daddr_t, int);
149 
150 static int
151 ffs_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 
166 static void
167 ffs_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 
238 static void
239 ffs_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 
286 static void
287 lfs_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 
322 static void
323 lfs_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 
377 static int
378 scan_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 
431 static void
432 usage(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 
441 int
442 main(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