/** * Pack the contents of a directory into a tarball. */ static void tarball_pack(const char *dir, filenames_feed_func *tar_filenames_feeder, time_t timestamp, struct compress_params *tar_compress_params, int fd_out) { int pipe_filenames[2], pipe_tarball[2]; pid_t pid_tar, pid_comp; /* Fork off a tar. We will feed it a list of filenames on stdin later. */ m_pipe(pipe_filenames); m_pipe(pipe_tarball); pid_tar = subproc_fork(); if (pid_tar == 0) { char mtime[50]; m_dup2(pipe_filenames[0], 0); close(pipe_filenames[0]); close(pipe_filenames[1]); m_dup2(pipe_tarball[1], 1); close(pipe_tarball[0]); close(pipe_tarball[1]); if (chdir(dir)) ohshite(_("failed to chdir to '%.255s'"), dir); snprintf(mtime, sizeof(mtime), "@%ld", timestamp); execlp(TAR, "tar", "-cf", "-", "--format=gnu", "--mtime", mtime, "--clamp-mtime", "--null", "--no-unquote", "--no-recursion", "-T", "-", NULL); ohshite(_("unable to execute %s (%s)"), "tar -cf", TAR); } close(pipe_filenames[0]); close(pipe_tarball[1]); /* Of course we should not forget to compress the archive as well. */ pid_comp = subproc_fork(); if (pid_comp == 0) { close(pipe_filenames[1]); compress_filter(tar_compress_params, pipe_tarball[0], fd_out, _("compressing tar member")); exit(0); } close(pipe_tarball[0]); /* All the pipes are set, now lets start feeding filenames to tar. */ tar_filenames_feeder(dir, pipe_filenames[1]); /* All done, clean up wait for tar and <compress> to finish their job. */ close(pipe_filenames[1]); subproc_reap(pid_comp, _("<compress> from tar -cf"), 0); subproc_reap(pid_tar, "tar -cf", 0); }
static void DPKG_ATTR_SENTINEL fd_fd_filter(int fd_in, int fd_out, const char *desc, const char *delenv[], const char *file, ...) { va_list args; struct command cmd; pid_t pid; int i; pid = subproc_fork(); if (pid == 0) { if (fd_in != 0) { m_dup2(fd_in, 0); close(fd_in); } if (fd_out != 1) { m_dup2(fd_out, 1); close(fd_out); } for (i = 0; delenv[i]; i++) unsetenv(delenv[i]); command_init(&cmd, file, desc); command_add_arg(&cmd, file); va_start(args, file); command_add_argv(&cmd, args); va_end(args); command_exec(&cmd); } subproc_reap(pid, desc, 0); }
/** * Remove a pathname and anything below it. * * This function removes pathname and all its contents recursively. */ void path_remove_tree(const char *pathname) { pid_t pid; const char *u; u = path_skip_slash_dotslash(pathname); assert(*u); debug(dbg_eachfile, "%s '%s'", __func__, pathname); if (!rmdir(pathname)) return; /* Deleted it OK, it was a directory. */ if (errno == ENOENT || errno == ELOOP) return; if (errno == ENOTDIR) { /* Either it's a file, or one of the path components is. If * one of the path components is this will fail again ... */ if (secure_unlink(pathname) == 0) return; /* OK, it was. */ if (errno == ENOTDIR) return; } if (errno != ENOTEMPTY && errno != EEXIST) /* Huh? */ ohshite(_("unable to securely remove '%.255s'"), pathname); pid = subproc_fork(); if (pid == 0) { execlp(RM, "rm", "-rf", "--", pathname, NULL); ohshite(_("unable to execute %s (%s)"), _("rm command for cleanup"), RM); } debug(dbg_eachfile, "%s running rm -rf '%s'", __func__, pathname); subproc_reap(pid, _("rm command for cleanup"), 0); }
static void movecontrolfiles(const char *thing) { char buf[200]; pid_t pid; sprintf(buf, "mv %s/* . && rmdir %s", thing, thing); pid = subproc_fork(); if (pid == 0) { command_shell(buf, _("shell command to move files")); } subproc_reap(pid, _("shell command to move files"), 0); }
static void movecontrolfiles(const char *thing) { char buf[200] = { '\0' }; pid_t pid; snprintf(buf, sizeof(buf) - 1, "mv %s/* . && rmdir %s", thing, thing); pid = subproc_fork(); if (pid == 0) command_shell(buf, "shell command to move files"); subproc_reap(pid, "shell command to move files", 0); }
void pager_reap(struct pager *pager) { if (!pager->used) return; m_dup2(pager->stdout_old, 1); subproc_reap(pager->pid, pager->desc, SUBPROC_NOPIPE); sigaction(SIGPIPE, &pager->sigpipe, NULL); free(pager); }
static void test_subproc_fork(void) { pid_t pid; int ret; /* Test exit(). */ pid = subproc_fork(); if (pid == 0) exit(0); ret = subproc_reap(pid, "subproc exit pass", SUBPROC_RETERROR); test_pass(ret == 0); pid = subproc_fork(); if (pid == 0) exit(128); ret = subproc_reap(pid, "subproc exit fail", SUBPROC_RETERROR); test_pass(ret == 128); /* Test signals. */ pid = subproc_fork(); if (pid == 0) raise(SIGINT); ret = subproc_reap(pid, "subproc signal", SUBPROC_WARN); test_pass(ret == -1); pid = subproc_fork(); if (pid == 0) raise(SIGTERM); ret = subproc_reap(pid, "subproc signal", SUBPROC_WARN); test_pass(ret == -1); pid = subproc_fork(); if (pid == 0) raise(SIGPIPE); ret = subproc_reap(pid, "subproc SIGPIPE", SUBPROC_WARN | SUBPROC_NOPIPE); test_pass(ret == 0); pid = subproc_fork(); if (pid == 0) raise(SIGPIPE); ret = subproc_reap(pid, "subproc SIGPIPE", SUBPROC_WARN); test_pass(ret == -1); }
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; }