XFrom: root@candle.uucp (Bruce Momjian)
Newsgroups: comp.unix.internals
Subject: Generating files from inodes (Was Re: BSD inodes vs. SYSV inodes?)
Keywords: direct blocks, indirect blocks, fragments
Date: 31 Dec 91 03:49:42 GMT
Organization: private system
X
[Note:  I am cross-posting to comp.unix.admin because this program may
be useful in restoring files from corrupted System V file systems. ]
X
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.
X
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.
X 
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: 
X
cd /usr
ncheck -a /dev/usr | grep -v 'FIFO' | grep -v '/dev/' | while read LINE
do
X	echo "$LINE"
X	fslook /dev/usr `echo "$LINE" | cut -d\	  -f1` | \
X		cmp -s - /usr/`echo "$LINE" | cut -d\	 -f2` 
X	[ "$?" -ne 0 ] && echo "$LINE" >>/tmp/usr.bad
done
X
It will display each file as it tests it, and put the bad ones in the
file /tmp/usr.bad.
X
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. 
X
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. :-)
X
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.
X
[...]
-- 
Jonathan Kamens						jik@MIT.Edu
MIT Information Systems/Athena		    Moderator, news.answers
X	      (Send news.answers-related correspondence
X		  to news-answers-request@MIT.Edu.)
SHAR_EOF
chmod 0664 POSTER ||
echo 'restore of POSTER failed'
Wc_c="`wc -c < 'POSTER'`"
test 3618 -eq "$Wc_c" ||
	echo 'POSTER: original size 3618, current size' "$Wc_c"
fi
# ============= fslook.c ==============
if test -f 'fslook.c' -a X"$1" != X"-c"; then
	echo 'x - skipping fslook.c (File already exists)'
