|
|
1.1 ! root 1: // ! 2: // Stuff to deal with tar-format files ! 3: // ! 4: ! 5: #include <sys/stat.h> ! 6: #include <stdlib.h> ! 7: #include <tar.h> ! 8: #include <stdio.h> ! 9: #include <fcntl.h> ! 10: #include <dirent.h> ! 11: #include <string.h> ! 12: #include <unistd.h> ! 13: ! 14: #include "buf.h" ! 15: #include "psxarc.h" ! 16: #include "tarhead.h" ! 17: #include "links.h" ! 18: ! 19: static unsigned long round_up(int, int); ! 20: static int tarAtoi(char *); ! 21: static void tarItoa(long, char *, size_t); ! 22: static void tar_dodir(PBUF pb, char *pchfile, struct stat *psb); ! 23: static char *modestring(PTAR_HEAD); ! 24: extern int fVerbose; ! 25: ! 26: void ! 27: TarRead(PBUF pb) ! 28: { ! 29: char name[155 + 1 + 100 + 1]; // (prefix '/' name '\0') ! 30: char linkname[TNAMSIZ + 1]; ! 31: unsigned long size, i; ! 32: int fdout; ! 33: PTAR_HEAD buf = (PTAR_HEAD)pb->data; ! 34: ! 35: ! 36: if (0 != strcmp(buf->s.magic, TMAGIC)) { ! 37: fprintf(stderr, "%s: bad magic number\n", progname); ! 38: exit(1); ! 39: } ! 40: ! 41: name[sizeof(name) - 1] = '\0'; ! 42: ! 43: do { ! 44: (void)strncpy(name, buf->s.prefix, 155); ! 45: // make sure 'name' is null-terminated. ! 46: name[155] = '\0'; ! 47: if ('\0' != name[0]) { ! 48: (void)strcat(name, "/"); ! 49: } ! 50: (void)strncat(name, buf->s.name, 100); ! 51: ! 52: if (fVerbose) { ! 53: printf("%s\n", name); ! 54: } ! 55: ! 56: if (DIRTYPE == buf->s.typeflag) { ! 57: if (-1 == mkdir(name, 0777)) { ! 58: fprintf(stderr, "%s: mkdir: ", progname); ! 59: perror(name); ! 60: } ! 61: bfill(pb); ! 62: continue; ! 63: } ! 64: if (FIFOTYPE == buf->s.typeflag) { ! 65: if (-1 == mkfifo(name, 0666)) { ! 66: fprintf(stderr, "%s: mkfifo: ", progname); ! 67: perror(name); ! 68: } ! 69: bfill(pb); ! 70: continue; ! 71: } ! 72: if (LNKTYPE == buf->s.typeflag) { ! 73: strncpy(linkname, buf->s.linkname, sizeof(linkname)); ! 74: if (-1 == link(linkname, name)) { ! 75: fprintf(stderr, "%s: link %s, %s: ", ! 76: progname, linkname, name); ! 77: perror(""); ! 78: continue; ! 79: } ! 80: } ! 81: ! 82: // regular file ! 83: if (-1 == (fdout = open(name, O_WRONLY | O_CREAT, 0666))) { ! 84: fprintf(stderr, "%s: open: ", progname); ! 85: perror(name); ! 86: continue; ! 87: } ! 88: ! 89: size = tarAtoi(buf->s.size); ! 90: ! 91: if (size > 0) { ! 92: bfill(pb); ! 93: ! 94: for (i = 0; i < size; ++i) { ! 95: int c; ! 96: ! 97: c = bgetc(pb); ! 98: write(fdout, &c, 1); ! 99: } ! 100: } ! 101: (void)close(fdout); ! 102: ! 103: bfill(pb); ! 104: } while (0 != buf->s.magic[0]); ! 105: } ! 106: ! 107: // ! 108: // TarWrite -- calls tar_dodir for directories, which calls back here. ! 109: // ! 110: ! 111: void ! 112: TarWrite(PBUF pb, char **ppchFiles, int count) ! 113: { ! 114: PTAR_HEAD pt; ! 115: auto struct stat statbuf; ! 116: int fdin; ! 117: int i; ! 118: unsigned chksum; ! 119: ! 120: // We reach into the buffer routines until it's time to write. ! 121: // Brutal but effective. ! 122: ! 123: pt = (PTAR_HEAD)pb->data; ! 124: ! 125: while (count > 0) { ! 126: int len; ! 127: ! 128: memset(pt, 0, sizeof(*pt)); ! 129: strcpy(pt->s.magic, TMAGIC); ! 130: strncpy(pt->s.version, TVERSION, TVERSLEN); ! 131: ! 132: if (fVerbose) { ! 133: fprintf(stderr, "%s\n", *ppchFiles); ! 134: } ! 135: if (-1 == stat(*ppchFiles, &statbuf)) { ! 136: fprintf(stderr, "%s: stat: "); ! 137: perror(*ppchFiles); ! 138: continue; ! 139: } ! 140: len = strlen(*ppchFiles); ! 141: if (len > TNAMSIZ + 155 + 1) { ! 142: // the filename just won't fit. Do something ! 143: // reasonable. ! 144: } else if (len <= TNAMSIZ) { ! 145: strncpy(pt->s.name, *ppchFiles, TNAMSIZ); ! 146: } else { ! 147: char *pch; ! 148: ! 149: // We try to put as much of the filename as will fit ! 150: // into the 'name' portion, and the rest goes in ! 151: // the prefix. To do this, we start 101 characters ! 152: // from the end; if that character is a slash, we ! 153: // split the string there. If it's not, we split the ! 154: // string at the next slash to the right. ! 155: ! 156: pch = *ppchFiles + (len - TNAMSIZ - 1); ! 157: if ('/' != *pch) { ! 158: pch = strchr(pch, '/'); ! 159: if (NULL == pch) { ! 160: // XXX.mjb: This filename has a trailing ! 161: // component more than 100 chars ! 162: // long. Do something reasonable. ! 163: --count; ! 164: ++ppchFiles; ! 165: continue; ! 166: } ! 167: } ! 168: *pch = '\0'; ! 169: strncpy(pt->s.name, pch + 1, TNAMSIZ); ! 170: strncpy(pt->s.prefix, *ppchFiles, 155); ! 171: } ! 172: ! 173: // ! 174: // XXX.mjb: this assumes tar mode bits are the same as ! 175: // the POSIX implementation's mode bits. Should really ! 176: // call a function to convert between. ! 177: // ! 178: tarItoa(statbuf.st_mode & 0777, pt->s.mode, ! 179: sizeof(pt->s.mode)); ! 180: ! 181: #if 0 ! 182: tarItoa(statbuf.st_uid, pt->s.uid, sizeof(pt->s.uid)); ! 183: tarItoa(statbuf.st_gid, pt->s.gid, sizeof(pt->s.gid)); ! 184: tarItoa(statbuf.st_mtime, pt->s.mtime, sizeof(pt->s.mtime)); ! 185: #endif ! 186: ! 187: if (S_ISDIR(statbuf.st_mode)) { ! 188: pt->s.typeflag = DIRTYPE; ! 189: memset(pt->s.size, '0', sizeof(pt->s.size)); ! 190: // put the directory entry on the tape ! 191: bflush(pb); ! 192: ! 193: // put the directory contents on tape ! 194: tar_dodir(pb, *ppchFiles, &statbuf); ! 195: ! 196: ++ppchFiles; ! 197: --count; ! 198: continue; ! 199: } ! 200: if (S_ISFIFO(statbuf.st_mode)) { ! 201: pt->s.typeflag = FIFOTYPE; ! 202: memset(pt->s.size, '0', sizeof(pt->s.size)); ! 203: bflush(pb); ! 204: ++ppchFiles; ! 205: --count; ! 206: continue; ! 207: } ! 208: if (statbuf.st_nlink > 1) { ! 209: PLINKFILE p; ! 210: ! 211: if (NULL != (p = GetLinkByIno(statbuf.st_ino))) { ! 212: pt->s.typeflag = LNKTYPE; ! 213: memset(pt->s.size, '0', sizeof(pt->s.size)); ! 214: strncpy(pt->s.linkname, p->name, TNAMSIZ); ! 215: bflush(pb); ! 216: ! 217: ++ppchFiles; ! 218: --count; ! 219: continue; ! 220: } ! 221: ! 222: AddLinkList(&statbuf, *ppchFiles); ! 223: } ! 224: ! 225: pt->s.typeflag = REGTYPE; ! 226: tarItoa((int)statbuf.st_size, pt->s.size, sizeof(pt->s.size)); ! 227: ! 228: // ! 229: // compute the checksum for the header ! 230: // ! 231: ! 232: memset(pt->s.chksum, ' ', sizeof(pt->s.chksum)); ! 233: for (i = 0, chksum = 0; i < sizeof(pt->buf); ++i) { ! 234: chksum += pt->buf[i]; ! 235: } ! 236: tarItoa(chksum, pt->s.chksum, sizeof(pt->s.chksum)); ! 237: ! 238: fdin = open(*ppchFiles, O_RDONLY, 0); ! 239: if (-1 == fdin) { ! 240: fprintf(stderr, "%s: open: "); ! 241: perror(*ppchFiles); ! 242: continue; ! 243: } ! 244: ! 245: // write the header ! 246: bflush(pb); ! 247: ! 248: if (0 == statbuf.st_size) { ! 249: // ! 250: // special case: don't write any data blocks. ! 251: // ! 252: close(fdin); ! 253: ++ppchFiles; ! 254: --count; ! 255: continue; ! 256: } ! 257: ! 258: // copy the file data ! 259: // XXX.mjb: what should happen here if we find that we can't ! 260: // read the number of bytes we thought we'd be able to ! 261: // read? (The file could change size, or some kind ! 262: // of error could occur.) We can't leave the data too ! 263: // small, or we'll hose the rest of the tar file. So ! 264: // we write extra blocks of zeroes. What if the file ! 265: // turns out to be longer than expected? Print a ! 266: // warning and continue? ! 267: ! 268: memset(pb->data, 0, sizeof(pb->data)); ! 269: while (statbuf.st_size > 0) { ! 270: int nbytes; ! 271: char c; ! 272: ! 273: nbytes = read(fdin, &c, 1); ! 274: if (-1 == nbytes) { ! 275: // error occurs before we have all the data. ! 276: } ! 277: bputc(pb, c); ! 278: --statbuf.st_size; ! 279: } ! 280: bflush(pb); ! 281: close(fdin); ! 282: ! 283: ++ppchFiles; ! 284: --count; ! 285: } ! 286: } ! 287: ! 288: void ! 289: TarList(PBUF pb) ! 290: { ! 291: PTAR_HEAD pt; ! 292: char name[155 + 1 + 100 + 1]; // "prefix / name \0" ! 293: unsigned long size, i; ! 294: char *pch; ! 295: ! 296: pt = (PTAR_HEAD)pb->data; ! 297: ! 298: do { ! 299: (void)strncpy(name, pt->s.prefix, 155); ! 300: name[155] = '\0'; ! 301: if ('\0' != name[0]) { ! 302: (void)strcat(name, "/"); ! 303: } ! 304: (void)strncat(name, pt->s.name, 100); ! 305: ! 306: size = tarAtoi(pt->s.size); ! 307: ! 308: if (!fVerbose) { ! 309: printf("%s\n", name); ! 310: } else { ! 311: pch = modestring(pt); ! 312: printf("%s %6ld %s\n", pch, size, name); ! 313: } ! 314: ! 315: size = round_up(size, 512); ! 316: for (i = 0; i < size; ++i) { ! 317: bfill(pb); ! 318: } ! 319: bfill(pb); ! 320: } while (0 != pt->s.magic[0]); ! 321: } ! 322: ! 323: // ! 324: // tarAtof -- translate tar-style octal strings to decimal ! 325: // ! 326: static int ! 327: tarAtoi(char *pch) ! 328: { ! 329: int num = 0; ! 330: ! 331: while ('\0' != *pch && ' ' != *pch) { ! 332: num = num * 8 + (*pch - '0'); ! 333: ++pch; ! 334: } ! 335: return num; ! 336: } ! 337: ! 338: // ! 339: // tarItoa -- for writing numeric fields in tar headers. ! 340: void ! 341: tarItoa(long i, char *pch, size_t len) ! 342: { ! 343: // XXX.mjb: should check width < len ! 344: sprintf(pch, "%-o", i); ! 345: } ! 346: ! 347: // ! 348: // round_up -- round num up to the nearest multiple of m. ! 349: // ! 350: unsigned long ! 351: round_up(int num, int m) ! 352: { ! 353: return (num + (m - 1))/m; ! 354: } ! 355: ! 356: static void ! 357: tar_dodir( ! 358: PBUF pb, // the tar image file, being written ! 359: char *pchfile, // the directory file to be added to the archv ! 360: struct stat *psb // result of stat on the directory file. ! 361: ) ! 362: { ! 363: DIR *dp; ! 364: struct dirent *dirent; ! 365: char *pch; ! 366: ! 367: dp = opendir(pchfile); ! 368: if (NULL == dp) { ! 369: fprintf(stderr, "%s: opendir: ", progname); ! 370: perror(pchfile); ! 371: return; ! 372: } ! 373: while (NULL != (dirent = readdir(dp))) { ! 374: if ('.' == dirent->d_name[0] && ! 375: ('\0' == dirent->d_name[1] || ! 376: 0 == strcmp(dirent->d_name, ".."))) { ! 377: continue; ! 378: } ! 379: ! 380: // ! 381: // Recurse. We append the file name read from the directory ! 382: // to the directory name we were given and call TarWrite to ! 383: // put it on the tape. It could be a directory, so we could ! 384: // end up back here. This means that we must allocate the ! 385: // space for the pathname dynamically. This seems like it ! 386: // will be a big time-waster. ! 387: // ! 388: ! 389: // strlen + 2: one extra for '/', one extra for null. ! 390: pch = malloc(strlen(pchfile) + strlen(dirent->d_name) + 2); ! 391: if (NULL == pch) { ! 392: fprintf(stderr, "%s: virtual memory exhausted\n", ! 393: progname); ! 394: exit(4); ! 395: } ! 396: strcpy(pch, pchfile); ! 397: strcat(pch, "/"); ! 398: strcat(pch, dirent->d_name); ! 399: ! 400: TarWrite(pb, &pch, 1); ! 401: free(pch); ! 402: } ! 403: (void)closedir(dp); ! 404: } ! 405: ! 406: static char * ! 407: modestring(PTAR_HEAD pt) ! 408: { ! 409: static char sb[11]; ! 410: unsigned long mask, mode; ! 411: char *rwx = "rwxrwxrwx"; ! 412: char *string; ! 413: ! 414: sb[11] = '\0'; ! 415: string = sb; ! 416: ! 417: switch (pt->s.typeflag) { ! 418: case LNKTYPE: ! 419: case REGTYPE: ! 420: string[0] = '-'; ! 421: break; ! 422: case DIRTYPE: ! 423: string[0] = 'd'; ! 424: break; ! 425: case FIFOTYPE: ! 426: string[0] = 'f'; ! 427: break; ! 428: case SYMTYPE: ! 429: string[0] = 'l'; ! 430: break; ! 431: case BLKTYPE: ! 432: string[0] = 'b'; ! 433: break; ! 434: case CHRTYPE: ! 435: string[0] = 'c'; ! 436: break; ! 437: case CONTTYPE: ! 438: string[0] = '='; ! 439: break; ! 440: default: ! 441: fprintf(stderr, "modestring shouldn't get here\n"); ! 442: } ! 443: string++; ! 444: ! 445: mode = tarAtoi(pt->s.mode); ! 446: ! 447: for (mask = 0400; mask != 0; mask >>= 1) { ! 448: if (mode & mask) { ! 449: *string++ = *rwx++; ! 450: } else { ! 451: *string++ = '-'; ! 452: rwx++; ! 453: } ! 454: } ! 455: ! 456: return sb; ! 457: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.