Ultrix-3.1/src/etc/rawfs.c

Compare this file to the similar file:
Show the results in this format:


/**********************************************************************
 *   Copyright (c) Digital Equipment Corporation 1984, 1985, 1986.    *
 *   All Rights Reserved. 					      *
 *   Reference "/usr/src/COPYRIGHT" for applicable restrictions.      *
 **********************************************************************/

static char *sccsid =  "@(#)rawfs.c	3.0	(ULTRIX-11)	4/22/86";
/*
 * rawfs - copy files from a raw file system.
 *	6/85	-Dave Borman
 *
 * Usage:
 *	See usage() function below.
 * Options:
 *	-n	No clobber. Existing files are not overwritten
 *	-s	Special. Copy character/block special files.
 *	-o #	File system starts at a block offset of #.
 *	-d	Debug mode. Multiple references increases debug info.
 *	-t	just do a listing, no copy.
 *	  -l	long listing
 *	  -i	print inode number
 *	  -g	print gid instead of uid
 *	  -R	Recursive, descend directories
 *	  -u	print last accessed time instead of modified time
 *	  -c	print creation time instead of modified time
 */
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/errno.h>
#include <sys/file.h>	/* for open() flags */

/*
 * Variables dealing with the file system.  These are the default
 * values, which are for a 512byte block file system.
 */
#define	BSIZE	512		/* 512 byte block */
#define	BMASK	0777		/* BSIZE-1 */
#define	NINDIR	(BSIZE/sizeof(long))
#define	NMASK	0177		/* NINDIR-1 */
#define	NSHIFT	7		/* LOG2(NINDIR) */
#define	INOPB	8		/* 8 inodes per block */
#define	NADDR	13		/* number of address entries used in inode */

#define	DIRSIZ	14		/* name length if directory entry */
#define	ROOTINO	((short)2)	/* i number of all roots */
#define	SUPERB	((long)1)	/* block number of the super block */
/*
 * These two don't matter much, because the only thing that we look
 * at in the superblock is s_fsize and s_isize, the first two entries.
 */
#define	NICINOD	100		/* number of inode in superblock */
#define	NICFREE	50		/* number of free blocks in superblock */
/*
 * Inode structure as it appears on
 * a disk block.
 */
struct dinode
{
	unsigned short	di_mode;/* mode and type of file */
	short	di_nlink;    	/* number of links to file */
	short	di_uid;      	/* owner's user id */
	short	di_gid;      	/* owner's group id */
	off_t	di_size;     	/* number of bytes in file */
	char  	di_addr[40];	/* disk block addresses */
	time_t	di_atime;   	/* time last accessed */
	time_t	di_mtime;   	/* time last modified */
	time_t	di_ctime;   	/* time created */
};
/* modes */
#define	IFMT	0170000		/* type of file */
#define		IFIFO	0010000	/* fifo special */
#define		IFREE	0000000	/* free */
#define		IFDIR	0040000	/* directory */
#define		IFCHR	0020000	/* character special */
#define		IFBLK	0060000	/* block special */
#define		IFREG	0100000	/* regular */
#define		IFLNK	0120000	/* symbolic link */
#define		IFSOCK	0140000	/* socket */
#define	ISUID	04000		/* set user id on execution */
#define	ISGID	02000		/* set group id on execution */
#define ISVTX	01000		/* save swapped text even after use */
#define	IREAD	0400		/* read, write, execute permissions */
#define	IWRITE	0200
#define	IEXEC	0100

/*
 * Structure of the super-block
 */
struct	filsys {
	unsigned short s_isize; /* size in blocks of i-list */
#ifndef	vax
	long	s_fsize;   	/* size in blocks of entire volume */
#define	SB_S_FSIZE	sb->s_fsize
#else
	/* Grrr, the vax will longword align s_fsize if it is long... */
	short	s_fsize1;
	short	s_fsize2;
#define	SB_S_FSIZE	(*((long *)&sb->s_fsize1))
#endif
	short  	s_nfree;   	/* number of addresses in s_free */
	long	s_free[NICFREE];/* free block list */
	short  	s_ninode;  	/* number of i-nodes in s_inode */
	short  	s_inode[NICINOD];/* free i-node list */
	char   	s_flock;   	/* lock during free list manipulation */
	char   	s_ilock;   	/* lock during i-list manipulation */
	char   	s_fmod;    	/* super block modified flag */
	char   	s_ronly;   	/* mounted read-only flag */
	time_t 	s_time;    	/* last super block update */
	/* set by mkfs, updated by fsck, now used by system */
	long	s_tfree;   	/* total free blocks*/
	short  	s_tinode;  	/* total free inodes */
	/* set by mkfs, used by fsck to salvage free lists */
	short   s_m;	/* interleave - free list spacing */
	short	s_n;	/* interleave - blocks per cylinder */
	/* set by mkfs and/or labelit, printed by fsck and labelit */
	char   	s_fname[6];	/* file system name */
	char   	s_fpack[6];	/* file system pack name */
	short	s_lasti;	/* start place for circular search */
	short	s_nbehind;	/* est # free inodes before s_lasti */
} *sb;

