DataMuseum.dk

Presents historical artifacts from the history of:

Commodore CBM-900

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about Commodore CBM-900

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - download

⟦9b6aa71c0⟧ TextFile

    Length: 17514 (0x446a)
    Types: TextFile
    Notes: UNIX file
    Names: »unmkfs.c«

Derivation

└─⟦f27320a65⟧ Bits:30001972 Commodore 900 hard disk image with partial source code
    └─⟦f4b8d8c84⟧ UNIX V7 Filesystem
        └─ ⟦this⟧ »local/unmkfs.c« 

TextFile

/*
 * Given a directory tree root and a filesystem size,
 * write the fewest mkfs proto files necessary to
 * copy the directory tree onto floppies.
 * Preserve the ownerships, modes, dates, links, and order of links
 * within a directory.
 * Make each fragment root based so that a series of
 *	mount /dev/fd0 /f0; cpdir /f0 destination; umount /dev/fd0
 * can be used to reinstall the original directory.
 *
 * The algorithm for partitioning is empirical and may not work very
 * well for directories other than the pc coherent distribution.
 * Some degree of interaction is probably desirable for getting
 * reasonable partitioning of arbitrary directory trees.
 *
 * Overview:
 *	After minimal checks for necessary conditions,
 *	Read the source directory tree into a memory
 *		resident pseudo file system in which MINODE inumbers
 *		identify unique files and replace dp->d_ino in the
 *		directories.
 *	While the original root directory is not flagged I_DONE,
 *		copy those parts of the tree that are not flagged I_DONE.
 *		While the copy is too big for the floppy partition
 *			prune the copy.
 *	For each pruned copy produced, write the mkfs proto.
 *
 * -- rec 26.VI.84 -- invent cpfrag.
 * -- rec 12.IX.84 -- reconstruct cpfrag -> unmkfs.
 * -- norm 04.I.85 -- fix misc. bugs for z8000
 */
#include <stdio.h>
#include <dir.h>
#include <assert.h>
#include <sys/const.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/filsys.h>
#include <sys/ino.h>

typedef struct MINODE {
	struct MINODE *i_link1;	/* dev x ino hash linkage */
	struct MINODE *i_link2;	/* my inumbering hash linkage */
	char	*i_linkname;	/* Name of first instance of file in copy */
	int	i_mino;		/* My inumber */
	int	i_flag;		/* Miscellaneous flags */
	int	i_blks;		/* Cumulative block size, includes indirects */
	int	i_inos;		/* Cumulative inodes used */
	int	i_size;		/* Total data, indir, and inode blocks */
	int	i_isdir;	/* Simplify many tests */
	dev_t	i_dev;		/* Some fields from stat() */
	ino_t	i_ino;
	int	i_mode;
	int	i_nlink;
	int	i_uid;
	int	i_gid;
	int	i_rdev;
	time_t	i_mtime;
	int	i_nent;		/* Number of directory entries */
	struct direct i_elem[];	/* Directory entries */
} MINODE;

#define I_DONE	1	/* Inode is done */
#define I_COUNT	2	/* Inode is counted */
#define I_PUT	8	/* Inode size reported */
#define I_DONE1	16	/* Inode has been done once, for directories */
#define I_PURGE	32	/* Inode should be purged */
#define I_KEEP	64	/* Keep entire subdirectory */
#define I_CANFIT 128	/* Subdirectory could fit on disk */
#define I_ISMADE 256	/* Inode is made, do link */

#define NDISK 32
MINODE *disks[NDISK];
int	dsize;
int	dused;
int	vflag = 1;
int	excess;
int	ndisk;
int	myuid;
int	mygid;

MINODE	*makeroot();
MINODE	*cpyroot();
MINODE	*cpydir();
MINODE	*select();
int	purge();
int	entermi();
MINODE *fetchmi();
long	blkuse();
char	*myalloc();
char	*string();

extern char edata[];

#define MAXFNAME	512
char fname[MAXFNAME];	/* Filename buffer */
char fname1[MAXFNAME];	/* Second file name buffer */
char cmdbuf[128];
struct stat sbuf;	/* Stat buffer */
struct stat tbuf;	/* Time buffer, leave zero for all times */
char *argv0;		/* For error recovery */

