scan_ffs.c revision 1.18 1 /* $NetBSD: scan_ffs.c,v 1.18 2007/05/01 21:43:37 xtraeme Exp $ */
2
3 /*
4 * Copyright (c) 2005, 2006, 2007 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Juan Romero Pardines.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by Juan Romero Pardines
21 * for the NetBSD Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 /*
40 * Copyright (c) 1998 Niklas Hallqvist, Tobias Weingartner
41 * All rights reserved.
42 *
43 * Redistribution and use in source and binary forms, with or without
44 * modification, are permitted provided that the following conditions
45 * are met:
46 * 1. Redistributions of source code must retain the above copyright
47 * notice, this list of conditions and the following disclaimer.
48 * 2. Redistributions in binary form must reproduce the above copyright
49 * notice, this list of conditions and the following disclaimer in the
50 * documentation and/or other materials provided with the distribution.
51 *
52 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
53 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
54 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
55 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
56 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
57 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
58 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
59 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
60 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
61 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
62 */
63
64 /*
65 * Currently it can detect FFS and LFS partitions (version 1 or 2)
66 * up to 8192/65536 fragsize/blocksize.
67 */
68
69 #include <sys/cdefs.h>
70 #ifndef lint
71 __RCSID("$NetBSD: scan_ffs.c,v 1.18 2007/05/01 21:43:37 xtraeme Exp $");
72 #endif /* not lint */
73
74 #include <sys/types.h>
75 #include <sys/param.h>
76 #include <sys/disklabel.h>
77 #include <sys/dkio.h>
78 #include <sys/ioctl.h>
79 #include <sys/fcntl.h>
80 #include <sys/mount.h>
81
82 #include <ufs/ufs/dinode.h>
83 #include <ufs/lfs/lfs.h>
84 #include <ufs/lfs/lfs_extern.h>
85
86 /* Undefine macros defined by both lfs/lfs.h and ffs/fs.h */
87 #undef fsbtodb
88 #undef dbtofsb
89 #undef blkoff
90 #undef fragoff
91 #undef lblktosize
92 #undef lblkno
93 #undef numfrags
94 #undef blkroundup
95 #undef fragroundup
96 #undef fragstoblks
97 #undef blkstofrags
98 #undef fragnum
99 #undef blknum
100 #undef blksize
101 #undef INOPB
102 #undef INOPF
103 #undef NINDIR
104
105 #include <ufs/ffs/fs.h>
106
107 /* Undefine macros defined by both lfs/lfs.h and ffs/fs.h */
108 /* ...to make sure we don't later depend on their (ambigious) definition */
109 #undef fsbtodb
110 #undef dbtofsb
111 #undef blkoff
112 #undef fragoff
113 #undef lblktosize
114 #undef lblkno
115 #undef numfrags
116 #undef blkroundup
117 #undef fragroundup
118 #undef fragstoblks
119 #undef blkstofrags
120 #undef fragnum
121 #undef blknum
122 #undef blksize
123 #undef INOPB
124 #undef INOPF
125 #undef NINDIR
126
127 #include <unistd.h>
128 #include <stdlib.h>
129 #include <stdio.h>
130 #include <string.h>
131 #include <err.h>
132 #include <util.h>
133
134 #define BLK_CNT (blk + (n / 512))
135
136 /* common struct for FFS/LFS */
137 struct sblockinfo {
138 struct lfs *lfs;
139 struct fs *ffs;
140 uint64_t lfs_off;
141 uint64_t ffs_off;
142 char lfs_path[MAXMNTLEN];
143 char ffs_path[MAXMNTLEN];
144 };
145
146 static daddr_t blk, lastblk;
147
148 static int eflag = 0;
149 static int fflag = 0;
150 static int flags = 0;
151 static int sbaddr = 0; /* counter for the LFS superblocks */
152
153 static char device[MAXPATHLEN];
154 static const char *fstypes[] = { "NONE", "FFSv1", "FFSv2" };
155
156 #define FSTYPE_NONE 0
157 #define FSTYPE_FFSV1 1
158 #define FSTYPE_FFSV2 2
159
160 #define SBCOUNT 128 /* may be changed */
161 #define SBPASS (SBCOUNT * SBLOCKSIZE / 512)
162
163 /* This is only useful for LFS */
164
165 /* first sblock address contains the correct offset */
166 #define FIRST_SBLOCK_ADDRESS 1
167 /* second and third sblock address contain lfs_fsmnt[MAXMNTLEN] */
168 #define SECOND_SBLOCK_ADDRESS 2
169 /* last sblock address in a LFS partition */
170 #define MAX_SBLOCK_ADDRESS 10
171
172 enum { NADA=0, VERBOSE=1, LABELS=2, BLOCKS=4 };
173
174 /* FFS functions */
175 static void ffs_printpart(struct sblockinfo *, int, size_t, int);
176 static void ffs_scan(struct sblockinfo *, int);
177 static int ffs_checkver(struct sblockinfo *);
178 /* LFS functions */
179 static void lfs_printpart(struct sblockinfo *, int, int);
180 static void lfs_scan(struct sblockinfo *, int);
181 /* common functions */
182 static void usage(void) __attribute__((__noreturn__));
183 static int scan_disk(int, daddr_t, daddr_t, int);
184
185 static int
186 ffs_checkver(struct sblockinfo *sbi)
187 {
188 switch (sbi->ffs->fs_magic) {
189 case FS_UFS1_MAGIC:
190 case FS_UFS1_MAGIC_SWAPPED:
191 sbi->ffs->fs_size = sbi->ffs->fs_old_size;
192 return FSTYPE_FFSV1;
193 case FS_UFS2_MAGIC:
194 case FS_UFS2_MAGIC_SWAPPED:
195 return FSTYPE_FFSV2;
196 default:
197 return FSTYPE_NONE;
198 }
199 }
200
201 static void
202 ffs_printpart(struct sblockinfo *sbi, int flag, size_t ffsize, int n)
203 {
204 int offset, ver;
205
206 switch (flag) {
207 case VERBOSE:
208 switch (ffs_checkver(sbi)) {
209 case FSTYPE_FFSV1:
210 (void)printf("offset: %" PRIu64 " n: %d "
211 "id: %x,%x size: %" PRIu64 "\n",
212 BLK_CNT - (2 * SBLOCKSIZE / 512), n,
213 sbi->ffs->fs_id[0], sbi->ffs->fs_id[1],
214 (uint64_t)sbi->ffs->fs_size *
215 sbi->ffs->fs_fsize / 512);
216 break;
217 case FSTYPE_FFSV2:
218 (void)printf("offset: %" PRIu64 " n: %d "
219 "id: %x,%x size: %" PRIu64 "\n",
220 BLK_CNT - (ffsize * SBLOCKSIZE / 512+128),
221 n, sbi->ffs->fs_id[0], sbi->ffs->fs_id[1],
222 (uint64_t)sbi->ffs->fs_size *
223 sbi->ffs->fs_fsize / 512);
224 break;
225 default:
226 break;
227 }
228 break;
229 case LABELS:
230 (void)printf("X: %9" PRIu64,
231 (uint64_t)(sbi->ffs->fs_size *
232 sbi->ffs->fs_fsize / 512));
233 switch (ffs_checkver(sbi)) {
234 case FSTYPE_FFSV1:
235 (void)printf(" %9" PRIu64,
236 BLK_CNT - (ffsize * SBLOCKSIZE / 512));
237 break;
238 case FSTYPE_FFSV2:
239 (void)printf(" %9" PRIu64,
240 BLK_CNT - (ffsize * SBLOCKSIZE / 512 + 128));
241 break;
242 default:
243 break;
244 }
245 (void)printf(" 4.2BSD %6d %5d %7d # %s [%s]\n",
246 sbi->ffs->fs_fsize, sbi->ffs->fs_bsize,
247 sbi->ffs->fs_old_cpg,
248 sbi->ffs_path, fstypes[ffs_checkver(sbi)]);
249 break;
250 case BLOCKS:
251 default:
252 (void)printf("%s ", fstypes[ffs_checkver(sbi)]);
253 ver = ffs_checkver(sbi);
254 if (ver == FSTYPE_NONE)
255 break;
256
257 offset = 0;
258 if (flag == BLOCKS)
259 (void)printf("sb ");
260 else if (ver == FSTYPE_FFSV1)
261 offset = (2 * SBLOCKSIZE / 512);
262 else if (ver == FSTYPE_FFSV2)
263 offset = (ffsize * SBLOCKSIZE / 512 + 128);
264
265 (void)printf("at %" PRIu64, BLK_CNT - offset);
266 (void)printf(" size %" PRIu64 ", last mounted on %s\n",
267 (uint64_t)(sbi->ffs->fs_size *
268 sbi->ffs->fs_fsize / 512), sbi->ffs_path);
269 break;
270 }
271 }
272
273 static void
274 ffs_scan(struct sblockinfo *sbi, int n)
275 {
276 size_t i = 0;
277
278 if (flags & BLOCKS) {
279 ffs_printpart(sbi, BLOCKS, 0, n);
280 return;
281 }
282 if (flags & VERBOSE)
283 ffs_printpart(sbi, VERBOSE, NADA, n);
284 switch (ffs_checkver(sbi)) {
285 case FSTYPE_FFSV1:
286 /* fsize/bsize > 512/4096 and < 4096/32768. */
287 if ((BLK_CNT - lastblk) == (SBLOCKSIZE / 512)) {
288 i = 2;
289 /* fsize/bsize 4096/32768. */
290 } else if ((BLK_CNT - lastblk) == (SBLOCKSIZE / 170)) {
291 i = 4;
292 /* fsize/bsize 8192/65536 */
293 } else if ((BLK_CNT - lastblk) == (SBLOCKSIZE / 73)) {
294 i = 8;
295 } else
296 break;
297
298 if (flags & LABELS)
299 ffs_printpart(sbi, LABELS, i, n);
300 else
301 ffs_printpart(sbi, NADA, i, n);
302
303 break;
304 case FSTYPE_FFSV2:
305 /*
306 * That checks for FFSv2 partitions with fragsize/blocksize:
307 * 512/4096, 1024/8192, 2048/16384, 4096/32768 and 8192/65536.
308 * Really enough for now.
309 */
310 for (i = 1; i < 16; i <<= 1)
311 if ((BLK_CNT - lastblk) == (i * SBLOCKSIZE / 512)) {
312 if (flags & LABELS)
313 ffs_printpart(sbi, LABELS, i, n);
314 else
315 ffs_printpart(sbi, NADA, i, n);
316 }
317 break;
318 }
319 }
320
321 static void
322 lfs_printpart(struct sblockinfo *sbi, int flag, int n)
323 {
324 if (flags & VERBOSE)
325 (void)printf("offset: %" PRIu64 " size %" PRIu32
326 " fsid %" PRIx32 "\n", sbi->lfs_off, sbi->lfs->lfs_size,
327 sbi->lfs->lfs_ident);
328 switch (flag) {
329 case LABELS:
330 (void)printf("X: %9" PRIu64,
331 (uint64_t)(sbi->lfs->lfs_size *
332 sbi->lfs->lfs_fsize / 512));
333 (void)printf(" %9" PRIu64, sbi->lfs_off);
334 (void)printf(" 4.4LFS %6d %5d %7d # %s [LFSv%d]\n",
335 sbi->lfs->lfs_fsize, sbi->lfs->lfs_bsize,
336 sbi->lfs->lfs_nseg, sbi->lfs_path,
337 sbi->lfs->lfs_version);
338 break;
339 case BLOCKS:
340 (void)printf("LFSv%d", sbi->lfs->lfs_version);
341 (void)printf(" sb at %" PRIu64, sbi->lfs_off + btodb(LFS_LABELPAD));
342 (void)printf(" fsid %" PRIx32, sbi->lfs->lfs_ident);
343 (void)printf(" size %" PRIu64 ", last mounted on %s\n",
344 (uint64_t)(sbi->lfs->lfs_size *
345 sbi->lfs->lfs_fsize / 512), sbi->lfs_path);
346 break;
347 default:
348 (void)printf("LFSv%d ", sbi->lfs->lfs_version);
349 (void)printf("at %" PRIu64, sbi->lfs_off);
350 (void)printf(" size %" PRIu64 ", last mounted on %s\n",
351 (uint64_t)(sbi->lfs->lfs_size *
352 sbi->lfs->lfs_fsize / 512), sbi->lfs_path);
353 break;
354 }
355 }
356
357 static void
358 lfs_scan(struct sblockinfo *sbi, int n)
359 {
360 /* Check to see if the sb checksums correctly */
361 if (lfs_sb_cksum(&(sbi->lfs->lfs_dlfs)) != sbi->lfs->lfs_cksum) {
362 if (flags & VERBOSE)
363 printf("LFS bad superblock at %" PRIu64 "\n",
364 BLK_CNT);
365 return;
366 }
367
368 /* backup offset */
369 lastblk = BLK_CNT - (LFS_SBPAD / 512);
370 /* increment counter */
371 ++sbaddr;
372
373 if (flags & BLOCKS) {
374 sbi->lfs_off = BLK_CNT - btodb(LFS_LABELPAD);
375 lfs_printpart(sbi, BLOCKS, n);
376 return;
377 }
378
379 switch (sbaddr) {
380 /*
381 * first superblock contains the right offset, but lfs_fsmnt is
382 * empty... fortunately the next superblock address has it.
383 */
384 case FIRST_SBLOCK_ADDRESS:
385 /* copy partition offset */
386 if (sbi->lfs_off != lastblk)
387 sbi->lfs_off = BLK_CNT - (LFS_LABELPAD / 512);
388 break;
389 case SECOND_SBLOCK_ADDRESS:
390 /* copy the path of last mount */
391 (void)memcpy(sbi->lfs_path, sbi->lfs->lfs_fsmnt, MAXMNTLEN);
392 /* print now that we have the info */
393 if (flags & LABELS)
394 lfs_printpart(sbi, LABELS, n);
395 else
396 lfs_printpart(sbi, NADA, n);
397 /* clear our struct */
398 (void)memset(sbi, 0, sizeof(*sbi));
399 break;
400 case MAX_SBLOCK_ADDRESS:
401 /*
402 * reset the counter, this is the last superblock address,
403 * the next one will be another partition maybe.
404 */
405 sbaddr = 0;
406 break;
407 default:
408 break;
409 }
410 }
411
412 static int
413 scan_disk(int fd, daddr_t beg, daddr_t end, int fflags)
414 {
415 struct sblockinfo sbinfo;
416 uint8_t buf[SBLOCKSIZE * SBCOUNT];
417 int n;
418
419 n = 0;
420 lastblk = -1;
421
422 /* clear our struct before using it */
423 (void)memset(&sbinfo, 0, sizeof(sbinfo));
424
425 if (fflags & LABELS)
426 (void)printf(
427 "# size offset fstype [fsize bsize cpg/sgs]\n");
428
429 for (blk = beg; blk <= end; blk += SBPASS) {
430 if (pread(fd, buf, sizeof(buf), blk * 512) == -1) {
431 if (fflag && fd >= 0)
432 (void)close(fd);
433 err(1, "pread");
434 }
435
436 for (n = 0; n < (SBLOCKSIZE * SBCOUNT); n += 512) {
437 sbinfo.ffs = (struct fs *)&buf[n];
438 sbinfo.lfs = (struct lfs *)&buf[n];
439
440 switch (ffs_checkver(&sbinfo)) {
441 case FSTYPE_FFSV1:
442 case FSTYPE_FFSV2:
443 ffs_scan(&sbinfo, n);
444 lastblk = BLK_CNT;
445 (void)memcpy(sbinfo.ffs_path,
446 sbinfo.ffs->fs_fsmnt, MAXMNTLEN);
447 break;
448 case FSTYPE_NONE:
449 /* maybe LFS? */
450 if (sbinfo.lfs->lfs_magic == LFS_MAGIC)
451 lfs_scan(&sbinfo, n);
452 break;
453 default:
454 break;
455 }
456 }
457 }
458
459 if (fflag && fd >= 0)
460 (void)close(fd);
461
462 return EXIT_SUCCESS;
463 }
464
465
466 static void
467 usage(void)
468 {
469 (void)fprintf(stderr,
470 "Usage: %s [-blv] [-e end] [-F file] [-s start] "
471 "device\n", getprogname());
472 exit(EXIT_FAILURE);
473 }
474
475
476 int
477 main(int argc, char **argv)
478 {
479 int ch, fd;
480 const char *fpath;
481 daddr_t end = -1, beg = 0;
482 struct disklabel dl;
483
484 fpath = NULL;
485
486 setprogname(*argv);
487 while ((ch = getopt(argc, argv, "be:F:ls:v")) != -1)
488 switch(ch) {
489 case 'b':
490 flags |= BLOCKS;
491 flags &= ~LABELS;
492 break;
493 case 'e':
494 eflag = 1;
495 end = atoi(optarg);
496 break;
497 case 'F':
498 fflag = 1;
499 fpath = optarg;
500 break;
501 case 'l':
502 flags |= LABELS;
503 flags &= ~BLOCKS;
504 break;
505 case 's':
506 beg = atoi(optarg);
507 break;
508 case 'v':
509 flags |= VERBOSE;
510 break;
511 default:
512 usage();
513 /* NOTREACHED */
514 }
515
516 argc -= optind;
517 argv += optind;
518
519 if (fflag) {
520 struct stat stp;
521
522 if (stat(fpath, &stp))
523 err(1, "Cannot stat `%s'", fpath);
524
525 if (!eflag)
526 end = (uint64_t)stp.st_size;
527
528 (void)printf("Total file size: %" PRIu64 "\n\n",
529 (uint64_t)stp.st_size);
530
531 fd = open(fpath, O_RDONLY | O_DIRECT);
532 } else {
533 if (argc != 1)
534 usage();
535
536 fd = opendisk(argv[0], O_RDONLY, device, sizeof(device), 0);
537
538 if (ioctl(fd, DIOCGDINFO, &dl) == -1) {
539 warn("Couldn't retrieve disklabel");
540 (void)memset(&dl, 0, sizeof(dl));
541 dl.d_secperunit = 0x7fffffff;
542 } else {
543 (void)printf("Disk: %s\n", dl.d_typename);
544 (void)printf("Total sectors on disk: %" PRIu32 "\n\n",
545 dl.d_secperunit);
546 }
547 }
548
549 if (!eflag && !fflag)
550 end = dl.d_secperunit; /* default to max sectors */
551
552 if (fd == -1)
553 err(1, "Cannot open `%s'", device);
554 /* NOTREACHED */
555
556 return scan_disk(fd, beg, end, flags);
557 }
558