struct	direct
{
	short	d_ino;
	char	d_name[DIRSIZ];
};

int	diskio;
extern int errno;

struct diskp {
	char	*name;
	long	offset[8];
};
/*
 * Known diskp partion offsets for ULTRIX-11 V2.0.
 * All offsets are in 512 byte blocks.
 */
struct diskp  u11v2dp[] = {
	"rl01", 0L,    -1L,    -1L,    -1L,     -1L,     -1L,     -1L,  0L,
	"rl02", 0L, 10240L,    -1L,    -1L,     -1L,     -1L,     -1L,  0L,
	"rp02", 0L,  9600L, 15000L,    -1L,  25600L,     -1L,      0L, -1L,
	"rp03", 0L,  9600L, 15000L, 25600L,     -1L,     -1L,     -1L,  0L,
	"rk06", 0L,  9636L, 15840L,    -1L,     -1L,     -1L,      0L, -1L,
	"rk07", 0L,  9636L, 15840L, 27126L,     -1L,     -1L,      0L,  0L,
	"rp04", 0L,  9614L, 15884L, 27170L,     -1L,     -1L,      0L, -1L,
	"rp05", 0L,  9614L, 15884L, 27170L,     -1L,     -1L,      0L, -1L,
	"rp06", 0L,  9614L, 15884L, 27170L,  27170L, 171380L,      0L,  0L,
	"rm02", 0L,  9600L, 15840L, 26400L,  26400L,  79040L,     -1L,  0L,
	"rm03", 0L,  9600L, 15840L, 26400L,  26400L,  79040L,     -1L,  0L,
	"rm05", 0L,  9728L, 16416L, 27968L, 133152L, 133152L, 316768L,  0L,
	"rd51", 0L,  8468L,    -1L,    -1L,     -1L,     -1L,     -1L,  0L,
	"rd52", 0L,  8468L, 21568L,    -1L,     -1L,     -1L,     -1L,  0L,
	"ra60", 0L,  9600L, 15870L, 27156L,  27156L, 213156L,     -1L,  0L,
	"ra80", 0L,  9600L, 15870L, 27156L,     -1L,     -1L,     -1L,  0L,
	"ra81", 0L,  9600L, 15870L, 27156L,  27156L, 213156L, 399156L,  0L,
	"rc25", 0L,  9600L, 15870L, 27156L,     -1L,     -1L,     -1L,  0L,
	0,
};

char *me;
int debug = 0;
int Sflag = 0;
int dospecial = 0;
int noclobber = 0;
int recursive = 0;
int verbose = 0;
int xtract = 0;
int dolist = 0;
int cflag = 0;
int gflag = 0;
int iflag = 0;
int lflag = 0;
int uflag = 0;
long offset = 0L;
long year;
int Bsize = BSIZE;
int Bmask = BMASK;
int Nindir = NINDIR;
int Naddr = NADDR;
int Nshift = NSHIFT;
int Nmask = NMASK;
int Inopb = INOPB;
long		*sdatabuf;
long		*ddatabuf;
long		*tdatabuf;
char		*databuf;
char		*malloc();
#define	NAMSIZE	512