main(argc, argv)
char *argv[];
{
	int i;
	MINODE *rip, *tip, *sip;

	argv0 = argv[0];
	if (argc < 3)
		usage();
	dsize = atoi(argv[2]);
	if (argc == 4) {
		if (stat(argv[3], &tbuf) < 0) {
			fprintf(stderr, "unmkfs: can't stat %s\n", argv[3]);
			exit(1);
		}
	} else
		tbuf.st_mtime = 0;	/* make time == 0 to get all files */
	rip = makeroot(argv[1]);
	while ((rip->i_flag & I_DONE) == 0) {
		tip = cpyroot(rip);
		dused = 4;
		keepers(tip);
		excess = tip->i_size + 2 - dsize;
		while (excess > 0) {
			while ((sip = select(tip)) == NULL) {
				excess += 1;
			}
			flagroot(sip, I_PURGE);
			purge(tip);
			sizeroot(tip);
			excess = tip->i_size + 2 - dsize;
		}
		donedir(tip);
		markdir(rip);
		disks[ndisk] = tip;
		ndisk += 1;
	}
	for (i = 0; i < ndisk; i += 1) {
		makedisk(i, argv[1], argv[2]);
	}
}

/*
** Get the MINODE * corresponding to fname, and call
** makedir() to build the in-memory tree.  Call sizeroot()
** return the MINODE corresponding to the root.
*/
MINODE *
makeroot(cp)
char *cp;
{
	MINODE *rip;

	if (strlen(cp) >= MAXFNAME) {
		fprintf(stderr, "unmkfs: initial path name too long\n");
		exit(1);
	}
	strcpy(fname, cp);
	if (stat(fname, &sbuf) < 0)
		cantstat();
	if ((sbuf.st_mode&S_IFMT) != S_IFDIR) {
		fprintf(stderr, "unmkfs: initial path not directory\n");
		exit(1);
	}
	rip = fetchmi(entermi());
	if (cp[0] == '/' && cp[1] == '\0')
		fname[0] = 0;
	makedir(rip);
	sizeroot(rip);
	return (rip);
}

/*
** Recursively build a tree of MINODE pointers
** for the directory ip.
*/
makedir(ip)
MINODE *ip;
{
	int	fd;
	int	i;
	struct direct *dp1, *dp2;
	MINODE *tip;
	char *cp;

	cp = fname + strlen(fname);
	if (cp + DIRSIZ + 2 >= fname + MAXFNAME) {
		fprintf(stderr, "unmkfs: directory tree too deep\n");
		exit(1);
	}
	if ((fd = open(fname, 0)) < 0) {
		fprintf(stderr, "unmkfs: cannot open: %s\n", fname);
		exit(1);
	}
	i = ip->i_nent * sizeof(struct direct);
	if (read(fd, ip->i_elem, i) != i) {
		fprintf(stderr, "unmkfs: read error: %s\n", fname);
		exit(1);
	}
	close(fd);
	*cp = '/';
	dp1 = dp2 = ip->i_elem;
	for (i = 0; i < ip->i_nent; i += 1) {
		if (dp2->d_name[0] == '.') {
			if (dp2->d_name[1] == 0
			 || (dp2->d_name[1] == '.' && dp2->d_name[2] == 0))
				dp2->d_ino = 0;
		}
		if (dp2->d_ino != 0) {
			strncpy(cp+1, dp2->d_name, DIRSIZ);
			if (stat(fname, &sbuf) < 0)
				cantstat();
			dp2->d_ino = entermi();
		}
		if (dp2->d_ino != 0) {
			if (dp1 != dp2)
				*dp1 = *dp2;
			dp1 += 1;
		}
		dp2 += 1;
	}
	ip->i_nent = dp1 - ip->i_elem;
	i = sizeof(MINODE) + ip->i_nent * sizeof(struct direct);
	if (realloc(ip, i) != ip) {
		fprintf(stderr, "unmkfs: realloc moved block\n");
		exit(1);
	}
	dp1 = ip->i_elem;
	for (i = 0; i < ip->i_nent; i += 1) {
		tip = fetchmi(dp1->d_ino);
		if (tip->i_isdir) {
			strncpy(cp+1, dp1->d_name, DIRSIZ);
			makedir(tip);
		}
		dp1 += 1;
	}
	*cp = 0;
}

printroot(cp, rip)
char *cp;
MINODE *rip;
{
	uflagroot(rip, I_PUT);
	strcpy(fname, cp);
	splat(rip);
	if (cp[0] == '/' && cp[1] == 0)
		fname[0] = 0;
	printdir(rip);
}

