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