main(argc, argv)
int	argc;
register char	**argv;
{
	register int	err;
	register char	*cp;
	char		tbuf[NAMSIZE];
	struct stat	statb;
	long		atol();

	me = *argv;
	++argv; --argc;
	for (; argc > 1 && **argv == '-'; ++argv, --argc) {
	    for (cp = &(*argv)[1]; *cp; cp++)
		switch(*cp) {
		/*
		 * Flags for listing and extracting
		 */
		default:
			fprintf(stderr, "unknown switch %c\n", *cp);
			usage(NULL);
		case 'd':
			++debug;
			break;
		case 'S':
			/* swap words in longs */
			++Sflag;
			break;
		case 'k':
			/*
			 * Set up paramaters for a 1K filesystem, ala
			 * 2.9 BSD.
			 */
			Bsize = 1024;
			Bmask = 01777;
			Nindir = 256;
			Inopb = 16;
			Nshift = 8;
			Nmask = 0377;
			Naddr = 7;
			break;
		case 'b':
			Bsize *= 2;		/* double the buffer size */
			Bmask = (Bmask<<1)|1;	/* add a bit to the mask */
			Nindir *= 2;		/* double the # on indirects */
			Inopb *= 2;
			Nshift++;
			Nmask = (Nmask<<1)|1;
			break;
		case 'o':
		    {
			register struct diskp	*dp;
			register int		pt;
			if (++argv, --argc <= 0) {
				fprintf(stderr, "missing argument for -o\n");
				usage(NULL);
			}
			if (**argv >= '0' && **argv <= '9') {
				offset = atol(*argv)*512L;
				continue;
			}
			if (strlen(*argv) != 6) {
				fprintf(stderr, "%s: unknown disk\n", *argv);
				exit(1);
			}
			for (dp = u11v2dp; dp->name; dp++) {
				if (strncmp(dp->name, *argv, 4) == 0)
					break;
			}
			pt = (*argv)[5] - '0';
			if (dp->name == NULL || pt > 7 || pt < 0 ||
			    dp->offset[pt] == -1) {
				fprintf(stderr, "%s: unknown disk\n", *argv);
				exit(1);
			}
			offset = dp->offset[pt]*512L;
			if (debug)
				printf("Offset is %D\n", offset);
			break;
		    }
		/*
		 * Flags only for extracting
		 */
		case 'x':
			xtract++;
			break;
		case 'n':
			noclobber++;
			break;
		case 's':
			dospecial = 1;
			break;
		case 'v':
			verbose++;
			break;
		/*
		 * Flags only for listing
		 */
		case 't':
			dolist++;
			break;
		case 'g':
			gflag++;
			break;
		case 'c':
			cflag++;
			break;
		case 'i':
			iflag++;
			break;
		case 'l':
			lflag++;
			break;
		case 'R':
			recursive++;
			break;
		case 'u':
			uflag++;
			break;
		}
	}
	if (cflag || iflag || lflag || recursive || uflag)
		++dolist;
	if (noclobber || dospecial || verbose)
		++xtract;
	if (!dolist && !xtract) {
		fprintf(stderr, "Either the -x or -t flag is required\n");
		usage(NULL);
	}
	if (dolist && xtract) {
		fprintf(stderr, "Cannot mix t,c,g,i,l,R and u with x,n,s and v\n");
		usage(NULL);
	}
	if (dolist) {
		if (argc < 1)
			usage(NULL);
	} else if (argc < 3)
		usage(NULL);
	if ((sb = (struct filsys *)malloc(Bsize)) == NULL ||
	    (databuf = malloc(Bsize)) == NULL ||
	    (sdatabuf = (long *)malloc(Bsize)) == NULL ||
	    (ddatabuf = (long *)malloc(Bsize)) == NULL ||
	    (tdatabuf = (long *)malloc(Bsize)) == NULL) {
		fprintf(stderr, "Out of memory\n");
		exit(1);
	}
	if ((diskio = open(*argv, O_RDONLY)) < 0)
		usage(*argv);
	--argc; ++argv;
	SB_S_FSIZE = SUPERB+1; /* so that getblock won't fail */
	if (getblock((long)SUPERB, sb) < 0)
		usage(*argv);
	verify();
	if (dolist) {
		struct dinode dis;
		long time();

		year = time(0) - 6L*60L*60L*24L*30L; /* ~6 months */
		if (argc == 0) {
			if (getinode(ROOTINO, &dis) < 0) {
				errno = ENOENT;
				perror("/");
				exit(1);
			}
			if ((dis.di_mode&IFMT) != IFDIR) {
				errno = ENOTDIR;
				perror("/");
				exit(1);
			}
			list(&dis, "");
			exit(0);
		}
		while (argc > 0) {
			unsigned inum;

			if ((inum = namei(*argv, &dis, ROOTINO)) == 0) {
				errno = ENOENT;
				perror(*argv);
				err++;
			} else {
				if ((dis.di_mode&IFMT) == IFDIR)
					list(&dis, *argv);
				else {
					if (iflag)
						printf("%5u ", inum);
					if (lflag)
						dostats(&dis);
					printf("%s\n", *argv);
				}
			}
			--argc; ++argv;
		}
		exit(err);
	}
	--argc;
	err = stat(argv[argc], &statb);
	if (err == -1) {
		if (argc != 1)
			usage(argv[argc]);
		sprintf(tbuf, "%s", argv[1]);
	} else if ((statb.st_mode&S_IFMT) != S_IFDIR) {
		if (argc == 1 && ((statb.st_mode&S_IFMT) == S_IFREG)) {
			sprintf(tbuf, "%s", argv[1]);
		} else {
			fprintf(stderr, "%s: not a directory\n", argv[argc]);
			usage(NULL);
		}
	} else {
		if (cp = rindex(*argv, '/'))
			cp++;
		else
			cp = *argv;
		sprintf(tbuf, "%s/%s", argv[argc], cp);
	}
	for (;;) {
		struct dinode dis;
		if (namei(*argv, &dis, ROOTINO) == 0) {
			errno = ENOENT;
			perror(*argv);
			err++;
		} else if (copyfile(&dis, tbuf) < 0) {
			fprintf(stderr, "%s:can't copy\n", *argv);
			err++;
		}
		if (--argc <= 0)
			break;
		++argv;
		if (cp = rindex(*argv, '/'))
			cp++;
		else
			cp = *argv;
		sprintf(tbuf, "%s/%s", argv[argc], cp);
	}
	exit(err);
}