printdir(ip)
MINODE *ip;
{
	int i;
	MINODE *tip;
	struct direct *dp;
	char *cp;

	dp = ip->i_elem;
	cp = fname + strlen(fname);
	*cp = '/';
	for (i = 0; i < ip->i_nent; i += 1) {
		tip = fetchmi(dp->d_ino);
		strncpy(cp+1, dp->d_name, DIRSIZ);
		splat(tip);
		if (tip->i_isdir)
			printdir(tip);
		dp += 1;
	}
	*cp = 0;
}

splat(ip)
MINODE *ip;
{
	printf("(%2d,%2d,%4d) ",
		major(ip->i_dev), minor(ip->i_dev), ip->i_ino);
	if ((ip->i_flag & I_PUT) != 0)
		printf("%6d %4d %6d ", 0, 0, 0);
	else
		printf("%6d %4d %6d ", ip->i_size, ip->i_inos, ip->i_blks);
	printf("%s\n", fname);
	ip->i_flag |= I_PUT;
}

FILE *ofp;

makedisk(n, cp, sp)
char *cp, *sp;
{
	MINODE *ip;
	static char dname[32];

	ip = disks[n];
	fprintf(stderr, "\ndisk %d, %d inodes, %d data blocks\n",
		n+1, ip->i_inos, ip->i_blks);
	ofp = stdout;
	mkfs(ip->i_inos);
	if (cp[0] == '/' && cp[1] == 0)
		fname[0] = 0;
	else
		strcpy(fname, cp);
	fprintf(ofp, "d--%03o %3d %3d\n", ip->i_mode&0777, ip->i_uid,
		ip->i_gid);
	indent(1);
	insdir(ip);
	indent(-1);
	fprintf(ofp, "$\n");
}

mkfs(nino)
{
	fprintf(ofp, "/dev/null xxxxx xxxxx\n");
	fprintf(ofp, "%d %d 1 1\n", dsize, nino);
}

insdir(ip)
MINODE *ip;
{
	int i;
	MINODE *tip;
	struct direct *dp;
	char *cp, *cp1;
	time_t date[2];
	char dtype;

	cp = fname + strlen(fname);
	cp1 = fname1 + strlen(fname1);
	*cp = '/';
	*cp1 = '/';
	dp = ip->i_elem;
	for (i = 0; i < ip->i_nent; i += 1) {
		tip = fetchmi(dp->d_ino);
		strncpy(cp+1, dp->d_name, DIRSIZ);
		strncpy(cp1+1, dp->d_name, DIRSIZ);
		indent(0);
		fprintf(ofp, "%-14s", dp->d_name);
		if (tip->i_flag & I_ISMADE) {
			fprintf(ofp, " l-----   0   0 %s\n", tip->i_linkname);
			dp += 1;
			continue;
		}
		switch (tip->i_mode & S_IFMT) {
		case S_IFDIR:
			dtype = 'd';
			break;
		case S_IFCHR:
			dtype = 'c';
			break;
		case S_IFBLK:
			dtype = 'b';
			break;
		case S_IFREG:
			dtype = '-';
			break;
		default:
			fprintf(stderr, "unmkfs: bad file type %d of %s\n",
				tip->i_mode&S_IFMT, fname);
			exit(1);
		}
		fprintf(ofp, " %c%c%c%03o %3d %3d",
			dtype,
			(tip->i_mode&ISUID) ? 'u' : '-',
			(tip->i_mode&ISGID) ? 'g' : '-',
			tip->i_mode&0777,
			tip->i_uid, tip->i_gid);
		switch (tip->i_mode & S_IFMT) {
		case S_IFDIR:
			fputc('\n', ofp);
			indent(1);
			insdir(tip);
			indent(-1);
			indent(0);
			fprintf(ofp, "$\n");
			break;
		case S_IFCHR:
		case S_IFBLK:
			fprintf(ofp, "%3d %3d\n", major(tip->i_rdev),
				minor(tip->i_rdev));
			break;
		case S_IFREG:
			fprintf(ofp, " %s\n", fname);
			break;
		}
		if (tip->i_nlink > 1)
			tip->i_linkname = string(fname1);
		tip->i_flag |= I_ISMADE;
		dp += 1;
	}
	*cp = 0;
	*cp1 = 0;
}

indent(n)
int n;
{
	static int indent;

	if (n < 0)
		indent -= 1;
	else if (n > 0)
		indent += 1;
	else for (n = indent; --n >= 0; fprintf(ofp, " "));
}

