1 /* $NetBSD: scan_ffs.c,v 1.20 2007/12/15 19:44:47 perry Exp $ */
4 * Copyright (c) 2005-2007 Juan Romero Pardines
5 * Copyright (c) 1998 Niklas Hallqvist, Tobias Weingartner
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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.
30 * Currently it can detect FFS and LFS partitions (version 1 or 2)
31 * up to 8192/65536 fragsize/blocksize.
34 #include <sys/cdefs.h>
36 __RCSID("$NetBSD: scan_ffs.c,v 1.20 2007/12/15 19:44:47 perry Exp $");
39 #include <sys/types.h>
40 #include <sys/param.h>
41 #include <sys/disklabel.h>
43 #include <sys/ioctl.h>
44 #include <sys/fcntl.h>
45 #include <sys/mount.h>
47 #include <ufs/ufs/dinode.h>
48 #include <ufs/lfs/lfs.h>
49 #include <ufs/lfs/lfs_extern.h>
51 /* Undefine macros defined by both lfs/lfs.h and ffs/fs.h */
70 #include <ufs/ffs/fs.h>
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 */
99 #define BLK_CNT (blk + (n / 512))
101 /* common struct for FFS/LFS */
107 char lfs_path
[MAXMNTLEN
];
108 char ffs_path
[MAXMNTLEN
];
111 static daddr_t blk
, lastblk
;
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 */
118 static char device
[MAXPATHLEN
];
119 static const char *fstypes
[] = { "NONE", "FFSv1", "FFSv2" };
121 #define FSTYPE_NONE 0
122 #define FSTYPE_FFSV1 1
123 #define FSTYPE_FFSV2 2
125 #define SBCOUNT 128 /* may be changed */
126 #define SBPASS (SBCOUNT * SBLOCKSIZE / 512)
128 /* This is only useful for LFS */
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
137 enum { NADA
=0, VERBOSE
=1, LABELS
=2, BLOCKS
=4 };
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
*);
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);
151 ffs_checkver(struct sblockinfo
*sbi
)
153 switch (sbi
->ffs
->fs_magic
) {
155 case FS_UFS1_MAGIC_SWAPPED
:
156 sbi
->ffs
->fs_size
= sbi
->ffs
->fs_old_size
;
159 case FS_UFS2_MAGIC_SWAPPED
:
167 ffs_printpart(struct sblockinfo
*sbi
, int flag
, size_t ffsize
, int n
)
173 switch (ffs_checkver(sbi
)) {
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);
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);
195 (void)printf("X: %9" PRIu64
,
196 (uint64_t)(sbi
->ffs
->fs_size
*
197 sbi
->ffs
->fs_fsize
/ 512));
198 switch (ffs_checkver(sbi
)) {
200 (void)printf(" %9" PRIu64
,
201 BLK_CNT
- (ffsize
* SBLOCKSIZE
/ 512));
204 (void)printf(" %9" PRIu64
,
205 BLK_CNT
- (ffsize
* SBLOCKSIZE
/ 512 + 128));
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
)]);
217 (void)printf("%s ", fstypes
[ffs_checkver(sbi
)]);
218 ver
= ffs_checkver(sbi
);
219 if (ver
== FSTYPE_NONE
)
225 else if (ver
== FSTYPE_FFSV1
)
226 offset
= (2 * SBLOCKSIZE
/ 512);
227 else if (ver
== FSTYPE_FFSV2
)
228 offset
= (ffsize
* SBLOCKSIZE
/ 512 + 128);
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
);
239 ffs_scan(struct sblockinfo
*sbi
, int n
)
243 if (flags
& BLOCKS
) {
244 ffs_printpart(sbi
, BLOCKS
, 0, n
);
248 ffs_printpart(sbi
, VERBOSE
, NADA
, n
);
249 switch (ffs_checkver(sbi
)) {
251 /* fsize/bsize > 512/4096 and < 4096/32768. */
252 if ((BLK_CNT
- lastblk
) == (SBLOCKSIZE
/ 512)) {
254 /* fsize/bsize 4096/32768. */
255 } else if ((BLK_CNT
- lastblk
) == (SBLOCKSIZE
/ 170)) {
257 /* fsize/bsize 8192/65536 */
258 } else if ((BLK_CNT
- lastblk
) == (SBLOCKSIZE
/ 73)) {
264 ffs_printpart(sbi
, LABELS
, i
, n
);
266 ffs_printpart(sbi
, NADA
, i
, n
);
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.
275 for (i
= 1; i
< 16; i
<<= 1)
276 if ((BLK_CNT
- lastblk
) == (daddr_t
)(i
* SBLOCKSIZE
/ 512)) {
278 ffs_printpart(sbi
, LABELS
, i
, n
);
280 ffs_printpart(sbi
, NADA
, i
, n
);
287 lfs_printpart(struct sblockinfo
*sbi
, int flag
, int n
)
290 (void)printf("offset: %" PRIu64
" size %" PRIu32
291 " fsid %" PRIx32
"\n", sbi
->lfs_off
, sbi
->lfs
->lfs_size
,
292 sbi
->lfs
->lfs_ident
);
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
);
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
);
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
);
323 lfs_scan(struct sblockinfo
*sbi
, int n
)
325 /* Check to see if the sb checksums correctly */
326 if (lfs_sb_cksum(&(sbi
->lfs
->lfs_dlfs
)) != sbi
->lfs
->lfs_cksum
) {
328 printf("LFS bad superblock at %" PRIu64
"\n",
334 lastblk
= BLK_CNT
- (LFS_SBPAD
/ 512);
335 /* increment counter */
338 if (flags
& BLOCKS
) {
339 sbi
->lfs_off
= BLK_CNT
- btodb(LFS_LABELPAD
);
340 lfs_printpart(sbi
, BLOCKS
, n
);
346 * first superblock contains the right offset, but lfs_fsmnt is
347 * empty... fortunately the next superblock address has it.
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);
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 */
359 lfs_printpart(sbi
, LABELS
, n
);
361 lfs_printpart(sbi
, NADA
, n
);
362 /* clear our struct */
363 (void)memset(sbi
, 0, sizeof(*sbi
));
365 case MAX_SBLOCK_ADDRESS
:
367 * reset the counter, this is the last superblock address,
368 * the next one will be another partition maybe.
378 scan_disk(int fd
, daddr_t beg
, daddr_t end
, int fflags
)
380 struct sblockinfo sbinfo
;
381 uint8_t buf
[SBLOCKSIZE
* SBCOUNT
];
387 /* clear our struct before using it */
388 (void)memset(&sbinfo
, 0, sizeof(sbinfo
));
392 "# size offset fstype [fsize bsize cpg/sgs]\n");
394 for (blk
= beg
; blk
<= end
; blk
+= SBPASS
) {
395 if (pread(fd
, buf
, sizeof(buf
), blk
* 512) == -1) {
396 if (fflag
&& fd
>= 0)
401 for (n
= 0; n
< (SBLOCKSIZE
* SBCOUNT
); n
+= 512) {
402 sbinfo
.ffs
= (struct fs
*)&buf
[n
];
403 sbinfo
.lfs
= (struct lfs
*)&buf
[n
];
405 switch (ffs_checkver(&sbinfo
)) {
408 ffs_scan(&sbinfo
, n
);
410 (void)memcpy(sbinfo
.ffs_path
,
411 sbinfo
.ffs
->fs_fsmnt
, MAXMNTLEN
);
415 if (sbinfo
.lfs
->lfs_magic
== LFS_MAGIC
)
416 lfs_scan(&sbinfo
, n
);
424 if (fflag
&& fd
>= 0)
434 (void)fprintf(stderr
,
435 "Usage: %s [-blv] [-e end] [-F file] [-s start] "
436 "device\n", getprogname());
442 main(int argc
, char **argv
)
446 daddr_t end
= -1, beg
= 0;
452 while ((ch
= getopt(argc
, argv
, "be:F:ls:v")) != -1)
487 if (stat(fpath
, &stp
))
488 err(1, "Cannot stat `%s'", fpath
);
491 end
= (uint64_t)stp
.st_size
;
493 (void)printf("Total file size: %" PRIu64
"\n\n",
494 (uint64_t)stp
.st_size
);
496 fd
= open(fpath
, O_RDONLY
| O_DIRECT
);
501 fd
= opendisk(argv
[0], O_RDONLY
, device
, sizeof(device
), 0);
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;
508 (void)printf("Disk: %s\n", dl
.d_typename
);
509 (void)printf("Total sectors on disk: %" PRIu32
"\n\n",
514 if (!eflag
&& !fflag
)
515 end
= dl
.d_secperunit
; /* default to max sectors */
518 err(1, "Cannot open `%s'", device
);
521 return scan_disk(fd
, beg
, end
, flags
);