usage(s)
char *s;
{
	if (s != NULL)
		perror(s);
	fprintf(stderr,
	 "usage: %s -x[Skbsnv] [-o offset] special file file\n", me);
	fprintf(stderr,
	 "       %s -x[Skbsnv] [-o offset] special file ... dir\n", me);
	fprintf(stderr,
	 "       %s -t[SkbcgilRu] [-o offset] special [file ...]\n", me);
	fprintf(stderr,
	 "         offset: 512byte block number or diskname/partition\n");
	exit(1);
}

swab(p)
register short *p;
{
	register short t;
	t = *p;
	*p = *(p+1);
	*(p+1) = t;
}

/*
 * Verify a filesystem by doing a few sanity checks on
 * the superblock and the root inode.
 */
verify()
{
	struct dinode		dis;
	register struct dinode	*dp = &dis;

	if (Sflag)
		swab(&SB_S_FSIZE);
	if (SB_S_FSIZE < 0 ||
	    SB_S_FSIZE > 077777777L ||
	    getblock(SB_S_FSIZE-1, databuf) < 0) {
		fprintf(stderr,
			"Bad file system size (%D blocks)\n", SB_S_FSIZE);
		exit(1);
	}
	if (sb->s_isize >= SB_S_FSIZE) {
		fprintf(stderr,
			"inode list larger than filesystem size (%u > %D)\n",
			sb->s_isize, SB_S_FSIZE);
		exit(1);
	}
	if (getinode(ROOTINO, dp) < 0) {
		fprintf(stderr, "Can't get root inode\n");
		exit(1);
	}
	switch(dp->di_mode&IFMT) {
	case IFDIR:
		break;
	case IFCHR:
		fprintf(stderr, "Root inode is a character special file!\n");
		exit(1);
	case IFBLK:
		fprintf(stderr, "Root inode is a block special file!\n");
		exit(1);
	case IFREG:
		fprintf(stderr, "Root inode is a regular file!\n");
		exit(1);
	default:
		fprintf(stderr, "Root inode is of unknown type! (%o)\n",
								dp->di_mode);
		exit(1);
	}
	if ((off_t)dp->di_nlink*(off_t)sizeof(struct direct) > dp->di_size) {
		fprintf(stderr, "Root inode has %u links and only %D entries",
			dp->di_nlink, dp->di_size/sizeof(struct direct));
		exit(1);
	}
}

copyfile(dp, to)
register struct dinode	*dp;
register char		*to;
{

	if (debug)
		printf("copyfile(%o, %s)\n", dp, to);
	switch (dp->di_mode&IFMT) {
	case IFREG:
		{
			register int fio;

			if (noclobber)
				fio = open(to, O_CREAT|O_WRONLY|O_EXCL, dp->di_mode);
			else
				fio = open(to, O_CREAT|O_WRONLY, dp->di_mode);
			if (fio < 0) {
				perror(to);
				return(-1);
			}
			if (copyi(dp, fio) < 0) {
				close(fio);
				unlink(to);
				return(-1);
			} else {
				close(fio);
				chown(to, dp->di_uid, dp->di_gid);
				chmod(to, dp->di_mode);
				utime(to, &dp->di_atime);
				if (verbose)
					printf("%s\n", to);
				return(0);
			}
		}
	case IFCHR:
	case IFBLK:
		if (!dospecial) {
			if (verbose)
				printf("%s: special file not copied\n", to);
		} else {
			register char *p1, *p2;
			long zarf;

			p1 = (char *)&zarf;
			p2 = (char *)&dp->di_addr[0];
			*p1++ = *p2++;
			*p1++ = 0;
			*p1++ = *p2++;
			*p1 = *p2;
			if (mknod(to, dp->di_mode, (int)zarf) < 0) {
				perror(to);
				return(-1);
			}
			chown(to, dp->di_uid, dp->di_gid);
			chmod(to, dp->di_mode);
			utime(to, &dp->di_atime);
			if (verbose)
				printf("%s\n", to);
			return(0);
		}
	case IFDIR:
		return(copydir(dp, to));
	/*
	 *	fprintf(stderr, "%s: can't copy directories (yet)\n", from);
	 */
	default:
		fprintf(stderr, "bad file type (%o)\n", dp->di_mode);
		break;
	}
	return(-1);
}

