diff options
-rw-r--r-- | isys/uncpio.c | 952 |
1 files changed, 952 insertions, 0 deletions
diff --git a/isys/uncpio.c b/isys/uncpio.c new file mode 100644 index 000000000..e65c4fc82 --- /dev/null +++ b/isys/uncpio.c @@ -0,0 +1,952 @@ +#define HAVE_ALLOCA_H 1 +#define MAJOR_IN_SYSMACROS 1 + +#if HAVE_ALLOCA_H +# include <alloca.h> +#endif + +#define _(foo) (foo) + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <utime.h> + +#include "cpio.h" + +#if MAJOR_IN_SYSMACROS +#include <sys/sysmacros.h> +#elif MAJOR_IN_MKDEV +#include <sys/mkdev.h> +#endif + +#define CPIO_NEWC_MAGIC "070701" +#define CPIO_CRC_MAGIC "070702" +#define TRAILER "TRAILER!!!" + +/* FIXME: We don't translate between cpio and system mode bits! These + should both be the same, but really odd things are going to happen if + that's not true! */ + +/* We need to maintain our oun file pointer to allow padding */ +struct ourfd { + gzFile fd; + int pos; +}; + +struct hardLink { + struct hardLink * next; + char ** files; /* there are nlink of these, used by install */ + int * fileMaps; /* used by build */ + dev_t dev; + ino_t inode; + int nlink; + int linksLeft; + int createdPath; + struct stat sb; +}; + +struct cpioCrcPhysicalHeader { + char magic[6]; + char inode[8]; + char mode[8]; + char uid[8]; + char gid[8]; + char nlink[8]; + char mtime[8]; + char filesize[8]; + char devMajor[8]; + char devMinor[8]; + char rdevMajor[8]; + char rdevMinor[8]; + char namesize[8]; + char checksum[8]; /* ignored !! */ +}; + +#define PHYS_HDR_SIZE 110 /* don't depend on sizeof(struct) */ + +struct cpioHeader { + ino_t inode; + mode_t mode; + uid_t uid; + gid_t gid; + int nlink; + time_t mtime; + long size; + dev_t dev, rdev; + char * path; +}; + +static inline off_t ourread(struct ourfd * thefd, void * buf, size_t size) { + off_t i; + + i = gzread(thefd->fd, buf, size); + thefd->pos += i; + + return i; +} + +static inline void padinfd(struct ourfd * fd, int modulo) { + int buf[10]; + int amount; + + amount = (modulo - fd->pos % modulo) % modulo; + ourread(fd, buf, amount); +} + +static inline off_t safewrite(int fd, void * vbuf, size_t amount) { + int rc = 0; + char * buf = vbuf; + + while (amount && (rc = write(fd, buf, amount)) < amount && rc > 0) { + buf += rc; + amount -= rc; + } + + if (rc < 0) return rc; + + return 0; +} + +static inline int padoutfd(int fd, size_t * where, int modulo) { + static int buf[10] = { '\0', '\0', '\0', '\0', '\0', + '\0', '\0', '\0', '\0', '\0' }; + int amount; + + amount = (modulo - *where % modulo) % modulo; + *where += amount; + + if (safewrite(fd, buf, amount)) + return CPIOERR_WRITE_FAILED; + return 0; +} + +static int strntoul(const char * str, char ** endptr, int base, int num) { + char * buf, * end; + unsigned long ret; + + buf = alloca(num + 1); + strncpy(buf, str, num); + buf[num] = '\0'; + + ret = strtoul(buf, &end, base); + if (*end) + *endptr = (char *)(str + (end - buf)); /* XXX discards const */ + else + *endptr = ""; + + return strtoul(buf, endptr, base); +} + +#define GET_NUM_FIELD(phys, log) \ + log = strntoul(phys, &end, 16, sizeof(phys)); \ + if (*end) return CPIOERR_BAD_HEADER; +#define SET_NUM_FIELD(phys, val, space) \ + sprintf(space, "%8.8lx", (unsigned long) (val)); \ + memcpy(phys, space, 8); + +static int getNextHeader(struct ourfd * fd, struct cpioHeader * chPtr) { + struct cpioCrcPhysicalHeader physHeader; + int nameSize; + char * end; + int major, minor; + + if (ourread(fd, &physHeader, PHYS_HDR_SIZE) != PHYS_HDR_SIZE) + return CPIOERR_READ_FAILED; + + if (strncmp(CPIO_CRC_MAGIC, physHeader.magic, strlen(CPIO_CRC_MAGIC)) && + strncmp(CPIO_NEWC_MAGIC, physHeader.magic, strlen(CPIO_NEWC_MAGIC))) + return CPIOERR_BAD_MAGIC; + + GET_NUM_FIELD(physHeader.inode, chPtr->inode); + GET_NUM_FIELD(physHeader.mode, chPtr->mode); + GET_NUM_FIELD(physHeader.uid, chPtr->uid); + GET_NUM_FIELD(physHeader.gid, chPtr->gid); + GET_NUM_FIELD(physHeader.nlink, chPtr->nlink); + GET_NUM_FIELD(physHeader.mtime, chPtr->mtime); + GET_NUM_FIELD(physHeader.filesize, chPtr->size); + + GET_NUM_FIELD(physHeader.devMajor, major); + GET_NUM_FIELD(physHeader.devMinor, minor); + chPtr->dev = makedev(major, minor); + + GET_NUM_FIELD(physHeader.rdevMajor, major); + GET_NUM_FIELD(physHeader.rdevMinor, minor); + chPtr->rdev = makedev(major, minor); + + GET_NUM_FIELD(physHeader.namesize, nameSize); + + chPtr->path = malloc(nameSize + 1); + if (ourread(fd, chPtr->path, nameSize) != nameSize) { + free(chPtr->path); + return CPIOERR_BAD_HEADER; + } + + /* this is unecessary chPtr->path[nameSize] = '\0'; */ + + padinfd(fd, 4); + + return 0; +} + +int cpioFileMapCmp(const void * a, const void * b) { + const struct cpioFileMapping * first = a; + const struct cpioFileMapping * second = b; + + return (strcmp(first->archivePath, second->archivePath)); +} + +/* This could trash files in the path! I'm not sure that's a good thing */ +static int createDirectory(char * path, mode_t perms) { + struct stat sb; + int dounlink; + + if (!lstat(path, &sb)) { + if (S_ISDIR(sb.st_mode)) { + return 0; + } else if (S_ISLNK(sb.st_mode)) { + if (stat(path, &sb)) { + if (errno != ENOENT) + return CPIOERR_STAT_FAILED; + dounlink = 1; + } else { + if (S_ISDIR(sb.st_mode)) + return 0; + dounlink = 1; + } + } else { + dounlink = 1; + } + + if (dounlink && unlink(path)) { + return CPIOERR_UNLINK_FAILED; + } + } + + if (mkdir(path, 000)) + return CPIOERR_MKDIR_FAILED; + + if (chmod(path, perms)) + return CPIOERR_CHMOD_FAILED; + + return 0; +} + +static int setInfo(struct cpioHeader * hdr) { + int rc = 0; + struct utimbuf stamp; + + stamp.actime = hdr->mtime; + stamp.modtime = hdr->mtime; + + if (!S_ISLNK(hdr->mode)) { + if (!getuid() && chown(hdr->path, hdr->uid, hdr->gid)) + rc = CPIOERR_CHOWN_FAILED; + if (!rc && chmod(hdr->path, hdr->mode & 07777)) + rc = CPIOERR_CHMOD_FAILED; + if (!rc && utime(hdr->path, &stamp)) + rc = CPIOERR_UTIME_FAILED; + } else { +# if ! CHOWN_FOLLOWS_SYMLINK + if (!getuid() && !rc && lchown(hdr->path, hdr->uid, hdr->gid)) + rc = CPIOERR_CHOWN_FAILED; +# endif + } + + return rc; +} + +static int checkDirectory(char * filename) { + static char * lastDir = NULL; + static int lastDirLength = 0; + static int lastDirAlloced = 0; + int length = strlen(filename); + char * buf; + char * chptr; + int rc = 0; + + buf = alloca(length + 1); + strcpy(buf, filename); + + for (chptr = buf + length - 1; chptr > buf; chptr--) { + if (*chptr == '/') break; + } + + if (chptr == buf) return 0; /* /filename - no directories */ + + *chptr = '\0'; /* buffer is now just directories */ + + length = strlen(buf); + if (lastDirLength == length && !strcmp(buf, lastDir)) return 0; + + if (lastDirAlloced < (length + 1)) { + lastDirAlloced = length + 100; + lastDir = realloc(lastDir, lastDirAlloced); + } + + strcpy(lastDir, buf); + lastDirLength = length; + + for (chptr = buf + 1; *chptr; chptr++) { + if (*chptr == '/') { + *chptr = '\0'; + rc = createDirectory(buf, 0755); + *chptr = '/'; + if (rc) return rc; + } + } + rc = createDirectory(buf, 0755); + + return rc; +} + +static int expandRegular(struct ourfd * fd, struct cpioHeader * hdr, + cpioCallback cb, void * cbData) { + int out; + char buf[8192]; + int bytesRead; + int left = hdr->size; + int rc = 0; + struct cpioCallbackInfo cbInfo; + struct stat sb; + + if (!lstat(hdr->path, &sb)) + if (unlink(hdr->path)) + return CPIOERR_UNLINK_FAILED; + + out = open(hdr->path, O_CREAT | O_WRONLY, 0); + if (out < 0) + return CPIOERR_OPEN_FAILED; + + cbInfo.file = hdr->path; + cbInfo.fileSize = hdr->size; + + while (left) { + bytesRead = ourread(fd, buf, left < sizeof(buf) ? left : sizeof(buf)); + if (bytesRead <= 0) { + rc = CPIOERR_READ_FAILED; + break; + } + + if (write(out, buf, bytesRead) != bytesRead) { + rc = CPIOERR_COPY_FAILED; + break; + } + + left -= bytesRead; + + /* don't call this with fileSize == fileComplete */ + if (!rc && cb && left) { + cbInfo.fileComplete = hdr->size - left; + cbInfo.bytesProcessed = fd->pos; + cb(&cbInfo, cbData); + } + } + + close(out); + + return rc; +} + +static int expandSymlink(struct ourfd * fd, struct cpioHeader * hdr) { + char buf[2048], buf2[2048]; + struct stat sb; + int len; + + if ((hdr->size + 1)> sizeof(buf)) + return CPIOERR_INTERNAL; + + if (ourread(fd, buf, hdr->size) != hdr->size) + return CPIOERR_READ_FAILED; + + buf[hdr->size] = '\0'; + + if (!lstat(hdr->path, &sb)) { + if (S_ISLNK(sb.st_mode)) { + len = readlink(hdr->path, buf2, sizeof(buf2) - 1); + if (len > 0) { + buf2[len] = '\0'; + if (!strcmp(buf, buf2)) return 0; + } + } + + if (unlink(hdr->path)) + return CPIOERR_UNLINK_FAILED; + } + + if (symlink(buf, hdr->path) < 0) + return CPIOERR_SYMLINK_FAILED; + + return 0; +} + +static int expandFifo(struct ourfd * fd, struct cpioHeader * hdr) { + struct stat sb; + + if (!lstat(hdr->path, &sb)) { + if (S_ISFIFO(sb.st_mode)) return 0; + + if (unlink(hdr->path)) + return CPIOERR_UNLINK_FAILED; + } + + if (mkfifo(hdr->path, 0)) + return CPIOERR_MKFIFO_FAILED; + + return 0; +} + +static int expandDevice(struct ourfd * fd, struct cpioHeader * hdr) { + struct stat sb; + + if (!lstat(hdr->path, &sb)) { + if ((S_ISCHR(sb.st_mode) || S_ISBLK(sb.st_mode)) && + (sb.st_rdev == hdr->rdev)) + return 0; + if (unlink(hdr->path)) + return CPIOERR_UNLINK_FAILED; + } + + if (mknod(hdr->path, hdr->mode & (~0777), hdr->rdev)) + return CPIOERR_MKNOD_FAILED; + + return 0; +} + +static void freeLink(struct hardLink * li) { + int i; + + for (i = 0; i < li->nlink; i++) { + if (li->files[i]) free(li->files[i]); + } + free(li->files); +} + +static int createLinks(struct hardLink * li, const char ** failedFile) { + int i; + struct stat sb; + + for (i = 0; i < li->nlink; i++) { + if (i == li->createdPath) continue; + if (!li->files[i]) continue; + + if (!lstat(li->files[i], &sb)) { + if (unlink(li->files[i])) { + *failedFile = strdup(li->files[i]); + return CPIOERR_UNLINK_FAILED; + } + } + + if (link(li->files[li->createdPath], li->files[i])) { + *failedFile = strdup(li->files[i]); + return CPIOERR_LINK_FAILED; + } + + free(li->files[i]); + li->files[i] = NULL; + li->linksLeft--; + } + + return 0; +} + +static int eatBytes(struct ourfd * fd, int amount) { + char buf[4096]; + int bite; + + while (amount) { + bite = (amount > sizeof(buf)) ? sizeof(buf) : amount; + if (ourread(fd, buf, bite) != bite) + return CPIOERR_READ_FAILED; + amount -= bite; + } + + return 0; +} + +int cpioInstallArchive(gzFile stream, struct cpioFileMapping * mappings, + int numMappings, cpioCallback cb, void * cbData, + const char ** failedFile) { + struct cpioHeader ch; + struct ourfd fd; + int rc = 0; + int linkNum = 0; + struct cpioFileMapping * map = NULL; + struct cpioFileMapping needle; + mode_t cpioMode; + int olderr; + struct cpioCallbackInfo cbInfo; + struct hardLink * links = NULL; + struct hardLink * li = NULL; + + fd.fd = stream; + fd.pos = 0; + + *failedFile = NULL; + + do { + if ((rc = getNextHeader(&fd, &ch))) { + fprintf(stderr, _("error %d reading header: %s\n"), rc, strerror(errno)); + return CPIOERR_BAD_HEADER; + } + + if (!strcmp(ch.path, TRAILER)) { + free(ch.path); + break; + } + + if (mappings) { + needle.archivePath = ch.path; + map = bsearch(&needle, mappings, numMappings, sizeof(needle), + cpioFileMapCmp); + } + + if (mappings && !map) { + eatBytes(&fd, ch.size); + } else { + cpioMode = ch.mode; + + if (map) { + if (map->mapFlags & CPIO_MAP_PATH) { + free(ch.path); + ch.path = strdup(map->fsPath); + } + + if (map->mapFlags & CPIO_MAP_MODE) + ch.mode = map->finalMode; + if (map->mapFlags & CPIO_MAP_UID) + ch.uid = map->finalUid; + if (map->mapFlags & CPIO_MAP_GID) + ch.gid = map->finalGid; + } + + /* This won't get hard linked symlinks right, but I can't seem + to create those anyway */ + + if (S_ISREG(ch.mode) && ch.nlink > 1) { + li = links; + for (li = links; li; li = li->next) { + if (li->inode == ch.inode && li->dev == ch.dev) break; + } + + if (!li) { + li = malloc(sizeof(*li)); + li->inode = ch.inode; + li->dev = ch.dev; + li->nlink = ch.nlink; + li->linksLeft = ch.nlink; + li->createdPath = -1; + li->files = calloc(sizeof(char *), li->nlink); + li->next = links; + links = li; + } + + for (linkNum = 0; linkNum < li->nlink; linkNum++) + if (!li->files[linkNum]) break; + li->files[linkNum] = strdup(ch.path); + } + + if ((ch.nlink > 1) && S_ISREG(ch.mode) && !ch.size && + li->createdPath == -1) { + /* defer file creation */ + } else if ((ch.nlink > 1) && S_ISREG(ch.mode) && + (li->createdPath != -1)) { + createLinks(li, failedFile); + + /* this only happens for cpio archives which contain + hardlinks w/ the contents of each hardlink being + listed (intead of the data being given just once. This + shouldn't happen, but I've made it happen w/ buggy + code, so what the heck? GNU cpio handles this well fwiw */ + if (ch.size) eatBytes(&fd, ch.size); + } else { + rc = checkDirectory(ch.path); + + if (!rc) { + if (S_ISREG(ch.mode)) + rc = expandRegular(&fd, &ch, cb, cbData); + else if (S_ISDIR(ch.mode)) + rc = createDirectory(ch.path, 000); + else if (S_ISLNK(ch.mode)) + rc = expandSymlink(&fd, &ch); + else if (S_ISFIFO(ch.mode)) + rc = expandFifo(&fd, &ch); + else if (S_ISCHR(ch.mode) || S_ISBLK(ch.mode)) + rc = expandDevice(&fd, &ch); + else if (S_ISSOCK(ch.mode)) { + /* this mimicks cpio but probably isnt' right */ + rc = expandFifo(&fd, &ch); + } else { + rc = CPIOERR_INTERNAL; + } + } + + if (!rc) + rc = setInfo(&ch); + + if (S_ISREG(ch.mode) && ch.nlink > 1) { + li->createdPath = linkNum; + li->linksLeft--; + rc = createLinks(li, failedFile); + } + } + + if (rc && !*failedFile) { + *failedFile = strdup(ch.path); + + olderr = errno; + unlink(ch.path); + errno = olderr; + } + } + + padinfd(&fd, 4); + + if (!rc && cb) { + cbInfo.file = ch.path; + cbInfo.fileSize = ch.size; + cbInfo.fileComplete = ch.size; + cbInfo.bytesProcessed = fd.pos; + cb(&cbInfo, cbData); + } + + free(ch.path); + } while (1 && !rc); + + li = links; + while (li && !rc) { + if (li->linksLeft) { + if (li->createdPath == -1) + rc = CPIOERR_INTERNAL; + else + rc = createLinks(li, failedFile); + } + + freeLink(li); + + links = li; + li = li->next; + free(links); + links = li; + } + + li = links; + /* if an error got us here links will still be eating some memory */ + while (li) { + freeLink(li); + links = li; + li = li->next; + free(links); + } + + return rc; +} + +static int writeFile(int fd, struct stat sb, struct cpioFileMapping * map, + size_t * sizeptr, int writeData) { + struct cpioCrcPhysicalHeader hdr; + char buf[8192], symbuf[2048]; + dev_t num; + int datafd; + size_t size, amount = 0; + int rc, olderrno; + + if (!(map->mapFlags & CPIO_MAP_PATH)) + map->archivePath = map->fsPath; + if (map->mapFlags & CPIO_MAP_MODE) + sb.st_mode = (sb.st_mode & S_IFMT) | map->finalMode; + if (map->mapFlags & CPIO_MAP_UID) + sb.st_uid = map->finalUid; + if (map->mapFlags & CPIO_MAP_GID) + sb.st_gid = map->finalGid; + + if (!writeData || S_ISDIR(sb.st_mode)) { + sb.st_size = 0; + } else if (S_ISLNK(sb.st_mode)) { + /* While linux puts the size of a symlink in the st_size field, + I don't think that's a specified standard */ + + amount = readlink(map->fsPath, symbuf, sizeof(symbuf)); + if (amount <= 0) { + return CPIOERR_READLINK_FAILED; + } + + sb.st_size = amount; + } + + memcpy(hdr.magic, CPIO_NEWC_MAGIC, sizeof(hdr.magic)); + SET_NUM_FIELD(hdr.inode, sb.st_ino, buf); + SET_NUM_FIELD(hdr.mode, sb.st_mode, buf); + SET_NUM_FIELD(hdr.uid, sb.st_uid, buf); + SET_NUM_FIELD(hdr.gid, sb.st_gid, buf); + SET_NUM_FIELD(hdr.nlink, sb.st_nlink, buf); + SET_NUM_FIELD(hdr.mtime, sb.st_mtime, buf); + SET_NUM_FIELD(hdr.filesize, sb.st_size, buf); + + num = major(sb.st_dev); SET_NUM_FIELD(hdr.devMajor, num, buf); + num = minor(sb.st_dev); SET_NUM_FIELD(hdr.devMinor, num, buf); + num = major(sb.st_rdev); SET_NUM_FIELD(hdr.rdevMajor, num, buf); + num = minor(sb.st_rdev); SET_NUM_FIELD(hdr.rdevMinor, num, buf); + + num = strlen(map->archivePath) + 1; SET_NUM_FIELD(hdr.namesize, num, buf); + memcpy(hdr.checksum, "00000000", 8); + + if ((rc = safewrite(fd, &hdr, PHYS_HDR_SIZE))) + return rc; + if ((rc = safewrite(fd, map->archivePath, num))) + return rc; + size = PHYS_HDR_SIZE + num; + if ((rc = padoutfd(fd, &size, 4))) + return rc; + + if (writeData && S_ISREG(sb.st_mode)) { + /* FIXME: we should use mmap here if it's available */ + if ((datafd = open(map->fsPath, O_RDONLY)) < 0) + return CPIOERR_OPEN_FAILED; + + size += sb.st_size; + + while (sb.st_size) { + amount = read(datafd, buf, + sb.st_size > sizeof(buf) ? sizeof(buf) : sb.st_size); + if (amount <= 0) { + olderrno = errno; + close(datafd); + errno = olderrno; + return CPIOERR_READ_FAILED; + } + + if ((rc = safewrite(fd, buf, amount))) { + olderrno = errno; + close(datafd); + errno = olderrno; + return rc; + } + + sb.st_size -= amount; + } + + close(datafd); + } else if (writeData && S_ISLNK(sb.st_mode)) { + if ((rc = safewrite(fd, symbuf, amount))) + return rc; + size += amount; + } + + /* this is a noop for most file types */ + if ((rc = padoutfd(fd, &size, 4))) + return rc; + + *sizeptr = size; + + return 0; +} + +static int writeLinkedFile(int fd, struct hardLink * hlink, + struct cpioFileMapping * mappings, + cpioCallback cb, void * cbData, + size_t * sizeptr, + char ** failedFile) { + int i, rc; + size_t size; + struct cpioCallbackInfo cbinfo; + + *sizeptr = 0; + + for (i = hlink->nlink - 1; i > hlink->linksLeft; i--) { + if ((rc = writeFile(fd, hlink->sb, mappings + hlink->fileMaps[i], + &size, 0))) { + if (failedFile) *failedFile = + mappings[hlink->fileMaps[i]].fsPath; + return rc; + } + + *sizeptr += size; + + if (cb) { + cbinfo.file = mappings[i].archivePath; + cb(&cbinfo, cbData); + } + } + + if ((rc = writeFile(fd, hlink->sb, + mappings + hlink->fileMaps[hlink->linksLeft], + &size, 1))) { + if (failedFile) + *failedFile = mappings[hlink->fileMaps[hlink->linksLeft]].fsPath; + return rc; + } + + *sizeptr += size; + + if (cb) { + cbinfo.file = mappings[i].archivePath; + cb(&cbinfo, cbData); + } + + return 0; +} + +int cpioBuildArchive(int fd, struct cpioFileMapping * mappings, + int numMappings, cpioCallback cb, void * cbData, + unsigned int * archiveSize, char ** failedFile) { + size_t size, totalsize = 0; + int rc; + int i; + struct cpioCallbackInfo cbinfo; + struct cpioCrcPhysicalHeader hdr; + struct stat sb; + struct hardLink hlinkList = { NULL }; + struct hardLink * hlink, * parent; + + hlinkList.next = NULL; + + for (i = 0; i < numMappings; i++) { + if (mappings[i].mapFlags & CPIO_FOLLOW_SYMLINKS) + rc = stat(mappings[i].fsPath, &sb); + else + rc = lstat(mappings[i].fsPath, &sb); + + if (rc) { + if (failedFile) *failedFile = mappings[i].fsPath; + return CPIOERR_STAT_FAILED; + } + + if (!S_ISDIR(sb.st_mode) && sb.st_nlink > 1) { + hlink = hlinkList.next; + while (hlink && + (hlink->dev != sb.st_dev || hlink->inode != sb.st_ino)) + hlink = hlink->next; + if (!hlink) { + hlink = malloc(sizeof(*hlink)); + hlink->next = hlinkList.next; + hlinkList.next = hlink; + hlink->sb = sb; + hlink->dev = sb.st_dev; + hlink->inode = sb.st_ino; + hlink->nlink = sb.st_nlink; + hlink->linksLeft = sb.st_nlink; + hlink->fileMaps = malloc(sizeof(*hlink->fileMaps) * + sb.st_nlink); + } + + hlink->fileMaps[--hlink->linksLeft] = i; + + if (!hlink->linksLeft) { + if ((rc = writeLinkedFile(fd, hlink, mappings, cb, cbData, + &size, failedFile))) + return rc; + + totalsize += size; + + free(hlink->fileMaps); + + parent = &hlinkList; + while (parent->next != hlink) parent = parent->next; + parent->next = parent->next->next; + free(hlink); + } + } else { + if ((rc = writeFile(fd, sb, mappings + i, &size, 1))) { + if (failedFile) *failedFile = mappings[i].fsPath; + return rc; + } + + if (cb) { + cbinfo.file = mappings[i].archivePath; + cb(&cbinfo, cbData); + } + + totalsize += size; + } + } + + hlink = hlinkList.next; + while (hlink) { + if ((rc = writeLinkedFile(fd, hlink, mappings, cb, cbData, + &size, failedFile))) + return rc; + free(hlink->fileMaps); + parent = hlink; + hlink = hlink->next; + free(parent); + + totalsize += size; + } + + memset(&hdr, '0', sizeof(hdr)); + memcpy(hdr.magic, CPIO_NEWC_MAGIC, sizeof(hdr.magic)); + memcpy(hdr.nlink, "00000001", 8); + memcpy(hdr.namesize, "0000000b", 8); + if ((rc = safewrite(fd, &hdr, PHYS_HDR_SIZE))) + return rc; + if ((rc = safewrite(fd, "TRAILER!!!", 11))) + return rc; + totalsize += PHYS_HDR_SIZE + 11; + + /* GNU cpio pads to 512 bytes here, but we don't. I'm not sure if + it matters or not */ + + if ((rc = padoutfd(fd, &totalsize, 4))) + return rc; + + if (archiveSize) *archiveSize = totalsize; + + return 0; +} + +const char * cpioStrerror(int rc) +{ + static char msg[256]; + char *s; + int l, myerrno = errno; + + strcpy(msg, "cpio: "); + switch (rc) { + default: + s = msg + strlen(msg); + sprintf(s, _("(error 0x%x)"), rc); + s = NULL; + break; + case CPIOERR_BAD_MAGIC: s = _("Bad magic"); break; + case CPIOERR_BAD_HEADER: s = _("Bad header"); break; + + case CPIOERR_OPEN_FAILED: s = "open"; break; + case CPIOERR_CHMOD_FAILED: s = "chmod"; break; + case CPIOERR_CHOWN_FAILED: s = "chown"; break; + case CPIOERR_WRITE_FAILED: s = "write"; break; + case CPIOERR_UTIME_FAILED: s = "utime"; break; + case CPIOERR_UNLINK_FAILED: s = "unlink"; break; + case CPIOERR_SYMLINK_FAILED: s = "symlink"; break; + case CPIOERR_STAT_FAILED: s = "stat"; break; + case CPIOERR_MKDIR_FAILED: s = "mkdir"; break; + case CPIOERR_MKNOD_FAILED: s = "mknod"; break; + case CPIOERR_MKFIFO_FAILED: s = "mkfifo"; break; + case CPIOERR_LINK_FAILED: s = "link"; break; + case CPIOERR_READLINK_FAILED: s = "readlink"; break; + case CPIOERR_READ_FAILED: s = "read"; break; + case CPIOERR_COPY_FAILED: s = "copy"; break; + + case CPIOERR_INTERNAL: s = _("Internal error"); break; + case CPIOERR_HDR_SIZE: s = _("Header size too big"); break; + case CPIOERR_UNKNOWN_FILETYPE: s = _("Unknown file type"); break; + } + + l = sizeof(msg) - strlen(msg) - 1; + if (s != NULL) { + if (l > 0) strncat(msg, s, l); + l -= strlen(s); + } + if (rc & CPIOERR_CHECK_ERRNO) { + s = _(" failed - "); + if (l > 0) strncat(msg, s, l); + l -= strlen(s); + if (l > 0) strncat(msg, strerror(myerrno), l); + } + return msg; +} |