uflagroot(rip, flag)
MINODE *rip;
{
	uflagdir(rip, flag);
	rip->i_flag &= ~flag;
}

/*
** Recursively turn off flag in ip and
** all directories below ip.
*/
uflagdir(ip, flag)
MINODE *ip;
{
	int i;
	MINODE *tip;
	struct direct *dp;

	dp = ip->i_elem;
	for (i = 0; i < ip->i_nent; i += 1) {
		tip = fetchmi(dp->d_ino);
		tip->i_flag &= ~flag;
		if (tip->i_isdir)
			uflagdir(tip, flag);
		dp += 1;
	}
}

flagroot(ip, flag)
MINODE *ip;
{
	if (ip->i_isdir)
		flagdir(ip, flag);
	ip->i_flag |= flag;
}

flagdir(ip, flag)
MINODE *ip;
{
	int i;
	MINODE *tip;
	struct direct *dp;

	dp = ip->i_elem;
	for (i = 0; i < ip->i_nent; i += 1) {
		tip = fetchmi(dp->d_ino);
		if (tip->i_isdir)
			flagdir(tip, flag);
		tip->i_flag |= flag;
		dp += 1;
	}
}

sizeroot(rip)
MINODE *rip;
{
	uflagroot(rip, I_COUNT);
	sizedir(rip);
	/* Add in bad block inode */
	rip->i_size = rip->i_blks + (++rip->i_inos+INOPB-1) / INOPB;
}

/*
** For subdirectories not flagged I_COUNT,
** add the isize and blksize to that of ip.
*/
sizedir(ip)
MINODE *ip;
{
	int i;
	MINODE *tip;
	struct direct *dp;

	ip->i_blks = 0;
	ip->i_inos = 0;
	dp = ip->i_elem;
	for (i = 0; i < ip->i_nent; i += 1) {
		tip = fetchmi(dp->d_ino);
		if (tip->i_isdir)
			sizedir(tip);
		if ((tip->i_flag&I_COUNT) == 0) {
			ip->i_inos += tip->i_inos;
			ip->i_blks += tip->i_blks;
			tip->i_flag |= I_COUNT;
		}
		dp += 1;
	}
	ip->i_inos += 1;	/* For me */
	ip->i_blks += blkuse((long)(ip->i_nent+2)*sizeof(struct direct));
	ip->i_size = ip->i_blks + (ip->i_inos+INOPB-1) / INOPB;
}

MINODE *
cpyroot(rip)
MINODE *rip;
{
	uflagdir(rip, I_PURGE|I_KEEP);
	rip = cpydir(rip);
	sizeroot(rip);
	return (rip);
}

MINODE *
cpydir(ip)
MINODE *ip;
{
	int i;
	MINODE *nip, *tip;
	struct direct *dp1, *dp2;

	nip = fetchmi(duplmi(ip));
	nip->i_nent = 0;
	dp1 = ip->i_elem;
	dp2 = nip->i_elem;
	for (i = 0; i < ip->i_nent; i += 1) {
		tip = fetchmi(dp1->d_ino);
		if ((tip->i_flag&I_DONE) != 0)
			tip = NULL;
		else if (tip->i_isdir)
			tip = cpydir(tip);
		if (tip != NULL) {
			dp2->d_ino = tip->i_mino;
			strncpy(dp2->d_name, dp1->d_name, DIRSIZ);
			nip->i_nent += 1;
			dp2 += 1;
		}
		dp1 += 1;
	}
	if (nip->i_nent < ip->i_nent) {
		i = sizeof(MINODE) + nip->i_nent * sizeof(struct direct);
		if (realloc(nip, i) != nip) {
			fprintf(stderr, "unmkfs: realloc moved block\n");
			exit(1);
		}
	}
	return (nip);
}

keepers(ip)
MINODE *ip;
{
	int i;
	MINODE *tip;
	struct direct *dp;

	dp = ip->i_elem;
	for (i = 0; i < ip->i_nent; i += 1) {
		tip = fetchmi(dp->d_ino);
		if (tip->i_isdir == 0) {
			dp += 1;
			continue;
		}
		if (tip->i_size < dsize - 4)
			tip->i_flag |= I_CANFIT;
		if (dused + tip->i_size < dsize) {
			tip->i_flag |= I_KEEP;
			dused += tip->i_size;
		}
		dp += 1;
	}
	dp = ip->i_elem;
	for (i = 0; i < ip->i_nent; i += 1) {
		tip = fetchmi(dp->d_ino);
		if (tip->i_isdir != 0
		 && (tip->i_flag & (I_CANFIT|I_KEEP)) == 0)
			keepers(tip);
		dp += 1;
	}
}