namei(name, inode, inum)
char			*name;
struct dinode		*inode;
register unsigned	inum;
{
	register char		*p1, *cp;
	struct dinode		dis;
	char			nbuf[DIRSIZ+1];
	unsigned		search();

	if (debug)
		printf("namei(%s, %o, %u)\n", name, inode, inum);
	for (p1 = name; *p1 == '/'; p1++)
		;
	for(;;) {
		if (getinode(inum, &dis) < 0)
			return(0);
		if (*p1 == '\0') {
			*inode = dis;
			return (inum);
		}
		if ((dis.di_mode&IFMT) != IFDIR)
			return(0);
		cp = nbuf;
		for (; *p1 && *p1 != '/'; p1++)
			if (cp < &nbuf[DIRSIZ])
				*cp++ = *p1;
		*cp = '\0';
		for (; *p1 == '/'; p1++)
			;
		inum = search(&dis, nbuf);
		if (inum == 0)
			return(0);
	}
	/*NOTREACHED*/
}

/*
 * laddr converts a 3 character block number as is
 * stored in a disk inode, and returns it as a long.
 */
long laddr(p2)
register char *p2;
{
	register char	*p1;
	long		t;

	if (debug>4)
		printf("laddr(%o %o %o) = ", p2[0], p2[1], p2[2]);
	p1 = (char *)&t;
	if (Sflag) {
		/* swap the words for VAX/PDP conflict */
		register char t;
		t = *p2++;
		*p1++ = *p2++;
		*p1++ = *p2;
		*p1++ = t;
		*p1 = 0;
	} else {
		*p1++ = *p2++;
		*p1++ = 0;
		*p1++ = *p2++;
		*p1 = *p2;
	}
	if (debug>4)
		printf("%D\n", t);
	return(t);
}

/*
 * Bmap defines the structure of file system storage
 * by returning the physical block number on a device given the
 * inode and the logical block number in a file.
 */
long
bmap(dp, bn)
register struct dinode	*dp;
long			bn;
{
	register	i;
	int		j, sh;
	long		nb;

	if (bn < 0)
		return((long)0);

	/*
	 * blocks 0..Naddr-4 are direct blocks
	 */
	if (bn < Naddr-3) {
		i = bn;
		nb = laddr(&dp->di_addr[i*3]);
		if (nb == 0)
			return((long)-1);
		return(nb);
	}

	/*
	 * addresses Naddr-3, Naddr-2, and Naddr-1
	 * have single, double, triple indirect blocks.
	 * the first step is to determine
	 * how many levels of indirection.
	 */
	sh = 0;
	nb = 1;
	bn -= Naddr-3;
	for (j=3; j>0; j--) {
		sh += Nshift;
		nb <<= Nshift;
		if (bn < nb)
			break;
		bn -= nb;
	}
	if (j == 0)
		return((long)0);

	/*
	 * fetch the address from the inode
	 */
	nb = laddr(&(dp->di_addr[(Naddr-j)*3]));
	if (nb == 0)
		return((long)-1);

	/*
	 * fetch through the indirect blocks
	 */
	for (; j<=3; j++) {
		if (getblock(nb, databuf) < 0) {
			nb = (long)0;
			break;
		}
		sh -= Nshift;
		i = (bn>>sh) & Nmask;
		nb = ((long *)databuf)[i];
		if (nb == 0)
			return((long)-1);
	}
	return(nb);
}