else
echo 'x - extracting fslook.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'fslook.c' &&
/*************************************************************************
**
** fslook.c - displays data from a file given it's inode
**	by Bruce Momjian ( root%candle.uucp@ls.com )
**
**************************************************************************/
X
#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>
X
#define USAGE 		fprintf(stderr,\
X							"Usage: %s file_system_device inode_number [start_byte] [length]\n",argv[0]);
X
X						/* assumes 3 bytes per disk block address in inode */
#define INOBLOCK(b,n)	( l3block( (b) + (n) * 3 ) ) 
X						/* assumes 4 bytes per disk block address in inode */
#define INDBLOCK(b,n)	( *(long *)((b) + (n) * 4) )	
X
#ifndef BSIZE
#define BSIZE 	1024	/* block size in bytes */
#endif
X
#ifndef INOPB
#define	INOPB	(BSIZE / sizeof(dinode))
#endif
X
long l3block();
X
int main(argc,argv)
int argc;
char *argv[];
{
X	int curquit(),
X		offset,
X		infd;
X
X	unsigned inode_num;
X
X	unsigned long start,
X		 length;
X
X	unsigned char 	*buf;
X	char 			*badchar;
X
X	struct dinode *ino;
X	
X	if ( argc < 3 || argc > 5 )
X	{
X		USAGE;
X		exit(1);
X	}
X
X	if ( (infd = open(argv[1],O_RDONLY)) == -1)
X	{
X		perror("Can not open device file");
X		exit(1);
X	}
X
X	buf = (unsigned char *) malloc(BSIZE); 
X	if ( buf == NULL)
X	{
X		fputs("No memory\n", stderr);
X		exit(1);
X	}
X
X	inode_num = strtol(argv[2],&badchar, 0);
X	if (*badchar != '\0')
X	{
X		fputs("Invalid inode number.\n", stderr);
X		USAGE;
X		exit(1);
X	}
X
X	if (argc >=4)
X	{
X		start = strtol(argv[3],&badchar, 0);
X		if (*badchar != '\0')
X		{
X			fputs("Invalid start byte.\n", stderr);
X			USAGE;
X			exit(1);
X		}
X	}
X	else
X		start = 0;
X		
X	if (argc >=5)
X	{
X		length = strtol(argv[4],&badchar, 0);
X		if (*badchar != '\0')
X		{
X			fputs("Invalid length byte.\n", stderr);
X			USAGE;
X			exit(1);
X		}
X	}
X	else
X	{
X		get_inode(infd, inode_num, &ino, buf);
X		length = ino->di_size;
X	}		
X	
X	signal(SIGINT, curquit);
X		
X	while (length > 0)
X	{
X		if (start / BSIZE == (start + length - 1) / BSIZE)
X			offset = length;
X		else
X			offset = BSIZE - start % BSIZE;
X		index_block(infd, inode_num, start, buf);
X		print_block(buf, start, offset);
X		length -= offset;
X		start += offset;
X	}
X	return(0);
}
X
X
X
/************************************************************************
**
**	index_block
**
*************************************************************************/
X
index_block(infd, inode_num, start, buf)
int infd;
unsigned inode_num;
unsigned long start;
unsigned char *buf;
{
X	struct dinode *ino;
X	get_inode(infd, inode_num, &ino, buf);
X
X	if (start / BSIZE < 10)		/* 10 DIRECT BLOCKS */
X		get_block(infd, INOBLOCK(ino->di_addr,start / BSIZE), buf);
X	else
X	{
X		start -= BSIZE * 10;
X		if ( start < BSIZE * (BSIZE / 4) )	/* INDIRECT BLOCKS */
X		{
X			get_block(infd, INOBLOCK(ino->di_addr, 10), buf);
X			get_block(infd, INDBLOCK(buf, start / BSIZE), buf);
X		}			
X		else
X		{
X			start -= BSIZE * (BSIZE / 4);
X			if (start < BSIZE * (BSIZE / 4) * (BSIZE / 4) )/* DOUBLE INDIRECT*/
X			{
X				get_block(infd, INOBLOCK(ino->di_addr, 11), buf);
X				get_block(infd, INDBLOCK(buf, start / (BSIZE/4*BSIZE)), buf);
X				start %= BSIZE / 4 * BSIZE;
X				get_block(infd, INDBLOCK(buf, start /  BSIZE), buf);
X			}	
X			else
X			{								/* TRIPLE INDIRECT */
X				start -= BSIZE * (BSIZE / 4) * ( BSIZE / 4 );
X				get_block(infd, INOBLOCK(ino->di_addr, 12), buf);
X				get_block(infd, INDBLOCK(buf, start / (BSIZE/4*BSIZE/4*BSIZE)),buf);
X				start %= BSIZE / 4 * BSIZE / 4 * BSIZE;
X				get_block(infd, INDBLOCK(buf, start / (BSIZE/4*BSIZE)), buf);
X				start %= BSIZE / 4 * BSIZE;
X				get_block(infd, INDBLOCK(buf, start /  BSIZE), buf);
X
X			}
X		}
X	}
}
X			
/************************************************************************
**
**	get_inode
**
*************************************************************************/
X
get_inode(infd, inode_num, ino, buf )
int infd;
unsigned inode_num;
struct dinode **ino;
unsigned char *buf;
{
X	inode_num--;			/* inodes start at num - 1 */
X	get_block(infd, (daddr_t)(inode_num / INOPB + 2), buf);
X	*ino = (struct dinode *)buf + (inode_num % INOPB);
}
X
/*************************************************************************
**
** 	print_block
**
**************************************************************************/
X
print_block( buf, start, offset)
int offset;
unsigned long start;
unsigned char *buf;
{
int cnt = 0;
X
X	while (cnt < offset )
X		printf("%c", buf[start % BSIZE + cnt++]);
}
X
/***************************************************************************
**
**	get_block
**
****************************************************************************/
get_block(infd, block, buf)
int infd;
long block;
unsigned char *buf;
{
X	int bufsize;	
X	if (lseek(infd, block * BSIZE, SEEK_SET) == -1)
X	{
X		perror("Can not seek in file");
X		exit(1);
X	}
X
X	if( (bufsize = read(infd, buf, BSIZE )) == -1 || bufsize != BSIZE)
X	{
X		perror("Can not read from file");
X		exit(1);
X	}
}
X	
/****************************************************************************
**
**	l3block
**
*****************************************************************************/
long l3block(blockptr)
char *blockptr;
{
X	long ret;
X
X	l3tol(&ret, blockptr, 1);
X	return ret;
}
X
/****************************************************************************
**
**	cur_quit
**
*****************************************************************************/
int curquit()
{
X	exit(0);
}
X
X
X
SHAR_EOF
chmod 0664 fslook.c ||
echo 'restore of fslook.c failed'
Wc_c="`wc -c < 'fslook.c'`"
test 5525 -eq "$Wc_c" ||
	echo 'fslook.c: original size 5525, current size' "$Wc_c"
fi
exit 0

-- 
Frank Seitz, Hamburg, 040/7927569, {fs,root,postmaster}@oops.zigzag.hanse.de