MINODE *
select(ip)
MINODE *ip;
{
	int i;
	MINODE *uip, *lip, *tip;
	struct direct *dp;

	uip = lip = NULL;
	dp = ip->i_elem;
	for (i = 0; i < ip->i_nent; i += 1) {
		tip = fetchmi(dp->d_ino);
		if (tip->i_flag & I_KEEP) {
			dp += 1;
			continue;
		}
		if (tip->i_flag & I_CANFIT)
			return (tip);
		if (tip->i_size == excess)
			return (tip);
		else if (tip->i_size > excess) {
			if (uip == NULL || uip->i_size > tip->i_size)
				uip = tip;
		} else {
			if (lip == NULL || lip->i_size < tip->i_size)
				lip = tip;
		}
		dp += 1;
	}
	if (lip != NULL)
		return (lip);
	if (uip->i_isdir)
		return (select(uip));
	return (uip);
}

purge(rip)
MINODE *rip;
{
	int i;
	MINODE *tip;
	struct direct *dp1, *dp2;

	assert(rip->i_isdir);
#if I8086
	assert((char *)&dp2 > edata + 16);
#endif
	dp1 = dp2 = rip->i_elem;
	for (i = 0; i < rip->i_nent; i += 1) {
		tip = fetchmi(dp1->d_ino);
		if (tip->i_isdir) {
			purge(tip);
			if ((tip->i_flag & I_PURGE) && tip->i_nent == 0) {
				dp1->d_ino = 0;
				freemi(tip->i_mino);
			}
		} else if (tip->i_flag & I_PURGE)
			dp1->d_ino = 0;
		if (dp1->d_ino != 0) {
			if (dp1 != dp2)
				*dp2 = *dp1;
			dp2 += 1;
		}
		dp1 += 1;
	}
	rip->i_nent = dp2 - rip->i_elem;
}

donedir(ip)
MINODE *ip;
{
	int i;
	MINODE *tip;
	struct direct *dp;

	ip->i_link1->i_flag |= I_DONE1;
	dp = ip->i_elem;
	for (i = 0; i < ip->i_nent; i += 1) {
		tip = fetchmi(dp->d_ino);
		if (tip->i_isdir)
			donedir(tip);
		else
			tip->i_flag |= I_DONE|I_DONE1;
		dp += 1;
	}
}

markdir(ip)
MINODE *ip;
{
	int i;
	MINODE *tip;
	struct direct *dp;
	int flag;

	if (ip->i_flag & I_DONE)
		return (ip->i_flag);
	dp = ip->i_elem;
	flag = I_DONE;
	for (i = 0; i < ip->i_nent; i += 1) {
		tip = fetchmi(dp->d_ino);
		if (tip->i_isdir)
			markdir(tip);
		flag &= tip->i_flag;
		dp += 1;
	}
	if ((flag & I_DONE) != 0 && (ip->i_flag & I_DONE1) != 0)
		ip->i_flag |= I_DONE;
}

#define IHASH	128
MINODE *ihash1[IHASH];	/* dev x ino hash */
MINODE *ihash2[IHASH];	/* mino hash */
int	minumber = 1;

/*
** Return the mino in the hash table ihash1 corresponding to 
** the statbuf.  If not found, enter it and return the resulting
** entry.
*/
entermi()
{
	MINODE *ip, **ipp;
	int	nent;

	ipp = &ihash1[sbuf.st_ino % IHASH];
	while ((ip = *ipp) != NULL) {
		if (ip->i_ino == sbuf.st_ino
		 && ip->i_dev == sbuf.st_dev)
			return (ip->i_mino);
		ipp = &ip->i_link1;
	}
	nent = 0;
	if ((sbuf.st_mode&S_IFMT) == S_IFDIR)
		nent = sbuf.st_size / sizeof(struct direct);
	else if (sbuf.st_mtime < tbuf.st_mtime)
		return 0;
	else if (blkuse(sbuf.st_size) > dsize-5) {
		fprintf(stderr, "unmkfs: file %s too large - omitted\n", fname);
		return 0;
	}
	*ipp = ip = myalloc(sizeof(MINODE) + nent * sizeof(struct direct));
	ip->i_dev = sbuf.st_dev;
	ip->i_ino = sbuf.st_ino;
	ip->i_mode = sbuf.st_mode;
	ip->i_nlink = sbuf.st_nlink;
	ip->i_uid = sbuf.st_uid;
	ip->i_gid = sbuf.st_gid;
	ip->i_rdev = sbuf.st_rdev;
	ip->i_mtime = sbuf.st_mtime;
	ip->i_blks = blkuse(sbuf.st_size);
	ip->i_inos = 1;
	ip->i_size = ip->i_blks;
	ip->i_nent = nent;
	ip->i_mino = minumber++;
	ip->i_isdir = (nent != 0);
	ipp = &ihash2[ip->i_mino % IHASH];
	ip->i_link2 = *ipp;
	*ipp = ip;
	return (ip->i_mino);
}