unsigned search(dp, name)
register struct dinode	*dp;
register char		*name;
{
	register struct direct	*dir, *edir;
	char			*buf;
	long			bn, lbn, llbn;

	if (debug)
		printf("search(%o, %s)\n", dp, name);
	if ((dp->di_mode&IFMT) != IFDIR) {
		if (debug)
			printf("   not a directory\n");
		return(0);
	}
	if ((buf = malloc(Bsize)) == NULL) {
		fprintf(stderr, "Out of memory\n");
		return(0);
	}
	edir = (struct direct *)&buf[Bsize];
	llbn = dp->di_size/Bsize;
	for (lbn = 0; lbn <= llbn; lbn++) {
		bn = bmap(dp, lbn);
		if ((bn == -1L) || (getblock(bn, buf) == -1))
			continue;
		if (lbn == llbn)
			edir = (struct direct *)&buf[dp->di_size&Bmask];
		for (dir = (struct direct *)buf; dir < edir; dir++) {
			if (debug>1)
				printf("    %5u %.14s\n", dir->d_ino, dir->d_name);
			if (dir->d_ino && !strncmp(dir->d_name, name, DIRSIZ)) {
				int inum = dir->d_ino;
				free(buf);
				return(inum);
			}
		}
	}
	free(buf);
	return(0);
}

copydir(dp, to)
register struct dinode	*dp;
register char		*to;
{
	int			statval;
	struct direct		*edir;
	long			bn, lbn, llbn;
	struct stat		statb;
	char			*buf, nbuf[NAMSIZE];
	char			*rindex();

	if (debug)
		printf("copydir(%o, %s)\n", dp, to);
	if ((statval = stat(to, &statb)) == 0) {
		if ((statb.st_mode&S_IFMT) != S_IFDIR) {
			fprintf(stderr, "%s: not a directory\n", to);
			return(-1);
		}
	} else {
		register char *cp;

		if (debug)
			printf("making empty directory %s\n", to);
		if (mknod(to, dp->di_mode, 0) < 0) {
			perror(to);
			return(-1);
		}
		sprintf(nbuf, "%s/.", to);
		link(to, nbuf);
		sprintf(nbuf, "%s/..", to);
		cp = rindex(to, '/');
		if (cp) {
			if (cp != to) {
				*cp = '\0';
				link(to, nbuf);
				*cp = '/';
			} else
				link("/", nbuf);
		} else
			link(".", nbuf);
	}
	if ((buf = malloc(Bsize)) == NULL) {
		fprintf(stderr, "Out of memory\n");
		return(-1);
	}
	edir = (struct direct *)&buf[Bsize];
	llbn = dp->di_size/Bsize;
	for (lbn = 0; lbn <= llbn; lbn++) {
		register struct direct	*dir;

		bn = bmap(dp, lbn);
		if (bn == -1L)
			continue;
		if (getblock(bn, buf) == -1)
			continue;
		if (lbn == llbn)
			edir = (struct direct *)&buf[dp->di_size&Bmask];
		for (dir = (struct direct *)buf; dir < edir; dir++) {
			struct dinode dis;
			if (debug>1)
				printf("    %5u %.14s\n", dir->d_ino, dir->d_name);
			if (!dir->d_ino)
				continue;
			if (dir->d_name[0] == '.' &&
			    (dir->d_name[1] == '\0' ||
			     (dir->d_name[1] == '.' &&
			      dir->d_name[2] == '\0')))
				continue;
			sprintf(nbuf, "%s/%.14s", to, dir->d_name);
			if (getinode(dir->d_ino, &dis) < 0) {
				printf("bad inode: %o %.14s\n",
						dir->d_ino, dir->d_name);
				continue;
			}
			if (copyfile(&dis, nbuf) < 0) {
				fprintf(stderr, "can't copy to %s\n", nbuf);
			}
		}
	}
	if (statval < 0) {
		chown(to, dp->di_uid, dp->di_gid);
		chmod(to, dp->di_mode);
		utime(to, &dp->di_atime);
		if (verbose)
			printf("%s\n", to);
	}
	free(buf);
	return(0);
}

