Subject: map block # to inode in fsdb
To: None <tech-userlevel@netbsd.org>
From: Manuel Bouyer <bouyer@antioche.lip6.fr>
List: tech-userlevel
Date: 04/17/2003 22:53:47
--h31gzZEtNLTqOjlF
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

Hi,
As I got a disk wich developed bad blocks, I've got the need (like other
peoples in the same situation) to find the files owning these blocks.
So I added a findblk command to fsdb, which maps a disk block back to
the inode (or range of inodes if it's an inode block). As this needs to
look at all the inodes of the filesystem, this can be quite long, so
you can specify multiple blocks numbers in the same findblk command.
The output looks like:
fsdb (inum: 2)> findblk 64 96 2176
block 64: inode block (0-64)
2176: data block of inode 2
block 96: inode block (128-192)

The attached patch is against 1.6.1. I'll port it to current (which I guess
is a bit different because of UFS2) after getting comments, improvements,
etc ..

--
Manuel Bouyer, LIP6, Universite Paris VI.           Manuel.Bouyer@lip6.fr
     NetBSD: 24 ans d'experience feront toujours la difference
--

--h31gzZEtNLTqOjlF
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="diff.fsdb"

? fsdb
? fsdb.cat8
Index: fsdb.8
===================================================================
RCS file: /cvsroot/src/sbin/fsdb/fsdb.8,v
retrieving revision 1.13
diff -u -r1.13 fsdb.8
--- fsdb.8	2002/02/08 01:30:43	1.13
+++ fsdb.8	2003/04/17 20:52:17
@@ -128,6 +128,10 @@
 .Pp
 .It Cm blks
 List the current inode's blocks numbers.
+.It Cm findblk Ar disk block number ...
+Find the inode(s) owning the specifed disk block(s) number(s).
+Note that these are not absolute disk blocks numbers, but offsets from the
+start or the partition.
 .Pp
 .It Cm rm Ar name
 .It Cm del Ar name
Index: fsdb.c
===================================================================
RCS file: /cvsroot/src/sbin/fsdb/fsdb.c,v
retrieving revision 1.20
diff -u -r1.20 fsdb.c
--- fsdb.c	2002/03/18 20:04:49	1.20
+++ fsdb.c	2003/04/17 20:52:18
@@ -79,6 +79,10 @@
 static int dotime __P((char *, int32_t *, int32_t *));
 static void print_blks __P((ufs_daddr_t *buf, int size, int *blknum));
 static void print_indirblks __P((daddr_t blk, int ind_level, int *blknum));
+static int compare_blk __P((ufs_daddr_t *, ufs_daddr_t));
+static int founddatablk __P((ufs_daddr_t));
+static int find_blks __P((ufs_daddr_t *buf, int size, ufs_daddr_t *blknum));
+static int find_indirblks __P((daddr_t blk, int ind_level, ufs_daddr_t *blknum));
 
 int     returntosingle = 0;
 struct dinode *curinode;
@@ -154,6 +158,7 @@
 CMDFUNC(quit);			/* quit */
 CMDFUNC(ls);			/* list directory */
 CMDFUNC(blks);			/* list blocks */
+CMDFUNC(findblk);		/* find block */
 CMDFUNC(rm);			/* remove name */
 CMDFUNC(ln);			/* add name */
 CMDFUNC(newtype);		/* change type */
@@ -185,6 +190,7 @@
 	{"linkcount", "Set link count to COUNT", 2, 2, linkcount},
 	{"ls", "List current inode as directory", 1, 1, ls},
 	{"blks", "List current inode's data blocks", 1, 1, blks},
+	{"findblk", "Find inode owning disk block(s)", 2, 33, findblk},
 	{"rm", "Remove NAME from current inode directory", 2, 2, rm},
 	{"del", "Remove NAME from current inode directory", 2, 2, rm},
 	{"ln", "Hardlink INO into current inode directory as NAME", 3, 3, ln},
@@ -457,12 +463,158 @@
 	print_blks(curinode->di_db, NDADDR, &blkno);
 	for (i = 0; i < NIADDR; i++) {
 		if (curinode->di_ib[i] != 0)
-			print_indirblks(iswap32(curinode->di_ib[i]), i,
-			    &blkno);
+			print_indirblks(iswap32(curinode->di_ib[i]), i, &blkno);
 	}
 	return 0;
 }
 
