- add cscope/ctags (Rodrigo Barbosa<rodrigob@conectiva.com.br>).
[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
12 #include "cpio.h"
13 /*@access FD_t@*/
14
15 #define xfree(_p)       free((void *)_p)
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  * @param cfd           payload file handle
420  * @param hdr           file name and stat info
421  * @param filemd5       file md5 sum
422  * @param cb            callback function
423  * @param cbData        callback private data
424  * @return              0 on success
425  */
426 static int expandRegular(FD_t cfd, const struct cpioHeader * hdr,
427                          const char * filemd5, cpioCallback cb, void * cbData)
428                 /*@modifies fileSystem, cfd @*/
429 {
430     FD_t ofd;
431     char buf[BUFSIZ];
432     int bytesRead;
433     const struct stat * st = &hdr->sb;
434     int left = st->st_size;
435     int rc = 0;
436     struct cpioCallbackInfo cbInfo = { NULL, 0, 0, 0 };
437     struct stat sb;
438
439     /* Rename the old file before attempting unlink to avoid EBUSY errors */
440     if (!lstat(hdr->path, &sb)) {
441         strcpy(buf, hdr->path);
442         strcat(buf, "-RPMDELETE");
443         if (rename(hdr->path, buf)) {
444             fprintf(stderr, _("can't rename %s to %s: %s\n"),
445                 hdr->path, buf, strerror(errno));
446             return CPIOERR_UNLINK_FAILED;
447         }
448
449         if (unlink(buf)) {
450             fprintf(stderr, _("can't unlink %s: %s\n"),
451                 buf, strerror(errno));
452 #if 0
453             return CPIOERR_UNLINK_FAILED;
454 #endif
455         }
456     }
457
458     ofd = Fopen(hdr->path, "w.ufdio");
459     if (ofd == NULL || Ferror(ofd))
460         return CPIOERR_OPEN_FAILED;
461
462     if (filemd5)
463         fdInitMD5(ofd);
464
465     cbInfo.file = hdr->path;
466     cbInfo.fileSize = st->st_size;
467
468     while (left) {
469         bytesRead = ourread(cfd, buf, left < sizeof(buf) ? left : sizeof(buf));
470         if (bytesRead <= 0) {
471             rc = CPIOERR_READ_FAILED;
472             break;
473         }
474
475         if (Fwrite(buf, sizeof(buf[0]), bytesRead, ofd) != bytesRead) {
476             rc = CPIOERR_COPY_FAILED;
477             break;
478         }
479
480         left -= bytesRead;
481
482         /* don't call this with fileSize == fileComplete */
483         if (!rc && cb && left) {
484             cbInfo.fileComplete = st->st_size - left;
485             cbInfo.bytesProcessed = fdGetCpioPos(cfd);
486             cb(&cbInfo, cbData);
487         }
488     }
489
490     if (filemd5) {
491         const char * md5sum = NULL;
492
493         Fflush(ofd);
494         fdFiniMD5(ofd, (void **)&md5sum, NULL, 1);
495
496         if (md5sum == NULL) {
497             rc = CPIOERR_MD5SUM_MISMATCH;
498         } else {
499             if (strcmp(md5sum, filemd5))
500                 rc = CPIOERR_MD5SUM_MISMATCH;
501             xfree(md5sum);
502         }
503     }
504
505     Fclose(ofd);
506
507     return rc;
508 }
509
510 /**
511  * Create symlink from payload stream.
512  * @param cfd           payload file handle
513  * @param hdr           file name and stat info
514  * @return              0 on success
515  */
516 static int expandSymlink(FD_t cfd, const struct cpioHeader * hdr)
517                 /*@modifies fileSystem, cfd @*/
518 {
519     char buf[2048], buf2[2048];
520     struct stat sb;
521     const struct stat * st = &hdr->sb;
522     int len;
523
524     if ((st->st_size + 1)> sizeof(buf))
525         return CPIOERR_HDR_SIZE;
526
527     if (ourread(cfd, buf, st->st_size) != st->st_size)
528         return CPIOERR_READ_FAILED;
529
530     buf[st->st_size] = '\0';
531
532     if (!lstat(hdr->path, &sb)) {
533         if (S_ISLNK(sb.st_mode)) {
534             len = readlink(hdr->path, buf2, sizeof(buf2) - 1);
535             if (len > 0) {
536                 buf2[len] = '\0';
537                 if (!strcmp(buf, buf2)) return 0;
538             }
539         }
540
541         if (unlink(hdr->path))
542             return CPIOERR_UNLINK_FAILED;
543     }
544
545     if (symlink(buf, hdr->path) < 0)
546         return CPIOERR_SYMLINK_FAILED;
547
548     return 0;
549 }
550
551 /**
552  * Create fifo from payload stream.
553  * @param cfd           payload file handle
554  * @param hdr           file name and stat info
555  * @return              0 on success
556  */
557 static int expandFifo( /*@unused@*/ FD_t cfd, const struct cpioHeader * hdr)
558                 /*@modifies fileSystem @*/
559 {
560     struct stat sb;
561
562     if (!lstat(hdr->path, &sb)) {
563         if (S_ISFIFO(sb.st_mode)) return 0;
564
565         if (unlink(hdr->path))
566             return CPIOERR_UNLINK_FAILED;
567     }
568
569     if (mkfifo(hdr->path, 0))
570         return CPIOERR_MKFIFO_FAILED;
571
572     return 0;
573 }
574
575 /**
576  * Create fifo from payload stream.
577  * @param cfd           payload file handle
578  * @param hdr           file name and stat info
579  * @return              0 on success
580  */
581 static int expandDevice( /*@unused@*/ FD_t cfd, const struct cpioHeader * hdr)
582                 /*@modifies fileSystem @*/
583 {
584     const struct stat * st = &hdr->sb;
585     struct stat sb;
586
587     if (!lstat(hdr->path, &sb)) {
588         if ((S_ISCHR(sb.st_mode) || S_ISBLK(sb.st_mode)) &&
589                 (sb.st_rdev == st->st_rdev))
590             return 0;
591         if (unlink(hdr->path))
592             return CPIOERR_UNLINK_FAILED;
593     }
594
595     if ( /*@-unrecog@*/ mknod(hdr->path, st->st_mode & (~0777), st->st_rdev) /*@=unrecog@*/ )
596         return CPIOERR_MKNOD_FAILED;
597
598     return 0;
599 }
600
601 /**
602  * Create and initialize set of hard links.
603  * @param st            link stat info
604  * @param hltype        type of hard link set to create
605  * @return              pointer to set of hard links
606  */
607 static /*@only@*/ struct hardLink * newHardLink(const struct stat * st,
608                                 enum hardLinkType hltype)       /*@*/
609 {
610     struct hardLink * li = xmalloc(sizeof(*li));
611
612     li->next = NULL;
613     li->nlink = st->st_nlink;
614     li->dev = st->st_dev;
615     li->inode = st->st_ino;
616     li->createdPath = -1;
617
618     switch (hltype) {
619     case HARDLINK_INSTALL:
620         li->linksLeft = st->st_nlink;
621         li->fileMaps = xmalloc(sizeof(li->fileMaps[0]) * st->st_nlink);
622         li->files = NULL;
623         break;
624     case HARDLINK_BUILD:
625         li->linksLeft = 0;
626         li->fileMaps = NULL;
627         li->files = xcalloc(st->st_nlink, sizeof(*li->files));
628         break;
629     }
630
631     {   struct stat * myst = (struct stat *) &li->sb;
632         *myst = *st;    /* structure assignment */
633     }
634
635     return li;
636 }
637
638 /**
639  * Destroy set of hard links.
640  * @param li            set of hard links
641  */
642 static void freeHardLink( /*@only@*/ struct hardLink * li)
643 {
644
645     if (li->files) {
646         int i;
647         for (i = 0; i < li->nlink; i++) {
648             if (li->files[i] == NULL) continue;
649             /*@-unqualifiedtrans@*/ free((void *)li->files[i]) /*@=unqualifiedtrans@*/ ;
650             li->files[i] = NULL;
651         }
652         free(li->files);
653         li->files = NULL;
654     }
655     if (li->fileMaps) {
656         free(li->fileMaps);
657         li->fileMaps = NULL;
658     }
659     free(li);
660 }
661
662 /**
663  * Create hard links to existing file.
664  * @param li            set of hard links
665  * @retval failedFile   on error, file name that failed
666  * @return              0 on success
667  */
668 static int createLinks(struct hardLink * li, /*@out@*/ const char ** failedFile)
669         /*@modifies fileSystem, *failedFile, li->files, li->linksLeft @*/
670 {
671     int i;
672     struct stat sb;
673
674     for (i = 0; i < li->nlink; i++) {
675         if (i == li->createdPath) continue;
676         if (li->files[i] == NULL) continue;
677
678         if (!lstat(li->files[i], &sb)) {
679             if (unlink(li->files[i])) {
680                 if (failedFile)
681                     *failedFile = xstrdup(li->files[i]);
682                 return CPIOERR_UNLINK_FAILED;
683             }
684         }
685
686         if (link(li->files[li->createdPath], li->files[i])) {
687             if (failedFile)
688                 *failedFile = xstrdup(li->files[i]);
689             return CPIOERR_LINK_FAILED;
690         }
691
692         /*@-unqualifiedtrans@*/ free((void *)li->files[i]) /*@=unqualifiedtrans@*/ ;
693         li->files[i] = NULL;
694         li->linksLeft--;
695     }
696
697     return 0;
698 }
699
700 /**
701  * Skip amount bytes on input payload stream.
702  * @param cfd           payload file handle
703  * @param amount        no. bytes to skip
704  * @return              0 on success
705  */
706 static int eatBytes(FD_t cfd, int amount)
707         /*@modifies cfd @*/
708 {
709     char buf[4096];
710     int bite;
711
712     while (amount) {
713         bite = (amount > sizeof(buf)) ? sizeof(buf) : amount;
714         if (ourread(cfd, buf, bite) != bite)
715             return CPIOERR_READ_FAILED;
716         amount -= bite;
717     }
718
719     return 0;
720 }
721
722 /** @todo Verify payload MD5 sum. */
723 int cpioInstallArchive(FD_t cfd, const struct cpioFileMapping * mappings,
724                        int numMappings, cpioCallback cb, void * cbData,
725                        const char ** failedFile)
726 {
727     struct cpioHeader ch, *hdr = &ch;
728     struct cpioFileMapping * map = NULL;
729     struct cpioFileMapping needle;
730     struct cpioCallbackInfo cbInfo = { NULL, 0, 0, 0 };
731     struct hardLink * links = NULL;
732     struct hardLink * li = NULL;
733     int rc = 0;
734
735 #ifdef  NOTYET
736     char * md5sum = NULL;
737
738     fdInitMD5(cfd);
739 #endif
740
741     fdSetCpioPos(cfd, 0);
742     if (failedFile)
743         *failedFile = NULL;
744
745     memset(hdr, 0, sizeof(*hdr));
746     hdr->path = NULL;
747     do {
748         struct stat * st;
749
750         if (hdr->path) {
751             xfree(hdr->path);
752             hdr->path = NULL;
753         }
754         if ((rc = getNextHeader(cfd, hdr))) {
755 #if 0   /* XXX this is the failure point for an unreadable rpm */
756             fprintf(stderr, _("getNextHeader: %s\n"), cpioStrerror(rc));
757 #endif
758             return rc;
759         }
760         st = &hdr->sb;
761
762         if (!strcmp(hdr->path, TRAILER))
763             break;
764
765         if (mappings) {
766             needle.archivePath = hdr->path;
767             map = bsearch(&needle, mappings, numMappings, sizeof(needle),
768                           cpioFileMapCmp);
769         }
770
771         if (mappings && !map) {
772             eatBytes(cfd, st->st_size);
773         } else {
774             if (map) {
775                 if (map->mapFlags & CPIO_MAP_PATH) {
776                     if (hdr->path) xfree(hdr->path);
777                     hdr->path = xstrdup(map->fsPath);
778                 }
779
780                 if (map->mapFlags & CPIO_MAP_MODE)
781                     st->st_mode = map->finalMode;
782                 if (map->mapFlags & CPIO_MAP_UID)
783                     st->st_uid = map->finalUid;
784                 if (map->mapFlags & CPIO_MAP_GID)
785                     st->st_gid = map->finalGid;
786             }
787
788             /* This won't get hard linked symlinks right, but I can't seem
789                to create those anyway */
790
791             if (S_ISREG(st->st_mode) && st->st_nlink > 1) {
792                 for (li = links; li; li = li->next) {
793                     if (li->inode == st->st_ino && li->dev == st->st_dev) break;
794                 }
795
796                 if (li == NULL) {
797                     li = newHardLink(st, HARDLINK_BUILD);
798                     li->next = links;
799                     links = li;
800                 }
801
802                 li->files[li->linksLeft++] = xstrdup(hdr->path);
803             }
804
805             if ((st->st_nlink > 1) && S_ISREG(st->st_mode) && !st->st_size &&
806                 li->createdPath == -1) {
807                 /* defer file creation */
808             } else if ((st->st_nlink > 1) && S_ISREG(st->st_mode) &&
809                        (li->createdPath != -1)) {
810                 createLinks(li, failedFile);
811
812                 /* this only happens for cpio archives which contain
813                    hardlinks w/ the contents of each hardlink being
814                    listed (intead of the data being given just once. This
815                    shouldn't happen, but I've made it happen w/ buggy
816                    code, so what the heck? GNU cpio handles this well fwiw */
817                 if (st->st_size) eatBytes(cfd, st->st_size);
818             } else {
819                 rc = checkDirectory(hdr->path);
820
821                 if (!rc) {
822                     if (S_ISREG(st->st_mode))
823                         rc = expandRegular(cfd, hdr, map->md5sum, cb, cbData);
824                     else if (S_ISDIR(st->st_mode))
825                         rc = createDirectory(hdr->path, 000);
826                     else if (S_ISLNK(st->st_mode))
827                         rc = expandSymlink(cfd, hdr);
828                     else if (S_ISFIFO(st->st_mode))
829                         rc = expandFifo(cfd, hdr);
830                     else if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode))
831                         rc = expandDevice(cfd, hdr);
832                     else if (S_ISSOCK(st->st_mode)) {
833                         /* this mimicks cpio but probably isnt' right */
834                         rc = expandFifo(cfd, hdr);
835                     } else {
836                         rc = CPIOERR_UNKNOWN_FILETYPE;
837                     }
838                 }
839
840                 if (!rc)
841                     rc = setInfo(hdr);
842
843                 if (S_ISREG(st->st_mode) && st->st_nlink > 1) {
844                     li->createdPath = --li->linksLeft;
845                     rc = createLinks(li, failedFile);
846                 }
847             }
848
849             if (rc && failedFile && *failedFile == NULL) {
850                 int olderrno;
851
852                 *failedFile = xstrdup(hdr->path);
853                 olderrno = errno;
854                 unlink(hdr->path);
855                 errno = olderrno;
856             }
857         }
858
859         padinfd(cfd, 4);
860
861         if (!rc && cb) {
862             cbInfo.file = hdr->path;
863             cbInfo.fileSize = st->st_size;
864             cbInfo.fileComplete = st->st_size;
865             cbInfo.bytesProcessed = fdGetCpioPos(cfd);
866             cb(&cbInfo, cbData);
867         }
868
869     } while (rc == 0);
870
871     if (hdr->path) {
872         xfree(hdr->path);
873         hdr->path = NULL;
874     }
875
876     /* Create any remaining links (if no error), and clean up. */
877     while ((li = links) != NULL) {
878         links = li->next;
879         li->next = NULL;
880
881         if (rc == 0 && li->linksLeft) {
882             if (li->createdPath == -1)
883                 rc = CPIOERR_MISSING_HARDLINK;
884             else
885                 rc = createLinks(li, failedFile);
886         }
887
888         freeHardLink(li);
889     }
890
891 #ifdef  NOTYET
892     fdFiniMD5(cfd, (void **)&md5sum, NULL, 1);
893
894     if (md5sum)
895         free(md5sum);
896 #endif
897
898     return rc;
899 }
900
901 /**
902  * Write next item to payload stream.
903  * @param cfd           payload file handle
904  * @param st            stat info for item
905  * @param map           mapping name and flags for item
906  * @retval sizep        address of no. bytes written
907  * @param writeData     should data be written?
908  * @return              0 on success
909  */
910 static int writeFile(FD_t cfd, const struct stat * st,
911         const struct cpioFileMapping * map, /*@out@*/ size_t * sizep,
912         int writeData)
913         /*@modifies cfd, *sizep @*/
914 {
915     struct cpioCrcPhysicalHeader hdr;
916     char buf[8192], symbuf[2048];
917     dev_t num;
918     FD_t datafd;
919     size_t st_size = st->st_size;       /* XXX hard links need size preserved */
920     const char * archivePath;
921     mode_t st_mode = st->st_mode;
922     uid_t st_uid = st->st_uid;
923     gid_t st_gid = st->st_gid;
924     size_t size, amount = 0;
925     int rc;
926
927     archivePath = (!(map->mapFlags & CPIO_MAP_PATH))
928         ? map->fsPath : map->archivePath;
929
930     if (map->mapFlags & CPIO_MAP_MODE)
931         st_mode = (st_mode & S_IFMT) | map->finalMode;
932     if (map->mapFlags & CPIO_MAP_UID)
933         st_uid = map->finalUid;
934     if (map->mapFlags & CPIO_MAP_GID)
935         st_gid = map->finalGid;
936
937     if (!writeData || S_ISDIR(st_mode)) {
938         st_size = 0;
939     } else if (S_ISLNK(st_mode)) {
940         /* While linux puts the size of a symlink in the st_size field,
941            I don't think that's a specified standard */
942
943         amount = Readlink(map->fsPath, symbuf, sizeof(symbuf));
944         if (amount <= 0) {
945             return CPIOERR_READLINK_FAILED;
946         }
947
948         st_size = amount;
949     }
950
951     memcpy(hdr.magic, CPIO_NEWC_MAGIC, sizeof(hdr.magic));
952     SET_NUM_FIELD(hdr.inode, st->st_ino, buf);
953     SET_NUM_FIELD(hdr.mode, st_mode, buf);
954     SET_NUM_FIELD(hdr.uid, st_uid, buf);
955     SET_NUM_FIELD(hdr.gid, st_gid, buf);
956     SET_NUM_FIELD(hdr.nlink, st->st_nlink, buf);
957     SET_NUM_FIELD(hdr.mtime, st->st_mtime, buf);
958     SET_NUM_FIELD(hdr.filesize, st_size, buf);
959
960     num = major((unsigned)st->st_dev); SET_NUM_FIELD(hdr.devMajor, num, buf);
961     num = minor((unsigned)st->st_dev); SET_NUM_FIELD(hdr.devMinor, num, buf);
962     num = major((unsigned)st->st_rdev); SET_NUM_FIELD(hdr.rdevMajor, num, buf);
963     num = minor((unsigned)st->st_rdev); SET_NUM_FIELD(hdr.rdevMinor, num, buf);
964
965     num = strlen(archivePath) + 1; SET_NUM_FIELD(hdr.namesize, num, buf);
966     memcpy(hdr.checksum, "00000000", 8);
967
968     if ((rc = safewrite(cfd, &hdr, PHYS_HDR_SIZE)) != PHYS_HDR_SIZE)
969         return rc;
970     if ((rc = safewrite(cfd, archivePath, num)) != num)
971         return rc;
972     size = PHYS_HDR_SIZE + num;
973     if ((rc = padoutfd(cfd, &size, 4)))
974         return rc;
975
976     if (writeData && S_ISREG(st_mode)) {
977         char *b;
978 #if HAVE_MMAP
979         void *mapped;
980         size_t nmapped;
981 #endif
982
983         /* XXX unbuffered mmap generates *lots* of fdio debugging */
984         datafd = Fopen(map->fsPath, "r.ufdio");
985         if (datafd == NULL || Ferror(datafd))
986             return CPIOERR_OPEN_FAILED;
987
988 #if HAVE_MMAP
989         nmapped = 0;
990         mapped = mmap(NULL, st_size, PROT_READ, MAP_SHARED, Fileno(datafd), 0);
991         if (mapped != (void *)-1) {
992             b = (char *)mapped;
993             nmapped = st_size;
994         } else
995 #endif
996         {
997             b = buf;
998         }
999
1000         size += st_size;
1001
1002         while (st_size) {
1003 #if HAVE_MMAP
1004           if (mapped != (void *)-1) {
1005             amount = nmapped;
1006           } else
1007 #endif
1008           {
1009             amount = Fread(b, sizeof(buf[0]),
1010                         (st_size > sizeof(buf) ? sizeof(buf) : st_size),
1011                         datafd);
1012             if (amount <= 0) {
1013                 int olderrno = errno;
1014                 Fclose(datafd);
1015                 errno = olderrno;
1016                 return CPIOERR_READ_FAILED;
1017             }
1018           }
1019
1020             if ((rc = safewrite(cfd, b, amount)) != amount) {
1021                 int olderrno = errno;
1022                 Fclose(datafd);
1023                 errno = olderrno;
1024                 return rc;
1025             }
1026
1027             st_size -= amount;
1028         }
1029
1030 #if HAVE_MMAP
1031         if (mapped != (void *)-1) {
1032             /*@-noeffect@*/ munmap(mapped, nmapped) /*@=noeffect@*/;
1033         }
1034 #endif
1035
1036         Fclose(datafd);
1037     } else if (writeData && S_ISLNK(st_mode)) {
1038         if ((rc = safewrite(cfd, symbuf, amount)) != amount)
1039             return rc;
1040         size += amount;
1041     }
1042
1043     /* this is a noop for most file types */
1044     if ((rc = padoutfd(cfd, &size, 4)))
1045         return rc;
1046
1047     if (sizep)
1048         *sizep = size;
1049
1050     return 0;
1051 }
1052
1053 /**
1054  * Write set of linked files to payload stream.
1055  * @param cfd           payload file handle
1056  * @param hlink         set of linked files
1057  * @param mappings      mapping name and flags for linked files
1058  * @param cb            callback function
1059  * @param cbData        callback private data
1060  * @retval sizep        address of no. bytes written
1061  * @retval failedFile   on error, file name that failed
1062  * @return              0 on success
1063  */
1064 static int writeLinkedFile(FD_t cfd, const struct hardLink * hlink,
1065                            const struct cpioFileMapping * mappings,
1066                            cpioCallback cb, void * cbData,
1067                            /*@out@*/size_t * sizep,
1068                            /*@out@*/const char ** failedFile)
1069         /*@modifies cfd, *sizep, *failedFile @*/
1070 {
1071     int i, rc;
1072     size_t size, total;
1073     struct cpioCallbackInfo cbInfo = { NULL, 0, 0, 0 };
1074
1075     total = 0;
1076
1077     for (i = hlink->nlink - 1; i > hlink->linksLeft; i--) {
1078         if ((rc = writeFile(cfd, &hlink->sb, mappings + hlink->fileMaps[i],
1079                             &size, 0))) {
1080             if (failedFile)
1081                 *failedFile = xstrdup(mappings[hlink->fileMaps[i]].fsPath);
1082             return rc;
1083         }
1084
1085         total += size;
1086
1087         if (cb) {
1088             cbInfo.file = mappings[i].archivePath;
1089             cb(&cbInfo, cbData);
1090         }
1091     }
1092
1093     if ((rc = writeFile(cfd, &hlink->sb,
1094                         mappings + hlink->fileMaps[hlink->linksLeft],
1095                         &size, 1))) {
1096         if (sizep)
1097             *sizep = total;
1098         if (failedFile)
1099             *failedFile = xstrdup(mappings[hlink->fileMaps[hlink->linksLeft]].fsPath);
1100         return rc;
1101     }
1102     total += size;
1103
1104     if (sizep)
1105         *sizep = total;
1106
1107     if (cb) {
1108         cbInfo.file = mappings[i].archivePath;
1109         cb(&cbInfo, cbData);
1110     }
1111
1112     return 0;
1113 }
1114
1115 int cpioBuildArchive(FD_t cfd, const struct cpioFileMapping * mappings,
1116                      int numMappings, cpioCallback cb, void * cbData,
1117                      unsigned int * archiveSize, const char ** failedFile)
1118 {
1119     size_t size, totalsize = 0;
1120     int rc;
1121     int i;
1122     struct cpioCallbackInfo cbInfo = { NULL, 0, 0, 0 };
1123     struct cpioCrcPhysicalHeader hdr;
1124 /*@-fullinitblock@*/
1125     struct hardLink hlinkList = { NULL };
1126 /*@=fullinitblock@*/
1127     struct stat * st = (struct stat *) &hlinkList.sb;
1128     struct hardLink * hlink;
1129
1130     hlinkList.next = NULL;
1131
1132     for (i = 0; i < numMappings; i++) {
1133         const struct cpioFileMapping * map;
1134
1135         map = mappings + i;
1136
1137         if (map->mapFlags & CPIO_FOLLOW_SYMLINKS)
1138             rc = Stat(map->fsPath, st);
1139         else
1140             rc = Lstat(map->fsPath, st);
1141
1142         if (rc) {
1143             if (failedFile)
1144                 *failedFile = xstrdup(map->fsPath);
1145             return CPIOERR_STAT_FAILED;
1146         }
1147
1148         if (!S_ISDIR(st->st_mode) && st->st_nlink > 1) {
1149             hlink = hlinkList.next;
1150             while (hlink &&
1151                   (hlink->dev != st->st_dev || hlink->inode != st->st_ino))
1152                         hlink = hlink->next;
1153             if (hlink == NULL) {
1154                 hlink = newHardLink(st, HARDLINK_INSTALL);
1155                 hlink->next = hlinkList.next;
1156                 hlinkList.next = hlink;
1157             }
1158
1159             hlink->fileMaps[--hlink->linksLeft] = i;
1160
1161             if (hlink->linksLeft == 0) {
1162                 struct hardLink * prev;
1163                 if ((rc = writeLinkedFile(cfd, hlink, mappings, cb, cbData,
1164                                           &size, failedFile)))
1165                     return rc;
1166
1167                 totalsize += size;
1168
1169                 prev = &hlinkList;
1170                 do {
1171                     if (prev->next != hlink)
1172                         continue;
1173                     prev->next = hlink->next;
1174                     hlink->next = NULL;
1175                     freeHardLink(hlink);
1176                     hlink = NULL;
1177                     break;
1178                 } while ((prev = prev->next) != NULL);
1179             }
1180         } else {
1181             if ((rc = writeFile(cfd, st, map, &size, 1))) {
1182                 if (failedFile)
1183                     *failedFile = xstrdup(mappings[i].fsPath);
1184                 return rc;
1185             }
1186
1187             if (cb) {
1188                 cbInfo.file = map->archivePath;
1189                 cb(&cbInfo, cbData);
1190             }
1191
1192             totalsize += size;
1193         }
1194     }
1195
1196     rc = 0;
1197     while ((hlink = hlinkList.next) != NULL) {
1198         hlinkList.next = hlink->next;
1199         hlink->next = NULL;
1200
1201         if (rc == 0) {
1202             rc = writeLinkedFile(cfd, hlink, mappings, cb, cbData,
1203                                   &size, failedFile);
1204             totalsize += size;
1205         }
1206         freeHardLink(hlink);
1207     }
1208     if (rc)
1209         return rc;
1210
1211     memset(&hdr, '0', PHYS_HDR_SIZE);
1212     memcpy(hdr.magic, CPIO_NEWC_MAGIC, sizeof(hdr.magic));
1213     memcpy(hdr.nlink, "00000001", 8);
1214     memcpy(hdr.namesize, "0000000b", 8);
1215     if ((rc = safewrite(cfd, &hdr, PHYS_HDR_SIZE)) != PHYS_HDR_SIZE)
1216         return rc;
1217     if ((rc = safewrite(cfd, "TRAILER!!!", 11)) != 11)
1218         return rc;
1219     totalsize += PHYS_HDR_SIZE + 11;
1220
1221     /* GNU cpio pads to 512 bytes here, but we don't. I'm not sure if
1222        it matters or not */
1223
1224     if ((rc = padoutfd(cfd, &totalsize, 4)))
1225         return rc;
1226
1227     if (archiveSize) *archiveSize = totalsize;
1228
1229     return 0;
1230 }
1231
1232 const char * cpioStrerror(int rc)
1233 {
1234     static char msg[256];
1235     char *s;
1236     int l, myerrno = errno;
1237
1238     strcpy(msg, "cpio: ");
1239     switch (rc) {
1240     default:
1241         s = msg + strlen(msg);
1242         sprintf(s, _("(error 0x%x)"), (unsigned)rc);
1243         s = NULL;
1244         break;
1245     case CPIOERR_BAD_MAGIC:     s = _("Bad magic");             break;
1246     case CPIOERR_BAD_HEADER:    s = _("Bad/unreadable  header");break;
1247
1248     case CPIOERR_OPEN_FAILED:   s = "open";     break;
1249     case CPIOERR_CHMOD_FAILED:  s = "chmod";    break;
1250     case CPIOERR_CHOWN_FAILED:  s = "chown";    break;
1251     case CPIOERR_WRITE_FAILED:  s = "write";    break;
1252     case CPIOERR_UTIME_FAILED:  s = "utime";    break;
1253     case CPIOERR_UNLINK_FAILED: s = "unlink";   break;
1254     case CPIOERR_SYMLINK_FAILED: s = "symlink"; break;
1255     case CPIOERR_STAT_FAILED:   s = "stat";     break;
1256     case CPIOERR_MKDIR_FAILED:  s = "mkdir";    break;
1257     case CPIOERR_MKNOD_FAILED:  s = "mknod";    break;
1258     case CPIOERR_MKFIFO_FAILED: s = "mkfifo";   break;
1259     case CPIOERR_LINK_FAILED:   s = "link";     break;
1260     case CPIOERR_READLINK_FAILED: s = "readlink";       break;
1261     case CPIOERR_READ_FAILED:   s = "read";     break;
1262     case CPIOERR_COPY_FAILED:   s = "copy";     break;
1263
1264     case CPIOERR_HDR_SIZE:      s = _("Header size too big");   break;
1265     case CPIOERR_UNKNOWN_FILETYPE: s = _("Unknown file type");  break;
1266     case CPIOERR_MISSING_HARDLINK: s = _("Missing hard link");  break;
1267     case CPIOERR_MD5SUM_MISMATCH: s = _("MD5 sum mismatch");    break;
1268     case CPIOERR_INTERNAL:      s = _("Internal error");        break;
1269     }
1270
1271     l = sizeof(msg) - strlen(msg) - 1;
1272     if (s != NULL) {
1273         if (l > 0) strncat(msg, s, l);
1274         l -= strlen(s);
1275     }
1276     if ((rc & CPIOERR_CHECK_ERRNO) && myerrno) {
1277         s = _(" failed - ");
1278         if (l > 0) strncat(msg, s, l);
1279         l -= strlen(s);
1280         if (l > 0) strncat(msg, strerror(myerrno), l);
1281     }
1282     return msg;
1283 }