/* Tap: read contents of a tap archive. * Author: Warren Toomey wkt@cs.adfa.edu.au * $Revision: 1.2 $ * * This works on a little-endian machine. You will probably have endian * trouble with the 2-, 3- and 4-octet fields in the tapdir structure. */ #include "defines.h" #define BLKSIZ 512 /* Size of tap tape blocks */ /* Values for v1stat iflags */ #define V1_ST_SETUID 0000040 #define V1_ST_EXEC 0000020 #define V1_ST_OWNREAD 0000010 #define V1_ST_OWNWRITE 0000004 #define V1_ST_WRLDREAD 0000002 #define V1_ST_WRLDWRITE 0000001 /* tap tape directory entry */ struct tapdir { char pathname[32]; /* Filename of file */ uint8_t mode; /* 1st Edition mode */ uint8_t uid; /* Owner of file */ uint16_t size; /* Size in bytes */ uint32_t modtime; /* Time of last modification */ uint16_t tapeaddr; /* Beginning block on tape */ char unused2[20]; uint16_t checksum; /* Checksum */ }; /* We build a linked list when * reading in the tape */ struct tlist { struct tapdir tdir; int size; struct tlist *next; }; void mkrecursdir(char *name); void swap16(i) uint16_t *i; { char *a, b; a= (char *)i; b= a[0]; a[0]= a[1]; a[1]=b; } void swap32(i) uint32_t *i; { char *a, b; a= (char *)i; b= a[0]; a[0]= a[3]; a[3]=b; b=a[1]; a[1]=a[2]; a[2]=b; } void checktypes() { if (sizeof(int8_t)!=1) { printf("Wrong size for type int8_t\n"); exit(1); } if (sizeof(uint8_t)!=1) { printf("Wrong size for type uint8_t\n"); exit(1); } if (sizeof(int16_t)!=2) { printf("Wrong size for type int16_t\n"); exit(1); } if (sizeof(uint16_t)!=2) { printf("Wrong size for type uint16_t\n"); exit(1); } if (sizeof(int32_t)!=4) { printf("Wrong size for type int32_t\n"); exit(1); } if (sizeof(uint32_t)!=4) { printf("Wrong size for type uint32_t\n"); exit(1); } } void usage() { printf("Usage: tap epoch [t|x] filename\n"); printf(" Epoch is the integers 71, 72 or 73\n"); exit(1); } int main(argc, argv) int argc; char *argv[]; { char buf[BLKSIZ]; FILE *zin, *zout; int toc = 1; int extract = 0; int mode; int epoch; /* 71, 72 or 73 */ uint32_t modtime; /* Real mod time */ char modstr[8]; char *Ctime, *pathname; uint32_t size; struct tlist *thead = NULL, *tent, *this; int blockbytes; uint8_t a, *b; struct utimbuf utbuf; checktypes(); /* Check size of our typedefs */ if (argc != 4) usage(); /* Give usage if wrong # args */ epoch=atoi(argv[1]); /* Get archive's epoch */ switch(epoch) { case 71: case 72: case 73: break; default: usage(); } epoch -= 70; if (argv[2][0] == 't') { toc = 1; extract = 0; } /* Either extract of give contents */ if (argv[2][0] == 'x') { toc = 0; extract = 1; } zin = fopen(argv[3], "r"); if (zin == NULL) { perror("Opening input file"); exit(1); } fseek(zin, (long) BLKSIZ, SEEK_SET); /* Read in the tape directory entries */ while (1) { tent = (struct tlist *) malloc(sizeof(struct tlist)); tent->next = NULL; if ((fread(&(tent->tdir), sizeof(struct tapdir), 1, zin)) != 1) { printf("fread failed on tap archive %s\n",argv[3]); break; } /* If entry has no name, we've reached end of the list */ if (tent->tdir.pathname[0] == '\0') break; /* Get the size of the file */ size = tent->tdir.size; tent->size = size; #ifdef BIGEND /* Convert multibyte fields */ swap16(&(tent->tdir.size)); swap16(&(tent->tdir.tapeaddr)); swap16(&(tent->tdir.modtime)); /* Convert the modification time into a normal Unix time value */ b = (uint8_t *) & (tent->tdir.modtime); a = b[0]; b[0] = b[1]; b[1] = a; a = b[2]; b[2] = b[3]; b[3] = a; #else /* Convert the modification time into a normal Unix time value */ b = (uint8_t *) & (tent->tdir.modtime); a = b[0]; b[0] = b[2]; b[2] = a; a = b[1]; b[1] = b[3]; b[3] = a; #endif /* Convert to seconds since epoch */ modtime= (tent->tdir.modtime / 60) + (epoch * 365 * 24 * 60 * 60); /* Print the table of contents */ if (toc) { strcpy(modstr, "s------"); if (tent->tdir.size >4095) modstr[0]='l'; if (tent->tdir.mode & V1_ST_SETUID) modstr[1]='u'; if (tent->tdir.mode & V1_ST_EXEC) modstr[2]='x'; if (tent->tdir.mode & V1_ST_OWNREAD) modstr[3]='r'; if (tent->tdir.mode & V1_ST_OWNWRITE) modstr[4]='w'; if (tent->tdir.mode & V1_ST_WRLDREAD) modstr[5]='r'; if (tent->tdir.mode & V1_ST_WRLDWRITE) modstr[6]='w'; Ctime=ctime((time_t *)&modtime); Ctime[strlen(Ctime)-1]='\0'; printf("%s %3d %5d %4d %s %s\n", modstr, tent->tdir.uid, size, tent->tdir.tapeaddr, Ctime, tent->tdir.pathname); } /* Add entry to the list */ if (thead == NULL) thead = this = tent; else { this->next = tent; this = tent; } } if (extract) for (this = thead; this; this = this->next) { /* Print the file's name on stdout */ printf("x %s %d, ", this->tdir.pathname, this->size); /* Convert file size into # of tape blocks */ blockbytes = BLKSIZ * ((this->size + 511) / 512); printf("%d blockbytes\n", blockbytes); /* Seek to the beginning of the file */ fseek(zin, BLKSIZ * this->tdir.tapeaddr, SEEK_SET); size = this->size; /* Open the output file */ pathname= this->tdir.pathname; if (pathname[0]=='/') pathname++; zout = fopen(pathname, "w"); if (zout == NULL) { /* Ok, try making the directories */ mkrecursdir(pathname); zout = fopen(pathname, "w"); if (zout == NULL) { printf("Can't open %s for writing\n", pathname); continue; } } /* Read and write the blocks from the archive to the file */ while (size) { if ((fread(&buf, BLKSIZ, 1, zin)) != 1) { printf("Error reading block for %s\n", pathname); break; } if (size < BLKSIZ) { fwrite(&buf, size, 1, zout); size = 0; } else { fwrite(&buf, BLKSIZ, 1, zout); size -= BLKSIZ; } } /* Close the file */ fclose(zout); /* Set up the file's mode, owner, group and modification time */ mode=0; if (this->tdir.mode & V1_ST_SETUID) mode |= S_ISUID; if (this->tdir.mode & V1_ST_EXEC) mode |= S_IXUSR|S_IXGRP|S_IXOTH; if (this->tdir.mode & V1_ST_OWNREAD) mode |= S_IRUSR; if (this->tdir.mode & V1_ST_OWNWRITE) mode |= S_IWUSR; if (this->tdir.mode & V1_ST_WRLDREAD) mode |= S_IRGRP | S_IROTH; if (this->tdir.mode & V1_ST_WRLDWRITE) mode |= S_IWGRP | S_IWOTH; chmod(pathname, mode); chown(pathname, this->tdir.uid, 0); /* Convert to seconds since 1970 */ modtime= (this->tdir.modtime / 60) + (epoch * 365 * 24 * 60 * 60); utbuf.actime = utbuf.modtime = modtime; utime(pathname, &utbuf); } exit(0); } /* Build the directory needed to create the file. * We cheat by using mkdir -p. */ void mkrecursdir(char *name) { char *c; char buf[300]; c = strrchr(name, '/'); if (c) { *c = '\0'; sprintf(buf, "/bin/mkdir -p %s", name); system(buf); *c = '/'; } }