2.9BSD/usr/contrib/grab/src/bread.c
/*
* bread.c
*
* Routines to sequentially retrieve blocks from a Unix file.
*
*---------------------------------------------------------------------------
*
* $Header: RCS/bread.c.v Revision 1.3 83/08/01 17:05:47 donn Exp$
* $Log: RCS/bread.c.v $
* Revision 1.3 83/08/01 17:05:47 donn
* Fixed bug that led to extra zero-filled blocks in tar output,
* ended up simplifying main loop of bput.
* Donn
*
* Revision 1.2 83/07/01 17:30:24 donn
* Changed idump to make it usable with new -L and -I options -- it's much
* more helpful now, and cleaner.
* Donn
*
*/
# include "grab.h"
/*
* bopen( name, ip, bp ) -- Prepare to output a file called name, using
* inode ip to allocate a bmap bp.
*/
int
bopen( name, ip, bp )
char *name;
register struct inode *ip;
register struct bmap *bp;
{
int ofile;
if ( xflag )
/*
* Send file to the standard output.
*/
ofile = STDOUT;
else if ( tflag ) {
/*
* Put out a "tar"-style header record.
*/
theader( TFILE, name, ip );
ofile = TFILE;
} else {
/*
* Create a file to copy into.
*/
ofile = creat( name, ip->i_mode & (pflag ? 07777 : 0777) );
if ( ofile < 0 ) {
fprintf( stderr, "grab: can't create %s\n", name );
return ( -1 );
}
if ( pflag )
CHOWN( name, ip->i_uid, ip->i_gid );
}
allocbuf( ip, bp, B_BIG );
return ( ofile );
}
/*
* bread( bp ) -- Fill the buffer of bmap bp with blocks from its file.
*/
bread( bp )
register struct bmap *bp;
{
register struct bstr
{
long bn_bno;
int bn_index;
}
*bb;
long bno;
long v7bnext(), v6bnext();
register int i;
int nbytes, nblocks;
int bcomp();
char *start;
char *malloc();
/*
* End of file? Buffer wraparound?
*/
if ( bp->b_offset >= bp->b_size )
return ( 0 );
if ( bp->b_cc >= bp->b_len )
bp->b_cc = 0;
/*
* A minor optimization: blocks are sorted by block number
* before reading. Here we figure out how many blocks to read and
* allocate a buffer to hold the block numbers for sorting.
*/
nbytes = min( bp->b_size-bp->b_offset, bp->b_len-bp->b_cc );
nblocks = (nbytes + (fsbsiz-1)) / fsbsiz;
bb = (struct bstr *) malloc( nblocks * sizeof (struct bstr) );
if ( bb == NULL ) {
fprintf( stderr, "grab: Out of memory\n" );
exit( 12 );
}
/*
* Find out which file system block(s) to read in.
*/
for ( i = 0; i < nblocks; ++i ) {
switch ( target_system ) {
case V7_2BSD:
case V7_4BSD:
bno = v7bnext( bp );
break;
case V6:
bno = v6bnext( bp );
break;
}
if ( bno == 0 )
/*
* A hole -- stop here and let bput do the work.
*/
break;
bb[i].bn_index = i;
bb[i].bn_bno = bno;
}
nblocks = i;
nbytes = i * fsbsiz;
/*
* Sort the block numbers.
*/
qsort( bb, nblocks, sizeof (struct bstr), bcomp );
/*
* Read the blocks off, in order.
*/
start = bp->b_data + bp->b_cc;
for ( i = 0; i < nblocks; ++i ) {
lseek( fsys, fsbtodb( bb[i].bn_bno ) * DBSIZE, 0 );
if ( read( fsys, start + (bb[i].bn_index * fsbsiz), fsbsiz ) != fsbsiz ) {
fprintf( stderr, "grab: error reading filesystem\n" );
exit( 10 );
}
}
free( bb );
/*
* Update the bmap. Pad out holes.
*/
if ( bno == 0L ) {
bp->b_type = B_HOLE;
nbytes += fsbsiz;
} else
bp->b_type = B_NORMAL;
bp->b_cc += nbytes;
bp->b_offset += nbytes;
# ifdef DEBUG
bdump( bp );
# endif
return ( nbytes );
}
/*
* bcomp( b1, b2 ) -- Compare block numbers in bstr's b1 and b2.
*/
bcomp( b1, b2 )
struct bstr
{
long bn_bno;
int bn_index;
}
*b1, *b2;
{
if ( b1->bn_bno < b2->bn_bno )
return( -1 );
return( b1->bn_bno > b2->bn_bno );
}
/*
* bput( ofile, bp ) -- Do a buffered transfer to ofile using data in bp.
*/
bput( ofile, bp )
int ofile;
register struct bmap *bp;
{
register int n;
register char *cp;
register char *dp;
long count;
int err;
int saveblen;
long saveboffset;
# ifdef DEBUG
fprintf( stderr, "Entering bput\n" );
# endif
/*
* Special case for holes in files... Bleah.
*/
if ( bp->b_type == B_HOLE )
if ( tflag || xflag ) {
/*
* Deal with tape output. In this case we need to zero
* out the last block brought in since holes in tape are
* inconvenient (to say the least)...
*/
cp = bp->b_data+(bp->b_cc-fsbsiz);
n = fsbsiz;
CLRBUF( cp, n );
} else {
/*
* Regular files need holes made in them. Force all
* current buffered data out now & pick up again later.
* Note that b_offset, b_cc are >= fsbsiz after bread.
*/
saveblen = bp->b_len;
saveboffset = bp->b_offset;
bp->b_offset -= fsbsiz;
bp->b_len = bp->b_cc - fsbsiz;
bp->b_cc = bp->b_len;
}
/*
* Output the buffer, if necessary.
*/
cp = bp->b_data;
dp = bp->b_data + bp->b_len;
n = bp->b_cc - bp->b_len;
if ( bp->b_size < bp->b_offset )
/*
* Correct the count for overshooting.
*/
count = bp->b_cc - (bp->b_offset - bp->b_size);
else
count = bp->b_cc;
while ( count > 0 ) {
if ( count < bp->b_len ) {
if ( tflag ) {
/*
* Only write full buffers.
*/
break;
}
err = write( ofile, bp->b_data, (int) count );
count = 0;
} else {
err = write( ofile, bp->b_data, bp->b_len );
count -= bp->b_len;
}
if ( err < 0 ) {
fprintf( stderr, "grab: write error\n" );
exit( 11 );
}
if ( count > 0 )
CPYBUF( cp, dp, n );
}
bp->b_cc = count;
/*
* Finish dirty work with holes.
*/
if ( bp->b_type == B_HOLE && ! (tflag || xflag) ) {
lseek( ofile, min( (long) fsbsiz, bp->b_size-bp->b_offset ), 1 );
bp->b_offset = saveboffset;
bp->b_len = saveblen;
}
}
/*
* bclose( ofile, name, ip, bp ) -- Wrap up a file named name with output
* channel ofile and inode ip and deallocate the buffers of its bmap bp.
*/
bclose( ofile, name, ip, bp )
int ofile;
char *name;
register struct inode *ip;
register struct bmap *bp;
{
freebuf( bp );
if ( ! xflag && ! tflag ) {
close( ofile );
# ifdef CLR_SETUID
if ( pflag && (ip->i_mode & (ISUID|ISGID|ISVTX)) )
chmod( name, ip->i_mode & 07777 );
# endif CLR_SETUID
# ifndef PDPV6
if ( pflag )
utime( name, &ip->i_atime );
# endif
}
if ( vflag ) {
printf( "%s\n", name );
fflush( stdout );
}
}
/*
* v7bnext( bp ) -- get the next file system block from the list
* associated with bp. [Version 7]
*/
long
v7bnext( bp )
register struct bmap *bp;
{
long lbn = bp->b_lbno++;
/*
* Blocks 0 thru V7NADDR-4 are direct blocks.
*/
if ( lbn < V7NADDR - 3 )
return ( bp->b_iaddr[lbn] );
/*
* An indirect block is needed.
*/
++bp->b_count[0];
v7indir( bp, 0 );
return ( ((long *) bp->b_indir[0]) [ bp->b_count[0] ] );
}
/*
* v7indir( bp, level ) -- make sure that the needed block number is
* available from the indirect blocks.
*/
v7indir( bp, level )
register struct bmap *bp;
register int level;
{
long bno;
char *malloc();
if ( level >= 3 ) {
/*
* Max. three levels of indirection!
*/
fprintf( stderr, "grab: file overflow\n" );
exit( 12 );
}
/*
* Has an indirect block of this level been looked at yet?
*/
if ( bp->b_indir[level] == NULL ) {
bp->b_indir[level] = malloc( fsbsiz );
if ( bp->b_indir[level] == NULL ) {
fprintf( stderr, "grab: out of memory\n" );
exit( 13 );
}
bno = bp->b_iaddr[(V7NADDR-3) + level];
}
/*
* Have we looked at all the blocks in this indirect block?
*/
else
if ( bp->b_count[level] >= fsbsiz / sizeof (long) ) {
++bp->b_count[level + 1];
v7indir( bp, level + 1 );
bno = ((long *) bp->b_indir[level+1]) [ bp->b_count[level+1] ];
}
/*
* This indirect block is still valid.
*/
else
return;
/*
* We need to read in a new indirect block.
*/
lseek( fsys, fsbtodb( bno ) * DBSIZE, 0 );
if ( read( fsys, bp->b_indir[level], fsbsiz ) < 0 ) {
fprintf( stderr, "grab: error reading filesystem\n" );
exit( 14 );
}
bp->b_count[level] = 0;
/*
* Swap words if necessary.
*/
switch ( target_system ) {
case V7_2BSD:
# ifndef PDP
wswap( bp->b_indir[level], fsbsiz / sizeof (long) );
# endif
break;
case V7_4BSD:
# ifdef PDP
wswap( bp->b_indir[level], fsbsiz / sizeof (long) );
# endif
break;
}
}
/*
* v6bnext( bp ) -- get the next file system block number from the list
* associated with bp. [Version 6]
*/
long
v6bnext( bp )
register struct bmap *bp;
{
long lbn = bp->b_lbno++;
/*
* If the file has small format then we use the direct blocks.
*/
if ( (bp->b_mode & V6_ILARGE) == 0 ) {
if ( lbn >= 8 ) {
fprintf( stderr, "grab: V6 small format file too big\n" );
return ( 0 );
}
return ( (unsigned short) bp->b_iaddr[lbn] );
}
/*
* An indirect block is needed.
*/
++bp->b_count[0];
v6indir( bp, 0 );
return ( ((unsigned short *) bp->b_indir[0]) [ bp->b_count[0] ] );
}
/*
* v6indir( bp, level ) -- make sure that the needed block number is
* available from the indirect blocks.
*/
v6indir( bp, level )
register struct bmap *bp;
register int level;
{
long bno;
char *malloc();
if ( level >= 2 ) {
fprintf( stderr, "grab: file overflow\n" );
exit( 15 );
}
/*
* Has an indirect block of this level been looked at yet?
*/
if ( bp->b_indir[level] == NULL ) {
bp->b_indir[level] = malloc( fsbsiz );
if ( bp->b_indir[level] == NULL ) {
fprintf( stderr, "grab: out of memory\n" );
exit( 16 );
}
bno = (unsigned short) bp->b_iaddr[ level == 0 ? 0 : 7 ];
}
/*
* Have we looked at all the blocks in this indirect block?
*/
else
if ( bp->b_count[level] >= fsbsiz / sizeof (short) ) {
register int n = bp->b_lbno / (fsbsiz / sizeof(short));
if ( n < 7 )
/*
* One of 7 singly indirect blocks in the inode.
*/
bno = (unsigned short) bp->b_iaddr[ n ];
else {
++bp->b_count[level + 1];
v6indir( bp, level + 1 );
bno = ((unsigned short *) bp->b_indir[level+1]) [ bp->b_count[level+1] ];
}
}
/*
* This indirect block is still valid.
*/
else
return;
/*
* We need to read in a new indirect block.
*/
lseek( fsys, fsbtodb( bno ) * DBSIZE, 0 );
if ( read( fsys, bp->b_indir[level], fsbsiz ) < 0 ) {
fprintf( stderr, "grab: error reading file system\n" );
exit( 17 );
}
bp->b_count[level] = 0;
}
/*
* allocbuf( ip, bp, flag ) -- associate a block map structure bp and a buffer
* with ip, using flag to determine buffering mode. Savetcc ought to be
* known only here but actually tflush uses it for a kluge... Sigh.
*/
char *bigbuf = NULL;
int savetcc = 0;
allocbuf( ip, bp, flag )
register struct inode *ip;
register struct bmap *bp;
int flag;
{
char *malloc();
bp->b_type = B_NORMAL;
bp->b_mode = ip->i_mode;
bp->b_size = ip->i_size;
bp->b_offset = 0L;
bp->b_iaddr = ip->i_addr;
bp->b_lbno = 0L;
if ( flag == B_SMALL ) {
bp->b_len = fsbsiz;
bp->b_cc = 0;
bp->b_data = malloc( fsbsiz );
if ( bp->b_data == NULL ) {
fprintf( stderr, "grab: out of memory\n" );
exit( 18 );
}
} else {
bp->b_len = nblock * TBLOCK;
if ( tflag ) {
/*
* Make sure to get tail end of last file in buffer.
* Also round up size to nearest TBLOCK.
*/
bp->b_size = ((bp->b_size + (TBLOCK-1))/TBLOCK) * TBLOCK;
bp->b_cc = savetcc;
} else
bp->b_cc = 0;
if ( bigbuf == NULL ) {
/*
* Allocate a large data buffer. Leave enough slop for
* odd-numbered blocksize outputs so that input doesn't
* get truncated...
*/
bigbuf = malloc( bp->b_len + fsbsiz );
if ( bigbuf == NULL ) {
fprintf( stderr, "grab: out of memory\n" );
exit( 19 );
}
}
bp->b_data = bigbuf;
}
bp->b_indir[0] = NULL;
bp->b_indir[1] = NULL;
bp->b_indir[2] = NULL;
bp->b_count[0] = -1;
bp->b_count[1] = -1;
bp->b_count[2] = -1;
}
/*
* freebuf( bp ) -- release all the data and indirect block buffers
* that are tied up with bp. Don't reallocate a big data buffer.
*/
freebuf( bp )
register struct bmap *bp;
{
register int n;
if ( bp->b_data == bigbuf )
savetcc = bp->b_cc;
else if ( bp->b_data != NULL )
free( bp->b_data );
for ( n = 0; n < 3; ++n )
if ( bp->b_indir[n] != NULL )
free( bp->b_indir[n] );
}
/*
* Some debugging aids. Idump() is also used for -I and -L options.
*/
/*
* bdump( bp ) -- Print out the printable contents of bmap bp.
*/
bdump( bp )
register struct bmap *bp;
{
fprintf(stderr, "type=%d, mode=%o, b_size=%D, b_offset=%D\n",
bp->b_type, (unsigned) bp->b_mode, bp->b_size, bp->b_offset);
fprintf(stderr, "cc=%d, len=%d, lbno=%D, data=%o\n",
bp->b_cc, bp->b_len, bp->b_lbno, bp->b_data );
}
/*
* ddump( dp ) -- Print out the contents of directory entry dp.
*/
ddump( dp )
struct direct *dp;
{
fprintf( stderr, "--%d\t%s\n", (unsigned) dp->d_ino, dp->d_name );
}
/*
* idump( ip, ino, name ) -- Print out the contents of inode ip with i-number
* ino and name name.
*/
idump( ip, ino, name )
struct inode *ip;
int ino;
char *name;
{
register int n;
if ( name != NULL )
fprintf( stderr, "Name:\t\t%.14s\n", name );
if ( ino != 0 )
fprintf( stderr, "Inode number:\t%u\n", ino );
fprintf( stderr, "\tMode:\t\t%o\n", (unsigned short) ip->i_mode );
fprintf( stderr, "\tSize:\t\t%D\n", ip->i_size );
fprintf( stderr, "\tLink count:\t%d\n", ip->i_nlink );
fprintf( stderr, "\tUid:\t\t%d\n", ip->i_uid );
fprintf( stderr, "\tGid:\t\t%d\n", ip->i_gid );
fprintf( stderr, "\tAccess time:\t%s", ctime( &(ip->i_atime) ) );
fprintf( stderr, "\tModify time:\t%s", ctime( &(ip->i_mtime) ) );
fprintf( stderr, "\tBlock numbers:" );
for ( n = 0; n < (target_system == V6 ? V6NADDR : V7NADDR); ++n ) {
if ( n % 4 == 0 )
fprintf( stderr, "\n\t\t" );
fprintf( stderr, "%10D ", ip->i_addr[n] );
}
fprintf( stderr, "\n\n" );
}