[Note: I am cross-posting to comp.unix.admin because this program may
be useful in restoring files from corrupted System V file systems. ]
Two weeks ago, I posted a program that generated a file's contents from
an inode number and file system device file, using the block numbers
stored in the inode. Unfortunately, I have discovered a bug in the
handling of the 2nd and 3rd indirect blocks. I am reposting the entire
program. I don't have a copy of the original posted source code.
I have now tested the program on every file on my system, as well as
one sparse file to test triple indirection. The new program uses
l3tol, as recommended by several posters. It now allows the start byte
and length to be optional, and if they are not provided, the entire
file is piped to the standard output.
The program was designed on AT&T 386 Unix SVr3.1, but should work on
SVr3.2 or earlier System V(?). To test it on your /usr file system,
try this:
cd /usr
ncheck -a /dev/usr | grep -v 'FIFO' | grep -v '/dev/' | while read LINE
do
echo "$LINE"
fslook /dev/usr `echo "$LINE" | cut -d\ -f1` | \
cmp -s - /usr/`echo "$LINE" | cut -d\ -f2`
[ "$?" -ne 0 ] && echo "$LINE" >>/tmp/usr.bad
done
It will display each file as it tests it, and put the bad ones in the
file /tmp/usr.bad.
For administrators, this program could be used to recover files from a
file system whose super block is trashed, or whose directory structure
is messed up. For best recovery, you should have a copy of a recent
"ncheck" for each file system, as well as an "ls -liR" for file
ownership and file modes. Obviously, if you keep those files only on
the file system that was trashed, you will not be able to get at them
easily.
My apologies for initially submitting a buggy program. I should have
checked it better. Also, my apologies for those of you in
comp.unix.internals who are tired of hearing about this trivial program
I wrote. :-)
I have assumed this program is too trivial to be posted to a source
group. I figured, if it was generally useful, someone would have
already posted it. Please e-mail your opinions of this decision.
------------- cut here -----------
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
# fslook.c
# This archive created: Mon Dec 30 22:44:53 1991
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'fslook.c'" '(5525 characters)'
if test -f 'fslook.c'
then
echo shar: "will not over-write existing file 'fslook.c'"
else
cat << \SHAR_EOF > 'fslook.c'
/*************************************************************************
**
** fslook.c - displays data from a file given it's inode
** by Bruce Momjian ( root%candle.u...@ls.com )
**
**************************************************************************/
#include <stdio.h>
#include <malloc.h>
#include <unistd.h>
#include <signal.h>
#include <ctype.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/fs/s5param.h> /* for INOPB */
#include <sys/ino.h> /* for dinode structure */
#include <errno.h>
#define USAGE fprintf(stderr,\
"Usage: %s file_system_device inode_number [start_byte] [length]\n",argv[0]);
/* assumes 3 bytes per disk block address in inode */
#define INOBLOCK(b,n) ( l3block( (b) + (n) * 3 ) )
/* assumes 4 bytes per disk block address in inode */
#define INDBLOCK(b,n) ( *(long *)((b) + (n) * 4) )
#ifndef BSIZE
#define BSIZE 1024 /* block size in bytes */
#endif
#ifndef INOPB
#define INOPB (BSIZE / sizeof(dinode))
#endif
long l3block();
int main(argc,argv)
int argc;
char *argv[];
{
int curquit(),
offset,
infd;
unsigned inode_num;
unsigned long start,
length;
unsigned char *buf;
char *badchar;
struct dinode *ino;
if ( argc < 3 || argc > 5 )
{
USAGE;
exit(1);
}
if ( (infd = open(argv[1],O_RDONLY)) == -1)
{
perror("Can not open device file");
exit(1);
}
buf = (unsigned char *) malloc(BSIZE);
if ( buf == NULL)
{
fputs("No memory\n", stderr);
exit(1);
}
inode_num = strtol(argv[2],&badchar, 0);
if (*badchar != '\0')
{
fputs("Invalid inode number.\n", stderr);
USAGE;
exit(1);
}
if (argc >=4)
{
start = strtol(argv[3],&badchar, 0);
if (*badchar != '\0')
{
fputs("Invalid start byte.\n", stderr);
USAGE;
exit(1);
}
}
else
start = 0;
if (argc >=5)
{
length = strtol(argv[4],&badchar, 0);
if (*badchar != '\0')
{
fputs("Invalid length byte.\n", stderr);
USAGE;
exit(1);
}
}
else
{
get_inode(infd, inode_num, &ino, buf);
length = ino->di_size;
}
signal(SIGINT, curquit);
while (length > 0)
{
if (start / BSIZE == (start + length - 1) / BSIZE)
offset = length;
else
offset = BSIZE - start % BSIZE;
index_block(infd, inode_num, start, buf);
print_block(buf, start, offset);
length -= offset;
start += offset;
}
return(0);
}
/************************************************************************
**
** index_block
**
*************************************************************************/
index_block(infd, inode_num, start, buf)
int infd;
unsigned inode_num;
unsigned long start;
unsigned char *buf;
{
struct dinode *ino;
get_inode(infd, inode_num, &ino, buf);
if (start / BSIZE < 10) /* 10 DIRECT BLOCKS */
get_block(infd, INOBLOCK(ino->di_addr,start / BSIZE), buf);
else
{
start -= BSIZE * 10;
if ( start < BSIZE * (BSIZE / 4) ) /* INDIRECT BLOCKS */
{
get_block(infd, INOBLOCK(ino->di_addr, 10), buf);
get_block(infd, INDBLOCK(buf, start / BSIZE), buf);
}
else
{
start -= BSIZE * (BSIZE / 4);
if (start < BSIZE * (BSIZE / 4) * (BSIZE / 4) )/* DOUBLE INDIRECT*/
{
get_block(infd, INOBLOCK(ino->di_addr, 11), buf);
get_block(infd, INDBLOCK(buf, start / (BSIZE/4*BSIZE)), buf);
start %= BSIZE / 4 * BSIZE;
get_block(infd, INDBLOCK(buf, start / BSIZE), buf);
}
else
{ /* TRIPLE INDIRECT */
start -= BSIZE * (BSIZE / 4) * ( BSIZE / 4 );
get_block(infd, INOBLOCK(ino->di_addr, 12), buf);
get_block(infd, INDBLOCK(buf, start / (BSIZE/4*BSIZE/4*BSIZE)),buf);
start %= BSIZE / 4 * BSIZE / 4 * BSIZE;
get_block(infd, INDBLOCK(buf, start / (BSIZE/4*BSIZE)), buf);
start %= BSIZE / 4 * BSIZE;
get_block(infd, INDBLOCK(buf, start / BSIZE), buf);
}
}
}
}
/************************************************************************
**
** get_inode
**
*************************************************************************/
get_inode(infd, inode_num, ino, buf )
int infd;
unsigned inode_num;
struct dinode **ino;
unsigned char *buf;
{
inode_num--; /* inodes start at num - 1 */
get_block(infd, (daddr_t)(inode_num / INOPB + 2), buf);
*ino = (struct dinode *)buf + (inode_num % INOPB);
}
/*************************************************************************
**
** print_block
**
**************************************************************************/
print_block( buf, start, offset)
int offset;
unsigned long start;
unsigned char *buf;
{
int cnt = 0;
while (cnt < offset )
printf("%c", buf[start % BSIZE + cnt++]);
}
/**************************************************************************
*
**
** get_block
**
****************************************************************************/
get_block(infd, block, buf)
int infd;
long block;
unsigned char *buf;
{
int bufsize;
if (lseek(infd, block * BSIZE, SEEK_SET) == -1)
{
perror("Can not seek in file");
exit(1);
}
if( (bufsize = read(infd, buf, BSIZE )) == -1 || bufsize != BSIZE)
{
perror("Can not read from file");
exit(1);
}
}
/****************************************************************************
**
** l3block
**
*****************************************************************************/
long l3block(blockptr)
char *blockptr;
{
long ret;
l3tol(&ret, blockptr, 1);
return ret;
}
/****************************************************************************
**
** cur_quit
**
*****************************************************************************/
int curquit()
{
exit(0);
}
SHAR_EOF
fi
exit 0
# End of shell archive
--
Bruce Momjian | 830 Blythe Avenue | home: (215)853-3000
root%candle.u...@ls.com | Drexel Hill, Pennsylvania 19026 | work: (215)353-9879