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