|
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 - download
Length: 20097 (0x4e81) Types: TextFile Notes: UNIX file Names: »tar.c«
└─⟦f27320a65⟧ Bits:30001972 Commodore 900 hard disk image with partial source code └─⟦f4b8d8c84⟧ UNIX Filesystem └─ ⟦this⟧ »cmd/tar.c«
/* * Tape archive * tar [0-7bcflmrtuvwx]+ [blocks] [archive] pathname* */ #include <stdio.h> #include <types.h> #include <errno.h> #include <canon.h> #include <stat.h> #include <dir.h> #define S_PERM 07777 /* should be in stat.h */ #define MAXBLK 20 #define roundup(n, r) (((n)+(r)-1)/(r)) typedef struct dirhd_t { dev_t t_dev; ino_t t_ino; unsigned short t_nlink, t_mode, t_uid, t_gid; size_t t_size; time_t t_mtime; char *t_name; struct dirhd_t *t_cont[]; } dirhd_t; typedef union tarhd_t { struct { char th_name[100], th_mode[8], th_uid[8], th_gid[8], th_size[12], th_mtime[12], th_check[8], th_islink, th_link[100], th_pad[255]; }; char th_data[BUFSIZ]; } tarhd_t; typedef unsigned short flag_t; /* fastest type for machine */ flag_t linkmsg = 0, /* message if not all links found */ modtime = 1, /* restore modtimes */ verbose = 0; unixbug = 0; /* avoid bug in U**X tar */ FILE *whether = (FILE *)NULL, /* ask about each file */ *tarfile; char tapedev[10] = '\0'; char *archive = &tapedev[0]; unsigned short blocking = 1; /* blocking factor */ time_t oldtime[2], /* for utime */ recently, /* set to 6 months ago for tv key */ time(); dirhd_t *newdirhd(), *update(), *research(); tarhd_t *readhdr(), *readblk(), *writeblk(); char *havelink(); long getoctl(); extern char *malloc(); extern char *ctime(); main(argc, argv) int argc; char *argv[]; { char *key, unit = '\0', function = 0, deffunc, prefix[101] = '\0'; unsigned short arg = 2; dirhd_t *args; if (argc < 2) { fprintf(stderr, "Usage: %s [crtux][0-7bflmvw] [blocks] [archive] pathname*\n", argv[0]); exit(-1); } for (key = argv[1]; *key != '\0'; key++) switch (*key) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': unit = *key; deffunc = 't'; continue; case 'b': if (argc <= arg) fatal("missing blocking factor"); else if ((blocking = atoi(argv[arg++])) <= 0 || blocking > MAXBLK) fatal("illegal blocking factor"); deffunc = 'r'; continue; case 'f': if (argc <= arg) fatal("missing archive name"); else archive = argv[arg++]; deffunc = 't'; continue; case 'l': linkmsg = 1; deffunc = 'r'; continue; case 'm': modtime = 0; deffunc = 'x'; continue; case 'v': verbose = 1; deffunc = 't'; continue; case 'U': unixbug = 1; deffunc = 't'; continue; case 'w': whether = fopen("/dev/tty", "r+w"); deffunc = 'x'; continue; case 'c': case 'r': case 't': case 'u': case 'x': if (function) fatal("keys c, r, t, u, x are mutually exclusive"); else function = *key; continue; default: fatal("illegal key `%c'", *key); } if (!function) function = deffunc; oldtime[0] = time((time_t *)0); if (verbose && function=='t') recently = oldtime[0] - 6*((time_t)30*24*60*60); /* construct descriptors for args given */ args = (dirhd_t *) malloc(sizeof (dirhd_t) + (argc-arg)*sizeof (dirhd_t *)); if (args == NULL) fatal("out of memory"); args->t_mode = S_IFDIR; args->t_nlink = 0; for (; arg < argc; arg++) { dirhd_t *argp; if ((argp = newdirhd(argv[arg], strlen(argv[arg]))) == NULL) fprintf(stderr, "Tar: %s: out of memory\n", argv[arg]); else args->t_cont[args->t_nlink++] = argp; } /* open archive file */ if (*archive == '\0') sprintf(archive, "/dev/%smt%c", blocking==1 ? "" : "r", unit); if (function=='t' || function=='x') if (strcmp(archive, "-")==0) tarfile = stdin; else tarfile = fopen(archive, "r"); else if (function=='c') if (strcmp(archive, "-")==0) tarfile = stdout; else tarfile = fopen(archive, "w"); else tarfile = fopen(archive, "r+w"); if (tarfile==NULL) { exit(perror("Tar: %s", archive)); } setbuf(tarfile, NULL); /* perform required function */ switch (function) { case 't': table(args); break; case 'x': extract(args); break; case 'r': while (readblk() != NULL) ; goto doappend; case 'u': if ((args = update(prefix, args, readhdr())) == NULL) break; case 'c': doappend: append(prefix, args); flushtar(); } exit(errno); } fatal(args) char *args; { fprintf(stderr, "Tar: %r\n", &args); exit(-1); } dirhd_t * newdirhd(name, len) register char *name; unsigned short len; { register dirhd_t *dp; if ((dp = (dirhd_t *) malloc(sizeof (dirhd_t))) != NULL && (dp->t_name = malloc(len+1)) == NULL) { free(dp); dp = NULL; } if (dp != NULL) { dp->t_nlink = 0; strncpy(dp->t_name, name, len); dp->t_name[len] = '\0'; } return (dp); } int perror(args) char *args; { register int err; if ((err=errno) >= sys_nerr) fprintf(stderr, "%r: Bad error number\n", &args); else if (err) fprintf(stderr, "%r: %s\n", &args, sys_errlist[err]); return (err); } table(args) register dirhd_t *args; { register tarhd_t *header; for (; (header = readhdr()) != NULL; skipfile(header)) { if (argcont(args, header->th_name, 0) < 0) continue; if (verbose) { register unsigned short mode = getoct(header->th_mode); unsigned short uid = getoct(header->th_uid), gid = getoct(header->th_gid); size_t size = getoctl(header->th_size); time_t mtime = getoctl(header->th_mtime); char *timestr = ctime(&mtime); if (header->th_name[strnlen(header->th_name, 100)-1] == '/') putchar('d'); else putchar('-'); printf("%c%c%c", mode&S_IREAD ? 'r' : '-', mode&S_IWRITE ? 'w' : '-', mode&S_ISUID ? 's' : mode&S_IEXEC ? 'x' : '-'); printf("%c%c%c", mode&S_IREAD>>3 ? 'r' : '-', mode&S_IWRITE>>3 ? 'w' : '-', mode&S_ISGID ? 's' : mode&S_IEXEC>>3 ? 'x' : '-'); printf("%c%c%c", mode&S_IREAD>>6 ? 'r' : '-', mode&S_IWRITE>>6 ? 'w' : '-', mode&S_ISVTX ? 't' : mode&S_IEXEC>>6 ? 'x' : '-'); printf("%3d %3d %6ld %.11s%.5s ", gid, uid, size, timestr, mtime > recently ? timestr+11 : timestr+19); } printf("%.100s", header->th_name); if (header->th_islink) if (verbose) printf("\n%33s link to %s\n", "", header->th_link); else printf(" link to %s\n", header->th_link); else putchar('\n'); } } extract(args) register dirhd_t *args; { register tarhd_t *header; short skipping = 0; while ((header = readhdr()) != NULL) { register char name[101]; unsigned short namelen = strnlen(header->th_name, 100), mode, uid, gid; if (!skipping || contains(name, header->th_name) >= 0) { strncpy(name, header->th_name, namelen); name[namelen--] = '\0'; if ((skipping = argcont(args, name, 0)) >= 0) skipping = disallow('x', name); } switch (skipping) { case -1: skipping = 0; case 1: skipfile(header); continue; } if (verbose) printf("x %s\n", name); mode = getoct(header->th_mode); uid = getoct(header->th_uid); gid = getoct(header->th_gid); oldtime[1] = getoctl(header->th_mtime); if (name[namelen] == '/') { name[namelen] = '\0'; makepath(name); chmod(name, mode); name[namelen] = '/'; } else if (header->th_islink == '\0') { int fd = recreate(name, mode); size_t size = getoctl(header->th_size); for (; size > 0; size -= sizeof (tarhd_t)) { header = readblk(); if (fd >= 0 && write(fd, header->th_data, size > sizeof (tarhd_t) ? sizeof (tarhd_t) : (int)size) <= 0) perror("Tar: %s", name); } if (fd >= 0) close(fd); } else { struct stat statbuf; flag_t xlink = 1; if (stat(header->th_link, &statbuf) < 0) close(recreate(header->th_link, mode)); else if (havelink(statbuf.st_dev, statbuf.st_ino, 0) != NULL) xlink = 0; if (xlink) fprintf(stderr, "Tar: Must extract %s\n", header->th_link); unlink(name); mkparent(name); if (link(header->th_link, name) < 0) fprintf(stderr, "Tar: Can't link %s to %s\n", name, header->th_link); } if (modtime) utime(name, oldtime); chown(name, uid, gid); } } dirhd_t * update(name, args, header) char *name; register dirhd_t *args; tarhd_t *header; { unsigned short namelen = strlen(name); flag_t donedir = 0; if (header == NULL) return (args); if (args->t_nlink == 0 && (args = research(name, args)) == NULL) return (NULL); do switch (args->t_mode&S_IFMT) { register short arg; register dirhd_t *argp; case S_IFREG: if (args->t_mtime <= getoctl(header->th_mtime)) args->t_nlink = 0; skipfile(header); continue; case S_IFDIR: if (namelen != 0 && !donedir) { name[namelen] = '/'; donedir++; } if ((arg = argcont(args, header->th_name+namelen+donedir, -1)) >= 0) { strcpy(name+namelen+donedir, (argp=args->t_cont[arg])->t_name); if ((args->t_cont[arg] = update(name, argp, header)) == NULL) args->t_cont[arg] = args->t_cont[--args->t_nlink]; } else skipfile(header); name[namelen+donedir] = '\0'; } while ((header=readhdr()) != NULL && contains(name, header->th_name)); if (header != NULL) ungetblk(); if (args->t_nlink == 0) { if (namelen != 0) free(args->t_name); free((char *) args); return (NULL); } else return (args); } append(name, args) char *name; register dirhd_t *args; { unsigned short namelen; register unsigned short arg; register dirhd_t *argp; if ((namelen = strlen(name)) != 0 && disallow('a', name)) return; if (args->t_nlink == 0 && (args = research(name, args)) == NULL) return; switch (args->t_mode&S_IFMT) { case S_IFDIR: if (namelen != 0) { name[namelen++] = '/'; name[namelen] = '\0'; writehdr(name, args, ""); } for (arg = 0; arg < args->t_nlink; arg++) { strcpy(name+namelen, (argp=args->t_cont[arg])->t_name); append(name, argp); } break; case S_IFREG: if (args->t_nlink != 1) { char *link; if ((link=havelink(args->t_dev, args->t_ino, 1))!=NULL) { writehdr(name, args, link); break; } else { filelink(args->t_dev, args->t_ino, args->t_nlink, name); } } writehdr(name, args, ""); writefil(name, args->t_size); } if (namelen != 0) free(args->t_name); free((char *) args); } dirhd_t * research(name, args) char *name; register dirhd_t *args; { unsigned short namelen; register short nfile; int fd; struct stat statbuf; if ((namelen = strlen(name)) == 0) name = "."; if (stat(name, &statbuf) < 0) { perror("Tar: %s", name); return (args); } args->t_dev = statbuf.st_dev; args->t_ino = statbuf.st_ino; args->t_nlink = statbuf.st_nlink; args->t_uid = statbuf.st_uid; args->t_gid = statbuf.st_gid; args->t_mtime = statbuf.st_mtime; switch ((args->t_mode = statbuf.st_mode)&S_IFMT) { case S_IFREG: args->t_size = statbuf.st_size; return (args); default: args->t_size = 0; return (args); case S_IFDIR: args->t_size = 0; } if ((nfile = (short) (statbuf.st_size/sizeof (struct direct))) > 2) { args = (dirhd_t *) realloc((char *)args, sizeof (dirhd_t) + (nfile-2)*sizeof (dirhd_t *)); if (args == NULL) { fprintf(stderr, "Tar: %s: out of memory\n", name); return (NULL); } } args->t_nlink = 0; if ((fd = open(name, 0)) < 0) { perror("Tar: %s", name); return (args); } for (; nfile > 0; --nfile) { struct direct dir_ent; unsigned short arglen; dirhd_t *argp; switch (read(fd, (char *)&dir_ent, sizeof (struct direct))) { case -1: perror("Tar: %s", name); case 0: break; default: if (dir_ent.d_ino == 0 || strcmp(dir_ent.d_name, ".") == 0 || strcmp(dir_ent.d_name, "..") == 0) continue; else if ((arglen=strnlen(dir_ent.d_name, DIRSIZ)) + namelen > 99) fprintf(stderr, "Tar: %s/%.*s: name too long\n", name, DIRSIZ, dir_ent.d_name); else if ((argp = newdirhd(dir_ent.d_name, arglen)) == NULL) fprintf(stderr, "Tar: %s/%.*s: out of memory\n", name, DIRSIZ, dir_ent.d_name); else args->t_cont[args->t_nlink++] = argp; continue; } break; } close(fd); return (args); } int argcont(args, name, ret) register dirhd_t *args; char *name; int ret; { register unsigned short arg; if (args->t_nlink == 0) return (ret); else for (arg = 0; arg < args->t_nlink; arg++) if (contains(args->t_cont[arg]->t_name, name)) return (arg); return (-1); } int strnlen(str, maxlen) register char *str; register unsigned short maxlen; { register unsigned short len = 0; while (maxlen && *str++ != '\0') { --maxlen; ++len; } return (len); } /* * Create file system paths */ int makepath(pathname) char *pathname; { struct stat statbuf; register int err; if (stat(pathname, &statbuf) == 0) if ((statbuf.st_mode&S_IFMT) == S_IFDIR) return (0); else errno = ENOTDIR; else if (errno == ENOENT) return ((err=mkparent(pathname)) != 0 ? err : mkdir(pathname)); return (perror("Tar: %s", pathname)); } int mkparent(pathname) register char *pathname; { register char *pathend = &pathname[strlen(pathname)]; while (pathend > pathname && *--pathend != '/') ; if (pathend > pathname) { *pathend = '\0'; errno = makepath(pathname); *pathend = '/'; } else errno = 0; return (errno); } mkdir(pathname) char *pathname; { int status; switch(fork()) { case -1: break; case 0: close(2); execl("/bin/mkdir", "mkdir", pathname, NULL); exit(errno); default: wait(&status); errno = status>>8; } return (perror("Tar: %s", pathname)); } int recreate(pathname, mode) char *pathname; unsigned short mode; { int fd; if ((fd = create(pathname, mode)) < 0 && (errno != ENOENT || mkparent(pathname) == 0 && (fd = create(pathname, mode)) < 0)) perror("Tar: %s", pathname); return (fd); } int create(pathname, mode) char *pathname; unsigned short mode; { int fd; unlink(pathname); if ((fd = creat(pathname, mode)) >= 0) { struct stat statbuf; fstat(fd, &statbuf); filelink(statbuf.st_dev, statbuf.st_ino, statbuf.st_nlink, pathname); } return (fd); } /* * Ask about current action */ int disallow(function, pathname) char function, *pathname; { while (whether) { register int c1, c; fprintf(whether, "%c %s? ", function, pathname); for (c1=c=getc(whether); c!=EOF && c!='\n'; c=getc(whether)) ; switch (c1) { case EOF: case 'x': if (function == 'a') flushtar(); exit(errno); case '\n': case 'n': case 'N': return (1); case 'y': case 'Y': return (0); } } return (0); } /* * High level I/O routines */ tarhd_t * readhdr() { register tarhd_t *header; while ((header = readblk()) != NULL) { if (header->th_name[0] == '\0') if (unixbug) { ungetblk(); return (NULL); } else continue; else { int check = getoct(header->th_check); strncpy(header->th_check, " ", sizeof(header->th_check)); if (checksum(header->th_data, sizeof(tarhd_t)) != check) fprintf(stderr, "Tar: %.100s: bad checksum\n", header->th_name); else break; } } return (header); } skipfile(header) tarhd_t *header; { register size_t size; if (header->th_islink) return; for (size = getoctl(header->th_size); size > 0 && readblk() != NULL; size -= sizeof (tarhd_t)) ; } writehdr(name, args, link) char *name; register dirhd_t *args; char *link; { register tarhd_t *header = writeblk(); if (verbose) fprintf(stderr, "a %s", name); strncpy(header->th_name, name, sizeof(header->th_name)); putoct(header->th_mode, args->t_mode&S_PERM); putoct(header->th_uid, args->t_uid); putoct(header->th_gid, args->t_gid); putoctl(header->th_size, args->t_size); putoctl(header->th_mtime, args->t_mtime); strncpy(header->th_check, " ", sizeof(header->th_check)); if (*link == '\0') { header->th_islink = 0; if (verbose) if (args->t_size == 0) putc('\n', stderr); else fprintf(stderr, " %ld block%s\n", roundup(args->t_size, BUFSIZ), args->t_size <= BUFSIZ ? "" : "s"); } else { header->th_islink = '1'; if (verbose) fprintf(stderr, " link to %s\n", link); } strncpy(header->th_link, link, sizeof(header->th_link)); strncpy(header->th_pad, "", sizeof(header->th_pad)); putoct(header->th_check, checksum(header->th_data, sizeof(tarhd_t))); } writefil(name, size) char *name; register size_t size; { int fd; register tarhd_t *header; if ((fd = open(name, 0)) < 0) { perror("Tar: %s", name); } for (; size > 0; size -= sizeof (tarhd_t)) { header = writeblk(); if (fd >= 0) read(fd, header->th_data, sizeof (tarhd_t)); else strncpy(header->th_data, "", sizeof (tarhd_t)); } if (fd >= 0) close(fd); } /* * Tape I/O, with record blocking if specified */ tarhd_t buffer[MAXBLK], *current = &buffer[0]; tarhd_t * readblk() { static unsigned short blocks = 0; if (feof(tarfile)) return (NULL); if (current == &buffer[blocks]) { current = &buffer[0]; blocks = fread((char *) buffer, sizeof (tarhd_t), MAXBLK, tarfile); if (ferror(tarfile)) { exit(perror("Tar: %s", archive)); } else if (feof(tarfile)) { return (NULL); } } return (current++); } ungetblk() { --current; } tarhd_t * writeblk() { if (current == &buffer[blocking]) { current = &buffer[0]; fwrite((char *)buffer, sizeof (tarhd_t), blocking, tarfile); if (ferror(tarfile)) { exit(perror("Tar: %s", archive)); } } return (current++); } flushtar() { register tarhd_t *header; while ((header=writeblk()) != &buffer[0]) strncpy(header->th_data, "", sizeof (tarhd_t)); if (linkmsg) misslink(); } /* * Keep track of files by ino in hash table */ typedef struct link_t { dev_t t_dev; ino_t t_ino; unsigned short t_nlink; struct link_t *t_next; char t_link[]; } link_t; #define NHASH 64 link_t *linklist[NHASH]; filelink(dev, ino, nlink, link) dev_t dev; ino_t ino; unsigned short nlink; char *link; { unsigned short id = ino%NHASH; register link_t *lp; lp = (link_t *) malloc(sizeof (link_t) + strlen(link) + 1); if (lp == NULL) fprintf(stderr, "Tar: %s: note: link info lost\n", link); else { lp->t_next = linklist[id]; linklist[id] = lp; lp->t_dev = dev; lp->t_ino = ino; lp->t_nlink = nlink; strcpy(lp->t_link, link); } } char * havelink(dev, ino, flag) dev_t dev; ino_t ino; flag_t flag; { register link_t *lp; for (lp = linklist[ino%NHASH]; lp != NULL; lp = lp->t_next) { if (lp->t_ino == ino && lp->t_dev == dev) { if (flag) --lp->t_nlink; return (lp->t_link); } } return (NULL); } misslink() { unsigned short ino; for (ino = 0; ino < NHASH; ino++) { register link_t *lp; for (lp = linklist[ino]; lp != NULL; lp=lp->t_next) { register short nlink; if (nlink = lp->t_nlink - 1) fprintf(stderr, "Tar: missed %d link%s to %s\n", nlink, nlink==1 ? "" : "s", lp->t_link); } } } /* * Read unsigned octal numbers */ int getoct(cp) register char *cp; { register int val = 0; register char c; while (*cp == ' ') cp++; while ((c = *cp++ - '0') >= 0 && c <= 7) val = val<<3 | c; return (val); } long getoctl(cp) register char *cp; { register long val = 0; register char c; while (*cp==' ') cp++; while ((c = *cp++ - '0') >= 0 && c <= 7) val = val<<3 | c; return (val); } /* * Write unsigned octal numbers * in fixed format */ putoct(str, val) char *str; unsigned short val; { register char *cp; *(cp = &str[7]) = '\0'; *--cp = ' '; *--cp = (val&07) + '0'; while (cp != str) *--cp = (val>>=3) ? (val&07) + '0' : ' '; } putoctl(str, val) char *str; register unsigned long val; { register char *cp; *(cp = &str[11]) = ' '; *--cp = (val&07) + '0'; while (cp != str) *--cp = (val>>=3) ? (val&07) + '0' : ' '; } /* * Compute checksum of string */ int checksum(str, len) register char *str; register int len; { register int check = 0; if (len) do check += *str++; while (--len); return (check); } /* * Return -1 if dname is a directory prefix of fname * 0 if no match * 1 if complete match */ int contains(dname, fname) register char *dname, *fname; { if (*dname=='\0') return (-1); while (*dname!='\0') if (*dname++ != *fname++) return (0); if (*fname=='\0') return (1); else if (*fname=='/' || *--fname=='/') return (-1); else return (0); }