Source and Documentation for 'Grab'
Donn Seeley
donn at sdchema.UUCP
Wed Aug 10 13:01:04 AEST 1983
#
# This is a C-shell archive for 'grab' sources. Make a source
# directory for 'grab', change to that directory and run 'csh -f SCRIPT'
# where SCRIPT is the name of this file.
#
#-------------------------------------------------------------------
echo extracting grab.1
cat << 'EOFEOF' > grab.1
.\" $Header: RCS/grab.1.v Revision 1.2 83/07/01 19:34:59 donn Exp$
.\" $Log: RCS/grab.1.v $
.\" Revision 1.2 83/07/01 19:34:59 donn
.\" Added descriptions of '-i' and '-L' options for inode grabbing
.\" and dumping.
.\" Donn
.\"
.\"
.TH GRAB 1 5/18/82
.UC 4
.SH NAME
grab \- get files from filesystems
.SH SYNOPSIS
.B grab
[ -246pxvilLtb ] filesystem [ name ... ]
.SH DESCRIPTION
.PP
.I Grab
is a program which reads Unix filesystem-format devices
and extracts named files.
.I Grab
will run on Version 6 Unix, 2.8 BSD Unix or 4.1 BSD Unix and
will read filesystems made on any of these three.
It is useful for reading filesystems that
cannot be mounted for various reasons
(e.g. there is damage,
system mountable filesystem parameter is set too low,
you are using dual-ported disks or controllers,
the filesystem is not the same type as the one
.I grab
is running on, etc.)
It can also be used as a fast disk copy program,
especially for big chunks of file systems,
since it can restore ownerships and permissions on files,
recreate links and make directories and devices.
.PP
.I Grab
needs to be supplied with the name of a filesystem format file or device,
and a list of pathnames for files on that filesystem
starting at the filesystem root.
For example if you need to extract a file
/mnt/donn/c/quote.c where /mnt is normally mounted
on /dev/xy2, then you type
.br
grab /dev/xy2 donn/c/quote.c
.br
and this will copy the remote file into a file named
.I quote.c
in your current directory.
(Mounted devices can be listed using
.IR df (1)
or
.IR mount (8).)
If a directory is given as a name,
all the files in that directory are (recursively) extracted
into a subdirectory in the current directory
that has the same name as the remote directory.
If any files extracted are linked together
on the remote filesystem,
the copies
.I grab
makes will also be linked.
The permission information on the files is copied
except for owner, setuid, setgid and sticky information
which must be requested specifically by the
.B \-p
option.
Device nodes will be copied for the super-user.
Here are the options in detail:
.TP 8
.B -2
The remote filesystem is a 2.8 BSD filesystem.
(Note: By default the remote filesystem
is assumed to be the same type as the host system.)
.TP 8
.B -4
The remote filesystem is a 4.1 BSD filesystem.
.TP 8
.B -6
The remote filesystem is a Version 6 filesystem.
(Note: On version 6 grab cannot consistently read filesystems
made on Version 7 that use more than 65K small blocks
since the V6 I/O system uses 16 bits to store block numbers...)
.TP 8
.B -p
Restore owner and group, setuid, setgid and sticky bits,
and access and modification times.
Only the superuser can reliably copy owner and group.
.I Grab
will not allow protected files on remote filesystems to be read
except by the super-user.
.TP 8
.B -v
Verbose option -- the names of copied files
are announced as they are finished being copied.
.TP 8
.B -x
Print option -- the requested files are copied
to the standard output.
If the standard output is a file then
this is the default.
.TP 8
.B -i
Inode option -- the arguments must be inode numbers
and the program looks at the files indicated by the given inodes.
If files are created without a name being available,
the inode number is converted into a name.
This is useful for recovering files on screwed-up filesystems
when the inode numbers are known through (say)
.IR dcheck (8).
.TP 8
.B -l
List option -- the names of files in the requested directories are listed.
This is useful if you don't know or can't remember
the name of a file to extract.
.TP 8
.B -L
Long listing option -- the program lists files individually
and displays useful information about them:
their name, inode number, mode word, link count, uid and gid,
access and modify times, and within-inode block numbers.
.TP 8
.B -t
.I Tar
option -- the requested files are placed on the standard output
in a format identical to that of the
.IR tar (1)
program.
Digits following the letter
.B t
are taken to be a blocking factor for the tape;
the default blocksize is 20.
To put a Version 6 directory on tape you might type:
.br
grab -6tv /dev/rxp0d /etc > /dev/rmt0
.TP 8
.B -b
This option allows you to set the size of the buffer
.I grab
uses for reading and writing.
Digits following the
.B b
are taken to be a buffer size in 512-byte blocks.
The default is 128 blocks on the VAX, 32 on PDPs.
Increasing the buffer size generally increases
the speed but you have to be careful not to exceed
the bounds of your machine.
.SH BUGS
Checking uids of remote files is
rather silly if the remote system
is not the same as the host system.
.sp
No warning is given about clobbering pre-existing files.
.sp
Version 6 is obsolete.
'EOFEOF'
#-------------------------------------------------------------------
echo extracting grab.1.v6
cat << 'EOFEOF' > grab.1.v6
.\" $Header: RCS/grab.1.v Revision 1.2 83/07/01 19:34:59 donn Exp$
.\" $Log: RCS/grab.1.v $
.\" Revision 1.2 83/07/01 19:34:59 donn
.\" Added descriptions of '-i' and '-L' options for inode grabbing
.\" and dumping.
.\" Donn
.\"
.\"
.th GRAB I 5/18/82
.sh NAME
grab \- get files from filesystems
.sh SYNOPSIS
.bd grab
[ -246pxvilLtb ] filesystem [ name ... ]
.sh DESCRIPTION
.it Grab
is a program which reads Unix filesystem-format devices
and extracts named files.
.it Grab
will run on Version 6 Unix, 2.8 BSD Unix or 4.1 BSD Unix and
will read filesystems made on any of these three.
It is useful for reading filesystems that
cannot be mounted for various reasons
(e.g. there is damage,
system mountable filesystem parameter is set too low,
you are using dual-ported disks or controllers,
the filesystem is not the same type as the one
.it grab
is running on, etc.)
It can also be used as a fast disk copy program,
especially for big chunks of file systems,
since it can restore ownerships and permissions on files,
recreate links and make directories and devices.
.s3
.it Grab
needs to be supplied with the name of a filesystem format file or device,
and a list of pathnames for files on that filesystem
starting at the filesystem root.
For example if you need to extract a file
/mnt/donn/c/quote.c where /mnt is normally mounted
on /dev/xy2, then you type
.br
grab /dev/xy2 donn/c/quote.c
.br
and this will copy the remote file into a file named
.it quote.c
in your current directory.
(Mounted devices can be listed using
.it df(I)
or
.it mount(VIII).)
If a directory is given as a name,
all the files in that directory are (recursively) extracted
into a subdirectory in the current directory
that has the same name as the remote directory.
If any files extracted are linked together
on the remote filesystem,
the copies
.it grab
makes will also be linked.
The permission information on the files is copied
except for owner, setuid, setgid and sticky information
which must be requested specifically by the
.bd \-p
option.
Device nodes will be copied for the super-user.
Here are the options in detail:
.s3
.lp +4 4
\fB-2\fR The remote filesystem is a 2.8 BSD filesystem.
(Note: By default the remote filesystem
is assumed to be the same type as the host system.)
.s3
.lp +4 4
\fB-4\fR The remote filesystem is a 4.1 BSD filesystem.
.s3
.lp +4 4
\fB-6\fR The remote filesystem is a Version 6 filesystem.
(Note: On version 6 grab cannot consistently read filesystems
made on Version 7 that use more than 65K small blocks
since the V6 I/O system uses 16 bits to store block numbers...)
.s3
.lp +4 4
\fB-p\fR Restore owner and group, setuid, setgid and sticky bits,
and access and modification times.
Only the superuser can reliably copy owner and group.
.it Grab
will not allow protected files on remote filesystems to be read
except by the super-user.
.s3
.lp +4 4
\fB-v\fR Verbose option -- the names of copied files
are announced as they are finished being copied.
.s3
.lp +4 4
\fB-x\fR Print option -- the requested files are copied
to the standard output.
If the standard output is a file then
this is the default.
.s3
.lp +4 4
\fB-i\fR Inode option -- the arguments must be inode numbers
and the program looks at the files indicated by the given inodes.
If files are created without a name being available,
the inode number is converted into a name.
This is useful for recovering files on screwed-up filesystems
when the inode numbers are known through (say)
.it dcheck(VIII).
.s3
.lp +4 4
\fB-l\fR List option -- the names of files in the requested directories are listed.
This is useful if you don't know or can't remember
the name of a file to extract.
.s3
.lp +4 4
\fB-L\fR Long listing option -- the program lists files individually
and displays useful information about them:
their name, inode number, mode word, link count, uid and gid,
access and modify times, and within-inode block numbers.
.s3
.lp +4 4
\fB-t\fR \fITar\fR
option -- the requested files are placed on the standard output
in a format identical to that of the
.it tar(I)
program.
Digits following the letter
.bd t
are taken to be a blocking factor for the tape;
the default blocksize is 20.
To put a Version 6 directory on tape you might type:
.br
grab -6tv /dev/rxp0d /etc > /dev/rmt0
.s3
.lp +4 4
\fB-b\fR This option allows you to set the size of the buffer
.it grab
uses for reading and writing.
Digits following the
.bd b
are taken to be a buffer size in 512-byte blocks.
The default is 128 blocks on the VAX, 32 on PDPs.
Increasing the buffer size generally increases
the speed but you have to be careful not to exceed
the bounds of your machine.
.i0
.sh BUGS
Checking uids of remote files is
rather silly if the remote system
is not the same as the host system.
.s3
No warning is given about clobbering pre-existing files.
.s3
Version 6 is obsolete.
'EOFEOF'
#-------------------------------------------------------------------
echo extracting Makefile.2.8
cat << 'EOFEOF' > Makefile.2.8
#
# Makefile for grab
# 2.8 BSD
#
#----------------------------------------------------------------------------
#
# $Header$
# $Log$
#
CFLAGS=-O -DPDP
OFILES=grab.o find.o readi.o bread.o tmode.o
LIBES=
grab: ${OFILES}
${CC} ${OFILES} ${LIBES} -o grab
'EOFEOF'
#-------------------------------------------------------------------
echo extracting Makefile.4.1
cat << 'EOFEOF' > Makefile.4.1
#
# Makefile for grab
# 4.1 BSD
#
#----------------------------------------------------------------------------
#
# $Header$
# $Log$
#
CFLAGS=-O -DVAX -DCLR_SETUID -DMPXFILES
OFILES=grab.o find.o readi.o bread.o tmode.o
LIBES=
grab: ${OFILES}
${CC} ${OFILES} ${LIBES} -o grab
'EOFEOF'
#-------------------------------------------------------------------
echo extracting Makefile.4.1a
cat << 'EOFEOF' > Makefile.4.1a
#
# Makefile for grab
# 4.1a BSD
#
#----------------------------------------------------------------------------
#
# $Header$
# $Log$
#
CFLAGS=-O -DVAX -DCLR_SETUID -DSYMLINKS
OFILES=grab.o find.o readi.o bread.o tmode.o
LIBES=
grab: ${OFILES}
${CC} ${OFILES} ${LIBES} -o grab
'EOFEOF'
#-------------------------------------------------------------------
echo extracting Makefile.v6
cat << 'EOFEOF' > Makefile.v6
#
# Makefile for grab
# Version 6 Unix
#
#----------------------------------------------------------------------------
#
# $Header$
# $Log$
#
CFLAGS=-O -DPDPV6
OFILES=grab.o find.o readi.o bread.o tmode.o
LIBES=-lS
grab: ${OFILES}
${CC} ${OFILES} ${LIBES} -o grab
'EOFEOF'
#-------------------------------------------------------------------
echo extracting grab.h
cat << 'EOFEOF' > grab.h
/*
* grab.h
*
* Include file for grab -- contains structure definitions,
* external definitions.
*
*---------------------------------------------------------------------------
*
* $Header: RCS/grab.h.v Revision 1.2 83/07/01 19:41:34 donn Exp$
* $Log: RCS/grab.h.v $
* Revision 1.2 83/07/01 19:41:34 donn
* Added stuff to support '-i' and '-L' flags.
* Donn
*
*/
# include <stdio.h>
extern int target_system; /* Type of file system to be read */
extern int fsbsiz; /* File system block size */
extern int fsys; /* File descriptor for file system */
extern int pflag; /* 1 if file protections are restored */
extern int xflag; /* 1 if output goes to stdout */
extern int vflag; /* 1 for verifying files during copy */
extern int iflag; /* 1 for grabbing by inode */
extern int lflag; /* >=1 for listing files in a directory */
extern int Lflag; /* 1 for long listings of files */
extern int tflag; /* 1 for producing "tar" output */
extern int nblock; /* "Tar" output blocking factor */
# define V7_2BSD 0
# define V7_4BSD 1
# define V6 2
/*
* Default settings for host system.
* Set PDPV6 for V6, PDP for V7_2BSD and VAX for V7_4BSD.
*/
# ifndef VAX
# ifndef PDP
# define PDPV6 1
# endif
# endif
# ifdef PDPV6
# define PDP 1
# define TARGET_DEFAULT V6
# define FSB_DEFAULT 512
# define geteuid() ((getuid() >> 8) & 0xff)
# define getegid() ((getgid() >> 8) & 0xff)
# define CHOWN(s,u,g) chown( (s), ((g)<<8) | ((u)&0xff) )
# define CONVMODE(m) ((m) & V6_IFMT)
# else
# ifdef VAX
# define TARGET_DEFAULT V7_4BSD
# else
# define TARGET_DEFAULT V7_2BSD
# endif
# define FSB_DEFAULT 1024
# define CHOWN chown
# define CONVMODE(m) ((m)&(target_system==V6?V6_IFMT:V7_IFMT))
# endif PDPV6
/*
* Generic inode structure.
* DEFICIENCY: this structure is used to recover the mode word
* from a "stat" call...
*/
struct inode
{
long i_size;
short i_mode;
short i_nlink;
short i_uid;
short i_gid;
long i_addr[13];
long i_atime;
long i_mtime;
};
/*
* Version 7 disk inode.
* Kluge for 2.8's 1/2 size inodes added.
*/
# define UCB_SMINO
# ifdef UCB_SMINO
# define V7NADDR (target_system==V7_2BSD?7:13)
# else
# define V7NADDR 13
# endif
struct v7dinode
{
short v7di_mode; /* mode and type of file */
short v7di_nlink; /* number of links to file */
short v7di_uid; /* owner's user id */
short v7di_gid; /* owner's group id */
long v7di_size; /* number of bytes in file */
char v7di_addr[40]; /* disk block addresses */
long v7di_atime; /* time last accessed */
long v7di_mtime; /* time last modified */
long v7di_ctime; /* time created */
};
/*
* Version 6 disk inode.
*/
# define V6NADDR 8
struct v6dinode
{
short v6di_mode; /* flags */
char v6di_nlink; /* number of links to file */
char v6di_uid; /* user ID of owner */
char v6di_gid; /* group ID of owner */
char v6di_sz0; /* high byte of 24-bit size */
short v6di_sz1; /* low word of 24-bit size */
short v6di_addr[8]; /* block numbers or device numbers */
long v6di_atime; /* time of last access */
long v6di_mtime; /* time of last modification */
};
/*
* Mode bits.
* It is assumed that the protection bits 0-11 are the same on all systems.
*/
#define V7_IFMT 0170000 /* type of file */
#define V7_IFCHR 0020000 /* character special */
#define V7_IFBLK 0060000 /* block special */
#define V7_IFMPC 0030000 /* multiplex char special */
#define V7_IFMPB 0070000 /* multiplex blk special */
#define V7_IFDIR 0040000 /* directory */
#define V7_IFLNK 0120000 /* symbolic link */
#define V7_IFREG 0100000 /* regular */
#define V6_IFMT 0060000 /* type of file */
#define V6_IFCHR 0020000 /* character special */
#define V6_IFBLK 0060000 /* block special */
#define V6_IFDIR 0040000 /* directory */
#define V6_IFREG 0000000 /* regular */
#define V6_ILARGE 0010000 /* large file */
#define ISUID 0004000 /* setuid */
#define ISGID 0002000 /* setgid */
#define ISVTX 0001000 /* sticky */
/*
* Block map structure. Used to keep track of a sequential read
* through the blocks of an inode.
*/
struct bmap
{
short b_type; /* Type of block: NORMAL/HOLE */
short b_mode; /* File mode */
long b_size; /* File size */
long b_offset; /* Offset within file (bytes) */
int b_len; /* Size of buffer */
int b_cc; /* Buffer character count */
long b_lbno; /* Logical block number in file */
long *b_iaddr; /* Block numbers in an inode */
char *b_data; /* Contents of current buffer */
char *b_indir[3]; /* Contents of indirect blocks */
int b_count[3]; /* Index into an indirect block */
};
/*
* Block types, buffer types and size.
*/
# define B_NORMAL 0
# define B_HOLE 1
# define B_SMALL 0
# define B_BIG 1
# ifdef VAX
# define B_BUFSIZE 128
# else PDP
# define B_BUFSIZE 32
# endif
/*
* Size of a disk sector.
*/
# define DBSIZE 512
/*
* Default blocking factor for "tar"-style output, and other "tar" stuff.
*/
# define TAR_BS_DEFAULT 20
# define TBLOCK 512
# define NAMSIZ 100
/*
* Tar header structure.
*/
union hblock {
char dummy[TBLOCK];
struct header {
char name[NAMSIZ];
char mode[8];
char uid[8];
char gid[8];
char size[12];
char mtime[12];
char chksum[8];
char linkflag;
char linkname[NAMSIZ];
} dbuf;
};
/*
* CLRBUF(a,b) -- Zero out b bytes starting at a.
* CPYBUF(a,b,c) -- Copy c bytes into a from b.
* These are just experiments to make use of the SOB instructions...
* The counts are left at 0, the pointers end up at the end of the data.
*/
# ifdef VAX
# define CLRBUF(a,b) if (b>0) { do *a++ = '\0'; while ( --b > 0 ); }
# define CPYBUF(a,b,c) if (c>0) { do *a++ = *b++; while ( --c > 0 ); }
# else
# define CLRBUF(a,b) if (b>0) { do *a++ = '\0'; while ( --b ); }
# define CPYBUF(a,b,c) if (c>0) { do *a++ = *b++; while ( --c ); }
# endif
/*
* Macros for decoding block and inode numbers.
* DEFICIENCY: This uses on the fact that there are just as many
* inodes per block under V6 as under V7 with big blocks.
*/
# define INOPB 16
# define fsbtodb(n) (target_system==V6?(n):(n)<<1)
# define itod(n) ((long)((((unsigned short)(n)+2*INOPB-1)/INOPB)))
# define itoo(n) ((int)(((unsigned short)(n)+2*INOPB-1)%INOPB))
/*
* Form of directory entries.
*/
#define DIRSIZ 14
#define ROOTINO (target_system==V6?1:2)
struct direct
{
unsigned short d_ino;
char d_name[DIRSIZ];
};
/*
* Miscellaneous.
*/
# define min(a,b) ((a)<(b)?(a):(b))
# define max(a,b) ((a)>(b)?(a):(b))
# define STDOUT 1
# define TFILE STDOUT
'EOFEOF'
#-------------------------------------------------------------------
echo extracting bread.c
cat << 'EOFEOF' > 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" );
}
'EOFEOF'
#-------------------------------------------------------------------
echo extracting find.c
cat << 'EOFEOF' > find.c
/*
* find.c
*
* Routines for descending the directory tree of a pathname.
*
*---------------------------------------------------------------------------
*
* $Header$
* $Log$
*/
# include "grab.h"
/*
* tail( path ) -- return pointer to the terminal name of path.
*/
char *
tail( path )
register char *path;
{
register char *name = path;
while ( *path != '\0' )
if ( *path++ == '/' )
name = path;
return ( name );
}
/*
* find( path ) -- return inode number corresponding to path.
*/
int
find( path )
char *path;
{
register char *p, *q;
char tmp;
int ino;
struct inode i;
if ( path == NULL || *path == '\0' ) {
fprintf( stderr, "grab: null filename\n" );
return ( 0 );
}
p = path;
/*
* Fetch the root inode information from the disk.
*/
geti( ROOTINO, &i );
/*
* Search the directories in the path for
* each pathname element in turn.
*/
while ( *p != '\0' ) {
/*
* Find and separate out a pathname element.
*/
while ( *p == '/' )
++p;
q = p;
while ( *q != '/' && *q != '\0' )
++q;
tmp = *q;
*q = '\0';
/*
* Look in the directory.
*/
if ( ! isdir( &i ) ) {
fprintf( stderr, "grab: bad directory in %s\n", path );
return ( 0 );
}
if ( (ino = dlook( p, path, &i )) == 0 )
return ( 0 );
if ( tmp == '\0' )
/*
* Found the terminal element.
*/
break;
geti( ino, &i );
*q = tmp;
p = q;
}
return( ino );
}
/*
* dlook( name, path, ip ) -- look for name in directory corresponding to the
* inode whose incore structure is pointed to by ip. Path is used for
* error reporting.
*/
int
dlook( name, path, ip )
char *name;
char *path;
struct inode *ip;
{
register int j, k;
int n, dpb, ino;
register struct direct *dp;
struct bmap b;
if ( ! chkaccess( ip ) ) {
fprintf( stderr, "grab: %s read-protected\n", path );
return ( 0 );
}
ino = 0;
n = ip->i_size / sizeof (struct direct);
dpb = fsbsiz / sizeof (struct direct);
allocbuf( ip, &b, B_SMALL );
for ( j = 0; j < n; ++j ) {
k = j % dpb;
if ( k == 0 )
bread( &b );
dp = &((struct direct *) b.b_data)[ k ];
if ( dp->d_ino == 0 )
continue;
if ( strncmp( name, dp->d_name, DIRSIZ ) == 0 ) {
ino = dp->d_ino;
break;
}
}
freebuf( &b );
/*
* Did we find it?
*/
if ( ino == 0 )
fprintf( stderr, "grab: %s not found\n", path );
return ( ino );
}
/*
* isreg( ip ) -- return true iff ip's inode corresponds to a regular file.
*/
int
isreg( ip )
struct inode *ip;
{
switch ( target_system ) {
case V7_2BSD:
case V7_4BSD:
return ( (ip->i_mode & V7_IFMT) == V7_IFREG );
case V6:
return ( (ip->i_mode & V6_IFMT) == V6_IFREG );
}
}
/*
* isdir( ip ) -- return true iff ip's inode corresponds to a directory file.
*/
int
isdir( ip )
struct inode *ip;
{
switch ( target_system ) {
case V7_2BSD:
case V7_4BSD:
return ( (ip->i_mode & V7_IFMT) == V7_IFDIR );
case V6:
return ( (ip->i_mode & V6_IFMT) == V6_IFDIR );
}
}
/*
* chkaccess( ip ) -- check permissions on readability of the given
* inode on the foreign system. Return 1 if readable.
*/
int
chkaccess( ip )
struct inode *ip;
{
static int uid = -1, gid = -1;
int iuid, igid, mode, ckmode;
if ( uid == -1 ) {
uid = geteuid();
gid = getegid();
}
iuid = ip->i_uid;
igid = ip->i_gid;
if ( uid == 0 )
return ( 1 );
if ( isdir( ip ) )
ckmode = 05; /* Read and execute on a directory */
else
ckmode = 04; /* Read on regular files */
mode = ip->i_mode & 0777;
if ( uid == iuid && ((mode >> 6) & ckmode) == ckmode )
return ( 1 );
if ( gid == igid && ((mode >> 3) & ckmode) == ckmode )
return ( 1 );
if ( (mode & ckmode) == ckmode )
return ( 1 );
return ( 0 );
}
'EOFEOF'
#-------------------------------------------------------------------
echo extracting grab.c
cat << 'EOFEOF' > grab.c
/*
* grab.c
*
* Name: grab
* Purpose: extract files from remote filesystems
* Usage: grab [-246pxvilLtb#] <filesystem> <files...>
* Environment: V6 or V7 (2.8BSD, 4.1BSD) Unix
* Compile: cc -O -s -D<type> grab.c find.c readi.c bread.c tmode.c -o grab
* Date: 4/29/82
* Author: Donn Seeley RRCF UCSD
* Remarks:
* This is meant to be used with dual-ported disks or controllers
* when it is either impossible or undesirable to mount another
* file system on that medium. It should allow any Version 6 or
* Version 7 (2.8BSD, 4.1BSD) system to read filesystems from any
* Version 6 or Version 7 (2.8BSD, 4.1BSD) system even if the systems
* are not of the same type. Version 6 systems will need at least
* a Phototypesetter level C compiler to make "grab". "Grab" will
* copy any conceivable (?) object on a filesystem including directories
* (recursively), links, file holes, setuid/setgid/sticky files and
* device nodes. "Grab" tries to prevent unauthorized readers from
* gazing at remote files but the only reliable way of maintaining
* security is to make "grab" setgid and then make fs's readable by
* group and not by other. ("Df" should work this way too.)
*
* To compile a V6 grab, define PDPV6. To compile a 2.8 grab define
* PDP, and to compile a 4.1 grab define VAX. If your machine has
* multiplex files, define MPXFILES, and if it has symbolic links
* you should define SYMLINKS.
*
* Options:
* -246 2.8, 4.1 or V6 system on the remote end.
* -p Preserve all permissions and ownerships, instead of
* just some of them (should be su).
* -x Act like "cat(1)" from the remote fs (but still
* recursively on directories).
* -v Verbose mode: print filenames as they are copied.
* -i The arguments are inode numbers; treat them as
* files with those inode numbers and named by the numbers.
* -l Act like a rudimentary "ls(1)" on a remote fs.
* -L Long version of -l: gives a gruesome inode dump.
* -t Act like "tar(1)" on a remote fs. Output goes to
* the standard output. Digits following the "t" are
* interpreted as a blocksize.
* -b Set blocksize independently: this manipulates the
* internal write buffer size. Digits following, etc.
*
*---------------------------------------------------------------------------
*
* $Header: RCS/grab.c.v Revision 1.2 83/07/01 19:29:13 donn Exp$
* $Log: RCS/grab.c.v $
* Revision 1.2 83/07/01 19:29:13 donn
* Added -i and -L flags for inode grabbing and inode dumping.
* Donn
*
*/
# include "grab.h"
int target_system = TARGET_DEFAULT;
int fsbsiz = FSB_DEFAULT;
int fsys;
int pflag = 0;
int xflag = 0;
int vflag = 0;
int iflag = 0;
int lflag = 0;
int Lflag = 0;
int tflag = 0;
int nblock = B_BUFSIZE;
main( argc, argv )
int argc;
char *argv[];
{
char *tail();
char *name;
int ino;
if ( argc < 3 ) {
fprintf( stderr, "grab: too few arguments\n" );
fprintf( stderr, "Usage: grab [-246pxvilLtb] <file-system> <files>\n" );
exit( 1 );
}
/*
* Process the command line.
*/
while ( *argv[1] == '-' ) {
while ( *++argv[1] != '\0' )
switch ( *argv[1] ) {
/*
* Select type of filesystem to read.
*/
case '2':
target_system = V7_2BSD;
fsbsiz = 1024;
break;
case '4':
target_system = V7_4BSD;
fsbsiz = 1024;
break;
case '6':
target_system = V6;
fsbsiz = 512;
break;
/*
* Restore previous owner, group & protections.
*/
case 'p':
++pflag;
# ifndef PDPV6
umask( 0 );
# endif
break;
/*
* Put files on the standard output.
*/
case 'x':
++xflag;
break;
/*
* Print names of copied files.
*/
case 'v':
++vflag;
break;
/*
* Grab by inode number.
*/
case 'i':
++iflag;
break;
/*
* List directories.
*/
case 'l':
/*
* If only one file then we leave out header.
*/
lflag = argc - 3;
Lflag = 0;
break;
/*
* Long listing.
*/
case 'L':
++Lflag;
lflag = 0;
break;
/*
* Output files in "tar" format.
*/
case 't':
++tflag;
nblock = TAR_BS_DEFAULT;
/* FALL THRU */
/*
* Pick up blocking factor (if any).
*/
case 'b':
if ( *(argv[1] + 1) >= '0' &&
*(argv[1] + 1) <= '9' )
nblock = atoi( ++argv[1] );
while ( *(argv[1]+1) >= '0' && *(argv[1]+1) <= '9' )
++argv[1];
break;
default:
fprintf( stderr, "grab: unknown option -%c\n", argv[1][1] );
fprintf( stderr, "Usage: grab [-246pxvilLtb] <file-system> <files>\n" );
exit( 2 );
}
++argv;
--argc;
}
if ( argc < 3 ) {
fprintf( stderr, "grab: too few arguments\n" );
fprintf( stderr, "Usage: grab [-246pxvilLtb] <file-system> <files>\n" );
exit( 3 );
}
/*
* If the standard output is a file, assume -x by default.
* This is for backward compatibility with Arthur Olsen's grab
* and may go away.
*/
if ( ! (xflag || tflag || lflag || Lflag) ) {
int statbuf[32];
if ( fstat( STDOUT, statbuf ) == 0
# ifdef PDPV6
&& (((struct inode *) statbuf)->i_mode & V6_IFMT) == V6_IFREG )
# else
&& (((struct inode *) statbuf)->i_mode & V7_IFMT) == V7_IFREG )
# endif
++xflag;
}
/*
* Try to separate file output and verbose mode output!
* The method here is a bit nonportable...
*/
if ( tflag || xflag )
fileno( stdout ) = fileno( stderr );
/*
* Open the file system device.
*/
fsys = open( argv[1], 0 );
if ( fsys < 0 ) {
fprintf( stderr, "grab: can't open %s\n", argv[1] );
exit( 4 );
}
/*
* Set id to real id. (V6 isn't setuid because nobody cares.)
*/
# ifndef PDPV6
setuid( getuid() );
setgid( getgid() );
# endif VAX
/*
* Find the files corresponding to the file arguments
* and read them off.
*/
while ( argc > 2 ) {
if ( iflag ) {
name = NULL;
ino = atoi( argv[2] );
} else {
name = tail( argv[2] );
ino = find( argv[2] );
}
readi( ino, name );
++argv, --argc;
}
/*
* Clean up on tape output.
* Tar puts out two blank records followed by junk
* from the buffer when it flushes the buffer...
*/
if ( tflag )
tflush( TFILE );
exit( 0 );
}
'EOFEOF'
#-------------------------------------------------------------------
echo extracting readi.c
cat << 'EOFEOF' > readi.c
/*
* readi.c
*
* Routines dealing with file system I/O.
*
*---------------------------------------------------------------------------
*
* $Header: RCS/readi.c.v Revision 1.3 83/08/01 17:09:48 donn Exp$
* $Log: RCS/readi.c.v $
* Revision 1.3 83/08/01 17:09:48 donn
* Added code for symbolic links, made MPX code conditionally compiled;
* did some general prettying up.
* Donn
*
* Revision 1.2 83/07/01 19:21:48 donn
* Added code to support -i, -I and -L flags. This allows you to grab by
* inode number and to display the contents of inodes in a useful way.
* Donn
*
*/
# include "grab.h"
/*
* readi( ino, name ) -- read file corresponding to ino off of fsys and
* put it in file "name".
*/
readi( ino, name )
int ino;
char *name;
{
int ofile;
struct inode i;
struct bmap b;
char iname[DIRSIZ + 1];
/*
* Get the inode.
* Make sure that the permissions are ok.
*/
if ( ino == 0 )
return;
geti( ino, &i );
if ( ! chkaccess( &i ) ) {
fprintf( stderr, "grab: %s read-protected\n", name );
return;
}
/*
* Is it a directory? If so, then recurse.
*/
if ( isdir( &i ) ) {
dodirs( name, ino, &i, &b );
return;
}
/*
* Take care of listings for individual files.
*/
if ( Lflag ) {
idump( &i, ino, name );
return;
}
if ( lflag ) {
fprintf( stderr, "grab: %s not a directory\n", name );
return;
}
/*
* If we have no name for a file, we call it by its inode number.
*/
if ( name == NULL ) {
if ( ! iflag ) {
fprintf( stderr, "grab: Internal error (see a guru)\n" );
exit( -1 );
}
name = &iname[0];
sprintf( name, "%u", ino );
}
/*
* Deal with links to files.
*/
if ( i.i_nlink > 1 && dolinks( name, ino ) )
return;
# ifdef SYMLINKS
if ( target_system == V7_4BSD && (i.i_mode & V7_IFMT) == V7_IFLNK ) {
dosymlinks( name, &i, &b );
return;
}
# endif SYMLINKS
/*
* Deal with non-directory special files.
*/
if ( ! isreg( &i ) ) {
maknode( name, &i );
return;
}
/*
* Copy the file.
*/
if ( (ofile = bopen( name, &i, &b )) == -1 )
return;
while ( bread( &b ) > 0 )
bput( ofile, &b );
bclose( ofile, name, &i, &b );
}
/*
* geti( ino, ip ) -- read inode number ino and use it to fill the generic
* inode structure pointed to by ip.
*/
geti( ino, ip )
int ino;
register struct inode *ip;
{
char *malloc();
char *ilist;
register int n;
long bn;
register struct v6dinode *v6dp;
register struct v7dinode *v7dp;
ilist = malloc( fsbsiz );
if ( ilist == NULL ) {
fprintf( stderr, "grab: out of memory\n" );
exit( 5 );
}
bn = itod( ino );
bn = fsbtodb( bn );
lseek( fsys, bn * DBSIZE, 0 );
if ( read( fsys, ilist, fsbsiz ) != fsbsiz ) {
fprintf( stderr, "grab: read I/O error\n" );
exit( 6 );
}
switch ( target_system ) {
case V7_2BSD:
v7dp = (struct v7dinode *) ilist;
v7dp = &v7dp[ itoo( ino ) ];
ip->i_mode = v7dp->v7di_mode;
ip->i_nlink = v7dp->v7di_nlink;
ip->i_size = v7dp->v7di_size;
ip->i_uid = v7dp->v7di_uid;
ip->i_gid = v7dp->v7di_gid;
l3tol( (char *)ip->i_addr, (char *)v7dp->v7di_addr, V7NADDR );
ip->i_atime = v7dp->v7di_atime;
ip->i_mtime = v7dp->v7di_mtime;
# ifndef PDP
wswap( (char *)&ip->i_size, 1 );
wswap( (char *)ip->i_addr, V7NADDR );
wswap( (char *)&ip->i_atime, 1 );
wswap( (char *)&ip->i_mtime, 1 );
# endif
break;
case V7_4BSD:
v7dp = (struct v7dinode *) ilist;
v7dp = &v7dp[ itoo( ino ) ];
ip->i_mode = v7dp->v7di_mode;
ip->i_nlink = v7dp->v7di_nlink;
ip->i_size = v7dp->v7di_size;
ip->i_uid = v7dp->v7di_uid;
ip->i_gid = v7dp->v7di_gid;
l3tol( (char *)ip->i_addr, (char *)v7dp->v7di_addr, V7NADDR );
ip->i_atime = v7dp->v7di_atime;
ip->i_mtime = v7dp->v7di_mtime;
# ifdef PDP
wswap( (char *)&ip->i_size, 1 );
wswap( (char *)ip->i_addr, V7NADDR );
wswap( (char *)&ip->i_atime, 1 );
wswap( (char *)&ip->i_mtime, 1 );
# endif
break;
case V6:
v6dp = (struct v6dinode *) ilist;
v6dp = &v6dp[ itoo( ino ) ];
ip->i_mode = v6dp->v6di_mode;
ip->i_nlink = v6dp->v6di_nlink & 0xff;
ip->i_size = (unsigned short) v6dp->v6di_sz1;
ip->i_size += ((long) (v6dp->v6di_sz0 & 0xff)) * 0x10000L;
ip->i_uid = v6dp->v6di_uid & 0xff;
ip->i_gid = v6dp->v6di_gid & 0xff;
for ( n = 0; n < V6NADDR; ++n )
ip->i_addr[n] = (unsigned short) v6dp->v6di_addr[n];
ip->i_atime = v6dp->v6di_atime;
ip->i_mtime = v6dp->v6di_mtime;
# ifndef PDP
wswap( (char *)&ip->i_atime, 1 );
wswap( (char *)&ip->i_mtime, 1 );
# endif
break;
}
free( ilist );
}
/*
* dodirs( name, ino, ip, bp ) -- Recursively extract directory contents.
*/
dodirs( name, ino, ip, bp )
char *name;
int ino;
struct inode *ip;
struct bmap *bp;
{
register struct direct *dp;
register int j, k, l = 0;
struct inode *dirip;
int n, dpb;
int dcomp();
char *s = NULL;
char *malloc();
/*
* If we are doing listings, list the directory itself.
*/
if ( Lflag ) {
fprintf( stderr, "Directory:\n\n" );
idump( ip, ino, name );
fprintf( stderr, "Contents of directory:\n\n" );
dirip = (struct inode *) malloc( sizeof (struct inode) );
}
if ( lflag > 1 )
printf( "\n%s:\n", name );
/*
* If we only have the inode, we must simulate the name.
*/
if ( name == NULL ) {
if ( ! iflag ) {
fprintf( stderr, "grab: Internal error (see a guru)\n" );
exit( -1 );
}
name = malloc( DIRSIZ + 1 );
sprintf( name, "%u", ino );
}
/*
* Tar header for a directory.
*/
if ( tflag )
theader( TFILE, name, ip );
/*
* Allocate a buffer to hold the pathname.
*/
j = strlen( name ) + DIRSIZ + 2;
s = malloc( j );
if ( s == NULL ) {
fprintf( stderr, "grab: out of memory\n" );
exit( 7 );
}
s[j-1] = '\0';
/*
* If we aren't merely printing files, make the directory.
*/
if ( ! (xflag || lflag || Lflag || tflag ) )
if ( makdir( name, ip ) < 0 )
return;
/*
* Iterate over the target directory's entries.
*/
n = ip->i_size / sizeof (struct direct);
if ( lflag || Lflag ) {
allocbuf( ip, bp, B_BIG );
dpb = (nblock * TBLOCK) / sizeof (struct direct);
} else {
allocbuf( ip, bp, B_SMALL );
dpb = fsbsiz / sizeof (struct direct);
}
for ( j = 0; j < n; ++j ) {
/*
* Read the directory (if necessary).
*/
k = j % dpb;
if ( k == 0 ) {
bread( bp );
if ( lflag )
/*
* Sort by directory name.
*/
qsort( bp->b_data, min( dpb, n-j ), sizeof (struct direct), dcomp );
}
/*
* Find directory entry.
*/
dp = &((struct direct *) bp->b_data)[ k ];
if ( dp->d_ino == 0 )
continue;
if ( strncmp( ".", dp->d_name, DIRSIZ ) == 0 ||
strncmp( "..", dp->d_name, DIRSIZ ) == 0 )
continue;
/*
* If -l or -L flag was selected, list the directory.
*/
if ( lflag ) {
if ( ++l % 5 != 0 )
printf( "%-14.14s ", dp->d_name );
else
printf( "%-.14s\n", dp->d_name );
continue;
}
if ( Lflag ) {
geti( dp->d_ino, dirip );
idump( dirip, dp->d_ino, dp->d_name );
continue;
}
/*
* Recursively extract files.
*/
strcpy( s, name );
strncat( s, "/", 2 );
strncat( s, dp->d_name, DIRSIZ );
readi( (unsigned int) dp->d_ino, s );
}
if ( lflag && l % 5 != 0 )
putchar( '\n' );
freebuf( bp );
free( s );
if ( Lflag )
free( dirip );
/*
* Repair volatile attributes of the directory.
*/
if ( xflag || lflag || Lflag || tflag )
return;
# 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
return;
}
/*
* dcomp( d1, d2 ) -- Order two directory entries by name.
*/
dcomp( d1, d2 )
register struct direct *d1, *d2;
{
return ( strncmp( d1->d_name, d2->d_name, DIRSIZ ) );
}
/*
* dolinks( name, ino ) -- Copy links. Return 1 if link was made.
* Strategy: search link records, each containing an inumber,
* a link to the next record and a pathname.
*/
struct link
{
short ino;
short pad;
struct link *next;
char path;
};
int
dolinks( name, ino )
char *name;
int ino;
{
char *malloc();
static struct link *root = NULL;
struct link *lp = root;
if ( xflag )
return ( 0 );
/*
* Look for ino in list.
*/
while ( lp != NULL ) {
if ( lp->ino == (short) ino ) {
/*
* Found it. Emit a "tar" link or a real link.
*/
if ( tflag )
tlink( TFILE, ino, name, &(lp->path) );
else
if ( link( &(lp->path), name ) == -1 ) {
fprintf( stderr, "grab: couldn't link %s to %s\n",
name, &(lp->path) );
return ( 0 );
}
if ( vflag ) {
printf( "%s (linked to %s)\n", name, &(lp->path) );
fflush( stdout );
}
return ( 1 );
}
lp = lp->next;
}
/*
* A new name. Store it away.
*/
lp = (struct link *) malloc( (2 * sizeof (short))
+ sizeof (struct link *) + strlen( name ) + 1 );
if ( lp == NULL ) {
fprintf( stderr, "grab: out of memory\n" );
exit( 8 );
}
lp->ino = (short) ino;
lp->next = root;
strcpy( &(lp->path), name );
root = lp;
return ( 0 );
}
/*
* makdir( name, ip ) -- create a directory named name, using mkdir.
*/
makdir( name, ip )
char *name;
struct inode *ip;
{
short statbuf[16];
int pid, deadpid, status;
if ( stat( name, statbuf ) == 0 ) {
/*
* File exists -- if it's a directory, okay;
* otherwise we complain.
*/
# ifdef PDPV6
if ( (((struct inode *) statbuf)->i_mode & V6_IFMT) == V6_IFDIR )
# else
if ( (((struct inode *) statbuf)->i_mode & V7_IFMT) == V7_IFDIR )
# endif
return ( 0 );
fprintf( stderr, "grab: %s: not a directory\n", name );
return ( -1 );
}
/*
* File doesn't exist: fork and exec mkdir.
*/
pid = fork();
if ( pid == 0 ) {
execl( "/bin/mkdir", "mkdir", name, 0 );
execl( "/usr/bin/mkdir", "mkdir", name, 0 );
fprintf( stderr, "grab: can't exec mkdir\n" );
exit( 9 );
}
while ( (deadpid = wait( &status )) >= 0 && deadpid != pid )
;
# ifdef PDPV6
/*
* Sigh. V6 mkdir doesn't return a useful status.
*/
if ( deadpid < 0 ) {
# else
if ( deadpid < 0 || status != 0 ) {
# endif
fprintf( stderr, "grab: can't create directory %s\n", name );
return ( -1 );
}
/*
* Copy directory permissions.
*/
chmod( name, ip->i_mode & (pflag ? 07777 : 0777 ) );
if ( pflag )
CHOWN( name, ip->i_uid, ip->i_gid );
return ( 0 );
}
# ifdef SYMLINKS
/*
* dosymlinks( name, ip, bp ) -- Make a symbolic link from name to the file
* whose name is contained in the file with inode *ip, using bmap *bp.
*/
dosymlinks( name, ip, bp )
register char *name;
register struct inode *ip;
register struct bmap *bp;
{
allocbuf( ip, bp, B_SMALL );
bread( bp );
if ( symlink( bp->b_data, name ) == -1 )
fprintf( stderr, "grab: couldn't make symbolic link from %s to %s\n", name, bp->b_data );
else if ( vflag ) {
printf( "%s (symbolic link to %s)\n", name, bp->b_data );
fflush( stdout );
}
freebuf( bp );
}
# endif SYMLINKS
/*
* maknode( name, ip ) -- Create a device entry.
*/
maknode( name, ip )
char *name;
struct inode *ip;
{
long addr = ip->i_addr[0];
short nodemode = ip->i_mode & (pflag ? 07777 : 0777);
if ( xflag ) {
fprintf( stderr, "grab: Can't print special file %s!\n", name );
return;
} else if ( tflag ) {
fprintf( stderr, "grab: %s: Can't put on tar tape\n", name );
return;
}
/*
* Check legality of modes.
*/
# ifndef MPXFILES
if ( target_system != V6 &&
( (ip->i_mode & V7_IFMT) == V7_IFMPC ||
(ip->i_mode & V7_IFMT) == V7_IFMPB ) ) {
fprintf( stderr, "grab: %s: can't make multiplex files\n", name );
return;
}
# endif MPXFILES
# ifndef SYMLINKS
if ( target_system == V7_4BSD && (ip->i_mode & V7_IFMT) == V7_IFLNK ) {
fprintf( stderr, "grab: %s: can't make symbolic links\n", name );
return;
}
# endif SYMLINKS
/*
* Convert modes.
*/
nodemode |= CONVMODE( ip->i_mode );
/*
* Create the special file.
*/
if ( mknod( name, nodemode, addr ) == -1 ) {
fprintf( stderr, "grab: can't make special file %s\n", name );
return;
}
if ( pflag ) {
CHOWN( name, ip->i_uid, ip->i_gid );
# ifdef CLR_SETUID
if ( ip->i_mode & (ISUID|ISGID|ISVTX) )
chmod( name, ip->i_mode & 07777 );
# endif CLR_SETUID
# ifndef PDPV6
utime( name, &ip->i_atime );
# endif
}
if ( vflag )
printf( "%s (special file)\n", name );
}
/*
* l3tol( lp, cp, n ) -- convert the n 3-byte integers pointed to by cp
* into longs and put them in the array pointed to by lp.
*
* PDP long integer brain damage strikes again -- on a VAX a 3-byte long
* is the first three bytes of a true long but on a PDP it's got a hole in
* it... thus the library routine can't be used.
*/
l3tol(lp, cp, n)
long *lp;
char *cp;
int n;
{
register i;
register char *a, *b;
a = (char *)lp;
b = cp;
for(i=0;i<n;i++) {
if ( target_system != V7_4BSD ) {
*a++ = *b++;
*a++ = 0;
*a++ = *b++;
*a++ = *b++;
} else {
*a++ = *b++;
*a++ = *b++;
*a++ = *b++;
*a++ = 0;
}
}
}
/*
* wswap( longlist, count ) -- swap the words of count long integers residing
* in longlist.
*/
wswap( longlist, count )
char *longlist;
register int count;
{
register short *wptr, tmp;
wptr = (short *) longlist;
while ( count-- ) {
tmp = *wptr;
*wptr = *(wptr+1);
*++wptr = tmp;
++wptr;
}
}
'EOFEOF'
#-------------------------------------------------------------------
echo extracting tmode.c
cat << 'EOFEOF' > tmode.c
/*
* tmode.c
*
* Routines for producing blocked "tar"-style output.
*
*---------------------------------------------------------------------------
*
* $Header$
* $Log$
*/
# include "grab.h"
/*
* theader( ofile, name, ip ) -- Write a "tar" header for a file called "name"
* on file "ofile" using inode information pointed to by "ip".
*/
theader( ofile, name, ip )
int ofile;
char *name;
struct inode *ip;
{
union hblock h;
register char *cp;
register int n = TBLOCK;
/*
* Clear out the buffer.
*/
cp = h.dummy;
CLRBUF( cp, n );
/*
* Insert name.
*/
n = strlen( name );
if ( n > NAMSIZ ) {
fprintf( stderr, "grab: name too long for tar output: %s\n", name );
fprintf( stderr, "grab: name truncated to: %.*s\n", NAMSIZ, name );
}
strncpy( h.dbuf.name, name, NAMSIZ );
if ( isdir( ip ) )
h.dbuf.name[ min( n, NAMSIZ-1 ) ] = '/';
/*
* Insert modes.
*/
tomodes( &h, ip );
tput( ofile, h.dummy, TBLOCK );
}
/*
* tlink( ofile, ino, name, linkname ) -- Write a "tar" header for a file named
* name with inode ino which is linked to an earlier file called linkname.
*/
tlink( ofile, ino, name, linkname )
int ofile;
int ino;
char *name;
char *linkname;
{
union hblock h;
struct inode i;
register char *cp;
register int n = TBLOCK;
/*
* Clear out the buffer.
*/
cp = h.dummy;
CLRBUF( cp, n );
/*
* Insert names.
*/
if ( strlen( name ) > NAMSIZ ) {
fprintf( stderr, "grab: name too long for tar output: %s\n", name );
fprintf( stderr, "grab: name truncated to: %.*s\n", NAMSIZ, name );
}
strncpy( h.dbuf.name, name, NAMSIZ );
strncpy( h.dbuf.linkname, linkname, NAMSIZ );
h.dbuf.linkflag = '1';
/*
* Get modes.
*/
geti( ino, &i );
tomodes( &h, &i );
tput( ofile, h.dummy, TBLOCK );
}
/*
* tomodes( hp, ip ) -- Store modes from inode ip in header block hp.
*/
tomodes( hp, ip )
union hblock *hp;
struct inode *ip;
{
sprintf( hp->dbuf.mode, "%6o ", ip->i_mode & 07777 );
sprintf( hp->dbuf.uid, "%6o ", ip->i_uid );
sprintf( hp->dbuf.gid, "%6o ", ip->i_gid );
sprintf( hp->dbuf.size, "%11lo ", isdir( ip ) ? 0 : ip->i_size );
sprintf( hp->dbuf.mtime, "%11lo ", ip->i_mtime );
checksum( hp );
}
/*
* checksum( hp ) -- Insert tar-style checksum in header block *hp.
*/
checksum( hp )
union hblock *hp;
{
register int i;
register char *cp = hp->dummy;
register char *last = hp->dummy + TBLOCK;
for ( i = 0; i < sizeof hp->dbuf.chksum; ++i )
hp->dbuf.chksum[i] = ' ';
i = 0;
do
i += *cp++;
while ( cp < last );
sprintf( hp->dbuf.chksum, "%6o", i );
}
/*
* tput( ofile, data, size ) -- Twiddle the buffer queuing mechanism to output
* tape data on ofile.
*/
tput( ofile, data, size )
int ofile;
register char *data;
int size;
{
struct inode i;
struct bmap b;
register int n;
register char *cp;
/*
* Get access to the big data buffer.
* Notice that it really doesn't matter much what's in inode i!
*/
i.i_size = size;
allocbuf( &i, &b, B_BIG );
/*
* Get the data in place and fiddle the bmap parameters to make
* bput think it's got something to do.
*/
cp = b.b_data + b.b_cc;
n = size;
CPYBUF( cp, data, n );
b.b_offset = 0;
b.b_cc += size;
/*
* Send out the data.
*/
bput( ofile, &b );
/*
* Wrap up. Simple, huh?
*/
freebuf( &b );
}
/*
* tflush( ofile ) -- Flush remaining tape record, with two zero blocks
* appended.
*/
tflush( ofile )
int ofile;
{
char buf[TBLOCK];
register char *cp;
register int n;
extern int savetcc;
/*
* Make an empty buffer.
*/
cp = buf;
n = TBLOCK;
CLRBUF( cp, n );
/*
* Cleverly put out 2 empty buffers and write.
* The chicanery in the final tput forces a write because it makes
* bput think it has a full buffer.
*/
tput( ofile, buf, TBLOCK );
tput( ofile, buf, TBLOCK );
savetcc = nblock * TBLOCK;
tput( ofile, buf, 0 );
}
'EOFEOF'
More information about the Comp.sources.unix
mailing list