예제 #1
0
파일: build.c 프로젝트: CharizTeam/dpkg
/**
 * 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);
}
예제 #2
0
파일: compress.c 프로젝트: CharizTeam/dpkg
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);
}
예제 #3
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);
}
예제 #4
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);
}
예제 #5
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);
}
예제 #6
0
파일: pager.c 프로젝트: guillemj/dpkg
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);
}
예제 #7
0
파일: t-subproc.c 프로젝트: mwhudson/dpkg
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);
}
예제 #8
0
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);
  }
}
예제 #9
0
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;
}