rpm 5.2.1

rpmio/tar.c

Go to the documentation of this file.
00001 
00006 #undef JBJ_WRITEPAD
00007 
00008 #include "system.h"
00009 
00010 #include <rpmio.h>
00011 #include <ugid.h>
00012 #include <tar.h>
00013 #define _IOSM_INTERNAL
00014 #include <iosm.h>
00015 
00016 #include "debug.h"
00017 
00018 /*@access IOSM_t @*/
00019 
00020 /*@unchecked@*/
00021 int _tar_debug = 0;
00022 
00023 /*@unchecked@*/
00024 static int nochksum = 0;
00025 
00034 static int strntoul(const char *str, /*@null@*/ /*@out@*/char **endptr,
00035                 int base, size_t num)
00036         /*@modifies *endptr @*/
00037         /*@requires maxSet(endptr) >= 0 @*/
00038 {
00039     char * buf, * end;
00040     unsigned long ret;
00041 
00042     buf = alloca(num + 1);
00043     strncpy(buf, str, num);
00044     buf[num] = '\0';
00045 
00046     ret = strtoul(buf, &end, base);
00047     if (endptr != NULL) {
00048         if (*end != '\0')
00049             *endptr = ((char *)str) + (end - buf);      /* XXX discards const */
00050         else
00051             *endptr = ((char *)str) + strlen(buf);
00052     }
00053 
00054     return ret;
00055 }
00056 
00057 /* Translate archive read/write ssize_t return for iosmStage(). */
00058 #define _IOSMRC(_rc)    \
00059         if ((_rc) <= 0) return ((_rc) ? (int) -rc : IOSMERR_HDR_TRAILER)
00060 
00061 static ssize_t tarRead(void * _iosm, void * buf, size_t count)
00062         /*@globals fileSystem @*/
00063         /*@modifies _iosm, *buf, fileSystem @*/
00064 {
00065     IOSM_t iosm = _iosm;
00066     char * t = buf;
00067     size_t nb = 0;
00068 
00069 if (_tar_debug)
00070 fprintf(stderr, "\ttarRead(%p, %p[%u])\n", iosm, buf, (unsigned)count);
00071 
00072     while (count > 0) {
00073         size_t rc;
00074 
00075         /* Read next tar block. */
00076         iosm->wrlen = count;
00077         rc = _iosmNext(iosm, IOSM_DREAD);
00078         if (!rc && iosm->rdnb != iosm->wrlen)
00079             rc = IOSMERR_READ_FAILED;
00080         if (rc) return -rc;
00081 
00082         /* Append to buffer. */
00083         rc = (count > iosm->rdnb ? iosm->rdnb : count);
00084         if (buf != iosm->wrbuf)
00085              memcpy(t + nb, iosm->wrbuf, rc);
00086         nb += rc;
00087         count -= rc;
00088     }
00089     return nb;
00090 }
00091 
00099 static ssize_t tarHeaderReadName(void * _iosm, size_t len,
00100                 /*@out@*/ const char ** fnp)
00101         /*@globals fileSystem, internalState @*/
00102         /*@modifies _iosm, *fnp, fileSystem, internalState @*/
00103 {
00104     IOSM_t iosm = _iosm;
00105     size_t nb = len + 1;
00106     char * t = xmalloc(nb);
00107     ssize_t rc = tarRead(iosm, t, nb);
00108 
00109     if (rc > 0)         /* success */
00110         t[rc] = '\0';
00111      else               /* failure or EOF */
00112         t = _free(t);
00113     if (fnp != NULL)
00114         *fnp = t;
00115 
00116 if (_tar_debug)
00117 fprintf(stderr, "\ttarHeaderReadName(%p, %u, %p) rc 0x%x\n", _iosm, (unsigned)len, fnp, (unsigned)rc);
00118 
00119     return rc;
00120 }
00121 
00122 int tarHeaderRead(void * _iosm, struct stat * st)
00123         /*@modifies _iosm, *st @*/
00124 {
00125     IOSM_t iosm = _iosm;
00126     tarHeader hdr = (tarHeader) iosm->wrbuf;
00127     char * t;
00128     size_t nb;
00129     int major, minor;
00130     ssize_t rc = 0;
00131     int zblk = 0;
00132 
00133 if (_tar_debug)
00134 fprintf(stderr, "  tarHeaderRead(%p, %p)\n", iosm, st);
00135 
00136 top:
00137     do {
00138         /* Read next header. */
00139         rc = tarRead(_iosm, hdr, TAR_BLOCK_SIZE);
00140         _IOSMRC(rc);
00141 
00142         /* Look for end-of-archive, i.e. 2 (or more) zero blocks. */
00143         if (hdr->name[0] == '\0' && hdr->checksum[0] == '\0') {
00144             if (++zblk == 2)
00145                 return IOSMERR_HDR_TRAILER;
00146         }
00147     } while (zblk > 0);
00148 
00149     /* Verify header checksum. */
00150     {   const unsigned char * hp = (const unsigned char *) hdr;
00151         char checksum[8];
00152         char hdrchecksum[8];
00153         long sum = 0;
00154         int i;
00155 
00156         memcpy(hdrchecksum, hdr->checksum, sizeof(hdrchecksum));
00157         memset(hdr->checksum, (int)' ', sizeof(hdr->checksum));
00158 
00159         for (i = 0; i < TAR_BLOCK_SIZE; i++)
00160             sum += (long)*hp++;
00161 
00162         memset(checksum, (int)' ', sizeof(checksum));
00163         sprintf(checksum, "%06o", (unsigned) (sum & 07777777));
00164 if (_tar_debug)
00165 fprintf(stderr, "\tmemcmp(\"%s\", \"%s\", %u)\n", hdrchecksum, checksum, (unsigned)sizeof(hdrchecksum));
00166         if (memcmp(hdrchecksum, checksum, sizeof(hdrchecksum)))
00167             if (!nochksum)
00168                 return IOSMERR_BAD_HEADER;
00169 
00170     }
00171 
00172     /* Verify header magic. */
00173     if (strncmp(hdr->magic, TAR_MAGIC, sizeof(TAR_MAGIC)-1))
00174         return IOSMERR_BAD_MAGIC;
00175 
00176     /* Convert header to stat(2). */
00177     st->st_size = strntoul(hdr->filesize, NULL, 8, sizeof(hdr->filesize));
00178 
00179     st->st_nlink = 1;
00180     st->st_mode = strntoul(hdr->mode, NULL, 8, sizeof(hdr->mode));
00181     st->st_mode &= ~S_IFMT;
00182     switch (hdr->typeflag) {
00183     case 'x':           /* Extended header referring to next file in archive. */
00184     case 'g':           /* Global extended header. */
00185     default:
00186         break;
00187     case '7':           /* reserved (contiguous files?) */
00188     case '\0':          /* (ancient) regular file */
00189     case '0':           /* regular file */
00190         st->st_mode |= S_IFREG;
00191         break;
00192     case '1':           /* hard link */
00193         st->st_mode |= S_IFREG;
00194 #ifdef DYING
00195         st->st_nlink++;
00196 #endif
00197         break;
00198     case '2':           /* symbolic link */
00199         st->st_mode |= S_IFLNK;
00200         break;
00201     case '3':           /* character special */
00202         st->st_mode |= S_IFCHR;
00203         break;
00204     case '4':           /* block special */
00205         st->st_mode |= S_IFBLK;
00206         break;
00207     case '5':           /* directory */
00208         st->st_mode |= S_IFDIR;
00209         st->st_nlink++;
00210         break;
00211     case '6':           /* FIFO special */
00212         st->st_mode |= S_IFIFO;
00213         break;
00214 #ifdef  REFERENCE
00215     case 'A':           /* Solaris ACL */
00216     case 'E':           /* Solaris XATTR */
00217     case 'I':           /* Inode only, as in 'star' */
00218     case 'X':           /* POSIX 1003.1-2001 eXtended (VU version) */
00219     case 'D':           /* GNU dumpdir (with -G, --incremental) */
00220     case 'M':           /* GNU multivol (with -M, --multi-volume) */
00221     case 'N':           /* GNU names */
00222     case 'S':           /* GNU sparse  (with -S, --sparse) */
00223     case 'V':           /* GNU tape/volume header (with -Vlll, --label=lll) */
00224 #endif
00225     case 'K':           /* GNU long (>100 chars) link name */
00226         rc = tarHeaderReadName(iosm, st->st_size, &iosm->lpath);
00227         _IOSMRC(rc);
00228         goto top;
00229         /*@notreached@*/ break;
00230     case 'L':           /* GNU long (>100 chars) file name */
00231         rc = tarHeaderReadName(iosm, st->st_size, &iosm->path);
00232         _IOSMRC(rc);
00233         goto top;
00234         /*@notreached@*/ break;
00235     }
00236 
00237     st->st_uid = strntoul(hdr->uid, NULL, 8, sizeof(hdr->uid));
00238     st->st_gid = strntoul(hdr->gid, NULL, 8, sizeof(hdr->gid));
00239     st->st_mtime = strntoul(hdr->mtime, NULL, 8, sizeof(hdr->mtime));
00240     st->st_ctime = st->st_atime = st->st_mtime;         /* XXX compat? */
00241 
00242     major = strntoul(hdr->devMajor, NULL, 8, sizeof(hdr->devMajor));
00243     minor = strntoul(hdr->devMinor, NULL, 8, sizeof(hdr->devMinor));
00244     /*@-shiftimplementation@*/
00245     st->st_dev = Makedev(major, minor);
00246     /*@=shiftimplementation@*/
00247     st->st_rdev = st->st_dev;           /* XXX compat? */
00248 
00249     /* char prefix[155]; */
00250     /* char padding[12]; */
00251 
00252     /* Read short file name. */
00253     if (iosm->path == NULL && hdr->name[0] != '\0') {
00254         nb = strlen(hdr->name);
00255         t = xmalloc(nb + 1);
00256         memcpy(t, hdr->name, nb);
00257         t[nb] = '\0';
00258         iosm->path = t;
00259     }
00260 
00261     /* Read short link name. */
00262     if (iosm->lpath == NULL && hdr->linkname[0] != '\0') {
00263         nb = strlen(hdr->linkname);
00264         t = xmalloc(nb + 1);
00265         memcpy(t, hdr->linkname, nb);
00266         t[nb] = '\0';
00267         iosm->lpath = t;
00268     }
00269 
00270     rc = 0;
00271 
00272 if (_tar_debug)
00273 fprintf(stderr, "\t     %06o%3d (%4d,%4d)%12lu %s\n\t-> %s\n",
00274                 (unsigned)st->st_mode, (int)st->st_nlink,
00275                 (int)st->st_uid, (int)st->st_gid, (unsigned long)st->st_size,
00276                 (iosm->path ? iosm->path : ""), (iosm->lpath ? iosm->lpath : ""));
00277 
00278     return (int) rc;
00279 }
00280 
00281 static ssize_t tarWrite(void * _iosm, const void *buf, size_t count)
00282         /*@globals fileSystem @*/
00283         /*@modifies _iosm, fileSystem @*/
00284 {
00285     IOSM_t iosm = _iosm;
00286     const char * s = buf;
00287     size_t nb = 0;
00288     size_t rc;
00289 
00290 if (_tar_debug)
00291 fprintf(stderr, "\t   tarWrite(%p, %p[%u])\n", iosm, buf, (unsigned)count);
00292 
00293     while (count > 0) {
00294 
00295         /* XXX DWRITE uses rdnb for I/O length. */
00296         iosm->rdnb = count;
00297         if (s != iosm->rdbuf)
00298             memmove(iosm->rdbuf, s + nb, iosm->rdnb);
00299 
00300         rc = _iosmNext(iosm, IOSM_DWRITE);
00301         if (!rc && iosm->rdnb != iosm->wrnb)
00302                 rc = IOSMERR_WRITE_FAILED;
00303         if (rc) return -rc;
00304 
00305         nb += iosm->rdnb;
00306         count -= iosm->rdnb;
00307     }
00308 
00309 #if defined(JBJ_WRITEPAD)
00310     /* Pad to next block boundary. */
00311     if ((rc = _iosmNext(iosm, IOSM_PAD)) != 0) return rc;
00312 #endif
00313 
00314     return nb;
00315 }
00316 
00323 static ssize_t tarHeaderWriteName(void * _iosm, const char * path)
00324         /*@globals fileSystem, internalState @*/
00325         /*@modifies _iosm, fileSystem, internalState @*/
00326 {
00327     ssize_t rc = tarWrite(_iosm, path, strlen(path));
00328 
00329 #if !defined(JBJ_WRITEPAD)
00330     if (rc >= 0) {
00331         rc = _iosmNext(_iosm, IOSM_PAD);
00332         if (rc) rc = -rc;
00333     }
00334 #endif
00335 
00336 if (_tar_debug)
00337 fprintf(stderr, "\ttarHeaderWriteName(%p, %s) rc 0x%x\n", _iosm, path, (unsigned)rc);
00338 
00339     return rc;
00340 }
00341 
00349 static ssize_t tarHeaderWriteBlock(void * _iosm, struct stat * st, tarHeader hdr)
00350         /*@globals fileSystem, internalState @*/
00351         /*@modifies _iosm, hdr, fileSystem, internalState @*/
00352 {
00353     IOSM_t iosm = _iosm;
00354     ssize_t rc;
00355 
00356 if (_tar_debug)
00357 fprintf(stderr, "\ttarHeaderWriteBlock(%p, %p) type %c\n", iosm, hdr, hdr->typeflag);
00358 if (_tar_debug)
00359 fprintf(stderr, "\t     %06o%3d (%4d,%4d)%12lu %s\n",
00360                 (unsigned)st->st_mode, (int)st->st_nlink,
00361                 (int)st->st_uid, (int)st->st_gid, (unsigned long)st->st_size,
00362                 (iosm->path ? iosm->path : ""));
00363 
00364 
00365     (void) stpcpy( stpcpy(hdr->magic, TAR_MAGIC), TAR_VERSION);
00366 
00367     /* Calculate header checksum. */
00368     {   const unsigned char * hp = (const unsigned char *) hdr;
00369         long sum = 0;
00370         int i;
00371 
00372         memset(hdr->checksum, (int)' ', sizeof(hdr->checksum));
00373         for (i = 0; i < TAR_BLOCK_SIZE; i++)
00374             sum += (long) *hp++;
00375         sprintf(hdr->checksum, "%06o", (unsigned)(sum & 07777777));
00376 if (_tar_debug)
00377 fprintf(stderr, "\thdrchksum \"%s\"\n", hdr->checksum);
00378     }
00379 
00380     rc = tarWrite(_iosm, hdr, TAR_BLOCK_SIZE);
00381 
00382     return rc;
00383 }
00384 
00385 int tarHeaderWrite(void * _iosm, struct stat * st)
00386 {
00387     IOSM_t iosm = _iosm;
00388 /*@observer@*/
00389     static const char * llname = "././@LongLink";
00390     tarHeader hdr = (tarHeader) iosm->rdbuf;
00391     char * t;
00392     dev_t dev;
00393     size_t nb;
00394     ssize_t rc = 0;
00395 
00396 if (_tar_debug)
00397 fprintf(stderr, "    tarHeaderWrite(%p, %p)\n", iosm, st);
00398 
00399     nb = strlen(iosm->path);
00400     if (nb > sizeof(hdr->name)) {
00401         memset(hdr, 0, sizeof(*hdr));
00402         strcpy(hdr->name, llname);
00403         sprintf(hdr->mode, "%07o", 0);
00404         sprintf(hdr->uid, "%07o", 0);
00405         sprintf(hdr->gid, "%07o", 0);
00406         sprintf(hdr->filesize, "%011o", (unsigned) (nb & 037777777777));
00407         sprintf(hdr->mtime, "%011o", 0);
00408         hdr->typeflag = 'L';
00409         strncpy(hdr->uname, "root", sizeof(hdr->uname));
00410         strncpy(hdr->gname, "root", sizeof(hdr->gname));
00411         rc = tarHeaderWriteBlock(iosm, st, hdr);
00412         _IOSMRC(rc);
00413         rc = tarHeaderWriteName(iosm, iosm->path);
00414         _IOSMRC(rc);
00415     }
00416 
00417     if (iosm->lpath && iosm->lpath[0] != '0') {
00418         nb = strlen(iosm->lpath);
00419         if (nb > sizeof(hdr->name)) {
00420             memset(hdr, 0, sizeof(*hdr));
00421             strcpy(hdr->linkname, llname);
00422             sprintf(hdr->mode, "%07o", 0);
00423             sprintf(hdr->uid, "%07o", 0);
00424             sprintf(hdr->gid, "%07o", 0);
00425             sprintf(hdr->filesize, "%011o", (unsigned) (nb & 037777777777));
00426             sprintf(hdr->mtime, "%011o", 0);
00427             hdr->typeflag = 'K';
00428             strncpy(hdr->uname, "root", sizeof(hdr->uname));
00429             strncpy(hdr->gname, "root", sizeof(hdr->gname));
00430             rc = tarHeaderWriteBlock(iosm, st, hdr);
00431             _IOSMRC(rc);
00432             rc = tarHeaderWriteName(iosm, iosm->lpath);
00433             _IOSMRC(rc);
00434         }
00435     }
00436 
00437     memset(hdr, 0, sizeof(*hdr));
00438 
00439     strncpy(hdr->name, iosm->path, sizeof(hdr->name));
00440 
00441     if (iosm->lpath && iosm->lpath[0] != '0')
00442         strncpy(hdr->linkname, iosm->lpath, sizeof(hdr->linkname));
00443 
00444     sprintf(hdr->mode, "%07o", (unsigned int)(st->st_mode & 00007777));
00445     sprintf(hdr->uid, "%07o", (unsigned int)(st->st_uid & 07777777));
00446     sprintf(hdr->gid, "%07o", (unsigned int)(st->st_gid & 07777777));
00447 
00448     sprintf(hdr->filesize, "%011o", (unsigned) (st->st_size & 037777777777));
00449     sprintf(hdr->mtime, "%011o", (unsigned) (st->st_mtime & 037777777777));
00450 
00451     hdr->typeflag = '0';        /* XXX wrong! */
00452     if (S_ISLNK(st->st_mode))
00453         hdr->typeflag = '2';
00454     else if (S_ISCHR(st->st_mode))
00455         hdr->typeflag = '3';
00456     else if (S_ISBLK(st->st_mode))
00457         hdr->typeflag = '4';
00458     else if (S_ISDIR(st->st_mode))
00459         hdr->typeflag = '5';
00460     else if (S_ISFIFO(st->st_mode))
00461         hdr->typeflag = '6';
00462 #ifdef WHAT2DO
00463     else if (S_ISSOCK(st->st_mode))
00464         hdr->typeflag = '?';
00465 #endif
00466     else if (S_ISREG(st->st_mode))
00467         hdr->typeflag = (iosm->lpath != NULL ? '1' : '0');
00468 
00469     /* XXX FIXME: map uname/gname from uid/gid. */
00470     t = uidToUname(st->st_uid);
00471     if (t == NULL) t = "root";
00472     strncpy(hdr->uname, t, sizeof(hdr->uname));
00473     t = gidToGname(st->st_gid);
00474     if (t == NULL) t = "root";
00475     strncpy(hdr->gname, t, sizeof(hdr->gname));
00476 
00477     /* XXX W2DO? st_dev or st_rdev? */
00478     dev = major((unsigned)st->st_dev);
00479     sprintf(hdr->devMajor, "%07o", (unsigned) (dev & 07777777));
00480     dev = minor((unsigned)st->st_dev);
00481     sprintf(hdr->devMinor, "%07o", (unsigned) (dev & 07777777));
00482 
00483     rc = tarHeaderWriteBlock(iosm, st, hdr);
00484     _IOSMRC(rc);
00485     rc = 0;
00486 
00487 #if !defined(JBJ_WRITEPAD)
00488     /* XXX Padding is unnecessary but shouldn't hurt. */
00489     rc = _iosmNext(iosm, IOSM_PAD);
00490 #endif
00491 
00492     return (int) rc;
00493 }
00494 
00495 int tarTrailerWrite(void * _iosm)
00496 {
00497     IOSM_t iosm = _iosm;
00498     ssize_t rc = 0;
00499 
00500 if (_tar_debug)
00501 fprintf(stderr, "    tarTrailerWrite(%p)\n", iosm);
00502 
00503     /* Pad up to 20 blocks (10Kb) of zeroes. */
00504     iosm->blksize *= 20;
00505 #if defined(JBJ_WRITEPAD)
00506     rc = tarWrite(iosm, NULL, 0);       /* XXX _iosmNext(iosm, IOSM_PAD) */
00507 #else
00508     rc = _iosmNext(iosm, IOSM_PAD);
00509 #endif
00510     iosm->blksize /= 20;
00511 #if defined(JBJ_WRITEPAD)
00512     _IOSMRC(rc);
00513 #endif
00514 
00515     return (int) -rc;
00516 }