/* * Program to walk a 4.2ish filesystem and print the relative * block numbers of all inodes, directories, and files. * * This appears to more or less be the only way to translate a block * number into a file (e.g. if you have a failing disk with a bad * block and want to find out what file has the problem) * * Only compiled and tested (not much) on SunOS 4.x. * Hopefully would not take much porting to Solaris or * some 4.4 filesystem. * * Copyright (c) 1998 Seth Robertson * All rights reserved */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DI_BSIZE sizeof(struct dinode) #define DB2OFF(x) (x*dev_bsize) #define MAXNINDIR (MAXBSIZE / sizeof(daddr_t)) /* List of work we have to do in LIFO order */ struct todo { char *path; int inum; struct todo *next; } *morehead = NULL; int dev_bsize = DEV_BSIZE; /* Fundamental block size */ int diskfd = -1; char sbbuf[2048]; /* superblock (storage) */ struct fs *sblock = NULL; /* superblock (ptr to) */ int TRY = 9; /* How often we should retry */ extern char *sys_errlist[]; /* Baka sunos */ /* * entry */ main(int argc, char *argv[]) { int tmpvar; int getopterr = 0; extern char *optarg; extern int optind; char *rawdisk = NULL; int start_inode = ROOTINO; off_t superblock = SBOFF; while ((tmpvar = getopt(argc,argv, "f:i:s:t:")) != -1) switch(tmpvar) { case 'f': rawdisk=optarg; break; case 'i': start_inode = atoi(optarg); break; case 't': TRY = atoi(optarg); break; case 's': superblock = atoi(optarg); break; case '?': getopterr++; } if (getopterr || !rawdisk) { fprintf(stderr,"Usage: %s -f [-i ] [-s ] [-t TRYCOUNT]\n", argv[0]); exit(2); } if ((diskfd = open(rawdisk, O_RDONLY)) < 0) die("could not open raw disk"); /* Read the (possibly alternate) superblock */ if (bread(diskfd, superblock, sbbuf, SBSIZE, "SUPERBLOCK") < 0) die("Could not read super block"); sblock = (struct fs *)(sbbuf); if (sblock->fs_magic != FS_MAGIC) die("Not a superblock!"); /* Prime the work stack */ moretodo(".",start_inode); /* While there is work */ while(morehead) { struct todo *more = morehead; morehead = more->next; do_inode(diskfd, more->path, more->inum); free(more->path); free(more); } fprintf(stderr,"Done\n"); exit(0); } /* * Get the requested inode and look at all of the * associated disk blocks */ int do_inode(int fd, char *path, int inum) { char *blk_buffer; struct dinode *dip; int n; off_t rfilesize; #if 0 fprintf(stderr,"Doing %s@%d\n",path,inum); #endif if (!(blk_buffer = malloc(sblock->fs_bsize))) die("malloc devblock"); /* Read the disk inode */ if (bread(fd, DB2OFF(fsbtodb(sblock,itod(sblock, inum))), blk_buffer, sblock->fs_bsize, path) < 0) { fprintf(stderr,"Could not do directory %s\n",path); return(0); } dip = (struct dinode *)(blk_buffer + DI_BSIZE * itoo(sblock, inum)); #if 0 if ((dip->di_mode & IFMT) != IFREG && (dip->di_mode & IFMT) != IFLNK && (dip->di_mode & IFMT) != IFDIR) { #if 0 fprintf(stderr,"What can I do with a %d %s\n",dip->di_mode,path); #endif return(0); } #endif /* * Filesize is important for directories. Disk * directories are only initialized to the next * highest fundamental disk block size, so * any space above that which may be allocated * because of disk policy MUST be ignored. * * (well, filesize is important for files as well, but I * am not actually reading files at the moment :-) */ rfilesize = roundup(dip->di_size, dev_bsize); fprintf(stdout,"%s: ",path); /* Look at all direct blocks */ for (n=0; n < NDADDR; n++) { /* Look at this block if is allocated */ if (dip->di_db[n] != 0) do_direct(fd, path, dip, dip->di_db[n], dblksize(sblock, dip, n), rfilesize); rfilesize -= dblksize(sblock, dip, n); } /* Look at all indirect blocks */ for (n=0; n < NIADDR; n++) { /* Look at this indirect block if it is allocated */ if (dip->di_ib[n] != 0) do_indirect(fd, path, dip, dip->di_ib[n],n, &rfilesize); } fprintf(stdout,"\n"); } /* * Look at this direct block (decode it if is a directory) */ int do_direct(int fd, char *path, struct dinode *dip, off_t diskblock, size_t unitsize, size_t rfilesize) { char dblk[MAXBSIZE]; struct direct *dp, *enddblk; char newpath[MAXPATHLEN]; /* Output what disk block this is */ fprintf(stdout,"%d ",diskblock); if ((dip->di_mode & IFMT) != IFDIR) { /* No actual non-directory stuff at this point */ return(0); } /* Read this direct block */ if (bread(fd, DB2OFF(fsbtodb(sblock, diskblock)), dblk, unitsize, path) < 0) { fprintf(stderr,"Cannot parse direct directory entry for %s\n",path); return(0); } /* Compute the ending location of the data I am interested in */ enddblk = (struct direct *)(dblk + MIN(unitsize,rfilesize)); /* For every direct directory entry */ for (dp=(struct direct *)dblk; dp < enddblk;dp=(struct direct *)(((char *)dp) + dp->d_reclen)) { if (dp->d_reclen == 0) { fprintf(stderr,"Corrupted directory %s %x %x %x\n",path,dblk,dp,enddblk); break; } if (dp->d_ino == 0) /* Unallocated */ continue; /* Don't want to look at myself or my parent again! */ if (!strcmp(dp->d_name,".") || !strcmp(dp->d_name,"..")) continue; #ifdef SNPRINTF snprintf(newpath,MAXPATHLEN,"%s/%s",path,dp->d_name); #else sprintf(newpath,"%s/%s",path,dp->d_name); #endif /* Add the directory entry to the todo list */ moretodo(newpath,dp->d_ino); } } /* * Handle an N level indirect entry (recursive) */ int do_indirect(int fd, char *path, struct dinode *dip, off_t diskblock, int indlev, int *rfilesize) { daddr_t indirect_block[MAXNINDIR]; int n; /* Read indirect block */ bread(fd, DB2OFF(fsbtodb(sblock, diskblock)), indirect_block, sblock->fs_bsize, path); /* We have gotten down to direct entries */ if (indlev <= 0) { for (n=0;nfs_bsize, *rfilesize); *rfilesize -= sblock->fs_bsize; } } else { /* Still indirect, go down a level and try some more */ for (n=0;n 0) { if (lseek(fd, loc, L_SET) < 0) { fprintf(stderr,"seek failed to %ld for %s: %s\n",loc,path,sys_errlist[errno]); if (try < 1) return(-1); try--; continue; } if ((n = read(fd, buf, size)) < 0) { fprintf(stderr,"read at %ld of %d for %s: %s\n",loc,size,path,sys_errlist[errno]); if (try < 1) return(-1); try--; continue; } if (n == size) break; fprintf(stderr,"Short read at %ld of %d for %s\n",loc, size, path); } return(n); } /* * Exit while printing an error message */ int die(char *msg) { perror(msg); exit(1); } /* * Add a directory inode to the list * of things to check (prepend to stack) */ int moretodo(char *path, int inum) { struct todo *more; if (!(more = malloc(sizeof(struct todo)))) die("Malloc todo"); if (!(more->path = strdup(path))) die("strdup path"); more->inum = inum; more->next = morehead; morehead = more; return(0); }