copyi(dp, fio)
register struct dinode	*dp;
int			fio;
{
	register i, resid;
	long bn, max;
	long t;

	if (debug)
		printf("copyi(%o, %d)\n", dp, fio);
	max = dp->di_size/Bsize;
	resid = dp->di_size & Bmask;

	/*
	 * Copy direct blocks first
	 */
	for (i = 0, bn = 0; i < Naddr-3 && bn <= max; i++, bn++)
	    if (cp_data(laddr(&dp->di_addr[i*3]),fio,(bn<max)?Bsize:resid) < 0)
		return(-1);

	/*
	 * Single indirect blocks.
	 */
	if (bn <= max+1)
	    if (cp_sindir(laddr(&dp->di_addr[(Naddr-3)*3]),&bn,max,fio,resid)<0)
		return(-1);

	/*
	 * Double indirect blocks.
	 */
	if (bn <= max+1)
	    if (cp_dindir(laddr(&dp->di_addr[(Naddr-2)*3]),&bn,max,fio,resid)<0)
		return(-1);

	/*
	 * Triple indirect blocks
	 */
	if (bn <= max) {
		if (laddr(&dp->di_addr[(Naddr-1)*3]) != 0L) {
		    if (getblock(laddr(&dp->di_addr[(Naddr-1)*3]),tdatabuf) < 0)
			return(-1);
		    if (Sflag) {
			for (i = 0; i < Nindir && bn <= max; i++) {
			    swab(&tdatabuf[i]);
			    if (cp_dindir(tdatabuf[i], &bn, max, fio, resid)<0)
				return(-1);
			}
		    } else {
			for (i = 0; i < Nindir && bn <= max; i++)
			    if (cp_dindir(tdatabuf[i], &bn, max, fio, resid)<0)
				return(-1);
		    }
		}
	}
	lseek(fio, dp->di_size, 0);
	return(0);
}

cp_dindir(block, bnp, max, fio, resid)
long		block, max;
register long	*bnp;
register int	fio;
int		resid;
{
	register int	i;

	if (debug>2)
		printf("cp_dindir(%D, %o, %D, %d %d)\n", block, bnp, max, fio, resid);
	if (block == 0) {
		*bnp += (long)Nindir*(long)Nindir;
		return(lseek(fio, (long)Nindir*(long)Nindir*(long)Bsize, 1));
	}
	if (getblock(block, ddatabuf) < 0)
		return(0);
	if (Sflag) {
		for (i = 0; i < Nindir && *bnp <= max; i++) {
			swab(&ddatabuf[i]);
			if (cp_sindir(ddatabuf[i], bnp, max, fio, resid) < 0)
				return(-1);
		}
		return(0);
	}
	for (i = 0; i < Nindir && *bnp <= max; i++)
		if (cp_sindir(ddatabuf[i], bnp, max, fio, resid) < 0)
			return(-1);
	return(0);
}

cp_sindir(block, bnp, max, fio, resid)
long		block, max;
register long	*bnp;
register int	fio;
int		resid;
{
	register int	i;

	if (debug>2)
		printf("cp_sindir(%D, %o, %D, %d)\n", block, bnp, max, fio);
	if (block == 0) {
		*bnp += Nindir;
		return(lseek(fio, (long)Nindir*(long)Bsize, 1));
	}
	if (getblock(block, sdatabuf) < 0)
		return(-1);
	if (Sflag) {
		for (i = 0; i < Nindir && *bnp <= max; i++, (*bnp)++) {
			swab(&sdatabuf[i]);
			if (cp_data(sdatabuf[i], fio,
					(*bnp < max) ? Bsize : resid) < 0)
				return(-1);
		}
		return(0);
	}
	for (i = 0; i < Nindir && *bnp <= max; i++, (*bnp)++)
		if (cp_data(sdatabuf[i], fio, (*bnp < max) ? Bsize : resid) < 0)
			return(-1);
	return(0);
}

cp_data(block, fio, resid)
long	block;
int	fio;
int	resid;
{
	if (debug>2)
		printf("cp_data(%D, %d)\n", block, fio);
	if (block == 0)
		return(lseek(fio, (long)Bsize, 1));
	if (getblock(block, databuf) < 0)
		return(-1);
	write(fio, databuf, resid);
	return(0);
}

getblock(block, buf)
long	block;
char	*buf;
{
	long	lseek();

	if (debug>3)
		printf("getblock(%D, %o)\n", block, buf);
	if (debug>5)
		printf("SB_S_FSIZE == %D\n", SB_S_FSIZE);
	if (block < 0L || block > SB_S_FSIZE) {
		fprintf(stderr, "getblock(%D): block out of range\n", block);
		fprintf(stderr, "    %D > %D\n", block, SB_S_FSIZE);
		return(-1);
	}
	if (lseek(diskio, offset + Bsize*block, 0) == -1L ||
	    read(diskio, buf, Bsize) != Bsize) {
		fprintf(stderr, "getblock(%lu): %s\n", block, perror(NULL));
		return(-1);
	}
	return(0);
}

getinode(inum, dp)
register unsigned	inum;
struct dinode		*dp;
{
	register int	i;

