brant@manta.pha.pa.us (Brant Cheikes) (02/14/89)
Since there seems to be no available utility (AT&T-supplied or PD) to find the inodes corresponding to a given logical block, I have written "bf," the block-find utility. Usage is bf fs blk blk ..., e.g., "bf /dev/rfp002 14016 2354". For each block, if bf finds that it is allocated to a file, it will print the corresponding inode number. You may then use ncheck to go from the inode number to the filename. A very handy utility when investigating hard disk errors. Bf has passed lint, compiled with both pcc (on 3.51) and gcc 1.33, and passed a bunch of test cases. I think it's pretty clean. Any comments, questions, suggestions, (gasp) bug reports, etc., to me please. Brant -----snip-----snip-----snip-----snip-----snip-----snip-----snip-----snip----- /* * bf.c 1.0 * Search filesystem for particular blocks, returning corresponding inodes. * * Brant Cheikes * University of Pennsylvania, Department of Computer and Information Science * brant@manta.pha.pa.us, brant@linc.cis.upenn.edu, bpa!manta!brant * * Copyright (C) 1989 Brant A. Cheikes. All rights reserved. * * bf is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY. No author or distributor accepts responsibility to anyone * for the consequences of using it or for whether it serves any particular * purpose or works at all, unless he says so in writing. * * Everyone is granted permission to copy, modify and redistribute bf, * but only under the conditions described in the GNU General Public License. * Among other things, the copyright notice and this notice must be preserved * on all copies. */ #define KERNEL #include <stdio.h> #include <ctype.h> #include <fcntl.h> #include <sys/filsys.h> #include <sys/ino.h> #include <sys/inode.h> #include <sys/types.h> #include <sys/param.h> /* * Macros to manipulate block bitmap. * setmapbit(m,b) turns on the bit in bitmap m corresponding to block b. * inmap(m,b) evaluates to non-zero if block b's bit is on in map m. */ #define setmapbit(m,b) *(m+((int)(b)>>3)) |= (char)(1<<((b)&7)) #define inmap(m,b) (*(m+((b)>>3)) & (char)(1<<((b)&7))) extern char *calloc(); extern long lseek(); /* * Main program. */ main(argc,argv) int argc; char **argv; { int fsdev; /* file system device */ int nblk; /* number of blocks to find */ struct filsys superblk; /* the superblock <sys/filsys.h> */ char *bitmap; /* bitmap of allocated blocks */ void getsb(); /* * Verify we were given at least three arguments. */ if (argc < 3) { (void) fprintf(stderr,"Usage: %s filesystem numbers\n",*argv); exit(1); } /* * Open the filesystem and read the superblock. */ if ((fsdev = open(argv[1],O_RDONLY)) == EOF) { char errmsg[100]; (void) sprintf(errmsg,"cannot open %s",argv[1]); (void) perror(errmsg); exit(1); } getsb(fsdev,&superblk); /* * Initialize the bitmap. * * The bitmap consists of one bit for each block in the filesystem. * Bits in the bitmap are turned on for blocks that are being sought. */ bitmap = calloc((unsigned)((superblk.s_fsize>>3)+1),sizeof(char)); if (bitmap == (char *)NULL) { (void) perror("calloc failed in main"); exit(1); } /* * Read list of blocks from command line. */ nblk = argc-2; argc -= 2; argv += 2; for (; argc-- ; ++argv) { long blk; extern long atol(); blk = atol(*argv); if (blk == 0) { (void) puts("block 0 is superblock!"); --nblk; } else if (blk == 1) { (void) puts("block 1 reserved"); --nblk; } else if (blk < superblk.s_isize+3) { (void) printf("block %ld in i-list\n",blk); --nblk; } else if (blk > superblk.s_fsize) { (void) printf("block %ld outside fs\n",blk); --nblk; } else /* * Turn on the bit in the bitmap corresponding to this block. */ setmapbit(bitmap,blk); } /* * Verify that we still have blocks to look for. */ if (nblk == 0) { (void) puts("feh! no blocks"); exit(0); } /* * Scan the allocated block list. */ return(scan_ilist(fsdev,&superblk,nblk,bitmap)); } /* * Top-level loop of search. * * We scan thru the i-list, looking at inodes for regular, directory, or * fifo files. For those inodes, we scan the allocated block lists for * the blocks we're searching for. * * Returns non-zero on error. */ int scan_ilist(fp,superb,blks,map) int fp, blks; struct filsys *superb; register char *map; { int inoblk; /* block in ilist being scanned */ int inode; /* current inode */ int bcnt = 0; /* count of sought blocks found */ struct dinode inobuf[INOPB]; /* a block's worth of inodes <sys/ino.h> */ /* * Start at the root inode block (ROOTINO defined in <sys/param.h>). * Loop through each block in the ilist. */ for (inoblk = ROOTINO, inode = 1; inoblk < superb->s_isize; ++inoblk) { int i; /* * Read a block. If an error occurs (like a read failure), just * move on to the next block in the ilist. */ if (getblk((long)inoblk,(char *)inobuf,fp)) continue; /* * Loop through each inode in this block. */ for (i = 0; i < INOPB; ++i, ++inode) { if (inobuf[i].di_mode & IFREG || /* regular file */ inobuf[i].di_mode & IFIFO || /* fifo */ inobuf[i].di_mode & IFDIR) { /* directory */ if ((bcnt += scan_inode(fp,inobuf+i,inode,map)) == blks) return(0); else if (bcnt > blks) { /* * A rare but possible scenario. If we find more blocks than * we were looking for, that means that there's at least one * block allocated to more than one file. So print error * message and quit. */ (void) fprintf(stderr, "warning: multiply-allocated block found--run fsck\n"); return(1); } } } } return(0); } /* * Scan the list of allocated blocks in a particular inode. * * Input: * fp: pointer to open filesystem * inoptr: pointer to a disk inode * ino: # of inode pointed to by inoptr * map: pointer to block bitmap * * Returns: * number of blocks found */ int scan_inode(fp,inoptr,ino,map) register struct dinode *inoptr; int fp, ino; register char *map; { int i, bcnt = 0; static long daddr[13]; extern void l3tol(); /* * Convert the di_addr area to an array of block numbers. */ l3tol(daddr,inoptr->di_addr,13); /* * Scan array. */ for (i = 0; i < 13; ++i) { if (daddr[i] && inmap(map,daddr[i])) { (void) printf("block %ld inode %d\n",daddr[i],ino); ++bcnt; } /* * Scan indirect blocks. */ if (i > 9 && daddr[i]) bcnt += scan_iblock(fp,daddr[i],i-9,ino,map); } return(bcnt); } /* * Recursive scan of indirect blocks. * Returns number of sought blocks that were found. */ int scan_iblock(fp,blk,ilevel,ino,map) long blk; int fp, ilevel, ino; register char *map; { if (ilevel < 1 || ilevel > 3) { (void) fprintf(stderr,"bad ilevel (%d) in scan_iblock\n",ilevel); exit(1); } else { int i, bcnt = 0; long *ib; if ((ib = (long *)calloc((unsigned)BSIZE,sizeof(char))) == NULL) { perror("calloc failed in scan_iblock"); exit(1); } /* * Try to read the indirect block. If that succeeds, * check each block that it points to. */ if (getblk(blk,(char *)ib,fp) == 0) { for (i = 0; i < BSIZE/sizeof(long); ++i) { long iblk = *(ib+i); if (iblk) { if (inmap(map,iblk)) { (void) printf("block %ld inode %d\n",iblk,ino); ++bcnt; } if (ilevel > 1) bcnt += scan_iblock(fp,iblk,ilevel-1,ino,map); } } } (void) free((char *)ib); return(bcnt); } } /* * Read the superblock off the specified (open) device. */ void getsb(fp,superb) int fp; register struct filsys *superb; { /* * SUPERBOFF is offset in bytes of superblock in block 0 of filesystem. * It's defined in <sys/param.h> */ if (lseek(fp,(long)SUPERBOFF,0) < 0L) { perror("lseek failed in getsb"); exit(1); } if (read(fp,(char *)superb,sizeof(struct filsys)) < sizeof(struct filsys)) { perror("read error in getsb"); exit(1); } if (superb->s_magic != FsMAGIC) { (void) fprintf(stderr,"bad magic in superblock\n"); exit(1); } } /* * getblk(n,bufp,dev) * * Read logical block #n of device dev into area pointed to by bufp. * * NB: This uses the (portable) lseek() call rather than diddling the * disk controller directly. As a result it may be slow. */ int getblk(n,bufp,fp) long n; register char *bufp; int fp; { char errmsg[100]; /* * lseek errors are fatal. * * dbtob(x) defined in <sys/sysmacros.h> * disk blocks to bytes */ if (lseek(fp,dbtob(n),0) < 0L) { (void) sprintf(errmsg,"lseek to block %ld failed",n); (void) perror(errmsg); exit(1); } /* * read errors are reported, but do not cause termination. * * BSIZE is size of a logical block in bytes * defined in <sys/param.h> */ if (read(fp,bufp,BSIZE) < BSIZE) { (void) sprintf(errmsg,"read error (block %ld)",n); (void) perror(errmsg); return(1); } return(0); } -----snip-----snip-----snip-----snip-----snip-----snip-----snip-----snip----- -- Brant Cheikes University of Pennsylvania, Department of Computer and Information Science brant@manta.pha.pa.us, brant@linc.cis.upenn.edu, bpa!manta!brant