rpm 5.3.12
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     const char * path = (iosm && iosm->path ? iosm->path : "");
00355     ssize_t rc;
00356 
00357 if (_tar_debug)
00358 fprintf(stderr, "\ttarHeaderWriteBlock(%p, %p) type %c\n", iosm, hdr, hdr->typeflag);
00359 if (_tar_debug)
00360 fprintf(stderr, "\t     %06o%3d (%4d,%4d)%12lu %s\n",
00361                 (unsigned)st->st_mode, (int)st->st_nlink,
00362                 (int)st->st_uid, (int)st->st_gid, (unsigned long)st->st_size,
00363                 path);
00364 
00365 
00366     (void) stpcpy( stpcpy(hdr->magic, TAR_MAGIC), TAR_VERSION);
00367 
00368     /* Calculate header checksum. */
00369     {   const unsigned char * hp = (const unsigned char *) hdr;
00370         long sum = 0;
00371         int i;
00372 
00373         memset(hdr->checksum, (int)' ', sizeof(hdr->checksum));
00374         for (i = 0; i < TAR_BLOCK_SIZE; i++)
00375             sum += (long) *hp++;
00376         sprintf(hdr->checksum, "%06o", (unsigned)(sum & 07777777));
00377 if (_tar_debug)
00378 fprintf(stderr, "\thdrchksum \"%s\"\n", hdr->checksum);
00379     }
00380 
00381     rc = tarWrite(_iosm, hdr, TAR_BLOCK_SIZE);
00382 
00383     return rc;
00384 }
00385 
00386 int tarHeaderWrite(void * _iosm, struct stat * st)
00387 {
00388     IOSM_t iosm = _iosm;
00389 /*@observer@*/
00390     static const char * llname = "././@LongLink";
00391     tarHeader hdr = (tarHeader) iosm->rdbuf;
00392     const char * path = (iosm && iosm->path ? iosm->path : "");
00393     const char * lpath = (iosm && iosm->lpath ? iosm->lpath : "");
00394     char * t;
00395     dev_t dev;
00396     size_t nb;
00397     ssize_t rc = 0;
00398 
00399 if (_tar_debug)
00400 fprintf(stderr, "    tarHeaderWrite(%p, %p)\n", iosm, st);
00401 
00402     nb = strlen(path);
00403     if (nb > sizeof(hdr->name)) {
00404         memset(hdr, 0, sizeof(*hdr));
00405         strcpy(hdr->name, llname);
00406         sprintf(hdr->mode, "%07o", 0);
00407         sprintf(hdr->uid, "%07o", 0);
00408         sprintf(hdr->gid, "%07o", 0);
00409         sprintf(hdr->filesize, "%011o", (unsigned) (nb & 037777777777));
00410         sprintf(hdr->mtime, "%011o", 0);
00411         hdr->typeflag = 'L';
00412         strncpy(hdr->uname, "root", sizeof(hdr->uname));
00413         strncpy(hdr->gname, "root", sizeof(hdr->gname));
00414         rc = tarHeaderWriteBlock(iosm, st, hdr);
00415         _IOSMRC(rc);
00416         rc = tarHeaderWriteName(iosm, path);
00417         _IOSMRC(rc);
00418     }
00419 
00420     if (lpath && lpath[0] != '0') {
00421         nb = strlen(lpath);
00422         if (nb > sizeof(hdr->name)) {
00423             memset(hdr, 0, sizeof(*hdr));
00424             strcpy(hdr->linkname, llname);
00425             sprintf(hdr->mode, "%07o", 0);
00426             sprintf(hdr->uid, "%07o", 0);
00427             sprintf(hdr->gid, "%07o", 0);
00428             sprintf(hdr->filesize, "%011o", (unsigned) (nb & 037777777777));
00429             sprintf(hdr->mtime, "%011o", 0);
00430             hdr->typeflag = 'K';
00431             strncpy(hdr->uname, "root", sizeof(hdr->uname));
00432             strncpy(hdr->gname, "root", sizeof(hdr->gname));
00433             rc = tarHeaderWriteBlock(iosm, st, hdr);
00434             _IOSMRC(rc);
00435             rc = tarHeaderWriteName(iosm, path);
00436             _IOSMRC(rc);
00437         }
00438     }
00439 
00440     memset(hdr, 0, sizeof(*hdr));
00441 
00442     strncpy(hdr->name, path, sizeof(hdr->name));
00443 
00444     if (lpath && lpath[0] != '\0')
00445         strncpy(hdr->linkname, lpath, sizeof(hdr->linkname));
00446 
00447     sprintf(hdr->mode, "%07o", (unsigned int)(st->st_mode & 00007777));
00448     sprintf(hdr->uid, "%07o", (unsigned int)(st->st_uid & 07777777));
00449     sprintf(hdr->gid, "%07o", (unsigned int)(st->st_gid & 07777777));
00450 
00451     sprintf(hdr->filesize, "%011o", (unsigned) (st->st_size & 037777777777));
00452     sprintf(hdr->mtime, "%011o", (unsigned) (st->st_mtime & 037777777777));
00453 
00454     hdr->typeflag = '0';        /* XXX wrong! */
00455     if (S_ISLNK(st->st_mode))
00456         hdr->typeflag = '2';
00457     else if (S_ISCHR(st->st_mode))
00458         hdr->typeflag = '3';
00459     else if (S_ISBLK(st->st_mode))
00460         hdr->typeflag = '4';
00461     else if (S_ISDIR(st->st_mode))
00462         hdr->typeflag = '5';
00463     else if (S_ISFIFO(st->st_mode))
00464         hdr->typeflag = '6';
00465 #ifdef WHAT2DO
00466     else if (S_ISSOCK(st->st_mode))
00467         hdr->typeflag = '?';
00468 #endif
00469     else if (S_ISREG(st->st_mode))
00470         hdr->typeflag = (lpath && lpath[0] != '\0' ? '1' : '0');
00471 
00472     /* XXX FIXME: map uname/gname from uid/gid. */
00473     t = uidToUname(st->st_uid);
00474     if (t == NULL) t = "root";
00475     strncpy(hdr->uname, t, sizeof(hdr->uname));
00476     t = gidToGname(st->st_gid);
00477     if (t == NULL) t = "root";
00478     strncpy(hdr->gname, t, sizeof(hdr->gname));
00479 
00480     /* XXX W2DO? st_dev or st_rdev? */
00481     dev = major((unsigned)st->st_dev);
00482     sprintf(hdr->devMajor, "%07o", (unsigned) (dev & 07777777));
00483     dev = minor((unsigned)st->st_dev);
00484     sprintf(hdr->devMinor, "%07o", (unsigned) (dev & 07777777));
00485 
00486     rc = tarHeaderWriteBlock(iosm, st, hdr);
00487     _IOSMRC(rc);
00488     rc = 0;
00489 
00490 #if !defined(JBJ_WRITEPAD)
00491     /* XXX Padding is unnecessary but shouldn't hurt. */
00492     rc = _iosmNext(iosm, IOSM_PAD);
00493 #endif
00494 
00495     return (int) rc;
00496 }
00497 
00498 int tarTrailerWrite(void * _iosm)
00499 {
00500     IOSM_t iosm = _iosm;
00501     ssize_t rc = 0;
00502 
00503 if (_tar_debug)
00504 fprintf(stderr, "    tarTrailerWrite(%p)\n", iosm);
00505 
00506     /* Pad up to 20 blocks (10Kb) of zeroes. */
00507     iosm->blksize *= 20;
00508 #if defined(JBJ_WRITEPAD)
00509     rc = tarWrite(iosm, NULL, 0);       /* XXX _iosmNext(iosm, IOSM_PAD) */
00510 #else
00511     rc = _iosmNext(iosm, IOSM_PAD);
00512 #endif
00513     iosm->blksize /= 20;
00514 #if defined(JBJ_WRITEPAD)
00515     _IOSMRC(rc);
00516 #endif
00517 
00518     return (int) -rc;
00519 }