--- tools/CMakeLists.txt.orig 2025-02-12 12:28:53.926181102 +0000 +++ tools/CMakeLists.txt 2025-02-12 12:31:14.961909654 +0000 @@ -40,11 +40,8 @@ if (READLINE_FOUND) target_link_libraries(rpmlua PRIVATE PkgConfig::READLINE) endif() -if (WITH_ARCHIVE) - add_executable(rpm2archive rpm2archive.c) - target_link_libraries(rpm2archive PRIVATE PkgConfig::LIBARCHIVE) - install(TARGETS rpm2archive) -endif() +add_executable(rpm2archive rpm2archive.c) +install(TARGETS rpm2archive) # Everything links to these get_property(executables DIRECTORY PROPERTY BUILDSYSTEM_TARGETS) @@ -60,12 +57,10 @@ foreach(cmd rpmverify rpmquery) ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${cmd} TYPE BIN) endforeach() -if (WITH_ARCHIVE) - add_custom_target(rpm2cpio ALL COMMAND - ${CMAKE_COMMAND} -E create_symlink rpm2archive rpm2cpio - ) - install(FILES ${CMAKE_CURRENT_BINARY_DIR}/rpm2cpio TYPE BIN) -endif() +add_custom_target(rpm2cpio ALL COMMAND + ${CMAKE_COMMAND} -E create_symlink rpm2archive rpm2cpio + ) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/rpm2cpio TYPE BIN) if (WITH_CXX) set (cxx_sources --- tools/rpm2archive.c.orig 2025-02-10 09:57:58.036626709 +0000 +++ tools/rpm2archive.c 2025-02-12 12:27:28.986344583 +0000 @@ -2,6 +2,14 @@ #include "system.h" +#if defined(MAJOR_IN_MKDEV) +#include +#elif defined(MAJOR_IN_SYSMACROS) +#include +#else +#include /* already included from system.h */ +#endif + #include /* rpmReadPackageFile .. */ #include #include @@ -12,8 +20,11 @@ #include +#if 0 #include #include +#endif + #include #include #include @@ -36,6 +47,8 @@ static struct poptOption optionsTable[] POPT_TABLEEND }; +#if 0 + static void fill_archive_entry(struct archive_entry * entry, rpmfi fi, char **hardlink) { @@ -282,6 +295,542 @@ static int process_package(rpmts ts, con return rc; } +#else + +static int do_fwrite(FD_t fdo, const void *p, size_t l) +{ + if (Fwrite(p, l, 1, fdo) != l) { + fprintf(stderr, "Error writing archive: %s\n", Fstrerror(fdo)); + return RPMRC_FAIL; + } + return RPMRC_OK; +} + +static int do_fwrite_content(FD_t fdo, char * buf, rpmfi fi) +{ + rpm_loff_t left = rpmfiFSize(fi); + size_t len, read; + + while (left) { + len = (left > BUFSIZE ? BUFSIZE : left); + read = rpmfiArchiveRead(fi, buf, len); + if (read != len) { + fprintf(stderr, "Error reading file from rpm payload\n"); + break; + } + if (do_fwrite(fdo, buf, len)) { + fprintf(stderr, "Error writing archive: %s\n", Fstrerror(fdo)); + break; + } + left -= len; + } + return (left > 0); +} + +/* cpio support */ + +static inline void write_cpio_entry_num(unsigned char *p, unsigned long val) +{ + char space[64]; + sprintf(space, "%8.8lx", val); + memcpy(p, space, 8); +} + +static int write_cpio_entry(FD_t fdo, rpmfi fi, const char *filename, struct stat *st, const char *flink, const char *hlink, char *buf) +{ + unsigned char cpioh[110]; + memcpy(cpioh, "070701", 6); + if (!fi) { + memset(cpioh + 6, '0', sizeof(cpioh) - 6); + write_cpio_entry_num(cpioh + 38, 1); + write_cpio_entry_num(cpioh + 94, 11); + if (do_fwrite(fdo, cpioh, sizeof(cpioh))) + return RPMRC_FAIL; + if (do_fwrite(fdo, "TRAILER!!!\0\0\0", 11 + 3)) + return RPMRC_FAIL; + return RPMRC_OK; + } + if (st->st_size > UINT32_MAX) { + fprintf(stderr, "Warning: file too large for format, skipping: %s\n", filename); + return RPMRC_OK; + } + size_t fnl = strlen(filename); + write_cpio_entry_num(cpioh + 6, st->st_ino); + write_cpio_entry_num(cpioh + 14, st->st_mode); + write_cpio_entry_num(cpioh + 22, st->st_uid); + write_cpio_entry_num(cpioh + 30, st->st_gid); + write_cpio_entry_num(cpioh + 38, st->st_nlink); + write_cpio_entry_num(cpioh + 46, st->st_mtime); + write_cpio_entry_num(cpioh + 54, st->st_size); + write_cpio_entry_num(cpioh + 62, major(st->st_dev)); + write_cpio_entry_num(cpioh + 70, minor(st->st_dev)); + write_cpio_entry_num(cpioh + 78, major(st->st_rdev)); + write_cpio_entry_num(cpioh + 86, minor(st->st_rdev)); + write_cpio_entry_num(cpioh + 94, fnl + 1); + write_cpio_entry_num(cpioh + 102, 0); + if (do_fwrite(fdo, cpioh, sizeof(cpioh))) + return RPMRC_FAIL; + if (do_fwrite(fdo, filename, fnl + 1)) + return RPMRC_FAIL; + fnl = (110 + fnl + 1) & 3; + if (fnl && do_fwrite(fdo, "\0\0\0", 4 - fnl)) + return RPMRC_FAIL; + if (S_ISLNK(st->st_mode)) { + if (st->st_size != strlen(flink)) + return RPMRC_FAIL; + if (do_fwrite(fdo, flink, st->st_size)) + return RPMRC_FAIL; + } else if (S_ISREG(st->st_mode)) { + if (st->st_size && do_fwrite_content(fdo, buf, fi)) + return RPMRC_FAIL; + } else { + return RPMRC_OK; + } + fnl = (st->st_size) & 3; + if (fnl && do_fwrite(fdo, "\0\0\0", 4 - fnl)) + return RPMRC_FAIL; + return RPMRC_OK; +} + +/* pax support */ + +static void add_pax_attrib(char **paxbuf, const char *pax, const char *val) +{ + size_t ten, len = 1 + strlen(pax) + 1 + strlen(val) + 1; + for (ten = 1; ten <= len; ten *= 10) + len++; + if (*paxbuf) + *paxbuf = realloc(*paxbuf, strlen(*paxbuf) + len + 1); + else { + *paxbuf = xmalloc(len + 1); + **paxbuf = 0; + } + sprintf(*paxbuf + strlen(*paxbuf), "%llu %s=%s\n", (unsigned long long)len, pax, val); +} + +static void set_pax_entry_num_base256(unsigned char *p, unsigned long long val, int size) +{ + /* use base-256 encoding */ + unsigned char *pe = p + size; + for (; pe > p; val >>= 8) + *pe-- = (unsigned char)(val & 255); + *p |= 0x80; +} + +static inline void set_pax_entry_num(unsigned char *p, unsigned long long val, int size, char *pax, char **paxbuf) +{ + char space[64]; + int sz = size == 12 ? size - 1 : size - 2; + if (paxbuf && val >= (unsigned long long)1 << (sz * 3)) { + /* add pax header */ + sprintf(space, "%llu", val); + add_pax_attrib(paxbuf, pax, space); + } + if (val >= (unsigned long long)1 << (size * 3)) { + set_pax_entry_num_base256(p, val, size); + return; + } + sprintf(space, "%0*llo ", sz, val); + memcpy(p, space, size); +} + +static int pax_is_ascii(const char *val) +{ + for (; *val; val++) + if (*(const unsigned char *)val >= 0x80) + return 0; + return 1; +} + +static inline void set_pax_entry_str(unsigned char *p, const char *val, int size, char *pax, char **paxbuf) +{ + size_t l = strlen(val); + if (paxbuf && (l > size || !pax_is_ascii(val))) + add_pax_attrib(paxbuf, pax, val); + memcpy(p, val, l < size ? l : size); +} + +static void set_pax_path_mangle(unsigned char *paxh, const char *filename, const char *insert) +{ + size_t l = strlen(filename); + size_t ilen = insert ? strlen(insert) + 1 : 0; + const char *p, *p2, *bn; + int isdir = 0; + /* strip trailing '/' and '/.' components */ + while (l && (filename[l - 1] == '/' || (filename[l - 1] == '.' && l > 1 && filename[l - 2] == '/'))) { + l--; + isdir = 1; + } + if (ilen) { + isdir = 0; /* no trailing slash for a PaxHeader */ + if (l == 0) { + filename = "/rootdir"; + l = 8; + } else if (l == 1 && filename[0] == '.') { + filename = "currentdir"; + l = 10; + } else if (l == 2 && filename[0] == '.' && filename[1] == '.') { + filename = "parrentdir"; + l = 10; + } + } + /* find the basename */ + bn = filename + l; + while (bn > filename && bn[-1] != '/') + bn--; + if (bn == filename + 1) + bn--; + /* truncate basename (we use 99 like libarchive so we can add a '/' if the prefix is empty) */ + l -= bn - filename; + if (l > 99 - (ilen + isdir)) + l = 99 - (ilen + isdir); + /* calculate prefix */ + if (bn - filename <= 100 - (l + ilen + isdir)) { + p = filename; /* no need for a prefix */ + } else { + p = bn - filename > 155 ? filename + 155 : bn; + while (p > filename && *p != '/') + p--; + /* move as much of the prefix into name as possible */ + if (p > filename && bn - p < 99 - (l + ilen + isdir)) { + p2 = strchr(bn - (99 - (l + ilen + isdir)), '/'); + if (p2 && p2 < p) + p = p2; + } + } + /* copy the prefix */ + if (p != filename) { + memcpy(paxh + 345, filename, p - filename); + p++; /* skip the '/' */ + } + /* copy rest of the dir */ + p2 = p + (100 - (l + ilen + isdir)) > bn ? bn : p + (100 - (l + ilen + isdir)); + while (p2 > p && *p2 != '/') + p2--; + if (p2 < bn && *p2 == '/') + p2++; /* always fits as we used 99 as size limit above */ + memcpy(paxh, p, p2 - p); + /* copy the insert */ + if (ilen) { + memcpy(paxh + (p2 - p), insert, ilen); + paxh[p2 - p + ilen - 1] = '/'; + } + /* copy the basename */ + memcpy(paxh + (p2 - p) + ilen, bn, l); + if (isdir) + paxh[p2 - p + ilen + l] = '/'; +} + +static int set_pax_path(unsigned char *paxh, const char *filename) +{ + size_t l = strlen(filename); + if (l <= 100) { + memcpy(paxh, filename, l); + return 0; + } + const char *p = strchr(filename + l - 100 - 1, '/'); + if (p == filename) + p = strchr(filename + 1, '/'); + if (p && p[1] && p - filename <= 155) { + memcpy(paxh, p + 1, l - (p + 1 - filename)); + memcpy(paxh + 345, filename, p - filename); + return 0; + } + set_pax_path_mangle(paxh, filename, NULL); + return 1; +} + +static int write_pax_entry_pax(FD_t fdo, rpmfi fi, const char *filename, struct stat *st, char *paxbuf); + +static int write_pax_entry(FD_t fdo, rpmfi fi, const char *filename, struct stat *st, const char *flink, const char *hlink, char *buf) +{ + unsigned char paxh[512]; + int tartype = -1; + rpm_loff_t size = 0; + + memset(paxh, 0, sizeof(paxh)); + if (!fi) { + if (do_fwrite(fdo, paxh, sizeof(paxh))) + return RPMRC_FAIL; + if (do_fwrite(fdo, paxh, sizeof(paxh))) + return RPMRC_FAIL; + return RPMRC_OK; + } + if (filename == NULL && flink) + tartype = 'x'; + else if (S_ISREG(st->st_mode)) + tartype = st->st_nlink > 1 && !rpmfiArchiveHasContent(fi) ? '1' : '0'; + else if (S_ISLNK(st->st_mode)) + tartype = '2'; + else if (S_ISCHR(st->st_mode)) + tartype = '3'; + else if (S_ISBLK(st->st_mode)) + tartype = '4'; + else if (S_ISDIR(st->st_mode)) + tartype = '5'; + else if (S_ISFIFO(st->st_mode)) + tartype = '6'; + if (tartype == -1) { + fprintf(stderr, "Warning: unsupported file type, skipping: %s\n", filename); + return RPMRC_OK; + } + if (tartype == '5') { + size_t l = strlen(filename); + if (!l || filename[l - 1] != '/') { + char *dirfilename = rstrscat(NULL, filename, "/", NULL); + int r = write_pax_entry(fdo, fi, dirfilename, st, flink, hlink, buf); + _free(dirfilename); + return r; + } + } + if (tartype == '0' || tartype == '1') + size = rpmfiFSize(fi); + else if (tartype == 'x') + size = (rpm_loff_t)strlen(buf); + + /* fill entry header */ + char *paxbuf = NULL; + char **paxbufp = tartype == 'x' ? NULL : &paxbuf; + if (tartype == 'x') { + set_pax_path_mangle(paxh, flink, "PaxHeader"); + } else { + if (set_pax_path(paxh, filename) || !pax_is_ascii(filename)) + add_pax_attrib(paxbufp, "path", filename); + } + set_pax_entry_num(paxh + 100, st->st_mode & 07777, 8, NULL, NULL); + set_pax_entry_num(paxh + 108, st->st_uid, 8, "uid", paxbufp); + set_pax_entry_num(paxh + 116, st->st_gid, 8, "gid", paxbufp); + set_pax_entry_num(paxh + 124, size, 12, "size", paxbufp); + set_pax_entry_num(paxh + 136, st->st_mtime, 12, "mtime", paxbufp); + memset(paxh + 148, ' ', 8); + paxh[156] = tartype; + if (tartype == '1' || tartype == '2') + set_pax_entry_str(paxh + 157, tartype == '1' ? hlink : flink, 100, "linkpath", paxbufp); + memcpy(paxh + 257, "ustar\00000", 8); + set_pax_entry_str(paxh + 265, rpmfiFUser(fi), 32, "user", paxbufp); + set_pax_entry_str(paxh + 297, rpmfiFGroup(fi), 32, "group", paxbufp); + set_pax_entry_num(paxh + 329, major(st->st_rdev), 8, "SCHILY.devmajor", paxbufp); + set_pax_entry_num(paxh + 337, minor(st->st_rdev), 8, "SCHILY.devminor", paxbufp); + int i, checksum = 0; + for (i = 0; i < 512; i++) + checksum += paxh[i]; + set_pax_entry_num(paxh + 148, checksum, 8, NULL, NULL); + paxh[148 + 6] = 0; + paxh[148 + 7] = ' '; + /* write pax header if we need it */ + if (paxbuf) { + int r = write_pax_entry_pax(fdo, fi, filename, st, paxbuf); + free(paxbuf); + if (r) + return RPMRC_FAIL; + } + /* write entry header */ + if (do_fwrite(fdo, paxh, 512)) + return RPMRC_FAIL; + if (tartype != '0' && tartype != 'x') + return RPMRC_OK; /* no content for those types */ + /* write content */ + if (tartype == '0' && size && do_fwrite_content(fdo, buf, fi)) + return RPMRC_FAIL; + if (tartype == 'x' && size && do_fwrite(fdo, buf, size)) + return RPMRC_FAIL; + /* write padding */ + size &= 511; + if (size) { + memset(paxh, 0, sizeof(paxh)); + if (do_fwrite(fdo, paxh, 512 - size)) + return RPMRC_FAIL; + } + return RPMRC_OK; +} + +static int write_pax_entry_pax(FD_t fdo, rpmfi fi, const char *filename, struct stat *st, char *paxbuf) +{ + /* tweak stat data and filename */ + struct stat paxst = *st; + paxst.st_size = strlen(paxbuf); + paxst.st_mode = paxst.st_mode & 0777; + if (paxst.st_uid >= (1 << 18)) + paxst.st_uid = (1 << 18) - 1; + if (paxst.st_gid >= (1 << 18)) + paxst.st_gid = (1 << 18) - 1; + if (paxst.st_mtime < 0) + paxst.st_mtime = 0; + if ((unsigned long long)paxst.st_mtime >= 1ULL << 33) + paxst.st_mtime = (time_t)((1ULL << 33) - 1); + return write_pax_entry(fdo, fi, NULL, &paxst, filename, NULL, paxbuf); +} + +static int process_package(rpmts ts, const char * filename) +{ + FD_t fdi; + FD_t gzdi; + FD_t fdo; + Header h; + int rc = 0; + char * rpmio_flags = NULL; + int iscpio = 0; + + if (!strcmp(filename, "-")) { + if(isatty(STDIN_FILENO)) { + fprintf(stderr, "Error: missing input RPM package\n"); + exit(EXIT_FAILURE); + } + fdi = fdDup(STDIN_FILENO); + } else { + fdi = Fopen(filename, "r.ufdio"); + } + + if (Ferror(fdi)) { + fprintf(stderr, "rpm2archive: %s: %s\n", + filename, Fstrerror(fdi)); + exit(EXIT_FAILURE); + } + + rc = rpmReadPackageFile(ts, fdi, "rpm2cpio", &h); + + switch (rc) { + case RPMRC_OK: + case RPMRC_NOKEY: + case RPMRC_NOTTRUSTED: + break; + case RPMRC_NOTFOUND: + fprintf(stderr, _("argument is not an RPM package\n")); + exit(EXIT_FAILURE); + break; + case RPMRC_FAIL: + default: + fprintf(stderr, _("error reading header from package\n")); + exit(EXIT_FAILURE); + break; + } + + + /* Retrieve payload size and compression type. */ + { const char *compr = headerGetString(h, RPMTAG_PAYLOADCOMPRESSOR); + rpmio_flags = rstrscat(NULL, "r.", compr ? compr : "gzip", NULL); + } + + gzdi = Fdopen(fdi, rpmio_flags); /* XXX gzdi == fdi */ + free(rpmio_flags); + + if (gzdi == NULL) { + fprintf(stderr, _("cannot re-open payload: %s\n"), Fstrerror(gzdi)); + exit(EXIT_FAILURE); + } + + if (rstreq(format, "pax")) { + iscpio = 0; + } else if (rstreq(format, "cpio")) { + iscpio = 1; + } else { + fprintf(stderr, "Error: Format %s is not supported\n", format); + exit(EXIT_FAILURE); + } + + if (!isatty(STDOUT_FILENO)) { + fdo = fdDup(STDOUT_FILENO); + } else { + if (!strcmp(filename, "-")) { + fprintf(stderr, "Error: refusing to output archive data to a terminal.\n"); + exit(EXIT_FAILURE); + } + char * outname; + if (urlIsURL(filename)) { + const char * fname = strrchr(filename, '/'); + if (fname != NULL) { + fname++; + } else { + fname = filename; + } + outname = rstrscat(NULL, fname, NULL); + } else { + outname = rstrscat(NULL, filename, NULL); + } + if (compress) { + outname = rstrscat(&outname, ".tgz", NULL); + } else { + outname = rstrscat(&outname, ".tar", NULL); + } + fdo = Fopen(outname, "w.ufdio"); + if (!fdo) { + fprintf(stderr, "Error: Can't open output file: %s\n", outname); + exit(EXIT_FAILURE); + } + _free(outname); + } + if (compress && fdo) + fdo = Fdopen(fdo, "w.gzdio"); + if (!fdo) { + fprintf(stderr, "Error: Can't setup output file\n"); + exit(EXIT_FAILURE); + } + + char * buf = (char *)xmalloc(BUFSIZE); + char * hardlink = NULL; + + rpmfiles files = rpmfilesNew(NULL, h, 0, RPMFI_KEEPHEADER); + rpmfi fi = rpmfiNewArchiveReader(gzdi, files, iscpio ? RPMFI_ITER_READ_ARCHIVE : RPMFI_ITER_READ_ARCHIVE_CONTENT_FIRST); + + while ((rc = rpmfiNext(fi)) >= 0) { + struct stat st; + const char *dn, *flink; + char *filename; + if (rpmfiStat(fi, 0, &st)) { + break; + } + dn = rpmfiDN(fi); + if (!strcmp(dn, "")) dn = "/"; + filename = rstrscat(NULL, ".", dn, rpmfiBN(fi), NULL); + flink = S_ISLNK(st.st_mode) ? rpmfiFLink(fi) : NULL; + if (st.st_nlink > 1 && !iscpio) { + if (rpmfiArchiveHasContent(fi)) { + /* hardlink sizes are special, see rpmfiStat() */ + _free(hardlink); + hardlink = xstrdup(filename); + } + } + if (iscpio) + rc = write_cpio_entry(fdo, fi, filename, &st, flink, st.st_nlink > 1 ? hardlink : NULL, buf); + else + rc = write_pax_entry(fdo, fi, filename, &st, flink, st.st_nlink > 1 ? hardlink : NULL, buf); + _free(filename); + if (rc == RPMRC_FAIL) + break; + } + /* End of iteration is not an error, everything else is */ + if (rc == RPMERR_ITER_END) { + rc = 0; + } else { + rc = 1; + } + + /* write trailer */ + if (!rc) { + if (iscpio) + rc = write_cpio_entry(fdo, NULL, NULL, NULL, NULL, NULL, buf); + else + rc = write_pax_entry(fdo, NULL, NULL, NULL, NULL, NULL, buf); + rc = rc == RPMRC_FAIL ? 1 : 0; + } + + if (Fclose(fdo) && !rc) { + fprintf(stderr, "Error writing archive\n"); + rc = 1; + } + + _free(hardlink); + + Fclose(gzdi); /* XXX gzdi == fdi */ + buf = _free(buf); + rpmfilesFree(files); + rpmfiFree(fi); + headerFree(h); + return rc; +} +#endif + + int main(int argc, char *argv[]) { int rc = 0;