duplmi(ip)
MINODE *ip;
{
	MINODE *nip, **ipp;

	nip = myalloc(sizeof(MINODE) + ip->i_nent * sizeof(struct direct));
	nip->i_dev = ip->i_dev;
	nip->i_ino = ip->i_ino;
	nip->i_mode = ip->i_mode;
	nip->i_nlink = ip->i_nlink;
	nip->i_uid = ip->i_uid;
	nip->i_gid = ip->i_gid;
	nip->i_rdev = ip->i_rdev;
	nip->i_blks = ip->i_blks;
	nip->i_inos = ip->i_inos;
	nip->i_size = ip->i_size;
	nip->i_nent = ip->i_nent;
	nip->i_mino = minumber++;
	nip->i_isdir = ip->i_isdir;
	ipp = &ihash2[nip->i_mino % IHASH];
	nip->i_link2 = *ipp;
	*ipp = nip;
	nip->i_link1 = ip;
	return (nip->i_mino);
}

/*
** Find the entry i ihash2 corresponding to mino.
** Die with message if not there.
*/
MINODE *
fetchmi(mino)
{
	MINODE *ip, **ipp;

	ipp = &ihash2[mino % IHASH];
	while ((ip = *ipp) != NULL)
		if (ip->i_mino == mino)
			return (ip);
		else
			ipp = &ip->i_link2;
	fprintf(stderr, "unmkfs: nonexistent internal inumber %d\n", mino);
	exit(1);
}

freemi(mino)
{
	MINODE *ip, **ipp;

	ipp = &ihash2[mino % IHASH];
	while ((ip = *ipp) != NULL)
		if (ip->i_mino == mino) {
			*ipp = ip->i_link2;
			free(ip);
			return;
		} else
			ipp = &ip->i_link2;
	fprintf(stderr, "unmkfs: nonexistent internal inumber %d\n", mino);
	exit(1);
}
/*
 * A corrected disk usage computation
 * for retrofit into /usr/src/cmd/du.c, /usr/src/cmd/ls.c/prsize(),
 * and /usr/src/cmd/quot.c since they are all wrong.
 *
 * And this is not quite right either since it doesn't deal with sparse
 * blocks.
 */
long
blkuse(nb)
long nb;
{
#undef NBN
#define NBN	128L
#define nindir(x)	(((x)+NBN-1)/NBN)
#define nblock(x)	(((x)+BSIZE-1)/BSIZE)
#define min(x, y)	((x)<(y) ? (x) : (y))
	long bu, ndir, nidir, niidir;

	nb = nblock(nb);
	ndir = min(nb, ND);
	nb -= ndir;
	bu = ndir;
	if (nb) {
		nidir = min(nb, NBN);
		nb -= nidir;
		bu += nidir + 1;
		if (nb) {
			niidir = min(nb, NBN*NBN);
			nb -= niidir;
			bu += niidir + 1 + nindir(niidir);
			if (nb)
				bu += nb + 1 + nindir(nindir(nb)) + nindir(nb);
		}
	}
	return (bu);
}

char *
string(cp)
char *cp;
{
	char *sp;

	sp = myalloc(strlen(cp)+1);
	strcpy(sp, cp);
	return (sp);
}

char *
myalloc(nb)
int nb;
{
	char *p;

	if ((p = malloc(nb)) == NULL) {
		fprintf(stderr, "unmkfs: out of space\n");
		exit(1);
	}
	while (--nb >= 0)
		p[nb] = 0;
	return (p);
}

usage()
{
	fprintf(stderr, "Usage: unmkfs directory size_in_blocks [filename]\n");
	exit(1);
}

cantstat()
{
	fprintf(stderr, "unmkfs: cannot stat: %s\n", fname);
	exit(1);
}