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