	if (debug>1)
		printf("getinode(%u, %o)\n", inum, dp);
	if (inum > (sb->s_isize - 2)*Inopb) {
		printf("getinode: bad inumber %u\n", inum);
		return(-1);
	}
#define ITOD(x)	((long)((((unsigned short)(x)+2*Inopb-1)/Inopb)))
	if (getblock(ITOD(inum), (char *)databuf) < 0)
		return(-1);

	i = ((inum - 1) % Inopb);
	*dp = ((struct dinode *)databuf)[i];
	if (Sflag) {
		swab(&dp->di_size);
	}
	return(0);
}

list(dp, prefix)
register struct dinode	*dp;
register char		*prefix;
{
	int			statval;
	struct direct		*edir;
	long			bn, lbn, llbn;
	char			*buf, nbuf[NAMSIZE];

	if (debug)
		printf("list(%o, %s)\n", dp, prefix);
	if ((buf = malloc(Bsize)) == NULL) {
		fprintf(stderr, "Out of memory\n");
		return(0);
	}
	edir = (struct direct *)&buf[Bsize];
	llbn = dp->di_size/Bsize;
	for (lbn = 0; lbn <= llbn; lbn++) {
		register struct direct	*dir;

		bn = bmap(dp, lbn);
		if (bn == -1L)
			continue;
		if (getblock(bn, buf) == -1)
			continue;
		if (lbn == llbn)
			edir = (struct direct *)&buf[dp->di_size&Bmask];
		for (dir = (struct direct *)buf; dir < edir; dir++) {
			struct dinode dis;
			if (debug>1)
				printf("    %5u %.14s\n", dir->d_ino, dir->d_name);
			if (!dir->d_ino)
				continue;
			if (dir->d_name[0] == '.' &&
			    (dir->d_name[1] == '\0' ||
			     (dir->d_name[1] == '.' &&
			      dir->d_name[2] == '\0')))
				continue;
			sprintf(nbuf, "%s/%.14s", prefix, dir->d_name);
			if (getinode(dir->d_ino, &dis) < 0) {
				printf("bad inode: %o %.14s\n",
						dir->d_ino, dir->d_name);
				continue;
			}
			if (iflag)
				printf("%5u ", dir->d_ino);
			if (lflag)
				dostats(&dis);
			printf("%s\n", nbuf);
			if (recursive && (dis.di_mode&IFMT)==IFDIR)
				list(&dis, nbuf);
		}
	}
	free(buf);
	return(0);
}

dostats(dp)
struct dinode *dp;
{
	register char	*cp;
	char		*ctime(), *getmode();
	long		mtime;

	printf("%s%2d ", getmode(dp->di_mode), dp->di_nlink);
	if (gflag)
		printf("%-8d", dp->di_gid);
	else
		printf("%-8d", dp->di_uid);
	switch(dp->di_mode&IFMT) {
	case IFBLK:
	case IFCHR:
		mtime = laddr(dp->di_addr);
		printf("%3d,%3d", (int)major(mtime), (int)minor(mtime));
		break;
	default:
		printf("%7ld", dp->di_size);
		break;
	}
	mtime = uflag ? dp->di_atime : (cflag ? dp->di_ctime : dp->di_mtime);
	if (Sflag)
		swab(&mtime);
	cp = ctime(&mtime);
	if (mtime < year)
		printf(" %-7.7s %-4.4s ", cp+4, cp+20);
	else
		printf(" %-12.12s ", cp+4);
}

char * getmode(p_mode)
register unsigned short p_mode;
{
	static char a_mode[16];
	register char *p = a_mode;
	register int	i = 0;

	switch (p_mode & IFMT) {
	case IFIFO:	*p++ = 'f'; break;
	case IFDIR:	*p++ = 'd'; break;
	case IFLNK:	*p++ = 'l'; break;
	case IFSOCK:	*p++ = 's'; break;
	case IFCHR:	*p++ = 'c'; break;
	case IFBLK:	*p++ = 'b'; break;
	case IFREG:
	default:	*p++ = (p_mode & ISVTX) ? 't' : '-'; break;
	}
	for (i = 0; i < 3; i++) {
		*p++ = (p_mode<<(3*i) & IREAD) ? 'r' : '-';
		*p++ = (p_mode<<(3*i) & IWRITE) ? 'w' : '-';
		*p++ = (i<2 && (p_mode<<i & ISUID)) ? 's' :
			      ((p_mode<<(3*i) & IEXEC ) ? 'x' : '-');
	}
	*p = '\0';
	return(a_mode);
}