|
|
DataMuseum.dkPresents historical artifacts from the history of: Commodore CBM-900 |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about Commodore CBM-900 Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - download
Length: 17514 (0x446a)
Types: TextFile
Notes: UNIX file
Names: »unmkfs.c«
└─⟦f27320a65⟧ Bits:30001972 Commodore 900 hard disk image with partial source code
└─⟦f4b8d8c84⟧ UNIX Filesystem
└─⟦this⟧ »local/unmkfs.c«
/*
* 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);
}