1 /** \ingroup payload rpmio
3 * Handle cpio payloads within rpm packages.
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
15 #define xfree(_p) free((void *)_p)
17 #define CPIO_NEWC_MAGIC "070701"
18 #define CPIO_CRC_MAGIC "070702"
19 #define TRAILER "TRAILER!!!"
22 * Keeps track of set of all hard linked files in archive.
25 struct hardLink * next;
26 const char ** files; /* nlink of these, used by install */
27 int * fileMaps; /* used by build */
44 * Cpio archive header information.
45 * @todo Add support for tar (soon) and ar (eventually) archive formats.
47 struct cpioCrcPhysicalHeader {
61 char checksum[8]; /* ignored !! */
64 #define PHYS_HDR_SIZE 110 /*!< Don't depend on sizeof(struct) */
67 * File name and stat information.
70 /*@owned@*/ const char * path;
75 static void prtli(const char *msg, struct hardLink * li)
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);
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
89 static inline off_t saferead(FD_t cfd, /*@out@*/void * vbuf, size_t amount)
90 /*@modifies cfd, *vbuf @*/
98 nb = Fread(buf, sizeof(buf[0]), amount, cfd);
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
117 static inline off_t ourread(FD_t cfd, /*@out@*/void * buf, size_t size)
118 /*@modifies cfd, *buf @*/
120 off_t i = saferead(cfd, buf, size);
122 fdSetCpioPos(cfd, fdGetCpioPos(cfd) + i);
127 * Align input payload handle, skipping input bytes.
128 * @param cfd payload file handle
129 * @param modulo data alignment
131 static inline void padinfd(FD_t cfd, int modulo)
137 amount = (modulo - fdGetCpioPos(cfd) % modulo) % modulo;
138 (void)ourread(cfd, buf, amount);
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
148 static inline off_t safewrite(FD_t cfd, const void * vbuf, size_t amount)
152 const char * buf = vbuf;
157 nb = Fwrite(buf, sizeof(buf[0]), amount, cfd);
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
176 static inline int padoutfd(FD_t cfd, size_t * where, int modulo)
177 /*@modifies cfd, *where @*/
179 static int buf[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
182 amount = (modulo - *where % modulo) % modulo;
185 if (safewrite(cfd, buf, amount) != amount)
186 return CPIOERR_WRITE_FAILED;
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
198 static int strntoul(const char *str, /*@out@*/char **endptr, int base, int num)
199 /*@modifies *endptr @*/
204 buf = alloca(num + 1);
205 strncpy(buf, str, num);
208 ret = strtoul(buf, &end, base);
210 *endptr = ((char *)str) + (end - buf); /* XXX discards const */
212 *endptr = ((char *)str) + strlen(str);
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);
225 * Process next cpio heasder.
226 * @param cfd payload file handle
227 * @retval hdr file name and stat info
228 * @return 0 on success
230 static int getNextHeader(FD_t cfd, struct cpioHeader * hdr)
231 /*@modifies cfd, hdr->path, hdr->sb @*/
233 struct cpioCrcPhysicalHeader physHeader;
234 struct stat * st = &hdr->sb;
239 if (ourread(cfd, &physHeader, PHYS_HDR_SIZE) != PHYS_HDR_SIZE)
240 return CPIOERR_READ_FAILED;
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;
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);
254 GET_NUM_FIELD(physHeader.devMajor, major);
255 GET_NUM_FIELD(physHeader.devMinor, minor);
256 st->st_dev = /*@-shiftsigned@*/ makedev(major, minor) /*@=shiftsigned@*/ ;
258 GET_NUM_FIELD(physHeader.rdevMajor, major);
259 GET_NUM_FIELD(physHeader.rdevMinor, minor);
260 st->st_rdev = /*@-shiftsigned@*/ makedev(major, minor) /*@=shiftsigned@*/ ;
262 GET_NUM_FIELD(physHeader.namesize, nameSize);
264 { char * t = xmalloc(nameSize + 1);
265 if (ourread(cfd, t, nameSize) != nameSize) {
268 return CPIOERR_BAD_HEADER;
273 /* this is unecessary hdr->path[nameSize] = '\0'; */
280 int cpioFileMapCmp(const void * a, const void * b)
282 const char * afn = ((const struct cpioFileMapping *)a)->archivePath;
283 const char * bfn = ((const struct cpioFileMapping *)b)->archivePath;
285 /* Match payloads with ./ prefixes as well. */
286 if (afn[0] == '.' && afn[1] == '/') afn += 2;
287 if (bfn[0] == '.' && bfn[1] == '/') bfn += 2;
289 return strcmp(afn, bfn);
292 /* This could trash files in the path! I'm not sure that's a good thing */
294 * @param path directory path
295 * @param perms directory permissions
296 * @return 0 on success
298 static int createDirectory(const char * path, mode_t perms)
299 /*@modifies fileSystem @*/
303 if (!lstat(path, &sb)) {
304 int dounlink = 0; /* XXX eliminate, dounlink==1 on all paths */
305 if (S_ISDIR(sb.st_mode)) {
307 } else if (S_ISLNK(sb.st_mode)) {
308 if (stat(path, &sb)) {
310 return CPIOERR_STAT_FAILED;
313 if (S_ISDIR(sb.st_mode))
321 if (dounlink && unlink(path)) {
322 return CPIOERR_UNLINK_FAILED;
326 if (mkdir(path, 000))
327 return CPIOERR_MKDIR_FAILED;
329 if (chmod(path, perms))
330 return CPIOERR_CHMOD_FAILED;
336 * Set owner, group, and modify/access times.
337 * @param hdr file name and stat info
338 * @return 0 on success
340 static int setInfo(struct cpioHeader * hdr)
341 /*@modifies fileSystem @*/
344 struct utimbuf stamp;
345 struct stat * st = &hdr->sb;
347 stamp.actime = st->st_mtime;
348 stamp.modtime = st->st_mtime;
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;
358 # if ! CHOWN_FOLLOWS_SYMLINK
359 if (!getuid() && !rc && lchown(hdr->path, st->st_uid, st->st_gid))
360 rc = CPIOERR_CHOWN_FAILED;
368 * Create directories in file path (like "mkdir -p").
369 * @param filename file path
370 * @return 0 on success
372 static int inline checkDirectory(const char * filename) /*@*/
374 /*@only@*/ static char * lastDir = NULL; /* XXX memory leak */
375 static int lastDirLength = 0;
376 static int lastDirAlloced = 0;
377 int length = strlen(filename);
382 buf = alloca(length + 1);
383 strcpy(buf, filename);
385 for (chptr = buf + length - 1; chptr > buf; chptr--) {
386 if (*chptr == '/') break;
389 if (chptr == buf) return 0; /* /filename - no directories */
391 *chptr = '\0'; /* buffer is now just directories */
393 length = strlen(buf);
394 if (lastDirLength == length && !strcmp(buf, lastDir)) return 0;
396 if (lastDirAlloced < (length + 1)) {
397 lastDirAlloced = length + 100;
398 lastDir = xrealloc(lastDir, lastDirAlloced); /* XXX memory leak */
401 strcpy(lastDir, buf);
402 lastDirLength = length;
404 for (chptr = buf + 1; *chptr; chptr++) {
407 rc = createDirectory(buf, 0755);
412 rc = createDirectory(buf, 0755);
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
426 static int expandRegular(FD_t cfd, const struct cpioHeader * hdr,
427 const char * filemd5, cpioCallback cb, void * cbData)
428 /*@modifies fileSystem, cfd @*/
433 const struct stat * st = &hdr->sb;
434 int left = st->st_size;
436 struct cpioCallbackInfo cbInfo = { NULL, 0, 0, 0 };
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;
450 fprintf(stderr, _("can't unlink %s: %s\n"),
451 buf, strerror(errno));
453 return CPIOERR_UNLINK_FAILED;
458 ofd = Fopen(hdr->path, "w.ufdio");
459 if (ofd == NULL || Ferror(ofd))
460 return CPIOERR_OPEN_FAILED;
465 cbInfo.file = hdr->path;
466 cbInfo.fileSize = st->st_size;
469 bytesRead = ourread(cfd, buf, left < sizeof(buf) ? left : sizeof(buf));
470 if (bytesRead <= 0) {
471 rc = CPIOERR_READ_FAILED;
475 if (Fwrite(buf, sizeof(buf[0]), bytesRead, ofd) != bytesRead) {
476 rc = CPIOERR_COPY_FAILED;
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);
491 const char * md5sum = NULL;
494 fdFiniMD5(ofd, (void **)&md5sum, NULL, 1);
496 if (md5sum == NULL) {
497 rc = CPIOERR_MD5SUM_MISMATCH;
499 if (strcmp(md5sum, filemd5))
500 rc = CPIOERR_MD5SUM_MISMATCH;
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
516 static int expandSymlink(FD_t cfd, const struct cpioHeader * hdr)
517 /*@modifies fileSystem, cfd @*/
519 char buf[2048], buf2[2048];
521 const struct stat * st = &hdr->sb;
524 if ((st->st_size + 1)> sizeof(buf))
525 return CPIOERR_HDR_SIZE;
527 if (ourread(cfd, buf, st->st_size) != st->st_size)
528 return CPIOERR_READ_FAILED;
530 buf[st->st_size] = '\0';
532 if (!lstat(hdr->path, &sb)) {
533 if (S_ISLNK(sb.st_mode)) {
534 len = readlink(hdr->path, buf2, sizeof(buf2) - 1);
537 if (!strcmp(buf, buf2)) return 0;
541 if (unlink(hdr->path))
542 return CPIOERR_UNLINK_FAILED;
545 if (symlink(buf, hdr->path) < 0)
546 return CPIOERR_SYMLINK_FAILED;
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
557 static int expandFifo( /*@unused@*/ FD_t cfd, const struct cpioHeader * hdr)
558 /*@modifies fileSystem @*/
562 if (!lstat(hdr->path, &sb)) {
563 if (S_ISFIFO(sb.st_mode)) return 0;
565 if (unlink(hdr->path))
566 return CPIOERR_UNLINK_FAILED;
569 if (mkfifo(hdr->path, 0))
570 return CPIOERR_MKFIFO_FAILED;
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
581 static int expandDevice( /*@unused@*/ FD_t cfd, const struct cpioHeader * hdr)
582 /*@modifies fileSystem @*/
584 const struct stat * st = &hdr->sb;
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))
591 if (unlink(hdr->path))
592 return CPIOERR_UNLINK_FAILED;
595 if ( /*@-unrecog@*/ mknod(hdr->path, st->st_mode & (~0777), st->st_rdev) /*@=unrecog@*/ )
596 return CPIOERR_MKNOD_FAILED;
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
607 static /*@only@*/ struct hardLink * newHardLink(const struct stat * st,
608 enum hardLinkType hltype) /*@*/
610 struct hardLink * li = xmalloc(sizeof(*li));
613 li->nlink = st->st_nlink;
614 li->dev = st->st_dev;
615 li->inode = st->st_ino;
616 li->createdPath = -1;
619 case HARDLINK_INSTALL:
620 li->linksLeft = st->st_nlink;
621 li->fileMaps = xmalloc(sizeof(li->fileMaps[0]) * st->st_nlink);
627 li->files = xcalloc(st->st_nlink, sizeof(*li->files));
631 { struct stat * myst = (struct stat *) &li->sb;
632 *myst = *st; /* structure assignment */
639 * Destroy set of hard links.
640 * @param li set of hard links
642 static void freeHardLink( /*@only@*/ struct hardLink * li)
647 for (i = 0; i < li->nlink; i++) {
648 if (li->files[i] == NULL) continue;
649 /*@-unqualifiedtrans@*/ free((void *)li->files[i]) /*@=unqualifiedtrans@*/ ;
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
668 static int createLinks(struct hardLink * li, /*@out@*/ const char ** failedFile)
669 /*@modifies fileSystem, *failedFile, li->files, li->linksLeft @*/
674 for (i = 0; i < li->nlink; i++) {
675 if (i == li->createdPath) continue;
676 if (li->files[i] == NULL) continue;
678 if (!lstat(li->files[i], &sb)) {
679 if (unlink(li->files[i])) {
681 *failedFile = xstrdup(li->files[i]);
682 return CPIOERR_UNLINK_FAILED;
686 if (link(li->files[li->createdPath], li->files[i])) {
688 *failedFile = xstrdup(li->files[i]);
689 return CPIOERR_LINK_FAILED;
692 /*@-unqualifiedtrans@*/ free((void *)li->files[i]) /*@=unqualifiedtrans@*/ ;
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
706 static int eatBytes(FD_t cfd, int amount)
713 bite = (amount > sizeof(buf)) ? sizeof(buf) : amount;
714 if (ourread(cfd, buf, bite) != bite)
715 return CPIOERR_READ_FAILED;
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)
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;
736 char * md5sum = NULL;
741 fdSetCpioPos(cfd, 0);
745 memset(hdr, 0, sizeof(*hdr));
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));
762 if (!strcmp(hdr->path, TRAILER))
766 needle.archivePath = hdr->path;
767 map = bsearch(&needle, mappings, numMappings, sizeof(needle),
771 if (mappings && !map) {
772 eatBytes(cfd, st->st_size);
775 if (map->mapFlags & CPIO_MAP_PATH) {
776 if (hdr->path) xfree(hdr->path);
777 hdr->path = xstrdup(map->fsPath);
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;
788 /* This won't get hard linked symlinks right, but I can't seem
789 to create those anyway */
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;
797 li = newHardLink(st, HARDLINK_BUILD);
802 li->files[li->linksLeft++] = xstrdup(hdr->path);
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);
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);
819 rc = checkDirectory(hdr->path);
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);
836 rc = CPIOERR_UNKNOWN_FILETYPE;
843 if (S_ISREG(st->st_mode) && st->st_nlink > 1) {
844 li->createdPath = --li->linksLeft;
845 rc = createLinks(li, failedFile);
849 if (rc && failedFile && *failedFile == NULL) {
852 *failedFile = xstrdup(hdr->path);
862 cbInfo.file = hdr->path;
863 cbInfo.fileSize = st->st_size;
864 cbInfo.fileComplete = st->st_size;
865 cbInfo.bytesProcessed = fdGetCpioPos(cfd);
876 /* Create any remaining links (if no error), and clean up. */
877 while ((li = links) != NULL) {
881 if (rc == 0 && li->linksLeft) {
882 if (li->createdPath == -1)
883 rc = CPIOERR_MISSING_HARDLINK;
885 rc = createLinks(li, failedFile);
892 fdFiniMD5(cfd, (void **)&md5sum, NULL, 1);
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
910 static int writeFile(FD_t cfd, const struct stat * st,
911 const struct cpioFileMapping * map, /*@out@*/ size_t * sizep,
913 /*@modifies cfd, *sizep @*/
915 struct cpioCrcPhysicalHeader hdr;
916 char buf[8192], symbuf[2048];
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;
927 archivePath = (!(map->mapFlags & CPIO_MAP_PATH))
928 ? map->fsPath : map->archivePath;
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;
937 if (!writeData || S_ISDIR(st_mode)) {
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 */
943 amount = Readlink(map->fsPath, symbuf, sizeof(symbuf));
945 return CPIOERR_READLINK_FAILED;
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);
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);
965 num = strlen(archivePath) + 1; SET_NUM_FIELD(hdr.namesize, num, buf);
966 memcpy(hdr.checksum, "00000000", 8);
968 if ((rc = safewrite(cfd, &hdr, PHYS_HDR_SIZE)) != PHYS_HDR_SIZE)
970 if ((rc = safewrite(cfd, archivePath, num)) != num)
972 size = PHYS_HDR_SIZE + num;
973 if ((rc = padoutfd(cfd, &size, 4)))
976 if (writeData && S_ISREG(st_mode)) {
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;
990 mapped = mmap(NULL, st_size, PROT_READ, MAP_SHARED, Fileno(datafd), 0);
991 if (mapped != (void *)-1) {
1004 if (mapped != (void *)-1) {
1009 amount = Fread(b, sizeof(buf[0]),
1010 (st_size > sizeof(buf) ? sizeof(buf) : st_size),
1013 int olderrno = errno;
1016 return CPIOERR_READ_FAILED;
1020 if ((rc = safewrite(cfd, b, amount)) != amount) {
1021 int olderrno = errno;
1031 if (mapped != (void *)-1) {
1032 /*@-noeffect@*/ munmap(mapped, nmapped) /*@=noeffect@*/;
1037 } else if (writeData && S_ISLNK(st_mode)) {
1038 if ((rc = safewrite(cfd, symbuf, amount)) != amount)
1043 /* this is a noop for most file types */
1044 if ((rc = padoutfd(cfd, &size, 4)))
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
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 @*/
1073 struct cpioCallbackInfo cbInfo = { NULL, 0, 0, 0 };
1077 for (i = hlink->nlink - 1; i > hlink->linksLeft; i--) {
1078 if ((rc = writeFile(cfd, &hlink->sb, mappings + hlink->fileMaps[i],
1081 *failedFile = xstrdup(mappings[hlink->fileMaps[i]].fsPath);
1088 cbInfo.file = mappings[i].archivePath;
1089 cb(&cbInfo, cbData);
1093 if ((rc = writeFile(cfd, &hlink->sb,
1094 mappings + hlink->fileMaps[hlink->linksLeft],
1099 *failedFile = xstrdup(mappings[hlink->fileMaps[hlink->linksLeft]].fsPath);
1108 cbInfo.file = mappings[i].archivePath;
1109 cb(&cbInfo, cbData);
1115 int cpioBuildArchive(FD_t cfd, const struct cpioFileMapping * mappings,
1116 int numMappings, cpioCallback cb, void * cbData,
1117 unsigned int * archiveSize, const char ** failedFile)
1119 size_t size, totalsize = 0;
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;
1130 hlinkList.next = NULL;
1132 for (i = 0; i < numMappings; i++) {
1133 const struct cpioFileMapping * map;
1137 if (map->mapFlags & CPIO_FOLLOW_SYMLINKS)
1138 rc = Stat(map->fsPath, st);
1140 rc = Lstat(map->fsPath, st);
1144 *failedFile = xstrdup(map->fsPath);
1145 return CPIOERR_STAT_FAILED;
1148 if (!S_ISDIR(st->st_mode) && st->st_nlink > 1) {
1149 hlink = hlinkList.next;
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;
1159 hlink->fileMaps[--hlink->linksLeft] = i;
1161 if (hlink->linksLeft == 0) {
1162 struct hardLink * prev;
1163 if ((rc = writeLinkedFile(cfd, hlink, mappings, cb, cbData,
1164 &size, failedFile)))
1171 if (prev->next != hlink)
1173 prev->next = hlink->next;
1175 freeHardLink(hlink);
1178 } while ((prev = prev->next) != NULL);
1181 if ((rc = writeFile(cfd, st, map, &size, 1))) {
1183 *failedFile = xstrdup(mappings[i].fsPath);
1188 cbInfo.file = map->archivePath;
1189 cb(&cbInfo, cbData);
1197 while ((hlink = hlinkList.next) != NULL) {
1198 hlinkList.next = hlink->next;
1202 rc = writeLinkedFile(cfd, hlink, mappings, cb, cbData,
1206 freeHardLink(hlink);
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)
1217 if ((rc = safewrite(cfd, "TRAILER!!!", 11)) != 11)
1219 totalsize += PHYS_HDR_SIZE + 11;
1221 /* GNU cpio pads to 512 bytes here, but we don't. I'm not sure if
1222 it matters or not */
1224 if ((rc = padoutfd(cfd, &totalsize, 4)))
1227 if (archiveSize) *archiveSize = totalsize;
1232 const char * cpioStrerror(int rc)
1234 static char msg[256];
1236 int l, myerrno = errno;
1238 strcpy(msg, "cpio: ");
1241 s = msg + strlen(msg);
1242 sprintf(s, _("(error 0x%x)"), (unsigned)rc);
1245 case CPIOERR_BAD_MAGIC: s = _("Bad magic"); break;
1246 case CPIOERR_BAD_HEADER: s = _("Bad/unreadable header");break;
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;
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;
1271 l = sizeof(msg) - strlen(msg) - 1;
1273 if (l > 0) strncat(msg, s, l);
1276 if ((rc & CPIOERR_CHECK_ERRNO) && myerrno) {
1277 s = _(" failed - ");
1278 if (l > 0) strncat(msg, s, l);
1280 if (l > 0) strncat(msg, strerror(myerrno), l);