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