Main Page   Modules   Compound List   File List   Compound Members   File Members   Related Pages  

lib/cpio.c

Go to the documentation of this file.
00001 
00010 #include "system.h"
00011 #include "cpio.h"
00012 #include "rpmerr.h"
00013 #include "debug.h"
00014 
00015 /*@access FD_t@*/
00016 
00017 #define CPIO_NEWC_MAGIC "070701"
00018 #define CPIO_CRC_MAGIC  "070702"
00019 #define TRAILER         "TRAILER!!!"
00020 
00024 struct hardLink {
00025     struct hardLink * next;
00026     const char ** files;        /* nlink of these, used by install */
00027     int * fileMaps;             /* used by build */
00028     dev_t dev;
00029     ino_t inode;
00030     int nlink;
00031     int linksLeft;
00032     int createdPath;
00033     const struct stat sb;
00034 };
00035 
00038 enum hardLinkType {
00039         HARDLINK_INSTALL=1,
00040         HARDLINK_BUILD
00041 };
00042 
00047 struct cpioCrcPhysicalHeader {
00048     char magic[6];
00049     char inode[8];
00050     char mode[8];
00051     char uid[8];
00052     char gid[8];
00053     char nlink[8];
00054     char mtime[8];
00055     char filesize[8];
00056     char devMajor[8];
00057     char devMinor[8];
00058     char rdevMajor[8];
00059     char rdevMinor[8];
00060     char namesize[8];
00061     char checksum[8];                   /* ignored !! */
00062 };
00063 
00064 #define PHYS_HDR_SIZE   110             
00069 struct cpioHeader {
00070     /*@owned@*/ const char * path;
00071     struct stat sb;
00072 };
00073 
00074 #if 0
00075 static void prtli(const char *msg, struct hardLink * li)
00076 {
00077     if (msg) fprintf(stderr, "%s", msg);
00078     fprintf(stderr, " next %p files %p fileMaps %p dev %x ino %x nlink %d left %d createdPath %d size %d\n", li->next, li->files, li->fileMaps, (unsigned)li->dev, (unsigned)li->inode, li->nlink, li->linksLeft, li->createdPath, li->sb.st_size);
00079 }
00080 #endif
00081 
00089 static inline off_t saferead(FD_t cfd, /*@out@*/void * vbuf, size_t amount)
00090         /*@modifies cfd, *vbuf @*/
00091 {
00092     off_t rc = 0;
00093     char * buf = vbuf;
00094 
00095     while (amount > 0) {
00096         size_t nb;
00097 
00098         nb = Fread(buf, sizeof(buf[0]), amount, cfd);
00099         if (nb <= 0)
00100                 return nb;
00101         rc += nb;
00102         if (rc >= amount)
00103                 break;
00104         buf += nb;
00105         amount -= nb;
00106     }
00107     return rc;
00108 }
00109 
00117 static inline off_t ourread(FD_t cfd, /*@out@*/void * buf, size_t size)
00118         /*@modifies cfd, *buf @*/
00119 {
00120     off_t i = saferead(cfd, buf, size);
00121     if (i > 0)
00122         fdSetCpioPos(cfd, fdGetCpioPos(cfd) + i);
00123     return i;
00124 }
00125 
00131 static inline void padinfd(FD_t cfd, int modulo)
00132         /*@modifies cfd @*/
00133 {
00134     int buf[10];
00135     int amount;
00136 
00137     amount = (modulo - fdGetCpioPos(cfd) % modulo) % modulo;
00138     (void)ourread(cfd, buf, amount);
00139 }
00140 
00148 static inline off_t safewrite(FD_t cfd, const void * vbuf, size_t amount)
00149         /*@modifies cfd @*/
00150 {
00151     off_t rc = 0;
00152     const char * buf = vbuf;
00153 
00154     while (amount > 0) {
00155         size_t nb;
00156 
00157         nb = Fwrite(buf, sizeof(buf[0]), amount, cfd);
00158         if (nb <= 0)
00159                 return nb;
00160         rc += nb;
00161         if (rc >= amount)
00162                 break;
00163         buf += nb;
00164         amount -= nb;
00165     }
00166 
00167     return rc;
00168 }
00169 
00176 static inline int padoutfd(FD_t cfd, size_t * where, int modulo)
00177         /*@modifies cfd, *where @*/
00178 {
00179     static int buf[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
00180     int amount;
00181 
00182     amount = (modulo - *where % modulo) % modulo;
00183     *where += amount;
00184 
00185     if (safewrite(cfd, buf, amount) != amount)
00186         return CPIOERR_WRITE_FAILED;
00187     return 0;
00188 }
00189 
00198 static int strntoul(const char *str, /*@out@*/char **endptr, int base, int num)
00199         /*@modifies *endptr @*/
00200 {
00201     char * buf, * end;
00202     unsigned long ret;
00203 
00204     buf = alloca(num + 1);
00205     strncpy(buf, str, num);
00206     buf[num] = '\0';
00207 
00208     ret = strtoul(buf, &end, base);
00209     if (*end)
00210         *endptr = ((char *)str) + (end - buf);  /* XXX discards const */
00211     else
00212         *endptr = ((char *)str) + strlen(str);
00213 
00214     return ret;
00215 }
00216 
00217 #define GET_NUM_FIELD(phys, log) \
00218         log = strntoul(phys, &end, 16, sizeof(phys)); \
00219         if (*end) return CPIOERR_BAD_HEADER;
00220 #define SET_NUM_FIELD(phys, val, space) \
00221         sprintf(space, "%8.8lx", (unsigned long) (val)); \
00222         memcpy(phys, space, 8);
00223 
00230 static int getNextHeader(FD_t cfd, struct cpioHeader * hdr)
00231         /*@modifies cfd, hdr->path, hdr->sb  @*/
00232 {
00233     struct cpioCrcPhysicalHeader physHeader;
00234     struct stat * st = &hdr->sb;
00235     int nameSize;
00236     char * end;
00237     int major, minor;
00238 
00239     if (ourread(cfd, &physHeader, PHYS_HDR_SIZE) != PHYS_HDR_SIZE)
00240         return CPIOERR_READ_FAILED;
00241 
00242     if (strncmp(CPIO_CRC_MAGIC, physHeader.magic, sizeof(CPIO_CRC_MAGIC)-1) &&
00243         strncmp(CPIO_NEWC_MAGIC, physHeader.magic, sizeof(CPIO_NEWC_MAGIC)-1))
00244         return CPIOERR_BAD_MAGIC;
00245 
00246     GET_NUM_FIELD(physHeader.inode, st->st_ino);
00247     GET_NUM_FIELD(physHeader.mode, st->st_mode);
00248     GET_NUM_FIELD(physHeader.uid, st->st_uid);
00249     GET_NUM_FIELD(physHeader.gid, st->st_gid);
00250     GET_NUM_FIELD(physHeader.nlink, st->st_nlink);
00251     GET_NUM_FIELD(physHeader.mtime, st->st_mtime);
00252     GET_NUM_FIELD(physHeader.filesize, st->st_size);
00253 
00254     GET_NUM_FIELD(physHeader.devMajor, major);
00255     GET_NUM_FIELD(physHeader.devMinor, minor);
00256     st->st_dev = /*@-shiftsigned@*/ makedev(major, minor) /*@=shiftsigned@*/ ;
00257 
00258     GET_NUM_FIELD(physHeader.rdevMajor, major);
00259     GET_NUM_FIELD(physHeader.rdevMinor, minor);
00260     st->st_rdev = /*@-shiftsigned@*/ makedev(major, minor) /*@=shiftsigned@*/ ;
00261 
00262     GET_NUM_FIELD(physHeader.namesize, nameSize);
00263 
00264     {   char * t = xmalloc(nameSize + 1);
00265         if (ourread(cfd, t, nameSize) != nameSize) {
00266             free(t);
00267             hdr->path = NULL;
00268             return CPIOERR_BAD_HEADER;
00269         }
00270         hdr->path = t;
00271     }
00272 
00273     /* this is unecessary hdr->path[nameSize] = '\0'; */
00274 
00275     padinfd(cfd, 4);
00276 
00277     return 0;
00278 }
00279 
00280 int cpioFileMapCmp(const void * a, const void * b)
00281 {
00282     const char * afn = ((const struct cpioFileMapping *)a)->archivePath;
00283     const char * bfn = ((const struct cpioFileMapping *)b)->archivePath;
00284 
00285     /* Match payloads with ./ prefixes as well. */
00286     if (afn[0] == '.' && afn[1] == '/') afn += 2;
00287     if (bfn[0] == '.' && bfn[1] == '/') bfn += 2;
00288 
00289     return strcmp(afn, bfn);
00290 }
00291 
00292 /* This could trash files in the path! I'm not sure that's a good thing */
00298 static int createDirectory(const char * path, mode_t perms)
00299         /*@modifies fileSystem @*/
00300 {
00301     struct stat sb;
00302 
00303     if (!lstat(path, &sb)) {
00304         int dounlink = 0;       /* XXX eliminate, dounlink==1 on all paths */
00305         if (S_ISDIR(sb.st_mode)) {
00306             return 0;
00307         } else if (S_ISLNK(sb.st_mode)) {
00308             if (stat(path, &sb)) {
00309                 if (errno != ENOENT)
00310                     return CPIOERR_STAT_FAILED;
00311                 dounlink = 1;
00312             } else {
00313                 if (S_ISDIR(sb.st_mode))
00314                     return 0;
00315                 dounlink = 1;
00316             }
00317         } else {
00318             dounlink = 1;
00319         }
00320 
00321         if (dounlink && unlink(path)) {
00322             return CPIOERR_UNLINK_FAILED;
00323         }
00324     }
00325 
00326     if (mkdir(path, 000))
00327         return CPIOERR_MKDIR_FAILED;
00328 
00329     if (chmod(path, perms))
00330         return CPIOERR_CHMOD_FAILED;
00331 
00332     return 0;
00333 }
00334 
00340 static int setInfo(struct cpioHeader * hdr)
00341         /*@modifies fileSystem @*/
00342 {
00343     int rc = 0;
00344     struct utimbuf stamp;
00345     struct stat * st = &hdr->sb;
00346 
00347     stamp.actime = st->st_mtime;
00348     stamp.modtime = st->st_mtime;
00349 
00350     if (!S_ISLNK(st->st_mode)) {
00351         if (!getuid() && chown(hdr->path, st->st_uid, st->st_gid))
00352             rc = CPIOERR_CHOWN_FAILED;
00353         if (!rc && chmod(hdr->path, st->st_mode & 07777))
00354             rc = CPIOERR_CHMOD_FAILED;
00355         if (!rc && utime(hdr->path, &stamp))
00356             rc = CPIOERR_UTIME_FAILED;
00357     } else {
00358 #if ! CHOWN_FOLLOWS_SYMLINK
00359             if (!getuid() && !rc && lchown(hdr->path, st->st_uid, st->st_gid))
00360 /* XXX Red Hat 5.2 with kernel-2.0.38 has unimplemented lchown() */
00361 #if defined(__linux__)
00362                 rc = 0;
00363 #else
00364                 rc = CPIOERR_CHOWN_FAILED;
00365 #endif
00366 #endif
00367     }
00368 
00369     return rc;
00370 }
00371 
00377 static int inline checkDirectory(const char * filename) /*@*/
00378 {
00379     /*@only@*/ static char * lastDir = NULL;    /* XXX memory leak */
00380     static int lastDirLength = 0;
00381     static int lastDirAlloced = 0;
00382     int length = strlen(filename);
00383     char * buf;
00384     char * chptr;
00385     int rc = 0;
00386 
00387     buf = alloca(length + 1);
00388     strcpy(buf, filename);
00389 
00390     for (chptr = buf + length - 1; chptr > buf; chptr--) {
00391         if (*chptr == '/') break;
00392     }
00393 
00394     if (chptr == buf) return 0;     /* /filename - no directories */
00395 
00396     *chptr = '\0';                  /* buffer is now just directories */
00397 
00398     length = strlen(buf);
00399     if (lastDirLength == length && !strcmp(buf, lastDir)) return 0;
00400 
00401     if (lastDirAlloced < (length + 1)) {
00402         lastDirAlloced = length + 100;
00403         lastDir = xrealloc(lastDir, lastDirAlloced);    /* XXX memory leak */
00404     }
00405 
00406     strcpy(lastDir, buf);
00407     lastDirLength = length;
00408 
00409     for (chptr = buf + 1; *chptr; chptr++) {
00410         if (*chptr == '/') {
00411             *chptr = '\0';
00412             rc = createDirectory(buf, 0755);
00413             *chptr = '/';
00414             if (rc) return rc;
00415         }
00416     }
00417     rc = createDirectory(buf, 0755);
00418 
00419     return rc;
00420 }
00421 
00432 static int expandRegular(FD_t cfd, const struct cpioHeader * hdr,
00433                          const char * filemd5, cpioCallback cb, void * cbData)
00434                 /*@modifies fileSystem, cfd @*/
00435 {
00436     FD_t ofd;
00437     char buf[BUFSIZ];
00438     int bytesRead;
00439     const struct stat * st = &hdr->sb;
00440     int left = st->st_size;
00441     int rc = 0;
00442     struct cpioCallbackInfo cbInfo = { NULL, 0, 0, 0 };
00443     struct stat sb;
00444 
00445     /* Rename the old file before attempting unlink to avoid EBUSY errors */
00446     if (!lstat(hdr->path, &sb)) {
00447         strcpy(buf, hdr->path);
00448         strcat(buf, "-RPMDELETE");
00449         if (rename(hdr->path, buf)) {
00450             rpmError(RPMERR_RENAME, _("can't rename %s to %s: %s\n"),
00451                 hdr->path, buf, strerror(errno));
00452             return CPIOERR_UNLINK_FAILED;
00453         }
00454 
00455         if (unlink(buf)) {
00456             rpmError(RPMERR_UNLINK, _("can't unlink %s: %s\n"),
00457                 buf, strerror(errno));
00458 #if 0
00459             return CPIOERR_UNLINK_FAILED;
00460 #endif
00461         }
00462     }
00463 
00464     ofd = Fopen(hdr->path, "w.ufdio");
00465     if (ofd == NULL || Ferror(ofd))
00466         return CPIOERR_OPEN_FAILED;
00467 
00468     /* XXX This doesn't support brokenEndian checks. */
00469 #if !defined(__sparc__)
00470     if (filemd5)
00471         fdInitMD5(ofd, 0);
00472 #endif
00473 
00474     cbInfo.file = hdr->path;
00475     cbInfo.fileSize = st->st_size;
00476 
00477     while (left) {
00478         bytesRead = ourread(cfd, buf, left < sizeof(buf) ? left : sizeof(buf));
00479         if (bytesRead <= 0) {
00480             rc = CPIOERR_READ_FAILED;
00481             break;
00482         }
00483 
00484         if (Fwrite(buf, sizeof(buf[0]), bytesRead, ofd) != bytesRead) {
00485             rc = CPIOERR_COPY_FAILED;
00486             break;
00487         }
00488 
00489         left -= bytesRead;
00490 
00491         /* don't call this with fileSize == fileComplete */
00492         if (!rc && cb && left) {
00493             cbInfo.fileComplete = st->st_size - left;
00494             cbInfo.bytesProcessed = fdGetCpioPos(cfd);
00495             cb(&cbInfo, cbData);
00496         }
00497     }
00498 
00499 #if !defined(__sparc__)
00500     if (filemd5) {
00501         const char * md5sum = NULL;
00502 
00503         Fflush(ofd);
00504         fdFiniMD5(ofd, (void **)&md5sum, NULL, 1);
00505 
00506         if (md5sum == NULL) {
00507             rc = CPIOERR_MD5SUM_MISMATCH;
00508         } else {
00509             if (strcmp(md5sum, filemd5))
00510                 rc = CPIOERR_MD5SUM_MISMATCH;
00511             free((void *)md5sum);
00512         }
00513     }
00514 #endif
00515 
00516     Fclose(ofd);
00517 
00518     return rc;
00519 }
00520 
00527 static int expandSymlink(FD_t cfd, const struct cpioHeader * hdr)
00528                 /*@modifies fileSystem, cfd @*/
00529 {
00530     char buf[2048], buf2[2048];
00531     struct stat sb;
00532     const struct stat * st = &hdr->sb;
00533     int len;
00534 
00535     if ((st->st_size + 1)> sizeof(buf))
00536         return CPIOERR_HDR_SIZE;
00537 
00538     if (ourread(cfd, buf, st->st_size) != st->st_size)
00539         return CPIOERR_READ_FAILED;
00540 
00541     buf[st->st_size] = '\0';
00542 
00543     if (!lstat(hdr->path, &sb)) {
00544         if (S_ISLNK(sb.st_mode)) {
00545             len = readlink(hdr->path, buf2, sizeof(buf2) - 1);
00546             if (len > 0) {
00547                 buf2[len] = '\0';
00548                 if (!strcmp(buf, buf2)) return 0;
00549             }
00550         }
00551 
00552         if (unlink(hdr->path))
00553             return CPIOERR_UNLINK_FAILED;
00554     }
00555 
00556     if (symlink(buf, hdr->path) < 0)
00557         return CPIOERR_SYMLINK_FAILED;
00558 
00559     return 0;
00560 }
00561 
00568 static int expandFifo( /*@unused@*/ FD_t cfd, const struct cpioHeader * hdr)
00569                 /*@modifies fileSystem @*/
00570 {
00571     struct stat sb;
00572 
00573     if (!lstat(hdr->path, &sb)) {
00574         if (S_ISFIFO(sb.st_mode)) return 0;
00575 
00576         if (unlink(hdr->path))
00577             return CPIOERR_UNLINK_FAILED;
00578     }
00579 
00580     if (mkfifo(hdr->path, 0))
00581         return CPIOERR_MKFIFO_FAILED;
00582 
00583     return 0;
00584 }
00585 
00592 static int expandDevice( /*@unused@*/ FD_t cfd, const struct cpioHeader * hdr)
00593                 /*@modifies fileSystem @*/
00594 {
00595     const struct stat * st = &hdr->sb;
00596     struct stat sb;
00597 
00598     if (!lstat(hdr->path, &sb)) {
00599         if ((S_ISCHR(sb.st_mode) || S_ISBLK(sb.st_mode)) &&
00600                 (sb.st_rdev == st->st_rdev))
00601             return 0;
00602         if (unlink(hdr->path))
00603             return CPIOERR_UNLINK_FAILED;
00604     }
00605 
00606     if ( /*@-unrecog@*/ mknod(hdr->path, st->st_mode & (~0777), st->st_rdev) /*@=unrecog@*/ )
00607         return CPIOERR_MKNOD_FAILED;
00608 
00609     return 0;
00610 }
00611 
00618 static /*@only@*/ struct hardLink * newHardLink(const struct stat * st,
00619                                 enum hardLinkType hltype)       /*@*/
00620 {
00621     struct hardLink * li = xmalloc(sizeof(*li));
00622 
00623     li->next = NULL;
00624     li->nlink = st->st_nlink;
00625     li->dev = st->st_dev;
00626     li->inode = st->st_ino;
00627     li->createdPath = -1;
00628 
00629     switch (hltype) {
00630     case HARDLINK_INSTALL:
00631         li->linksLeft = st->st_nlink;
00632         li->fileMaps = xmalloc(sizeof(li->fileMaps[0]) * st->st_nlink);
00633         li->files = NULL;
00634         break;
00635     case HARDLINK_BUILD:
00636         li->linksLeft = 0;
00637         li->fileMaps = NULL;
00638         li->files = xcalloc(st->st_nlink, sizeof(*li->files));
00639         break;
00640     }
00641 
00642     {   struct stat * myst = (struct stat *) &li->sb;
00643         *myst = *st;    /* structure assignment */
00644     }
00645 
00646     return li;
00647 }
00648 
00653 static void freeHardLink( /*@only@*/ struct hardLink * li)
00654 {
00655 
00656     if (li->files) {
00657         int i;
00658         for (i = 0; i < li->nlink; i++) {
00659             if (li->files[i] == NULL) continue;
00660             /*@-unqualifiedtrans@*/ free((void *)li->files[i]) /*@=unqualifiedtrans@*/ ;
00661             li->files[i] = NULL;
00662         }
00663         free(li->files);
00664         li->files = NULL;
00665     }
00666     if (li->fileMaps) {
00667         free(li->fileMaps);
00668         li->fileMaps = NULL;
00669     }
00670     free(li);
00671 }
00672 
00679 static int createLinks(struct hardLink * li, /*@out@*/ const char ** failedFile)
00680         /*@modifies fileSystem, *failedFile, li->files, li->linksLeft @*/
00681 {
00682     int i;
00683     struct stat sb;
00684 
00685     for (i = 0; i < li->nlink; i++) {
00686         if (i == li->createdPath) continue;
00687         if (li->files[i] == NULL) continue;
00688 
00689         if (!lstat(li->files[i], &sb)) {
00690             if (unlink(li->files[i])) {
00691                 if (failedFile)
00692                     *failedFile = xstrdup(li->files[i]);
00693                 return CPIOERR_UNLINK_FAILED;
00694             }
00695         }
00696 
00697         if (link(li->files[li->createdPath], li->files[i])) {
00698             if (failedFile)
00699                 *failedFile = xstrdup(li->files[i]);
00700             return CPIOERR_LINK_FAILED;
00701         }
00702 
00703         /*@-unqualifiedtrans@*/ free((void *)li->files[i]) /*@=unqualifiedtrans@*/ ;
00704         li->files[i] = NULL;
00705         li->linksLeft--;
00706     }
00707 
00708     return 0;
00709 }
00710 
00717 static int eatBytes(FD_t cfd, int amount)
00718         /*@modifies cfd @*/
00719 {
00720     char buf[4096];
00721     int bite;
00722 
00723     while (amount) {
00724         bite = (amount > sizeof(buf)) ? sizeof(buf) : amount;
00725         if (ourread(cfd, buf, bite) != bite)
00726             return CPIOERR_READ_FAILED;
00727         amount -= bite;
00728     }
00729 
00730     return 0;
00731 }
00732 
00734 int cpioInstallArchive(FD_t cfd, const struct cpioFileMapping * mappings,
00735                        int numMappings, cpioCallback cb, void * cbData,
00736                        const char ** failedFile)
00737 {
00738     struct cpioHeader ch, *hdr = &ch;
00739     struct cpioFileMapping * map = NULL;
00740     struct cpioFileMapping needle;
00741     struct cpioCallbackInfo cbInfo = { NULL, 0, 0, 0 };
00742     struct hardLink * links = NULL;
00743     struct hardLink * li = NULL;
00744     int rc = 0;
00745 
00746 #ifdef  NOTYET
00747     char * md5sum = NULL;
00748 
00749     fdInitMD5(cfd, 0);
00750 #endif
00751 
00752     fdSetCpioPos(cfd, 0);
00753     if (failedFile)
00754         *failedFile = NULL;
00755 
00756     memset(hdr, 0, sizeof(*hdr));
00757     hdr->path = NULL;
00758     do {
00759         struct stat * st;
00760 
00761         if (hdr->path) {
00762             free((void *)hdr->path);
00763             hdr->path = NULL;
00764         }
00765         if ((rc = getNextHeader(cfd, hdr))) {
00766 #if 0   /* XXX this is the failure point for an unreadable rpm */
00767             rpmError(RPMERR_BADPACKAGE, _("getNextHeader: %s\n"),
00768                         cpioStrerror(rc));
00769 #endif
00770             return rc;
00771         }
00772         st = &hdr->sb;
00773 
00774         if (!strcmp(hdr->path, TRAILER))
00775             break;
00776 
00777         if (mappings) {
00778             needle.archivePath = hdr->path;
00779             map = bsearch(&needle, mappings, numMappings, sizeof(needle),
00780                           cpioFileMapCmp);
00781         }
00782 
00783         if (mappings && !map) {
00784             eatBytes(cfd, st->st_size);
00785         } else {
00786             if (map) {
00787                 if (map->mapFlags & CPIO_MAP_PATH) {
00788                     if (hdr->path) free((void *)hdr->path);
00789                     hdr->path = xstrdup(map->fsPath);
00790                 }
00791 
00792                 if (map->mapFlags & CPIO_MAP_MODE)
00793                     st->st_mode = map->finalMode;
00794                 if (map->mapFlags & CPIO_MAP_UID)
00795                     st->st_uid = map->finalUid;
00796                 if (map->mapFlags & CPIO_MAP_GID)
00797                     st->st_gid = map->finalGid;
00798             }
00799 
00800             /* This won't get hard linked symlinks right, but I can't seem
00801                to create those anyway */
00802 
00803             if (S_ISREG(st->st_mode) && st->st_nlink > 1) {
00804                 for (li = links; li; li = li->next) {
00805                     if (li->inode == st->st_ino && li->dev == st->st_dev) break;
00806                 }
00807 
00808                 if (li == NULL) {
00809                     li = newHardLink(st, HARDLINK_BUILD);
00810                     li->next = links;
00811                     links = li;
00812                 }
00813 
00814                 li->files[li->linksLeft++] = xstrdup(hdr->path);
00815             }
00816 
00817             if ((st->st_nlink > 1) && S_ISREG(st->st_mode) && !st->st_size &&
00818                 li->createdPath == -1) {
00819                 /* defer file creation */
00820             } else if ((st->st_nlink > 1) && S_ISREG(st->st_mode) &&
00821                        (li->createdPath != -1)) {
00822                 createLinks(li, failedFile);
00823 
00824                 /* this only happens for cpio archives which contain
00825                    hardlinks w/ the contents of each hardlink being
00826                    listed (intead of the data being given just once. This
00827                    shouldn't happen, but I've made it happen w/ buggy
00828                    code, so what the heck? GNU cpio handles this well fwiw */
00829                 if (st->st_size) eatBytes(cfd, st->st_size);
00830             } else {
00831                 rc = checkDirectory(hdr->path);
00832 
00833                 if (!rc) {
00834                     if (S_ISREG(st->st_mode))
00835                         rc = expandRegular(cfd, hdr, map->md5sum, cb, cbData);
00836                     else if (S_ISDIR(st->st_mode))
00837                         rc = createDirectory(hdr->path, 000);
00838                     else if (S_ISLNK(st->st_mode))
00839                         rc = expandSymlink(cfd, hdr);
00840                     else if (S_ISFIFO(st->st_mode))
00841                         rc = expandFifo(cfd, hdr);
00842                     else if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode))
00843                         rc = expandDevice(cfd, hdr);
00844                     else if (S_ISSOCK(st->st_mode)) {
00845                         /* this mimicks cpio but probably isnt' right */
00846                         rc = expandFifo(cfd, hdr);
00847                     } else {
00848                         rc = CPIOERR_UNKNOWN_FILETYPE;
00849                     }
00850                 }
00851 
00852                 if (!rc)
00853                     rc = setInfo(hdr);
00854 
00855                 if (S_ISREG(st->st_mode) && st->st_nlink > 1) {
00856                     li->createdPath = --li->linksLeft;
00857                     rc = createLinks(li, failedFile);
00858                 }
00859             }
00860 
00861             if (rc && failedFile && *failedFile == NULL) {
00862                 int olderrno;
00863 
00864                 *failedFile = xstrdup(hdr->path);
00865                 olderrno = errno;
00866                 unlink(hdr->path);
00867                 errno = olderrno;
00868             }
00869         }
00870 
00871         padinfd(cfd, 4);
00872 
00873         if (!rc && cb) {
00874             cbInfo.file = hdr->path;
00875             cbInfo.fileSize = st->st_size;
00876             cbInfo.fileComplete = st->st_size;
00877             cbInfo.bytesProcessed = fdGetCpioPos(cfd);
00878             cb(&cbInfo, cbData);
00879         }
00880 
00881     } while (rc == 0);
00882 
00883     if (hdr->path) {
00884         free((void *)hdr->path);
00885         hdr->path = NULL;
00886     }
00887 
00888     /* Create any remaining links (if no error), and clean up. */
00889     while ((li = links) != NULL) {
00890         links = li->next;
00891         li->next = NULL;
00892 
00893         if (rc == 0 && li->linksLeft) {
00894             if (li->createdPath == -1)
00895                 rc = CPIOERR_MISSING_HARDLINK;
00896             else
00897                 rc = createLinks(li, failedFile);
00898         }
00899 
00900         freeHardLink(li);
00901     }
00902 
00903 #ifdef  NOTYET
00904     fdFiniMD5(cfd, (void **)&md5sum, NULL, 1);
00905 
00906     if (md5sum)
00907         free(md5sum);
00908 #endif
00909 
00910     return rc;
00911 }
00912 
00922 static int writeFile(FD_t cfd, const struct stat * st,
00923         const struct cpioFileMapping * map, /*@out@*/ size_t * sizep,
00924         int writeData)
00925         /*@modifies cfd, *sizep @*/
00926 {
00927     struct cpioCrcPhysicalHeader hdr;
00928     char buf[8192], symbuf[2048];
00929     dev_t num;
00930     FD_t datafd;
00931     size_t st_size = st->st_size;       /* XXX hard links need size preserved */
00932     const char * archivePath;
00933     mode_t st_mode = st->st_mode;
00934     uid_t st_uid = st->st_uid;
00935     gid_t st_gid = st->st_gid;
00936     size_t size, amount = 0;
00937     int rc;
00938 
00939     archivePath = (!(map->mapFlags & CPIO_MAP_PATH))
00940         ? map->fsPath : map->archivePath;
00941 
00942     if (map->mapFlags & CPIO_MAP_MODE)
00943         st_mode = (st_mode & S_IFMT) | map->finalMode;
00944     if (map->mapFlags & CPIO_MAP_UID)
00945         st_uid = map->finalUid;
00946     if (map->mapFlags & CPIO_MAP_GID)
00947         st_gid = map->finalGid;
00948 
00949     if (!writeData || S_ISDIR(st_mode)) {
00950         st_size = 0;
00951     } else if (S_ISLNK(st_mode)) {
00952         /* While linux puts the size of a symlink in the st_size field,
00953            I don't think that's a specified standard */
00954 
00955         amount = Readlink(map->fsPath, symbuf, sizeof(symbuf));
00956         if (amount <= 0) {
00957             return CPIOERR_READLINK_FAILED;
00958         }
00959 
00960         st_size = amount;
00961     }
00962 
00963     memcpy(hdr.magic, CPIO_NEWC_MAGIC, sizeof(hdr.magic));
00964     SET_NUM_FIELD(hdr.inode, st->st_ino, buf);
00965     SET_NUM_FIELD(hdr.mode, st_mode, buf);
00966     SET_NUM_FIELD(hdr.uid, st_uid, buf);
00967     SET_NUM_FIELD(hdr.gid, st_gid, buf);
00968     SET_NUM_FIELD(hdr.nlink, st->st_nlink, buf);
00969     SET_NUM_FIELD(hdr.mtime, st->st_mtime, buf);
00970     SET_NUM_FIELD(hdr.filesize, st_size, buf);
00971 
00972     num = major((unsigned)st->st_dev); SET_NUM_FIELD(hdr.devMajor, num, buf);
00973     num = minor((unsigned)st->st_dev); SET_NUM_FIELD(hdr.devMinor, num, buf);
00974     num = major((unsigned)st->st_rdev); SET_NUM_FIELD(hdr.rdevMajor, num, buf);
00975     num = minor((unsigned)st->st_rdev); SET_NUM_FIELD(hdr.rdevMinor, num, buf);
00976 
00977     num = strlen(archivePath) + 1; SET_NUM_FIELD(hdr.namesize, num, buf);
00978     memcpy(hdr.checksum, "00000000", 8);
00979 
00980     if ((rc = safewrite(cfd, &hdr, PHYS_HDR_SIZE)) != PHYS_HDR_SIZE)
00981         return rc;
00982     if ((rc = safewrite(cfd, archivePath, num)) != num)
00983         return rc;
00984     size = PHYS_HDR_SIZE + num;
00985     if ((rc = padoutfd(cfd, &size, 4)))
00986         return rc;
00987 
00988     if (writeData && S_ISREG(st_mode)) {
00989         char *b;
00990 #if HAVE_MMAP
00991         void *mapped;
00992         size_t nmapped;
00993 #endif
00994 
00995         /* XXX unbuffered mmap generates *lots* of fdio debugging */
00996         datafd = Fopen(map->fsPath, "r.ufdio");
00997         if (datafd == NULL || Ferror(datafd))
00998             return CPIOERR_OPEN_FAILED;
00999 
01000 #if HAVE_MMAP
01001         nmapped = 0;
01002         mapped = mmap(NULL, st_size, PROT_READ, MAP_SHARED, Fileno(datafd), 0);
01003         if (mapped != (void *)-1) {
01004             b = (char *)mapped;
01005             nmapped = st_size;
01006         } else
01007 #endif
01008         {
01009             b = buf;
01010         }
01011 
01012         size += st_size;
01013 
01014         while (st_size) {
01015 #if HAVE_MMAP
01016           if (mapped != (void *)-1) {
01017             amount = nmapped;
01018           } else
01019 #endif
01020           {
01021             amount = Fread(b, sizeof(buf[0]),
01022                         (st_size > sizeof(buf) ? sizeof(buf) : st_size),
01023                         datafd);
01024             if (amount <= 0) {
01025                 int olderrno = errno;
01026                 Fclose(datafd);
01027                 errno = olderrno;
01028                 return CPIOERR_READ_FAILED;
01029             }
01030           }
01031 
01032             if ((rc = safewrite(cfd, b, amount)) != amount) {
01033                 int olderrno = errno;
01034                 Fclose(datafd);
01035                 errno = olderrno;
01036                 return rc;
01037             }
01038 
01039             st_size -= amount;
01040         }
01041 
01042 #if HAVE_MMAP
01043         if (mapped != (void *)-1) {
01044             /*@-noeffect@*/ munmap(mapped, nmapped) /*@=noeffect@*/;
01045         }
01046 #endif
01047 
01048         Fclose(datafd);
01049     } else if (writeData && S_ISLNK(st_mode)) {
01050         if ((rc = safewrite(cfd, symbuf, amount)) != amount)
01051             return rc;
01052         size += amount;
01053     }
01054 
01055     /* this is a noop for most file types */
01056     if ((rc = padoutfd(cfd, &size, 4)))
01057         return rc;
01058 
01059     if (sizep)
01060         *sizep = size;
01061 
01062     return 0;
01063 }
01064 
01076 static int writeLinkedFile(FD_t cfd, const struct hardLink * hlink,
01077                            const struct cpioFileMapping * mappings,
01078                            cpioCallback cb, void * cbData,
01079                            /*@out@*/size_t * sizep,
01080                            /*@out@*/const char ** failedFile)
01081         /*@modifies cfd, *sizep, *failedFile @*/
01082 {
01083     int i, rc;
01084     size_t size, total;
01085     struct cpioCallbackInfo cbInfo = { NULL, 0, 0, 0 };
01086 
01087     total = 0;
01088 
01089     for (i = hlink->nlink - 1; i > hlink->linksLeft; i--) {
01090         if ((rc = writeFile(cfd, &hlink->sb, mappings + hlink->fileMaps[i],
01091                             &size, 0))) {
01092             if (failedFile)
01093                 *failedFile = xstrdup(mappings[hlink->fileMaps[i]].fsPath);
01094             return rc;
01095         }
01096 
01097         total += size;
01098 
01099         if (cb) {
01100             cbInfo.file = mappings[i].archivePath;
01101             cb(&cbInfo, cbData);
01102         }
01103     }
01104 
01105     if ((rc = writeFile(cfd, &hlink->sb,
01106                         mappings + hlink->fileMaps[hlink->linksLeft],
01107                         &size, 1))) {
01108         if (sizep)
01109             *sizep = total;
01110         if (failedFile)
01111             *failedFile = xstrdup(mappings[hlink->fileMaps[hlink->linksLeft]].fsPath);
01112         return rc;
01113     }
01114     total += size;
01115 
01116     if (sizep)
01117         *sizep = total;
01118 
01119     if (cb) {
01120         cbInfo.file = mappings[i].archivePath;
01121         cb(&cbInfo, cbData);
01122     }
01123 
01124     return 0;
01125 }
01126 
01127 int cpioBuildArchive(FD_t cfd, const struct cpioFileMapping * mappings,
01128                      int numMappings, cpioCallback cb, void * cbData,
01129                      unsigned int * archiveSize, const char ** failedFile)
01130 {
01131     size_t size, totalsize = 0;
01132     int rc;
01133     int i;
01134     struct cpioCallbackInfo cbInfo = { NULL, 0, 0, 0 };
01135     struct cpioCrcPhysicalHeader hdr;
01136 /*@-fullinitblock@*/
01137     struct hardLink hlinkList = { NULL };
01138 /*@=fullinitblock@*/
01139     struct stat * st = (struct stat *) &hlinkList.sb;
01140     struct hardLink * hlink;
01141 
01142     hlinkList.next = NULL;
01143 
01144     for (i = 0; i < numMappings; i++) {
01145         const struct cpioFileMapping * map;
01146 
01147         map = mappings + i;
01148 
01149         if (map->mapFlags & CPIO_FOLLOW_SYMLINKS)
01150             rc = Stat(map->fsPath, st);
01151         else
01152             rc = Lstat(map->fsPath, st);
01153 
01154         if (rc) {
01155             if (failedFile)
01156                 *failedFile = xstrdup(map->fsPath);
01157             return CPIOERR_STAT_FAILED;
01158         }
01159 
01160         if (!S_ISDIR(st->st_mode) && st->st_nlink > 1) {
01161             hlink = hlinkList.next;
01162             while (hlink &&
01163                   (hlink->dev != st->st_dev || hlink->inode != st->st_ino))
01164                         hlink = hlink->next;
01165             if (hlink == NULL) {
01166                 hlink = newHardLink(st, HARDLINK_INSTALL);
01167                 hlink->next = hlinkList.next;
01168                 hlinkList.next = hlink;
01169             }
01170 
01171             hlink->fileMaps[--hlink->linksLeft] = i;
01172 
01173             if (hlink->linksLeft == 0) {
01174                 struct hardLink * prev;
01175                 if ((rc = writeLinkedFile(cfd, hlink, mappings, cb, cbData,
01176                                           &size, failedFile)))
01177                     return rc;
01178 
01179                 totalsize += size;
01180 
01181                 prev = &hlinkList;
01182                 do {
01183                     if (prev->next != hlink)
01184                         continue;
01185                     prev->next = hlink->next;
01186                     hlink->next = NULL;
01187                     freeHardLink(hlink);
01188                     hlink = NULL;
01189                     break;
01190                 } while ((prev = prev->next) != NULL);
01191             }
01192         } else {
01193             if ((rc = writeFile(cfd, st, map, &size, 1))) {
01194                 if (failedFile)
01195                     *failedFile = xstrdup(mappings[i].fsPath);
01196                 return rc;
01197             }
01198 
01199             if (cb) {
01200                 cbInfo.file = map->archivePath;
01201                 cb(&cbInfo, cbData);
01202             }
01203 
01204             totalsize += size;
01205         }
01206     }
01207 
01208     rc = 0;
01209     while ((hlink = hlinkList.next) != NULL) {
01210         hlinkList.next = hlink->next;
01211         hlink->next = NULL;
01212 
01213         if (rc == 0) {
01214             rc = writeLinkedFile(cfd, hlink, mappings, cb, cbData,
01215                                   &size, failedFile);
01216             totalsize += size;
01217         }
01218         freeHardLink(hlink);
01219     }
01220     if (rc)
01221         return rc;
01222 
01223     memset(&hdr, '0', PHYS_HDR_SIZE);
01224     memcpy(hdr.magic, CPIO_NEWC_MAGIC, sizeof(hdr.magic));
01225     memcpy(hdr.nlink, "00000001", 8);
01226     memcpy(hdr.namesize, "0000000b", 8);
01227     if ((rc = safewrite(cfd, &hdr, PHYS_HDR_SIZE)) != PHYS_HDR_SIZE)
01228         return rc;
01229     if ((rc = safewrite(cfd, "TRAILER!!!", 11)) != 11)
01230         return rc;
01231     totalsize += PHYS_HDR_SIZE + 11;
01232 
01233     /* GNU cpio pads to 512 bytes here, but we don't. I'm not sure if
01234        it matters or not */
01235 
01236     if ((rc = padoutfd(cfd, &totalsize, 4)))
01237         return rc;
01238 
01239     if (archiveSize) *archiveSize = totalsize;
01240 
01241     return 0;
01242 }
01243 
01244 const char * cpioStrerror(int rc)
01245 {
01246     static char msg[256];
01247     char *s;
01248     int l, myerrno = errno;
01249 
01250     strcpy(msg, "cpio: ");
01251     switch (rc) {
01252     default:
01253         s = msg + strlen(msg);
01254         sprintf(s, _("(error 0x%x)"), (unsigned)rc);
01255         s = NULL;
01256         break;
01257     case CPIOERR_BAD_MAGIC:     s = _("Bad magic");             break;
01258     case CPIOERR_BAD_HEADER:    s = _("Bad/unreadable  header");break;
01259 
01260     case CPIOERR_OPEN_FAILED:   s = "open";     break;
01261     case CPIOERR_CHMOD_FAILED:  s = "chmod";    break;
01262     case CPIOERR_CHOWN_FAILED:  s = "chown";    break;
01263     case CPIOERR_WRITE_FAILED:  s = "write";    break;
01264     case CPIOERR_UTIME_FAILED:  s = "utime";    break;
01265     case CPIOERR_UNLINK_FAILED: s = "unlink";   break;
01266     case CPIOERR_SYMLINK_FAILED: s = "symlink"; break;
01267     case CPIOERR_STAT_FAILED:   s = "stat";     break;
01268     case CPIOERR_MKDIR_FAILED:  s = "mkdir";    break;
01269     case CPIOERR_MKNOD_FAILED:  s = "mknod";    break;
01270     case CPIOERR_MKFIFO_FAILED: s = "mkfifo";   break;
01271     case CPIOERR_LINK_FAILED:   s = "link";     break;
01272     case CPIOERR_READLINK_FAILED: s = "readlink";       break;
01273     case CPIOERR_READ_FAILED:   s = "read";     break;
01274     case CPIOERR_COPY_FAILED:   s = "copy";     break;
01275 
01276     case CPIOERR_HDR_SIZE:      s = _("Header size too big");   break;
01277     case CPIOERR_UNKNOWN_FILETYPE: s = _("Unknown file type");  break;
01278     case CPIOERR_MISSING_HARDLINK: s = _("Missing hard link");  break;
01279     case CPIOERR_MD5SUM_MISMATCH: s = _("MD5 sum mismatch");    break;
01280     case CPIOERR_INTERNAL:      s = _("Internal error");        break;
01281     }
01282 
01283     l = sizeof(msg) - strlen(msg) - 1;
01284     if (s != NULL) {
01285         if (l > 0) strncat(msg, s, l);
01286         l -= strlen(s);
01287     }
01288     if ((rc & CPIOERR_CHECK_ERRNO) && myerrno) {
01289         s = _(" failed - ");
01290         if (l > 0) strncat(msg, s, l);
01291         l -= strlen(s);
01292         if (l > 0) strncat(msg, strerror(myerrno), l);
01293     }
01294     return msg;
01295 }

Generated at Sun Apr 8 18:42:59 2001 for rpm by doxygen1.2.3 written by Dimitri van Heesch, © 1997-2000