+static int findblk_numtofind;
+CMDFUNCSTART(findblk)
+{
+	ino_t   inum;
+	ufs_daddr_t *wantedblk;
+	int i;
+
+	ocurrent = curinum;
+	wantedblk = malloc(sizeof(ufs_daddr_t) * (argc - 1));
+	if (wantedblk == NULL) {
+		perror("malloc");
+		return 1;
+	}
+	memset(wantedblk, 0, sizeof(ufs_daddr_t) * (argc - 1));
+	for (i = 1; i < argc; i++)
+		wantedblk[i - 1] = dbtofsb(sblock, strtoull(argv[i], NULL, 0)); 
+	findblk_numtofind = argc - 1;
+	for (inum = 2; inum < sblock->fs_ipg * sblock->fs_ncg ; inum++)
+	{
+		if (compare_blk(wantedblk, ino_to_fsba(sblock, inum))) {
+			printf("block %llu: inode block (%d-%d)\n",
+			    (unsigned long long)fsbtodb(sblock,
+				ino_to_fsba(sblock, inum)),
+			    (inum / INOPB(sblock)) * INOPB(sblock),
+			    (inum / INOPB(sblock) + 1) * INOPB(sblock));
+			    findblk_numtofind--;
+			    if (findblk_numtofind == 0)
+				goto end;
+		}
+		curinum = inum;
+		curinode = ginode(inum);
+		switch (iswap16(curinode->di_mode) & IFMT) {
+		case IFDIR:
+		case IFREG:
+			break;
+		case IFLNK:
+			if (iswap64(curinode->di_size) > 0 &&
+			    iswap64(curinode->di_size) < MAXSYMLINKLEN &&
+			    curinode->di_blocks == 0)
+				continue;
+			else
+				break;
+		default:
+			continue;
+		}
+		if (find_blks(curinode->di_db, NDADDR, wantedblk)) {
+			goto end;
+		}
+		for (i = 0; i < NIADDR; i++) {
+			if (compare_blk(wantedblk,
+			    iswap32(curinode->di_ib[i]))) {
+				if (founddatablk(iswap32(curinode->di_ib[i])))
+					goto end;
+			}
+			if (curinode->di_ib[i] != 0)
+				if (find_indirblks(iswap32(curinode->di_ib[i]),
+				    i, wantedblk)) {
+						goto end;
+				} 
+		}
+	}
+end:
+	free(wantedblk);
+	curinum = ocurrent;
+	curinode = ginode(curinum);
+	return 0;
+}
+
+static int
+compare_blk(wantedblk, curblk)
+	ufs_daddr_t *wantedblk;
+	ufs_daddr_t curblk;
+{
+	int i;
+	for (i = 0; i < 32; i++) {
+		if (wantedblk[i] != 0 && wantedblk[i] == curblk) {
+			wantedblk[i] = 0;
+			return 1;
+		}
+	}
+	return 0;
+}
+
+static int
+founddatablk(blk)
+	ufs_daddr_t blk;
+{
+	printf("%llu: data block of inode %d\n",
+	    (unsigned long long)fsbtodb(sblock, blk), curinum);
+	findblk_numtofind--;
+	if (findblk_numtofind == 0)
+		return 1;
+	return 0;
+}
+
+static int
+find_blks(buf, size, wantedblk)
+	ufs_daddr_t *buf;
+	int size;
+	ufs_daddr_t *wantedblk;
+	
+{
+	int blk;
+	for(blk = 0; blk < size; blk++) {
+		if (buf[blk] == 0)
+			continue;
+		if (compare_blk(wantedblk, iswap32(buf[blk]))) {
+			if (founddatablk(iswap32(buf[blk])))
+				return 1;
+		}
+	}
+	return 0;
+}
+
+static int
+find_indirblks(blk, ind_level, wantedblk)
+	daddr_t blk;
+	int ind_level;
+	ufs_daddr_t *wantedblk;
+{
+#define MAXNINDIR	(MAXBSIZE / sizeof(daddr_t))
+	daddr_t idblk[MAXNINDIR];
+	int i;
+
+	bread(fsreadfd, (char *)idblk, fsbtodb(sblock, blk),
+	    (int)sblock->fs_bsize);
+	if (ind_level <= 0) {
+		if (find_blks(idblk,
+		    sblock->fs_bsize / sizeof(daddr_t), wantedblk))
+			return 1;
+	} else {
+		ind_level--;
+		for (i = 0; i < sblock->fs_bsize / sizeof(daddr_t); i++) {
+			if (compare_blk(wantedblk, iswap32(idblk[i]))) {
+				if (founddatablk(iswap32(idblk[i])))
+					return 1;
+			}
+			if(idblk[i] != 0)
+				if (find_indirblks(iswap32(idblk[i]),
+				    ind_level, wantedblk))
+				return 1;
+		}
+	}
+#undef MAXNINDIR
+	return 0;
+}
+
 static void
 print_blks(buf, size, blknum)
 	ufs_daddr_t *buf;
@@ -493,7 +645,7 @@
 }
 
 static void
-print_indirblks(blk,ind_level, blknum)
+print_indirblks(blk, ind_level, blknum)
 	daddr_t blk;
 	int ind_level;
 	int *blknum;
@@ -501,7 +653,7 @@
 #define MAXNINDIR	(MAXBSIZE / sizeof(daddr_t))
 	daddr_t idblk[MAXNINDIR];
 	int i;
- 
+
 	printf("Indirect block %d (level %d):\n", blk, ind_level+1);
 	bread(fsreadfd, (char *)idblk, fsbtodb(sblock, blk),
 	    (int)sblock->fs_bsize);

--h31gzZEtNLTqOjlF--