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