Typo.
[platform/upstream/rpm.git] / lib / cpio.c
1 /** \ingroup payload rpmio
2  * \file lib/cpio.c
3  *  Handle cpio payloads within rpm packages.
4  *
5  * \warning FIXME: We don't translate between cpio and system mode bits! These
6  * should both be the same, but really odd things are going to happen if
7  * that's not true!
8  */
9
10 #include "system.h"
11 #include "cpio.h"
12 #include "rpmerr.h"
13 #include "debug.h"
14
15 /*@access FD_t@*/
16
17 #define CPIO_NEWC_MAGIC "070701"
18 #define CPIO_CRC_MAGIC  "070702"
19 #define TRAILER         "TRAILER!!!"
20
21 /** \ingroup payload
22  * Keeps track of set of all hard linked files in archive.
23  */
24 struct hardLink {
25     struct hardLink * next;
26     const char ** files;        /* nlink of these, used by install */
27     int * fileMaps;             /* used by build */
28     dev_t dev;
29     ino_t inode;
30     int nlink;
31     int linksLeft;
32     int createdPath;
33     const struct stat sb;
34 };
35
36 /** \ingroup payload
37  */
38 enum hardLinkType {
39         HARDLINK_INSTALL=1,
40         HARDLINK_BUILD
41 };
42
43 /** \ingroup payload
44  * Cpio archive header information.
45  * @todo Add support for tar (soon) and ar (eventually) archive formats.
46  */
47 struct cpioCrcPhysicalHeader {
48     char magic[6];
49     char inode[8];
50     char mode[8];
51     char uid[8];
52     char gid[8];
53     char nlink[8];
54     char mtime[8];
55     char filesize[8];
56     char devMajor[8];
57     char devMinor[8];
58     char rdevMajor[8];
59     char rdevMinor[8];
60     char namesize[8];
61     char checksum[8];                   /* ignored !! */
62 };
63
64 #define PHYS_HDR_SIZE   110             /*!< Don't depend on sizeof(struct) */
65
66 /** \ingroup payload
67  * File name and stat information.
68  */
69 struct cpioHeader {
70     /*@owned@*/ const char * path;
71     struct stat sb;
72 };
73
74 #if 0
75 static void prtli(const char *msg, struct hardLink * li)
76 {
77     if (msg) fprintf(stderr, "%s", msg);
78     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);
79 }
80 #endif
81
82 /**
83  * Read data from payload.
84  * @param cfd           payload file handle
85  * @retval vbuf         data from read
86  * @param amount        no. bytes to read
87  * @return              no. bytes read
88  */
89 static inline off_t saferead(FD_t cfd, /*@out@*/void * vbuf, size_t amount)
90         /*@modifies cfd, *vbuf @*/
91 {
92     off_t rc = 0;
93     char * buf = vbuf;
94
95     while (amount > 0) {
96         size_t nb;
97
98         nb = Fread(buf, sizeof(buf[0]), amount, cfd);
99         if (nb <= 0)
100                 return nb;
101         rc += nb;
102         if (rc >= amount)
103                 break;
104         buf += nb;
105         amount -= nb;
106     }
107     return rc;
108 }
109
110 /**
111  * Read data from payload and update number of bytes read.
112  * @param cfd           payload file handle
113  * @retval buf          data from read
114  * @param size          no. bytes to read
115  * @return              no. bytes read
116  */
117 static inline off_t ourread(FD_t cfd, /*@out@*/void * buf, size_t size)
118         /*@modifies cfd, *buf @*/
119 {
120     off_t i = saferead(cfd, buf, size);
121     if (i > 0)
122         fdSetCpioPos(cfd, fdGetCpioPos(cfd) + i);
123     return i;
124 }
125
126 /**
127  * Align input payload handle, skipping input bytes.
128  * @param cfd           payload file handle
129  * @param modulo        data alignment
130  */
131 static inline void padinfd(FD_t cfd, int modulo)
132         /*@modifies cfd @*/
133 {
134     int buf[10];
135     int amount;
136
137     amount = (modulo - fdGetCpioPos(cfd) % modulo) % modulo;
138     (void)ourread(cfd, buf, amount);
139 }
140
141 /**
142  * Write data to payload.
143  * @param cfd           payload file handle
144  * @param vbuf          data to write
145  * @param amount        no. bytes to write
146  * @return              no. bytes written
147  */
148 static inline off_t safewrite(FD_t cfd, const void * vbuf, size_t amount)
149         /*@modifies cfd @*/
150 {
151     off_t rc = 0;
152     const char * buf = vbuf;
153
154     while (amount > 0) {
155         size_t nb;
156
157         nb = Fwrite(buf, sizeof(buf[0]), amount, cfd);
158         if (nb <= 0)
159                 return nb;
160         rc += nb;
161         if (rc >= amount)
162                 break;
163         buf += nb;
164         amount -= nb;
165     }
166
167     return rc;
168 }
169
170 /**
171  * Align output payload handle, padding with zeroes.
172  * @param cfd           payload file handle
173  * @param modulo        data alignment
174  * @return              0 on success, CPIOERR_WRITE_FAILED
175  */
176 static inline int padoutfd(FD_t cfd, size_t * where, int modulo)
177         /*@modifies cfd, *where @*/
178 {
179     static int buf[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
180     int amount;
181
182     amount = (modulo - *where % modulo) % modulo;
183     *where += amount;
184
185     if (safewrite(cfd, buf, amount) != amount)
186         return CPIOERR_WRITE_FAILED;
187     return 0;
188 }
189
190 /**
191  * Convert string to unsigned integer (with buffer size check).
192  * @param               input string
193  * @retval              address of 1st character not processed
194  * @param base          numerical conversion base
195  * @param num           max no. of bytes to read
196  * @return              converted integer
197  */
198 static int strntoul(const char *str, /*@out@*/char **endptr, int base, int num)
199         /*@modifies *endptr @*/
200 {
201     char * buf, * end;
202     unsigned long ret;
203
204     buf = alloca(num + 1);
205     strncpy(buf, str, num);
206     buf[num] = '\0';
207
208     ret = strtoul(buf, &end, base);
209     if (*end)
210         *endptr = ((char *)str) + (end - buf);  /* XXX discards const */
211     else
212         *endptr = ((char *)str) + strlen(str);
213
214     return ret;
215 }
216
217 #define GET_NUM_FIELD(phys, log) \
218         log = strntoul(phys, &end, 16, sizeof(phys)); \
219         if (*end) return CPIOERR_BAD_HEADER;
220 #define SET_NUM_FIELD(phys, val, space) \
221         sprintf(space, "%8.8lx", (unsigned long) (val)); \
222         memcpy(phys, space, 8);
223
224 /**
225  * Process next cpio heasder.
226  * @param cfd           payload file handle
227  * @retval hdr          file name and stat info
228  * @return              0 on success
229  */
230 static int getNextHeader(FD_t cfd, struct cpioHeader * hdr)
231         /*@modifies cfd, hdr->path, hdr->sb  @*/
232 {
233     struct cpioCrcPhysicalHeader physHeader;
234     struct stat * st = &hdr->sb;
235     int nameSize;
236     char * end;
237     int major, minor;
238
239     if (ourread(cfd, &physHeader, PHYS_HDR_SIZE) != PHYS_HDR_SIZE)
240         return CPIOERR_READ_FAILED;
241
242     if (strncmp(CPIO_CRC_MAGIC, physHeader.magic, sizeof(CPIO_CRC_MAGIC)-1) &&
243         strncmp(CPIO_NEWC_MAGIC, physHeader.magic, sizeof(CPIO_NEWC_MAGIC)-1))
244         return CPIOERR_BAD_MAGIC;
245
246     GET_NUM_FIELD(physHeader.inode, st->st_ino);
247     GET_NUM_FIELD(physHeader.mode, st->st_mode);
248     GET_NUM_FIELD(physHeader.uid, st->st_uid);
249     GET_NUM_FIELD(physHeader.gid, st->st_gid);
250     GET_NUM_FIELD(physHeader.nlink, st->st_nlink);
251     GET_NUM_FIELD(physHeader.mtime, st->st_mtime);
252     GET_NUM_FIELD(physHeader.filesize, st->st_size);
253
254     GET_NUM_FIELD(physHeader.devMajor, major);
255     GET_NUM_FIELD(physHeader.devMinor, minor);
256     st->st_dev = /*@-shiftsigned@*/ makedev(major, minor) /*@=shiftsigned@*/ ;
257
258     GET_NUM_FIELD(physHeader.rdevMajor, major);
259     GET_NUM_FIELD(physHeader.rdevMinor, minor);
260     st->st_rdev = /*@-shiftsigned@*/ makedev(major, minor) /*@=shiftsigned@*/ ;
261
262     GET_NUM_FIELD(physHeader.namesize, nameSize);
263
264     {   char * t = xmalloc(nameSize + 1);
265         if (ourread(cfd, t, nameSize) != nameSize) {
266             free(t);
267             hdr->path = NULL;
268             return CPIOERR_BAD_HEADER;
269         }
270         hdr->path = t;
271     }
272
273     /* this is unecessary hdr->path[nameSize] = '\0'; */
274
275     padinfd(cfd, 4);
276
277     return 0;
278 }
279
280 int cpioFileMapCmp(const void * a, const void * b)
281 {
282     const char * afn = ((const struct cpioFileMapping *)a)->archivePath;
283     const char * bfn = ((const struct cpioFileMapping *)b)->archivePath;
284
285     /* Match payloads with ./ prefixes as well. */
286     if (afn[0] == '.' && afn[1] == '/') afn += 2;
287     if (bfn[0] == '.' && bfn[1] == '/') bfn += 2;
288
289     return strcmp(afn, bfn);
290 }
291
292 /* This could trash files in the path! I'm not sure that's a good thing */
293 /**
294  * @param path          directory path
295  * @param perms         directory permissions
296  * @return              0 on success
297  */
298 static int createDirectory(const char * path, mode_t perms)
299         /*@modifies fileSystem @*/
300 {
301     struct stat sb;
302
303     if (!lstat(path, &sb)) {
304         int dounlink = 0;       /* XXX eliminate, dounlink==1 on all paths */
305         if (S_ISDIR(sb.st_mode)) {
306             return 0;
307         } else if (S_ISLNK(sb.st_mode)) {
308             if (stat(path, &sb)) {
309                 if (errno != ENOENT)
310                     return CPIOERR_STAT_FAILED;
311                 dounlink = 1;
312             } else {
313                 if (S_ISDIR(sb.st_mode))
314                     return 0;
315                 dounlink = 1;
316             }
317         } else {
318             dounlink = 1;
319         }
320
321         if (dounlink && unlink(path)) {
322             return CPIOERR_UNLINK_FAILED;
323         }
324     }
325
326     if (mkdir(path, 000))
327         return CPIOERR_MKDIR_FAILED;
328
329     if (chmod(path, perms))
330         return CPIOERR_CHMOD_FAILED;
331
332     return 0;
333 }
334
335 /**
336  * Set owner, group, and modify/access times.
337  * @param hdr           file name and stat info
338  * @return              0 on success
339  */
340 static int setInfo(struct cpioHeader * hdr)
341         /*@modifies fileSystem @*/
342 {
343     int rc = 0;
344     struct utimbuf stamp;
345     struct stat * st = &hdr->sb;
346
347     stamp.actime = st->st_mtime;
348     stamp.modtime = st->st_mtime;
349
350     if (!S_ISLNK(st->st_mode)) {
351         if (!getuid() && chown(hdr->path, st->st_uid, st->st_gid))
352             rc = CPIOERR_CHOWN_FAILED;
353         if (!rc && chmod(hdr->path, st->st_mode & 07777))
354             rc = CPIOERR_CHMOD_FAILED;
355         if (!rc && utime(hdr->path, &stamp))
356             rc = CPIOERR_UTIME_FAILED;
357     } else {
358 #       if ! CHOWN_FOLLOWS_SYMLINK
359             if (!getuid() && !rc && lchown(hdr->path, st->st_uid, st->st_gid))
360                 rc = CPIOERR_CHOWN_FAILED;
361 #       endif
362     }
363
364     return rc;
365 }
366
367 /**
368  * Create directories in file path (like "mkdir -p").
369  * @param filename      file path
370  * @return              0 on success
371  */
372 static int inline checkDirectory(const char * filename) /*@*/
373 {
374     /*@only@*/ static char * lastDir = NULL;    /* XXX memory leak */
375     static int lastDirLength = 0;
376     static int lastDirAlloced = 0;
377     int length = strlen(filename);
378     char * buf;
379     char * chptr;
380     int rc = 0;
381
382     buf = alloca(length + 1);
383     strcpy(buf, filename);
384
385     for (chptr = buf + length - 1; chptr > buf; chptr--) {
386         if (*chptr == '/') break;
387     }
388
389     if (chptr == buf) return 0;     /* /filename - no directories */
390
391     *chptr = '\0';                  /* buffer is now just directories */
392
393     length = strlen(buf);
394     if (lastDirLength == length && !strcmp(buf, lastDir)) return 0;
395
396     if (lastDirAlloced < (length + 1)) {
397         lastDirAlloced = length + 100;
398         lastDir = xrealloc(lastDir, lastDirAlloced);    /* XXX memory leak */
399     }
400
401     strcpy(lastDir, buf);
402     lastDirLength = length;
403
404     for (chptr = buf + 1; *chptr; chptr++) {
405         if (*chptr == '/') {
406             *chptr = '\0';
407             rc = createDirectory(buf, 0755);
408             *chptr = '/';
409             if (rc) return rc;
410         }
411     }
412     rc = createDirectory(buf, 0755);
413
414     return rc;
415 }
416
417 /**
418  * Create file from payload stream.
419  * @todo Legacy: support brokenEndian MD5 checks?
420  * @param cfd           payload file handle
421  * @param hdr           file name and stat info
422  * @param filemd5       file md5 sum
423  * @param cb            callback function
424  * @param cbData        callback private data
425  * @return              0 on success
426  */
427 static int expandRegular(FD_t cfd, const struct cpioHeader * hdr,
428                          const char * filemd5, cpioCallback cb, void * cbData)
429                 /*@modifies fileSystem, cfd @*/
430 {
431     FD_t ofd;
432     char buf[BUFSIZ];
433     int bytesRead;
434     const struct stat * st = &hdr->sb;
435     int left = st->st_size;
436     int rc = 0;
437     struct cpioCallbackInfo cbInfo = { NULL, 0, 0, 0 };
438     struct stat sb;
439
440     /* Rename the old file before attempting unlink to avoid EBUSY errors */
441     if (!lstat(hdr->path, &sb)) {
442         strcpy(buf, hdr->path);
443         strcat(buf, "-RPMDELETE");
444         if (rename(hdr->path, buf)) {
445             rpmError(RPMERR_RENAME, _("can't rename %s to %s: %s\n"),
446                 hdr->path, buf, strerror(errno));
447             return CPIOERR_UNLINK_FAILED;
448         }
449
450         if (unlink(buf)) {
451             rpmError(RPMERR_UNLINK, _("can't unlink %s: %s\n"),
452                 buf, strerror(errno));
453 #if 0
454             return CPIOERR_UNLINK_FAILED;
455 #endif
456         }
457     }
458
459     ofd = Fopen(hdr->path, "w.ufdio");
460     if (ofd == NULL || Ferror(ofd))
461         return CPIOERR_OPEN_FAILED;
462
463     /* XXX This doesn't support brokenEndian checks. */
464     if (filemd5)
465         fdInitMD5(ofd, 0);
466
467     cbInfo.file = hdr->path;
468     cbInfo.fileSize = st->st_size;
469
470     while (left) {
471         bytesRead = ourread(cfd, buf, left < sizeof(buf) ? left : sizeof(buf));
472         if (bytesRead <= 0) {
473             rc = CPIOERR_READ_FAILED;
474             break;
475         }
476
477         if (Fwrite(buf, sizeof(buf[0]), bytesRead, ofd) != bytesRead) {
478             rc = CPIOERR_COPY_FAILED;
479             break;
480         }
481
482         left -= bytesRead;
483
484         /* don't call this with fileSize == fileComplete */
485         if (!rc && cb && left) {
486             cbInfo.fileComplete = st->st_size - left;
487             cbInfo.bytesProcessed = fdGetCpioPos(cfd);
488             cb(&cbInfo, cbData);
489         }
490     }
491
492     if (filemd5) {
493         const char * md5sum = NULL;
494
495         Fflush(ofd);
496         fdFiniMD5(ofd, (void **)&md5sum, NULL, 1);
497
498         if (md5sum == NULL) {
499             rc = CPIOERR_MD5SUM_MISMATCH;
500         } else {
501             if (strcmp(md5sum, filemd5))
502                 rc = CPIOERR_MD5SUM_MISMATCH;
503             free((void *)md5sum);
504         }
505     }
506
507     Fclose(ofd);
508
509     return rc;
510 }
511
512 /**
513  * Create symlink from payload stream.
514  * @param cfd           payload file handle
515  * @param hdr           file name and stat info
516  * @return              0 on success
517  */
518 static int expandSymlink(FD_t cfd, const struct cpioHeader * hdr)
519                 /*@modifies fileSystem, cfd @*/
520 {
521     char buf[2048], buf2[2048];
522     struct stat sb;
523     const struct stat * st = &hdr->sb;
524     int len;
525
526     if ((st->st_size + 1)> sizeof(buf))
527         return CPIOERR_HDR_SIZE;
528
529     if (ourread(cfd, buf, st->st_size) != st->st_size)
530         return CPIOERR_READ_FAILED;
531
532     buf[st->st_size] = '\0';
533
534     if (!lstat(hdr->path, &sb)) {
535         if (S_ISLNK(sb.st_mode)) {
536             len = readlink(hdr->path, buf2, sizeof(buf2) - 1);
537             if (len > 0) {
538                 buf2[len] = '\0';
539                 if (!strcmp(buf, buf2)) return 0;
540             }
541         }
542
543         if (unlink(hdr->path))
544             return CPIOERR_UNLINK_FAILED;
545     }
546
547     if (symlink(buf, hdr->path) < 0)
548         return CPIOERR_SYMLINK_FAILED;
549
550     return 0;
551 }
552
553 /**
554  * Create fifo from payload stream.
555  * @param cfd           payload file handle
556  * @param hdr           file name and stat info
557  * @return              0 on success
558  */
559 static int expandFifo( /*@unused@*/ FD_t cfd, const struct cpioHeader * hdr)
560                 /*@modifies fileSystem @*/
561 {
562     struct stat sb;
563
564     if (!lstat(hdr->path, &sb)) {
565         if (S_ISFIFO(sb.st_mode)) return 0;
566
567         if (unlink(hdr->path))
568             return CPIOERR_UNLINK_FAILED;
569     }
570
571     if (mkfifo(hdr->path, 0))
572         return CPIOERR_MKFIFO_FAILED;
573
574     return 0;
575 }
576
577 /**
578  * Create fifo from payload stream.
579  * @param cfd           payload file handle
580  * @param hdr           file name and stat info
581  * @return              0 on success
582  */
583 static int expandDevice( /*@unused@*/ FD_t cfd, const struct cpioHeader * hdr)
584                 /*@modifies fileSystem @*/
585 {
586     const struct stat * st = &hdr->sb;
587     struct stat sb;
588
589     if (!lstat(hdr->path, &sb)) {
590         if ((S_ISCHR(sb.st_mode) || S_ISBLK(sb.st_mode)) &&
591                 (sb.st_rdev == st->st_rdev))
592             return 0;
593         if (unlink(hdr->path))
594             return CPIOERR_UNLINK_FAILED;
595     }
596
597     if ( /*@-unrecog@*/ mknod(hdr->path, st->st_mode & (~0777), st->st_rdev) /*@=unrecog@*/ )
598         return CPIOERR_MKNOD_FAILED;
599
600     return 0;
601 }
602
603 /**
604  * Create and initialize set of hard links.
605  * @param st            link stat info
606  * @param hltype        type of hard link set to create
607  * @return              pointer to set of hard links
608  */
609 static /*@only@*/ struct hardLink * newHardLink(const struct stat * st,
610                                 enum hardLinkType hltype)       /*@*/
611 {
612     struct hardLink * li = xmalloc(sizeof(*li));
613
614     li->next = NULL;
615     li->nlink = st->st_nlink;
616     li->dev = st->st_dev;
617     li->inode = st->st_ino;
618     li->createdPath = -1;
619
620     switch (hltype) {
621     case HARDLINK_INSTALL:
622         li->linksLeft = st->st_nlink;
623         li->fileMaps = xmalloc(sizeof(li->fileMaps[0]) * st->st_nlink);
624         li->files = NULL;
625         break;
626     case HARDLINK_BUILD:
627         li->linksLeft = 0;
628         li->fileMaps = NULL;
629         li->files = xcalloc(st->st_nlink, sizeof(*li->files));
630         break;
631     }
632
633     {   struct stat * myst = (struct stat *) &li->sb;
634         *myst = *st;    /* structure assignment */
635     }
636
637     return li;
638 }
639
640 /**
641  * Destroy set of hard links.
642  * @param li            set of hard links
643  */
644 static void freeHardLink( /*@only@*/ struct hardLink * li)
645 {
646
647     if (li->files) {
648         int i;
649         for (i = 0; i < li->nlink; i++) {
650             if (li->files[i] == NULL) continue;
651             /*@-unqualifiedtrans@*/ free((void *)li->files[i]) /*@=unqualifiedtrans@*/ ;
652             li->files[i] = NULL;
653         }
654         free(li->files);
655         li->files = NULL;
656     }
657     if (li->fileMaps) {
658         free(li->fileMaps);
659         li->fileMaps = NULL;
660     }
661     free(li);
662 }
663
664 /**
665  * Create hard links to existing file.
666  * @param li            set of hard links
667  * @retval failedFile   on error, file name that failed
668  * @return              0 on success
669  */
670 static int createLinks(struct hardLink * li, /*@out@*/ const char ** failedFile)
671         /*@modifies fileSystem, *failedFile, li->files, li->linksLeft @*/
672 {
673     int i;
674     struct stat sb;
675
676     for (i = 0; i < li->nlink; i++) {
677         if (i == li->createdPath) continue;
678         if (li->files[i] == NULL) continue;
679
680         if (!lstat(li->files[i], &sb)) {
681             if (unlink(li->files[i])) {
682                 if (failedFile)
683                     *failedFile = xstrdup(li->files[i]);
684                 return CPIOERR_UNLINK_FAILED;
685             }
686         }
687
688         if (link(li->files[li->createdPath], li->files[i])) {
689             if (failedFile)
690                 *failedFile = xstrdup(li->files[i]);
691             return CPIOERR_LINK_FAILED;
692         }
693
694         /*@-unqualifiedtrans@*/ free((void *)li->files[i]) /*@=unqualifiedtrans@*/ ;
695         li->files[i] = NULL;
696         li->linksLeft--;
697     }
698
699     return 0;
700 }
701
702 /**
703  * Skip amount bytes on input payload stream.
704  * @param cfd           payload file handle
705  * @param amount        no. bytes to skip
706  * @return              0 on success
707  */
708 static int eatBytes(FD_t cfd, int amount)
709         /*@modifies cfd @*/
710 {
711     char buf[4096];
712     int bite;
713
714     while (amount) {
715         bite = (amount > sizeof(buf)) ? sizeof(buf) : amount;
716         if (ourread(cfd, buf, bite) != bite)
717             return CPIOERR_READ_FAILED;
718         amount -= bite;
719     }
720
721     return 0;
722 }
723
724 /** @todo Verify payload MD5 sum. */
725 int cpioInstallArchive(FD_t cfd, const struct cpioFileMapping * mappings,
726                        int numMappings, cpioCallback cb, void * cbData,
727                        const char ** failedFile)
728 {
729     struct cpioHeader ch, *hdr = &ch;
730     struct cpioFileMapping * map = NULL;
731     struct cpioFileMapping needle;
732     struct cpioCallbackInfo cbInfo = { NULL, 0, 0, 0 };
733     struct hardLink * links = NULL;
734     struct hardLink * li = NULL;
735     int rc = 0;
736
737 #ifdef  NOTYET
738     char * md5sum = NULL;
739
740     fdInitMD5(cfd, 0);
741 #endif
742
743     fdSetCpioPos(cfd, 0);
744     if (failedFile)
745         *failedFile = NULL;
746
747     memset(hdr, 0, sizeof(*hdr));
748     hdr->path = NULL;
749     do {
750         struct stat * st;
751
752         if (hdr->path) {
753             free((void *)hdr->path);
754             hdr->path = NULL;
755         }
756         if ((rc = getNextHeader(cfd, hdr))) {
757 #if 0   /* XXX this is the failure point for an unreadable rpm */
758             rpmError(RPMERR_BADPACKAGE, _("getNextHeader: %s\n"),
759                         cpioStrerror(rc));
760 #endif
761             return rc;
762         }
763         st = &hdr->sb;
764
765         if (!strcmp(hdr->path, TRAILER))
766             break;
767
768         if (mappings) {
769             needle.archivePath = hdr->path;
770             map = bsearch(&needle, mappings, numMappings, sizeof(needle),
771                           cpioFileMapCmp);
772         }
773
774 #ifdef  HACK_ALERT
775         if (mappings && !map)
776 #else
777         if (mappings && !map && (st->st_nlink == 1 || !S_ISREG(st->st_mode)
778                         || !st->st_size)) {
779         } else if ((st->st_nlink > 1) && S_ISREG(st->st_mode) &&
780                         (li->createdPath != -1))
781 #endif
782         {
783             eatBytes(cfd, st->st_size);
784         } else {
785             if (map) {
786                 if (map->mapFlags & CPIO_MAP_PATH) {
787                     if (hdr->path) free((void *)hdr->path);
788                     hdr->path = xstrdup(map->fsPath);
789                 }
790
791                 if (map->mapFlags & CPIO_MAP_MODE)
792                     st->st_mode = map->finalMode;
793                 if (map->mapFlags & CPIO_MAP_UID)
794                     st->st_uid = map->finalUid;
795                 if (map->mapFlags & CPIO_MAP_GID)
796                     st->st_gid = map->finalGid;
797             }
798
799             /* This won't get hard linked symlinks right, but I can't seem
800                to create those anyway */
801
802             if (S_ISREG(st->st_mode) && st->st_nlink > 1) {
803                 for (li = links; li; li = li->next) {
804                     if (li->inode == st->st_ino && li->dev == st->st_dev) break;
805                 }
806
807                 if (li == NULL) {
808                     li = newHardLink(st, HARDLINK_BUILD);
809                     li->next = links;
810                     links = li;
811                 }
812
813                 li->files[li->linksLeft++] = xstrdup(hdr->path);
814             }
815
816             if ((st->st_nlink > 1) && S_ISREG(st->st_mode) && !st->st_size &&
817                 li->createdPath == -1) {
818                 /* defer file creation */
819             } else if ((st->st_nlink > 1) && S_ISREG(st->st_mode) &&
820                        (li->createdPath != -1)) {
821                 createLinks(li, failedFile);
822
823                 /* this only happens for cpio archives which contain
824                    hardlinks w/ the contents of each hardlink being
825                    listed (intead of the data being given just once. This
826                    shouldn't happen, but I've made it happen w/ buggy
827                    code, so what the heck? GNU cpio handles this well fwiw */
828                 if (st->st_size) eatBytes(cfd, st->st_size);
829             } else {
830                 rc = checkDirectory(hdr->path);
831
832                 if (!rc) {
833                     if (S_ISREG(st->st_mode))
834                         rc = expandRegular(cfd, hdr, map->md5sum, cb, cbData);
835                     else if (S_ISDIR(st->st_mode))
836                         rc = createDirectory(hdr->path, 000);
837                     else if (S_ISLNK(st->st_mode))
838                         rc = expandSymlink(cfd, hdr);
839                     else if (S_ISFIFO(st->st_mode))
840                         rc = expandFifo(cfd, hdr);
841                     else if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode))
842                         rc = expandDevice(cfd, hdr);
843                     else if (S_ISSOCK(st->st_mode)) {
844                         /* this mimicks cpio but probably isnt' right */
845                         rc = expandFifo(cfd, hdr);
846                     } else {
847                         rc = CPIOERR_UNKNOWN_FILETYPE;
848                     }
849                 }
850
851                 if (!rc)
852                     rc = setInfo(hdr);
853
854                 if (S_ISREG(st->st_mode) && st->st_nlink > 1) {
855                     li->createdPath = --li->linksLeft;
856                     rc = createLinks(li, failedFile);
857                 }
858             }
859
860             if (rc && failedFile && *failedFile == NULL) {
861                 int olderrno;
862
863                 *failedFile = xstrdup(hdr->path);
864                 olderrno = errno;
865                 unlink(hdr->path);
866                 errno = olderrno;
867             }
868         }
869
870         padinfd(cfd, 4);
871
872         if (!rc && cb) {
873             cbInfo.file = hdr->path;
874             cbInfo.fileSize = st->st_size;
875             cbInfo.fileComplete = st->st_size;
876             cbInfo.bytesProcessed = fdGetCpioPos(cfd);
877             cb(&cbInfo, cbData);
878         }
879
880     } while (rc == 0);
881
882     if (hdr->path) {
883         free((void *)hdr->path);
884         hdr->path = NULL;
885     }
886
887     /* Create any remaining links (if no error), and clean up. */
888     while ((li = links) != NULL) {
889         links = li->next;
890         li->next = NULL;
891
892         if (rc == 0 && li->linksLeft) {
893             if (li->createdPath == -1)
894                 rc = CPIOERR_MISSING_HARDLINK;
895             else
896                 rc = createLinks(li, failedFile);
897         }
898
899         freeHardLink(li);
900     }
901
902 #ifdef  NOTYET
903     fdFiniMD5(cfd, (void **)&md5sum, NULL, 1);
904
905     if (md5sum)
906         free(md5sum);
907 #endif
908
909     return rc;
910 }
911
912 /**
913  * Write next item to payload stream.
914  * @param cfd           payload file handle
915  * @param st            stat info for item
916  * @param map           mapping name and flags for item
917  * @retval sizep        address of no. bytes written
918  * @param writeData     should data be written?
919  * @return              0 on success
920  */
921 static int writeFile(FD_t cfd, const struct stat * st,
922         const struct cpioFileMapping * map, /*@out@*/ size_t * sizep,
923         int writeData)
924         /*@modifies cfd, *sizep @*/
925 {
926     struct cpioCrcPhysicalHeader hdr;
927     char buf[8192], symbuf[2048];
928     dev_t num;
929     FD_t datafd;
930     size_t st_size = st->st_size;       /* XXX hard links need size preserved */
931     const char * archivePath;
932     mode_t st_mode = st->st_mode;
933     uid_t st_uid = st->st_uid;
934     gid_t st_gid = st->st_gid;
935     size_t size, amount = 0;
936     int rc;
937
938     archivePath = (!(map->mapFlags & CPIO_MAP_PATH))
939         ? map->fsPath : map->archivePath;
940
941     if (map->mapFlags & CPIO_MAP_MODE)
942         st_mode = (st_mode & S_IFMT) | map->finalMode;
943     if (map->mapFlags & CPIO_MAP_UID)
944         st_uid = map->finalUid;
945     if (map->mapFlags & CPIO_MAP_GID)
946         st_gid = map->finalGid;
947
948     if (!writeData || S_ISDIR(st_mode)) {
949         st_size = 0;
950     } else if (S_ISLNK(st_mode)) {
951         /* While linux puts the size of a symlink in the st_size field,
952            I don't think that's a specified standard */
953
954         amount = Readlink(map->fsPath, symbuf, sizeof(symbuf));
955         if (amount <= 0) {
956             return CPIOERR_READLINK_FAILED;
957         }
958
959         st_size = amount;
960     }
961
962     memcpy(hdr.magic, CPIO_NEWC_MAGIC, sizeof(hdr.magic));
963     SET_NUM_FIELD(hdr.inode, st->st_ino, buf);
964     SET_NUM_FIELD(hdr.mode, st_mode, buf);
965     SET_NUM_FIELD(hdr.uid, st_uid, buf);
966     SET_NUM_FIELD(hdr.gid, st_gid, buf);
967     SET_NUM_FIELD(hdr.nlink, st->st_nlink, buf);
968     SET_NUM_FIELD(hdr.mtime, st->st_mtime, buf);
969     SET_NUM_FIELD(hdr.filesize, st_size, buf);
970
971     num = major((unsigned)st->st_dev); SET_NUM_FIELD(hdr.devMajor, num, buf);
972     num = minor((unsigned)st->st_dev); SET_NUM_FIELD(hdr.devMinor, num, buf);
973     num = major((unsigned)st->st_rdev); SET_NUM_FIELD(hdr.rdevMajor, num, buf);
974     num = minor((unsigned)st->st_rdev); SET_NUM_FIELD(hdr.rdevMinor, num, buf);
975
976     num = strlen(archivePath) + 1; SET_NUM_FIELD(hdr.namesize, num, buf);
977     memcpy(hdr.checksum, "00000000", 8);
978
979     if ((rc = safewrite(cfd, &hdr, PHYS_HDR_SIZE)) != PHYS_HDR_SIZE)
980         return rc;
981     if ((rc = safewrite(cfd, archivePath, num)) != num)
982         return rc;
983     size = PHYS_HDR_SIZE + num;
984     if ((rc = padoutfd(cfd, &size, 4)))
985         return rc;
986
987     if (writeData && S_ISREG(st_mode)) {
988         char *b;
989 #if HAVE_MMAP
990         void *mapped;
991         size_t nmapped;
992 #endif
993
994         /* XXX unbuffered mmap generates *lots* of fdio debugging */
995         datafd = Fopen(map->fsPath, "r.ufdio");
996         if (datafd == NULL || Ferror(datafd))
997             return CPIOERR_OPEN_FAILED;
998
999 #if HAVE_MMAP
1000         nmapped = 0;
1001         mapped = mmap(NULL, st_size, PROT_READ, MAP_SHARED, Fileno(datafd), 0);
1002         if (mapped != (void *)-1) {
1003             b = (char *)mapped;
1004             nmapped = st_size;
1005         } else
1006 #endif
1007         {
1008             b = buf;
1009         }
1010
1011         size += st_size;
1012
1013         while (st_size) {
1014 #if HAVE_MMAP
1015           if (mapped != (void *)-1) {
1016             amount = nmapped;
1017           } else
1018 #endif
1019           {
1020             amount = Fread(b, sizeof(buf[0]),
1021                         (st_size > sizeof(buf) ? sizeof(buf) : st_size),
1022                         datafd);
1023             if (amount <= 0) {
1024                 int olderrno = errno;
1025                 Fclose(datafd);
1026                 errno = olderrno;
1027                 return CPIOERR_READ_FAILED;
1028             }
1029           }
1030
1031             if ((rc = safewrite(cfd, b, amount)) != amount) {
1032                 int olderrno = errno;
1033                 Fclose(datafd);
1034                 errno = olderrno;
1035                 return rc;
1036             }
1037
1038             st_size -= amount;
1039         }
1040
1041 #if HAVE_MMAP
1042         if (mapped != (void *)-1) {
1043             /*@-noeffect@*/ munmap(mapped, nmapped) /*@=noeffect@*/;
1044         }
1045 #endif
1046
1047         Fclose(datafd);
1048     } else if (writeData && S_ISLNK(st_mode)) {
1049         if ((rc = safewrite(cfd, symbuf, amount)) != amount)
1050             return rc;
1051         size += amount;
1052     }
1053
1054     /* this is a noop for most file types */
1055     if ((rc = padoutfd(cfd, &size, 4)))
1056         return rc;
1057
1058     if (sizep)
1059         *sizep = size;
1060
1061     return 0;
1062 }
1063
1064 /**
1065  * Write set of linked files to payload stream.
1066  * @param cfd           payload file handle
1067  * @param hlink         set of linked files
1068  * @param mappings      mapping name and flags for linked files
1069  * @param cb            callback function
1070  * @param cbData        callback private data
1071  * @retval sizep        address of no. bytes written
1072  * @retval failedFile   on error, file name that failed
1073  * @return              0 on success
1074  */
1075 static int writeLinkedFile(FD_t cfd, const struct hardLink * hlink,
1076                            const struct cpioFileMapping * mappings,
1077                            cpioCallback cb, void * cbData,
1078                            /*@out@*/size_t * sizep,
1079                            /*@out@*/const char ** failedFile)
1080         /*@modifies cfd, *sizep, *failedFile @*/
1081 {
1082     int i, rc;
1083     size_t size, total;
1084     struct cpioCallbackInfo cbInfo = { NULL, 0, 0, 0 };
1085
1086     total = 0;
1087
1088     for (i = hlink->nlink - 1; i > hlink->linksLeft; i--) {
1089         if ((rc = writeFile(cfd, &hlink->sb, mappings + hlink->fileMaps[i],
1090                             &size, 0))) {
1091             if (failedFile)
1092                 *failedFile = xstrdup(mappings[hlink->fileMaps[i]].fsPath);
1093             return rc;
1094         }
1095
1096         total += size;
1097
1098         if (cb) {
1099             cbInfo.file = mappings[i].archivePath;
1100             cb(&cbInfo, cbData);
1101         }
1102     }
1103
1104     if ((rc = writeFile(cfd, &hlink->sb,
1105                         mappings + hlink->fileMaps[hlink->linksLeft],
1106                         &size, 1))) {
1107         if (sizep)
1108             *sizep = total;
1109         if (failedFile)
1110             *failedFile = xstrdup(mappings[hlink->fileMaps[hlink->linksLeft]].fsPath);
1111         return rc;
1112     }
1113     total += size;
1114
1115     if (sizep)
1116         *sizep = total;
1117
1118     if (cb) {
1119         cbInfo.file = mappings[i].archivePath;
1120         cb(&cbInfo, cbData);
1121     }
1122
1123     return 0;
1124 }
1125
1126 int cpioBuildArchive(FD_t cfd, const struct cpioFileMapping * mappings,
1127                      int numMappings, cpioCallback cb, void * cbData,
1128                      unsigned int * archiveSize, const char ** failedFile)
1129 {
1130     size_t size, totalsize = 0;
1131     int rc;
1132     int i;
1133     struct cpioCallbackInfo cbInfo = { NULL, 0, 0, 0 };
1134     struct cpioCrcPhysicalHeader hdr;
1135 /*@-fullinitblock@*/
1136     struct hardLink hlinkList = { NULL };
1137 /*@=fullinitblock@*/
1138     struct stat * st = (struct stat *) &hlinkList.sb;
1139     struct hardLink * hlink;
1140
1141     hlinkList.next = NULL;
1142
1143     for (i = 0; i < numMappings; i++) {
1144         const struct cpioFileMapping * map;
1145
1146         map = mappings + i;
1147
1148         if (map->mapFlags & CPIO_FOLLOW_SYMLINKS)
1149             rc = Stat(map->fsPath, st);
1150         else
1151             rc = Lstat(map->fsPath, st);
1152
1153         if (rc) {
1154             if (failedFile)
1155                 *failedFile = xstrdup(map->fsPath);
1156             return CPIOERR_STAT_FAILED;
1157         }
1158
1159         if (!S_ISDIR(st->st_mode) && st->st_nlink > 1) {
1160             hlink = hlinkList.next;
1161             while (hlink &&
1162                   (hlink->dev != st->st_dev || hlink->inode != st->st_ino))
1163                         hlink = hlink->next;
1164             if (hlink == NULL) {
1165                 hlink = newHardLink(st, HARDLINK_INSTALL);
1166                 hlink->next = hlinkList.next;
1167                 hlinkList.next = hlink;
1168             }
1169
1170             hlink->fileMaps[--hlink->linksLeft] = i;
1171
1172             if (hlink->linksLeft == 0) {
1173                 struct hardLink * prev;
1174                 if ((rc = writeLinkedFile(cfd, hlink, mappings, cb, cbData,
1175                                           &size, failedFile)))
1176                     return rc;
1177
1178                 totalsize += size;
1179
1180                 prev = &hlinkList;
1181                 do {
1182                     if (prev->next != hlink)
1183                         continue;
1184                     prev->next = hlink->next;
1185                     hlink->next = NULL;
1186                     freeHardLink(hlink);
1187                     hlink = NULL;
1188                     break;
1189                 } while ((prev = prev->next) != NULL);
1190             }
1191         } else {
1192             if ((rc = writeFile(cfd, st, map, &size, 1))) {
1193                 if (failedFile)
1194                     *failedFile = xstrdup(mappings[i].fsPath);
1195                 return rc;
1196             }
1197
1198             if (cb) {
1199                 cbInfo.file = map->archivePath;
1200                 cb(&cbInfo, cbData);
1201             }
1202
1203             totalsize += size;
1204         }
1205     }
1206
1207     rc = 0;
1208     while ((hlink = hlinkList.next) != NULL) {
1209         hlinkList.next = hlink->next;
1210         hlink->next = NULL;
1211
1212         if (rc == 0) {
1213             rc = writeLinkedFile(cfd, hlink, mappings, cb, cbData,
1214                                   &size, failedFile);
1215             totalsize += size;
1216         }
1217         freeHardLink(hlink);
1218     }
1219     if (rc)
1220         return rc;
1221
1222     memset(&hdr, '0', PHYS_HDR_SIZE);
1223     memcpy(hdr.magic, CPIO_NEWC_MAGIC, sizeof(hdr.magic));
1224     memcpy(hdr.nlink, "00000001", 8);
1225     memcpy(hdr.namesize, "0000000b", 8);
1226     if ((rc = safewrite(cfd, &hdr, PHYS_HDR_SIZE)) != PHYS_HDR_SIZE)
1227         return rc;
1228     if ((rc = safewrite(cfd, "TRAILER!!!", 11)) != 11)
1229         return rc;
1230     totalsize += PHYS_HDR_SIZE + 11;
1231
1232     /* GNU cpio pads to 512 bytes here, but we don't. I'm not sure if
1233        it matters or not */
1234
1235     if ((rc = padoutfd(cfd, &totalsize, 4)))
1236         return rc;
1237
1238     if (archiveSize) *archiveSize = totalsize;
1239
1240     return 0;
1241 }
1242
1243 const char * cpioStrerror(int rc)
1244 {
1245     static char msg[256];
1246     char *s;
1247     int l, myerrno = errno;
1248
1249     strcpy(msg, "cpio: ");
1250     switch (rc) {
1251     default:
1252         s = msg + strlen(msg);
1253         sprintf(s, _("(error 0x%x)"), (unsigned)rc);
1254         s = NULL;
1255         break;
1256     case CPIOERR_BAD_MAGIC:     s = _("Bad magic");             break;
1257     case CPIOERR_BAD_HEADER:    s = _("Bad/unreadable  header");break;
1258
1259     case CPIOERR_OPEN_FAILED:   s = "open";     break;
1260     case CPIOERR_CHMOD_FAILED:  s = "chmod";    break;
1261     case CPIOERR_CHOWN_FAILED:  s = "chown";    break;
1262     case CPIOERR_WRITE_FAILED:  s = "write";    break;
1263     case CPIOERR_UTIME_FAILED:  s = "utime";    break;
1264     case CPIOERR_UNLINK_FAILED: s = "unlink";   break;
1265     case CPIOERR_SYMLINK_FAILED: s = "symlink"; break;
1266     case CPIOERR_STAT_FAILED:   s = "stat";     break;
1267     case CPIOERR_MKDIR_FAILED:  s = "mkdir";    break;
1268     case CPIOERR_MKNOD_FAILED:  s = "mknod";    break;
1269     case CPIOERR_MKFIFO_FAILED: s = "mkfifo";   break;
1270     case CPIOERR_LINK_FAILED:   s = "link";     break;
1271     case CPIOERR_READLINK_FAILED: s = "readlink";       break;
1272     case CPIOERR_READ_FAILED:   s = "read";     break;
1273     case CPIOERR_COPY_FAILED:   s = "copy";     break;
1274
1275     case CPIOERR_HDR_SIZE:      s = _("Header size too big");   break;
1276     case CPIOERR_UNKNOWN_FILETYPE: s = _("Unknown file type");  break;
1277     case CPIOERR_MISSING_HARDLINK: s = _("Missing hard link");  break;
1278     case CPIOERR_MD5SUM_MISMATCH: s = _("MD5 sum mismatch");    break;
1279     case CPIOERR_INTERNAL:      s = _("Internal error");        break;
1280     }
1281
1282     l = sizeof(msg) - strlen(msg) - 1;
1283     if (s != NULL) {
1284         if (l > 0) strncat(msg, s, l);
1285         l -= strlen(s);
1286     }
1287     if ((rc & CPIOERR_CHECK_ERRNO) && myerrno) {
1288         s = _(" failed - ");
1289         if (l > 0) strncat(msg, s, l);
1290         l -= strlen(s);
1291         if (l > 0) strncat(msg, strerror(myerrno), l);
1292     }
1293     return msg;
1294 }