- fix: avoid locale issues with strcasecmp/strncasecmp (#23199).
[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         if (mappings && !map) {
775             eatBytes(cfd, st->st_size);
776         } else {
777             if (map) {
778                 if (map->mapFlags & CPIO_MAP_PATH) {
779                     if (hdr->path) free((void *)hdr->path);
780                     hdr->path = xstrdup(map->fsPath);
781                 }
782
783                 if (map->mapFlags & CPIO_MAP_MODE)
784                     st->st_mode = map->finalMode;
785                 if (map->mapFlags & CPIO_MAP_UID)
786                     st->st_uid = map->finalUid;
787                 if (map->mapFlags & CPIO_MAP_GID)
788                     st->st_gid = map->finalGid;
789             }
790
791             /* This won't get hard linked symlinks right, but I can't seem
792                to create those anyway */
793
794             if (S_ISREG(st->st_mode) && st->st_nlink > 1) {
795                 for (li = links; li; li = li->next) {
796                     if (li->inode == st->st_ino && li->dev == st->st_dev) break;
797                 }
798
799                 if (li == NULL) {
800                     li = newHardLink(st, HARDLINK_BUILD);
801                     li->next = links;
802                     links = li;
803                 }
804
805                 li->files[li->linksLeft++] = xstrdup(hdr->path);
806             }
807
808             if ((st->st_nlink > 1) && S_ISREG(st->st_mode) && !st->st_size &&
809                 li->createdPath == -1) {
810                 /* defer file creation */
811             } else if ((st->st_nlink > 1) && S_ISREG(st->st_mode) &&
812                        (li->createdPath != -1)) {
813                 createLinks(li, failedFile);
814
815                 /* this only happens for cpio archives which contain
816                    hardlinks w/ the contents of each hardlink being
817                    listed (intead of the data being given just once. This
818                    shouldn't happen, but I've made it happen w/ buggy
819                    code, so what the heck? GNU cpio handles this well fwiw */
820                 if (st->st_size) eatBytes(cfd, st->st_size);
821             } else {
822                 rc = checkDirectory(hdr->path);
823
824                 if (!rc) {
825                     if (S_ISREG(st->st_mode))
826                         rc = expandRegular(cfd, hdr, map->md5sum, cb, cbData);
827                     else if (S_ISDIR(st->st_mode))
828                         rc = createDirectory(hdr->path, 000);
829                     else if (S_ISLNK(st->st_mode))
830                         rc = expandSymlink(cfd, hdr);
831                     else if (S_ISFIFO(st->st_mode))
832                         rc = expandFifo(cfd, hdr);
833                     else if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode))
834                         rc = expandDevice(cfd, hdr);
835                     else if (S_ISSOCK(st->st_mode)) {
836                         /* this mimicks cpio but probably isnt' right */
837                         rc = expandFifo(cfd, hdr);
838                     } else {
839                         rc = CPIOERR_UNKNOWN_FILETYPE;
840                     }
841                 }
842
843                 if (!rc)
844                     rc = setInfo(hdr);
845
846                 if (S_ISREG(st->st_mode) && st->st_nlink > 1) {
847                     li->createdPath = --li->linksLeft;
848                     rc = createLinks(li, failedFile);
849                 }
850             }
851
852             if (rc && failedFile && *failedFile == NULL) {
853                 int olderrno;
854
855                 *failedFile = xstrdup(hdr->path);
856                 olderrno = errno;
857                 unlink(hdr->path);
858                 errno = olderrno;
859             }
860         }
861
862         padinfd(cfd, 4);
863
864         if (!rc && cb) {
865             cbInfo.file = hdr->path;
866             cbInfo.fileSize = st->st_size;
867             cbInfo.fileComplete = st->st_size;
868             cbInfo.bytesProcessed = fdGetCpioPos(cfd);
869             cb(&cbInfo, cbData);
870         }
871
872     } while (rc == 0);
873
874     if (hdr->path) {
875         free((void *)hdr->path);
876         hdr->path = NULL;
877     }
878
879     /* Create any remaining links (if no error), and clean up. */
880     while ((li = links) != NULL) {
881         links = li->next;
882         li->next = NULL;
883
884         if (rc == 0 && li->linksLeft) {
885             if (li->createdPath == -1)
886                 rc = CPIOERR_MISSING_HARDLINK;
887             else
888                 rc = createLinks(li, failedFile);
889         }
890
891         freeHardLink(li);
892     }
893
894 #ifdef  NOTYET
895     fdFiniMD5(cfd, (void **)&md5sum, NULL, 1);
896
897     if (md5sum)
898         free(md5sum);
899 #endif
900
901     return rc;
902 }
903
904 /**
905  * Write next item to payload stream.
906  * @param cfd           payload file handle
907  * @param st            stat info for item
908  * @param map           mapping name and flags for item
909  * @retval sizep        address of no. bytes written
910  * @param writeData     should data be written?
911  * @return              0 on success
912  */
913 static int writeFile(FD_t cfd, const struct stat * st,
914         const struct cpioFileMapping * map, /*@out@*/ size_t * sizep,
915         int writeData)
916         /*@modifies cfd, *sizep @*/
917 {
918     struct cpioCrcPhysicalHeader hdr;
919     char buf[8192], symbuf[2048];
920     dev_t num;
921     FD_t datafd;
922     size_t st_size = st->st_size;       /* XXX hard links need size preserved */
923     const char * archivePath;
924     mode_t st_mode = st->st_mode;
925     uid_t st_uid = st->st_uid;
926     gid_t st_gid = st->st_gid;
927     size_t size, amount = 0;
928     int rc;
929
930     archivePath = (!(map->mapFlags & CPIO_MAP_PATH))
931         ? map->fsPath : map->archivePath;
932
933     if (map->mapFlags & CPIO_MAP_MODE)
934         st_mode = (st_mode & S_IFMT) | map->finalMode;
935     if (map->mapFlags & CPIO_MAP_UID)
936         st_uid = map->finalUid;
937     if (map->mapFlags & CPIO_MAP_GID)
938         st_gid = map->finalGid;
939
940     if (!writeData || S_ISDIR(st_mode)) {
941         st_size = 0;
942     } else if (S_ISLNK(st_mode)) {
943         /* While linux puts the size of a symlink in the st_size field,
944            I don't think that's a specified standard */
945
946         amount = Readlink(map->fsPath, symbuf, sizeof(symbuf));
947         if (amount <= 0) {
948             return CPIOERR_READLINK_FAILED;
949         }
950
951         st_size = amount;
952     }
953
954     memcpy(hdr.magic, CPIO_NEWC_MAGIC, sizeof(hdr.magic));
955     SET_NUM_FIELD(hdr.inode, st->st_ino, buf);
956     SET_NUM_FIELD(hdr.mode, st_mode, buf);
957     SET_NUM_FIELD(hdr.uid, st_uid, buf);
958     SET_NUM_FIELD(hdr.gid, st_gid, buf);
959     SET_NUM_FIELD(hdr.nlink, st->st_nlink, buf);
960     SET_NUM_FIELD(hdr.mtime, st->st_mtime, buf);
961     SET_NUM_FIELD(hdr.filesize, st_size, buf);
962
963     num = major((unsigned)st->st_dev); SET_NUM_FIELD(hdr.devMajor, num, buf);
964     num = minor((unsigned)st->st_dev); SET_NUM_FIELD(hdr.devMinor, num, buf);
965     num = major((unsigned)st->st_rdev); SET_NUM_FIELD(hdr.rdevMajor, num, buf);
966     num = minor((unsigned)st->st_rdev); SET_NUM_FIELD(hdr.rdevMinor, num, buf);
967
968     num = strlen(archivePath) + 1; SET_NUM_FIELD(hdr.namesize, num, buf);
969     memcpy(hdr.checksum, "00000000", 8);
970
971     if ((rc = safewrite(cfd, &hdr, PHYS_HDR_SIZE)) != PHYS_HDR_SIZE)
972         return rc;
973     if ((rc = safewrite(cfd, archivePath, num)) != num)
974         return rc;
975     size = PHYS_HDR_SIZE + num;
976     if ((rc = padoutfd(cfd, &size, 4)))
977         return rc;
978
979     if (writeData && S_ISREG(st_mode)) {
980         char *b;
981 #if HAVE_MMAP
982         void *mapped;
983         size_t nmapped;
984 #endif
985
986         /* XXX unbuffered mmap generates *lots* of fdio debugging */
987         datafd = Fopen(map->fsPath, "r.ufdio");
988         if (datafd == NULL || Ferror(datafd))
989             return CPIOERR_OPEN_FAILED;
990
991 #if HAVE_MMAP
992         nmapped = 0;
993         mapped = mmap(NULL, st_size, PROT_READ, MAP_SHARED, Fileno(datafd), 0);
994         if (mapped != (void *)-1) {
995             b = (char *)mapped;
996             nmapped = st_size;
997         } else
998 #endif
999         {
1000             b = buf;
1001         }
1002
1003         size += st_size;
1004
1005         while (st_size) {
1006 #if HAVE_MMAP
1007           if (mapped != (void *)-1) {
1008             amount = nmapped;
1009           } else
1010 #endif
1011           {
1012             amount = Fread(b, sizeof(buf[0]),
1013                         (st_size > sizeof(buf) ? sizeof(buf) : st_size),
1014                         datafd);
1015             if (amount <= 0) {
1016                 int olderrno = errno;
1017                 Fclose(datafd);
1018                 errno = olderrno;
1019                 return CPIOERR_READ_FAILED;
1020             }
1021           }
1022
1023             if ((rc = safewrite(cfd, b, amount)) != amount) {
1024                 int olderrno = errno;
1025                 Fclose(datafd);
1026                 errno = olderrno;
1027                 return rc;
1028             }
1029
1030             st_size -= amount;
1031         }
1032
1033 #if HAVE_MMAP
1034         if (mapped != (void *)-1) {
1035             /*@-noeffect@*/ munmap(mapped, nmapped) /*@=noeffect@*/;
1036         }
1037 #endif
1038
1039         Fclose(datafd);
1040     } else if (writeData && S_ISLNK(st_mode)) {
1041         if ((rc = safewrite(cfd, symbuf, amount)) != amount)
1042             return rc;
1043         size += amount;
1044     }
1045
1046     /* this is a noop for most file types */
1047     if ((rc = padoutfd(cfd, &size, 4)))
1048         return rc;
1049
1050     if (sizep)
1051         *sizep = size;
1052
1053     return 0;
1054 }
1055
1056 /**
1057  * Write set of linked files to payload stream.
1058  * @param cfd           payload file handle
1059  * @param hlink         set of linked files
1060  * @param mappings      mapping name and flags for linked files
1061  * @param cb            callback function
1062  * @param cbData        callback private data
1063  * @retval sizep        address of no. bytes written
1064  * @retval failedFile   on error, file name that failed
1065  * @return              0 on success
1066  */
1067 static int writeLinkedFile(FD_t cfd, const struct hardLink * hlink,
1068                            const struct cpioFileMapping * mappings,
1069                            cpioCallback cb, void * cbData,
1070                            /*@out@*/size_t * sizep,
1071                            /*@out@*/const char ** failedFile)
1072         /*@modifies cfd, *sizep, *failedFile @*/
1073 {
1074     int i, rc;
1075     size_t size, total;
1076     struct cpioCallbackInfo cbInfo = { NULL, 0, 0, 0 };
1077
1078     total = 0;
1079
1080     for (i = hlink->nlink - 1; i > hlink->linksLeft; i--) {
1081         if ((rc = writeFile(cfd, &hlink->sb, mappings + hlink->fileMaps[i],
1082                             &size, 0))) {
1083             if (failedFile)
1084                 *failedFile = xstrdup(mappings[hlink->fileMaps[i]].fsPath);
1085             return rc;
1086         }
1087
1088         total += size;
1089
1090         if (cb) {
1091             cbInfo.file = mappings[i].archivePath;
1092             cb(&cbInfo, cbData);
1093         }
1094     }
1095
1096     if ((rc = writeFile(cfd, &hlink->sb,
1097                         mappings + hlink->fileMaps[hlink->linksLeft],
1098                         &size, 1))) {
1099         if (sizep)
1100             *sizep = total;
1101         if (failedFile)
1102             *failedFile = xstrdup(mappings[hlink->fileMaps[hlink->linksLeft]].fsPath);
1103         return rc;
1104     }
1105     total += size;
1106
1107     if (sizep)
1108         *sizep = total;
1109
1110     if (cb) {
1111         cbInfo.file = mappings[i].archivePath;
1112         cb(&cbInfo, cbData);
1113     }
1114
1115     return 0;
1116 }
1117
1118 int cpioBuildArchive(FD_t cfd, const struct cpioFileMapping * mappings,
1119                      int numMappings, cpioCallback cb, void * cbData,
1120                      unsigned int * archiveSize, const char ** failedFile)
1121 {
1122     size_t size, totalsize = 0;
1123     int rc;
1124     int i;
1125     struct cpioCallbackInfo cbInfo = { NULL, 0, 0, 0 };
1126     struct cpioCrcPhysicalHeader hdr;
1127 /*@-fullinitblock@*/
1128     struct hardLink hlinkList = { NULL };
1129 /*@=fullinitblock@*/
1130     struct stat * st = (struct stat *) &hlinkList.sb;
1131     struct hardLink * hlink;
1132
1133     hlinkList.next = NULL;
1134
1135     for (i = 0; i < numMappings; i++) {
1136         const struct cpioFileMapping * map;
1137
1138         map = mappings + i;
1139
1140         if (map->mapFlags & CPIO_FOLLOW_SYMLINKS)
1141             rc = Stat(map->fsPath, st);
1142         else
1143             rc = Lstat(map->fsPath, st);
1144
1145         if (rc) {
1146             if (failedFile)
1147                 *failedFile = xstrdup(map->fsPath);
1148             return CPIOERR_STAT_FAILED;
1149         }
1150
1151         if (!S_ISDIR(st->st_mode) && st->st_nlink > 1) {
1152             hlink = hlinkList.next;
1153             while (hlink &&
1154                   (hlink->dev != st->st_dev || hlink->inode != st->st_ino))
1155                         hlink = hlink->next;
1156             if (hlink == NULL) {
1157                 hlink = newHardLink(st, HARDLINK_INSTALL);
1158                 hlink->next = hlinkList.next;
1159                 hlinkList.next = hlink;
1160             }
1161
1162             hlink->fileMaps[--hlink->linksLeft] = i;
1163
1164             if (hlink->linksLeft == 0) {
1165                 struct hardLink * prev;
1166                 if ((rc = writeLinkedFile(cfd, hlink, mappings, cb, cbData,
1167                                           &size, failedFile)))
1168                     return rc;
1169
1170                 totalsize += size;
1171
1172                 prev = &hlinkList;
1173                 do {
1174                     if (prev->next != hlink)
1175                         continue;
1176                     prev->next = hlink->next;
1177                     hlink->next = NULL;
1178                     freeHardLink(hlink);
1179                     hlink = NULL;
1180                     break;
1181                 } while ((prev = prev->next) != NULL);
1182             }
1183         } else {
1184             if ((rc = writeFile(cfd, st, map, &size, 1))) {
1185                 if (failedFile)
1186                     *failedFile = xstrdup(mappings[i].fsPath);
1187                 return rc;
1188             }
1189
1190             if (cb) {
1191                 cbInfo.file = map->archivePath;
1192                 cb(&cbInfo, cbData);
1193             }
1194
1195             totalsize += size;
1196         }
1197     }
1198
1199     rc = 0;
1200     while ((hlink = hlinkList.next) != NULL) {
1201         hlinkList.next = hlink->next;
1202         hlink->next = NULL;
1203
1204         if (rc == 0) {
1205             rc = writeLinkedFile(cfd, hlink, mappings, cb, cbData,
1206                                   &size, failedFile);
1207             totalsize += size;
1208         }
1209         freeHardLink(hlink);
1210     }
1211     if (rc)
1212         return rc;
1213
1214     memset(&hdr, '0', PHYS_HDR_SIZE);
1215     memcpy(hdr.magic, CPIO_NEWC_MAGIC, sizeof(hdr.magic));
1216     memcpy(hdr.nlink, "00000001", 8);
1217     memcpy(hdr.namesize, "0000000b", 8);
1218     if ((rc = safewrite(cfd, &hdr, PHYS_HDR_SIZE)) != PHYS_HDR_SIZE)
1219         return rc;
1220     if ((rc = safewrite(cfd, "TRAILER!!!", 11)) != 11)
1221         return rc;
1222     totalsize += PHYS_HDR_SIZE + 11;
1223
1224     /* GNU cpio pads to 512 bytes here, but we don't. I'm not sure if
1225        it matters or not */
1226
1227     if ((rc = padoutfd(cfd, &totalsize, 4)))
1228         return rc;
1229
1230     if (archiveSize) *archiveSize = totalsize;
1231
1232     return 0;
1233 }
1234
1235 const char * cpioStrerror(int rc)
1236 {
1237     static char msg[256];
1238     char *s;
1239     int l, myerrno = errno;
1240
1241     strcpy(msg, "cpio: ");
1242     switch (rc) {
1243     default:
1244         s = msg + strlen(msg);
1245         sprintf(s, _("(error 0x%x)"), (unsigned)rc);
1246         s = NULL;
1247         break;
1248     case CPIOERR_BAD_MAGIC:     s = _("Bad magic");             break;
1249     case CPIOERR_BAD_HEADER:    s = _("Bad/unreadable  header");break;
1250
1251     case CPIOERR_OPEN_FAILED:   s = "open";     break;
1252     case CPIOERR_CHMOD_FAILED:  s = "chmod";    break;
1253     case CPIOERR_CHOWN_FAILED:  s = "chown";    break;
1254     case CPIOERR_WRITE_FAILED:  s = "write";    break;
1255     case CPIOERR_UTIME_FAILED:  s = "utime";    break;
1256     case CPIOERR_UNLINK_FAILED: s = "unlink";   break;
1257     case CPIOERR_SYMLINK_FAILED: s = "symlink"; break;
1258     case CPIOERR_STAT_FAILED:   s = "stat";     break;
1259     case CPIOERR_MKDIR_FAILED:  s = "mkdir";    break;
1260     case CPIOERR_MKNOD_FAILED:  s = "mknod";    break;
1261     case CPIOERR_MKFIFO_FAILED: s = "mkfifo";   break;
1262     case CPIOERR_LINK_FAILED:   s = "link";     break;
1263     case CPIOERR_READLINK_FAILED: s = "readlink";       break;
1264     case CPIOERR_READ_FAILED:   s = "read";     break;
1265     case CPIOERR_COPY_FAILED:   s = "copy";     break;
1266
1267     case CPIOERR_HDR_SIZE:      s = _("Header size too big");   break;
1268     case CPIOERR_UNKNOWN_FILETYPE: s = _("Unknown file type");  break;
1269     case CPIOERR_MISSING_HARDLINK: s = _("Missing hard link");  break;
1270     case CPIOERR_MD5SUM_MISMATCH: s = _("MD5 sum mismatch");    break;
1271     case CPIOERR_INTERNAL:      s = _("Internal error");        break;
1272     }
1273
1274     l = sizeof(msg) - strlen(msg) - 1;
1275     if (s != NULL) {
1276         if (l > 0) strncat(msg, s, l);
1277         l -= strlen(s);
1278     }
1279     if ((rc & CPIOERR_CHECK_ERRNO) && myerrno) {
1280         s = _(" failed - ");
1281         if (l > 0) strncat(msg, s, l);
1282         l -= strlen(s);
1283         if (l > 0) strncat(msg, strerror(myerrno), l);
1284     }
1285     return msg;
1286 }