static void test_ar_normalize_name(void) { struct ar_hdr arh; strncpy(arh.ar_name, "member-name/ ", sizeof(arh.ar_name)); dpkg_ar_normalize_name(&arh); test_str(arh.ar_name, ==, "member-name"); strncpy(arh.ar_name, "member-name ", sizeof(arh.ar_name)); dpkg_ar_normalize_name(&arh); test_str(arh.ar_name, ==, "member-name"); }
void extracthalf(const char *debar, const char *dir, enum dpkg_tar_options taroption, int admininfo) { struct dpkg_error err; const char *errstr; char versionbuf[40]; struct deb_version version; off_t ctrllennum, memberlen = 0; ssize_t r; int dummy; pid_t c1=0,c2,c3; int p1[2], p2[2]; int p2_out; int arfd; struct stat stab; char nlc; int adminmember = -1; bool header_done; enum compressor_type decompressor = COMPRESSOR_TYPE_GZIP; if (strcmp(debar, "-") == 0) arfd = STDIN_FILENO; else arfd = open(debar, O_RDONLY); if (arfd < 0) ohshite(_("failed to read archive '%.255s'"), debar); if (fstat(arfd, &stab)) ohshite(_("failed to fstat archive")); r = read_line(arfd, versionbuf, strlen(DPKG_AR_MAGIC), sizeof(versionbuf) - 1); if (r < 0) read_fail(r, debar, _("archive magic version number")); if (strcmp(versionbuf, DPKG_AR_MAGIC) == 0) { ctrllennum= 0; header_done = false; for (;;) { struct ar_hdr arh; r = fd_read(arfd, &arh, sizeof(arh)); if (r != sizeof(arh)) read_fail(r, debar, _("archive member header")); dpkg_ar_normalize_name(&arh); if (dpkg_ar_member_is_illegal(&arh)) ohshit(_("file '%.250s' is corrupt - bad archive header magic"), debar); memberlen = dpkg_ar_member_get_size(debar, &arh); if (!header_done) { char *infobuf; if (strncmp(arh.ar_name, DEBMAGIC, sizeof(arh.ar_name)) != 0) ohshit(_("file '%.250s' is not a debian binary archive (try dpkg-split?)"), debar); infobuf= m_malloc(memberlen+1); r = fd_read(arfd, infobuf, memberlen + (memberlen & 1)); if (r != (memberlen + (memberlen & 1))) read_fail(r, debar, _("archive information header member")); infobuf[memberlen] = '\0'; if (strchr(infobuf, '\n') == NULL) ohshit(_("archive has no newlines in header")); errstr = deb_version_parse(&version, infobuf); if (errstr) ohshit(_("archive has invalid format version: %s"), errstr); if (version.major != 2) ohshit(_("archive is format version %d.%d; get a newer dpkg-deb"), version.major, version.minor); free(infobuf); header_done = true; } else if (arh.ar_name[0] == '_') { /* Members with ‘_’ are noncritical, and if we don't understand * them we skip them. */ if (fd_skip(arfd, memberlen + (memberlen & 1), &err) < 0) ohshit(_("cannot skip archive member from '%s': %s"), debar, err.str); } else { if (strncmp(arh.ar_name, ADMINMEMBER, strlen(ADMINMEMBER)) == 0) { const char *extension = arh.ar_name + strlen(ADMINMEMBER); adminmember = 1; decompressor = compressor_find_by_extension(extension); if (decompressor != COMPRESSOR_TYPE_NONE && decompressor != COMPRESSOR_TYPE_GZIP && decompressor != COMPRESSOR_TYPE_XZ) ohshit(_("archive '%s' uses unknown compression for member '%.*s', " "giving up"), debar, (int)sizeof(arh.ar_name), arh.ar_name); } else { if (adminmember != 1) ohshit(_("archive '%s' has premature member '%.*s' before '%s', " "giving up"), debar, (int)sizeof(arh.ar_name), arh.ar_name, ADMINMEMBER); if (strncmp(arh.ar_name, DATAMEMBER, strlen(DATAMEMBER)) == 0) { const char *extension = arh.ar_name + strlen(DATAMEMBER); adminmember= 0; decompressor = compressor_find_by_extension(extension); if (decompressor == COMPRESSOR_TYPE_UNKNOWN) ohshit(_("archive '%s' uses unknown compression for member '%.*s', " "giving up"), debar, (int)sizeof(arh.ar_name), arh.ar_name); } else { ohshit(_("archive '%s' has premature member '%.*s' before '%s', " "giving up"), debar, (int)sizeof(arh.ar_name), arh.ar_name, DATAMEMBER); } } if (adminmember == 1) { if (ctrllennum != 0) ohshit(_("archive '%.250s' contains two control members, giving up"), debar); ctrllennum= memberlen; } if (!adminmember != !admininfo) { if (fd_skip(arfd, memberlen + (memberlen & 1), &err) < 0) ohshit(_("cannot skip archive member from '%s': %s"), debar, err.str); } else { /* Yes! - found it. */ break; } } } if (admininfo >= 2) { printf(_(" new debian package, version %d.%d.\n" " size %jd bytes: control archive=%jd bytes.\n"), version.major, version.minor, (intmax_t)stab.st_size, (intmax_t)ctrllennum); m_output(stdout, _("<standard output>")); } } else if (strncmp(versionbuf, "0.93", 4) == 0) { char ctrllenbuf[40]; int l; l = strlen(versionbuf); if (strchr(versionbuf, '\n') == NULL) ohshit(_("archive has no newlines in header")); errstr = deb_version_parse(&version, versionbuf); if (errstr) ohshit(_("archive has invalid format version: %s"), errstr); r = read_line(arfd, ctrllenbuf, 1, sizeof(ctrllenbuf) - 1); if (r < 0) read_fail(r, debar, _("archive control member size")); if (sscanf(ctrllenbuf, "%jd%c%d", &ctrllennum, &nlc, &dummy) != 2 || nlc != '\n') ohshit(_("archive has malformatted control member size '%s'"), ctrllenbuf); if (admininfo) { memberlen = ctrllennum; } else { memberlen = stab.st_size - ctrllennum - strlen(ctrllenbuf) - l; if (fd_skip(arfd, ctrllennum, &err) < 0) ohshit(_("cannot skip archive control member from '%s': %s"), debar, err.str); } if (admininfo >= 2) { printf(_(" old debian package, version %d.%d.\n" " size %jd bytes: control archive=%jd, main archive=%jd.\n"), version.major, version.minor, (intmax_t)stab.st_size, (intmax_t)ctrllennum, (intmax_t)(stab.st_size - ctrllennum - strlen(ctrllenbuf) - l)); m_output(stdout, _("<standard output>")); } } else { if (strncmp(versionbuf, "!<arch>", 7) == 0) { notice(_("file looks like it might be an archive which has been\n" " corrupted by being downloaded in ASCII mode")); } ohshit(_("'%.255s' is not a debian format archive"), debar); } m_pipe(p1); c1 = subproc_fork(); if (!c1) { close(p1[0]); if (fd_fd_copy(arfd, p1[1], memberlen, &err) < 0) ohshit(_("cannot copy archive member from '%s' to decompressor pipe: %s"), debar, err.str); if (close(p1[1])) ohshite(_("cannot close decompressor pipe")); exit(0); } close(p1[1]); if (taroption) { m_pipe(p2); p2_out = p2[1]; } else { p2_out = 1; } c2 = subproc_fork(); if (!c2) { if (taroption) close(p2[0]); decompress_filter(decompressor, p1[0], p2_out, _("decompressing archive member")); exit(0); } close(p1[0]); close(arfd); if (taroption) close(p2[1]); if (taroption) { c3 = subproc_fork(); if (!c3) { struct command cmd; command_init(&cmd, TAR, "tar"); command_add_arg(&cmd, "tar"); if ((taroption & DPKG_TAR_LIST) && (taroption & DPKG_TAR_EXTRACT)) command_add_arg(&cmd, "-xv"); else if (taroption & DPKG_TAR_EXTRACT) command_add_arg(&cmd, "-x"); else if (taroption & DPKG_TAR_LIST) command_add_arg(&cmd, "-tv"); else internerr("unknown or missing tar action '%d'", taroption); if (taroption & DPKG_TAR_PERMS) command_add_arg(&cmd, "-p"); if (taroption & DPKG_TAR_NOMTIME) command_add_arg(&cmd, "-m"); command_add_arg(&cmd, "-f"); command_add_arg(&cmd, "-"); command_add_arg(&cmd, "--warning=no-timestamp"); m_dup2(p2[0],0); close(p2[0]); unsetenv("TAR_OPTIONS"); if (dir) { if (chdir(dir)) { if (errno != ENOENT) ohshite(_("failed to chdir to directory")); if (mkdir(dir, 0777)) ohshite(_("failed to create directory")); if (chdir(dir)) ohshite(_("failed to chdir to directory after creating it")); } } command_exec(&cmd); } close(p2[0]); subproc_reap(c3, "tar", 0); } subproc_reap(c2, _("<decompress>"), SUBPROC_NOPIPE); if (c1 != -1) subproc_reap(c1, _("paste"), 0); if (version.major == 0 && admininfo) { /* Handle the version as a float to preserve the behaviour of old code, * because even if the format is defined to be padded by 0's that might * not have been always true for really ancient versions... */ while (version.minor && (version.minor % 10) == 0) version.minor /= 10; if (version.minor == 931) movecontrolfiles(OLDOLDDEBDIR); else if (version.minor == 932 || version.minor == 933) movecontrolfiles(OLDDEBDIR); } }
int main(int argc, char *argv[]) { char cwd[PATH_MAX] = { '\0' }; char *dir = NULL; enum dpkg_tar_options taroption = DPKG_TAR_EXTRACT | DPKG_TAR_NOMTIME; int admininfo = 0; char *debar = argv[1]; int arfd = -1; struct stat stab; char versionbuf[40] = { '\0' }; ssize_t r; off_t ctrllennum, memberlen = 0; bool header_done; char *infobuf = NULL; struct dpkg_error err; const char *errstr; struct deb_version version; int adminmember = -1; enum compressor_type decompressor = COMPRESSOR_TYPE_GZIP; char nlc; int dummy; int p1[2], p2[2]; pid_t c1 = 0, c2, c3; int p2_out; if (getcwd(cwd, PATH_MAX)) { dir = m_malloc(PATH_MAX); snprintf(dir, PATH_MAX - 1, "%s/extract", cwd); } arfd = open(debar, O_RDONLY); if (arfd < 0) ohshite("failed to read archive %s", debar); if (fstat(arfd, &stab)) ohshite("failed to fstat archive"); r = read_line(arfd, versionbuf, strlen(DPKG_AR_MAGIC), sizeof(versionbuf) - 1); if (r < 0) read_fail(r, debar, "archive magic version number"); if (strcmp(versionbuf, DPKG_AR_MAGIC) == 0) { ctrllennum = 0; header_done = false; for (;;) { struct ar_hdr arh; r = fd_read(arfd, &arh, sizeof(arh)); if (r != sizeof(arh)) read_fail(r, debar, "archive member header"); dpkg_ar_normalize_name(&arh); if (dpkg_ar_member_is_illegal(&arh)) { ohshit("file '%.250s' is corrupt - bad archive header magic", debar); } memberlen = dpkg_ar_member_get_size(debar, &arh); if (!header_done) { infobuf = NULL; if (strncmp(arh.ar_name, DEBMAGIC, sizeof(arh.ar_name)) != 0) { ohshit("file '%.250s' is not a debian binary archive " "(try dpkg-split?)", debar); } infobuf = m_malloc(memberlen + 1); r = fd_read(arfd, infobuf, memberlen + (memberlen & 1)); if (r != (memberlen + (memberlen & 1))) read_fail(r, debar, "archive information header member"); infobuf[memberlen] = '\0'; if (strchr(infobuf, '\n') == NULL) ohshit("archive has no newlines in header"); errstr = deb_version_parse(&version, infobuf); if (errstr) ohshit("archive has invalid format version: %s", errstr); if (version.major != 2) { ohshit("archive is format version %d.%d; get a newer dpkg-deb", version.major, version.minor); } if (infobuf) { free(infobuf); infobuf = NULL; } header_done = true; } else if (arh.ar_name[0] == '_') { if (fd_skip(arfd, memberlen + (memberlen & 1), &err) < 0) { ohshit("cannot skip archive member from '%s': %s", debar, err.str); } } else { if (strncmp(arh.ar_name, ADMINMEMBER, strlen(ADMINMEMBER)) == 0) { const char *extension = arh.ar_name + strlen(ADMINMEMBER); adminmember = 1; decompressor = compressor_find_by_extension(extension); if (decompressor != COMPRESSOR_TYPE_NONE && decompressor != COMPRESSOR_TYPE_GZIP && decompressor != COMPRESSOR_TYPE_XZ) { ohshit("ERROR: archive '%s' uses unknown compression " "for member '%.*s', giving up", debar, (int)sizeof(arh.ar_name), arh.ar_name); } } else { if (adminmember != 1) { ohshit("archive '%s' has premature member '%.*s' " "before '%s', giving up", debar, (int)sizeof(arh.ar_name), arh.ar_name, ADMINMEMBER); } if (strncmp(arh.ar_name, DATAMEMBER, strlen(DATAMEMBER)) == 0) { const char *extension = arh.ar_name + strlen(DATAMEMBER); adminmember = 0; decompressor = compressor_find_by_extension(extension); if (decompressor == COMPRESSOR_TYPE_UNKNOWN) { ohshit("archive '%s' uses unknown " "compression for member '%.*s', giving up", debar, (int)sizeof(arh.ar_name), arh.ar_name); } } else { ohshit("archive '%s' has premature member '%.*s' " "before '%s', giving up", debar, (int)sizeof(arh.ar_name), arh.ar_name, DATAMEMBER); } } if (adminmember == 1) { if (ctrllennum != 0) { ohshit("archive '%.250s' contains two control members, giving up", debar); } ctrllennum = memberlen; } if (!adminmember != !admininfo) { if (fd_skip(arfd, memberlen + (memberlen & 1), &err) < 0) { ohshit("cannot skip archive member from '%s': %s", debar, err.str); } } else { break; } } } if (admininfo >= 2) { printf(" new debian package, version %d.%d.\n" " size %jd bytes: control archive=%jd bytes.\n", version.major, version.minor, (intmax_t)stab.st_size, (intmax_t)ctrllennum); m_output(stdout, "<standard output>"); } } else if (strncmp(versionbuf, "0.93", 4) == 0) { char ctrllenbuf[40] = { '\0' }; int l; l = strlen(versionbuf); if (strchr(versionbuf, '\n') == NULL) ohshit("archive has no newlines in header"); errstr = deb_version_parse(&version, versionbuf); if (errstr) ohshit("archive has invalid format version: %s", errstr); r = read_line(arfd, ctrllenbuf, 1, sizeof(ctrllenbuf)); if (r < 0) read_fail(r, debar, "archive control member size"); if (sscanf(ctrllenbuf, "%jd%c%d", &ctrllennum, &nlc, &dummy) != 2 || nlc != '\n') { ohshit("archive has malformatted control member size '%s'", ctrllenbuf); } if (admininfo) { memberlen = ctrllennum; } else { memberlen = stab.st_size - ctrllennum - strlen(ctrllenbuf) - l; if (fd_skip(arfd, ctrllennum, &err) < 0) { ohshit("cannot skip archive control member from '%s': %s", debar, err.str); } } if (admininfo >= 2) { printf(" old debian package, version %d.%d.\n" " size %jd bytes: control archive=%jd, main archive=%jd.\n", version.major, version.minor, (intmax_t)stab.st_size, (intmax_t)ctrllennum, (intmax_t)(stab.st_size - ctrllennum - strlen(ctrllenbuf) - l)); m_output(stdout, "<standard output>"); } } else { if (strncmp(versionbuf, "!<arch>", 7) == 0) { notice("file looks like it might be an archive which has been\n" " corrupted by being downloaded in ASCII mode"); } ohshit("'%.255s' is not a debian format archive", debar); } m_pipe(p1); c1 = subproc_fork(); if (!c1) { close(p1[0]); if (fd_fd_copy(arfd, p1[1], memberlen, &err) < 0) ohshit("cannot copy archive member from '%s' to decompressor pipe: %s", debar, err.str); if (close(p1[1])) ohshite("cannot close decompressor pipe"); exit(0); } close(p1[1]); if (taroption) { m_pipe(p2); p2_out = p2[1]; } else { p2_out = 1; } c2 = subproc_fork(); if (!c2) { if (taroption) close(p2[0]); decompress_filter(decompressor, p1[0], p2_out, "decompressing archive member"); exit(0); } close(p1[0]); close(arfd); if (taroption) close(p2[1]); if (taroption) { c3 = subproc_fork(); if (!c3) { struct command cmd; command_init(&cmd, TAR, "tar"); command_add_arg(&cmd, "tar"); if ((taroption & DPKG_TAR_LIST) && (taroption & DPKG_TAR_EXTRACT)) command_add_arg(&cmd, "-xv"); else if (taroption & DPKG_TAR_EXTRACT) command_add_arg(&cmd, "-x"); else if (taroption & DPKG_TAR_LIST) command_add_arg(&cmd, "-tv"); else internerr("unknown or missing tar action '%d'", taroption); if (taroption & DPKG_TAR_PERMS) command_add_arg(&cmd, "-p"); if (taroption & DPKG_TAR_NOMTIME) command_add_arg(&cmd, "-m"); command_add_arg(&cmd, "-f"); command_add_arg(&cmd, "-"); command_add_arg(&cmd, "--warning=no-timestamp"); m_dup2(p2[0],0); close(p2[0]); unsetenv("TAR_OPTIONS"); if (dir) { if (chdir(dir)) { if (errno != ENOENT) ohshite("failed to chdir to directory"); if (mkdir(dir, 0777)) ohshite("failed to create directory"); if (chdir(dir)) ohshite("failed to chdir to directory after creating it"); } } command_exec(&cmd); } close(p2[0]); subproc_reap(c3, "tar", 0); } subproc_reap(c2, "<decompress>", SUBPROC_NOPIPE); if (c1 != -1) subproc_reap(c1, "paste", 0); if (version.major == 0 && admininfo) { while (version.minor && (version.minor % 10) == 0) version.minor /= 10; if (version.minor == 931) movecontrolfiles(OLDOLDDEBDIR); else if (version.minor == 932 || version.minor == 933) movecontrolfiles(OLDDEBDIR); } return 0; }
/** * Read a deb-split part archive. * * @return Part info (nfmalloc'd) if was an archive part and we read it, * NULL if it wasn't. */ struct partinfo * read_info(struct dpkg_ar *ar, struct partinfo *ir) { static char *readinfobuf= NULL; static size_t readinfobuflen= 0; size_t thisilen; intmax_t templong; char magicbuf[sizeof(DPKG_AR_MAGIC) - 1], *rip, *partnums, *slash; const char *err; struct ar_hdr arh; ssize_t rc; rc = fd_read(ar->fd, magicbuf, sizeof(magicbuf)); if (rc != sizeof(magicbuf)) { if (rc < 0) ohshite(_("error reading %.250s"), ar->name); else return NULL; } if (memcmp(magicbuf, DPKG_AR_MAGIC, sizeof(magicbuf))) return NULL; rc = fd_read(ar->fd, &arh, sizeof(arh)); if (rc != sizeof(arh)) read_fail(rc, ar->name, "ar header"); dpkg_ar_normalize_name(&arh); if (strncmp(arh.ar_name, PARTMAGIC, sizeof(arh.ar_name)) != 0) return NULL; if (dpkg_ar_member_is_illegal(&arh)) ohshit(_("file '%.250s' is corrupt - bad magic at end of first header"), ar->name); thisilen = dpkg_ar_member_get_size(ar, &arh); if (thisilen >= readinfobuflen) { readinfobuflen = thisilen + 2; readinfobuf= m_realloc(readinfobuf,readinfobuflen); } rc = fd_read(ar->fd, readinfobuf, thisilen + (thisilen & 1)); if (rc != (ssize_t)(thisilen + (thisilen & 1))) read_fail(rc, ar->name, "reading header member"); if (thisilen & 1) { int c = readinfobuf[thisilen + 1]; if (c != '\n') ohshit(_("file '%.250s' is corrupt - bad padding character (code %d)"), ar->name, c); } readinfobuf[thisilen] = '\0'; if (memchr(readinfobuf,0,thisilen)) ohshit(_("file '%.250s' is corrupt - nulls in info section"), ar->name); ir->filename = ar->name; rip= readinfobuf; err = deb_version_parse(&ir->fmtversion, nextline(&rip, ar->name, _("format version number"))); if (err) ohshit(_("file '%.250s' has invalid format version: %s"), ar->name, err); if (ir->fmtversion.major != 2) ohshit(_("file '%.250s' is format version %d.%d; get a newer dpkg-split"), ar->name, ir->fmtversion.major, ir->fmtversion.minor); ir->package = nfstrsave(nextline(&rip, ar->name, _("package name"))); ir->version = nfstrsave(nextline(&rip, ar->name, _("package version number"))); ir->md5sum = nfstrsave(nextline(&rip, ar->name, _("package file MD5 checksum"))); if (strlen(ir->md5sum) != MD5HASHLEN || strspn(ir->md5sum, "0123456789abcdef") != MD5HASHLEN) ohshit(_("file '%.250s' is corrupt - bad MD5 checksum '%.250s'"), ar->name, ir->md5sum); ir->orglength = parse_intmax(nextline(&rip, ar->name, _("archive total size")), ar->name, _("archive total size")); ir->maxpartlen = parse_intmax(nextline(&rip, ar->name, _("archive part offset")), ar->name, _("archive part offset")); partnums = nextline(&rip, ar->name, _("archive part numbers")); slash= strchr(partnums,'/'); if (!slash) ohshit(_("file '%.250s' is corrupt - no slash between archive part numbers"), ar->name); *slash++ = '\0'; templong = parse_intmax(slash, ar->name, _("number of archive parts")); if (templong <= 0 || templong > INT_MAX) ohshit(_("file '%.250s' is corrupt - bad number of archive parts"), ar->name); ir->maxpartn= templong; templong = parse_intmax(partnums, ar->name, _("archive parts number")); if (templong <= 0 || templong > ir->maxpartn) ohshit(_("file '%.250s' is corrupt - bad archive part number"), ar->name); ir->thispartn= templong; /* If the package was created with dpkg 1.16.1 or later it will include * the architecture. */ if (*rip != '\0') ir->arch = nfstrsave(nextline(&rip, ar->name, _("package architecture"))); else ir->arch = NULL; rc = fd_read(ar->fd, &arh, sizeof(arh)); if (rc != sizeof(arh)) read_fail(rc, ar->name, "reading data part member ar header"); dpkg_ar_normalize_name(&arh); if (dpkg_ar_member_is_illegal(&arh)) ohshit(_("file '%.250s' is corrupt - bad magic at end of second header"), ar->name); if (strncmp(arh.ar_name,"data",4)) ohshit(_("file '%.250s' is corrupt - second member is not data member"), ar->name); ir->thispartlen = dpkg_ar_member_get_size(ar, &arh); ir->thispartoffset= (ir->thispartn-1)*ir->maxpartlen; if (ir->maxpartn != (ir->orglength+ir->maxpartlen-1)/ir->maxpartlen) ohshit(_("file '%.250s' is corrupt - wrong number of parts for quoted sizes"), ar->name); if (ir->thispartlen != (ir->thispartn == ir->maxpartn ? ir->orglength - ir->thispartoffset : ir->maxpartlen)) ohshit(_("file '%.250s' is corrupt - size is wrong for quoted part number"), ar->name); ir->filesize = (strlen(DPKG_AR_MAGIC) + sizeof(arh) + thisilen + (thisilen & 1) + sizeof(arh) + ir->thispartlen + (ir->thispartlen & 1)); if (S_ISREG(ar->mode)) { /* Don't do this check if it's coming from a pipe or something. It's * only an extra sanity check anyway. */ if (ar->size < ir->filesize) ohshit(_("file '%.250s' is corrupt - too short"), ar->name); } ir->headerlen = strlen(DPKG_AR_MAGIC) + sizeof(arh) + thisilen + (thisilen & 1) + sizeof(arh); return ir; }