Exemplo n.º 1
0
static void
info_spew(const char *debar, const char *dir, const char *const *argv)
{
  struct dpkg_error err;
  const char *component;
  struct varbuf controlfile = VARBUF_INIT;
  int fd;
  int re= 0;

  while ((component = *argv++) != NULL) {
    varbuf_reset(&controlfile);
    varbuf_printf(&controlfile, "%s/%s", dir, component);

    fd = open(controlfile.buf, O_RDONLY);
    if (fd >= 0) {
      if (fd_fd_copy(fd, 1, -1, &err) < 0)
        ohshit(_("cannot extract control file '%s' from '%s': %s"),
               controlfile.buf, debar, err.str);
      close(fd);
    } else if (errno == ENOENT) {
      notice(_("'%.255s' contains no control component '%.255s'"),
             debar, component);
      re++;
    } else {
      ohshite(_("open component `%.255s' (in %.255s) failed in an unexpected way"),
              component, dir);
    }
  }
  varbuf_destroy(&controlfile);

  if (re > 0)
    ohshit(P_("%d requested control component is missing",
              "%d requested control components are missing", re), re);
}
Exemplo n.º 2
0
Arquivo: info.c Projeto: Minipig/dpkg
static void info_spew(const char *debar, const char *directory,
                      const char *const *argv) {
  const char *component;
  struct varbuf controlfile = VARBUF_INIT;
  int fd;
  int re= 0;

  while ((component = *argv++) != NULL) {
    varbufreset(&controlfile);
    varbufaddstr(&controlfile, directory);
    varbufaddc(&controlfile, '/');
    varbufaddstr(&controlfile, component);
    varbufaddc(&controlfile, '\0');

    fd = open(controlfile.buf, O_RDONLY);
    if (fd >= 0) {
      fd_fd_copy(fd, 1, -1, _("control file '%s'"), controlfile.buf);
      close(fd);
    } else if (errno == ENOENT) {
      fprintf(stderr,
              _("dpkg-deb: `%.255s' contains no control component `%.255s'\n"),
              debar, component);
      re++;
    } else {
      ohshite(_("open component `%.255s' (in %.255s) failed in an unexpected way"),
	      component, directory);
    }
  }
  varbuf_destroy(&controlfile);

  if (re > 0)
    ohshit(P_("%d requested control component is missing",
              "%d requested control components are missing", re), re);
}
Exemplo n.º 3
0
static void
compress_none(int fd_in, int fd_out, struct compress_params *params, const char *desc)
{
	struct dpkg_error err;

	if (fd_fd_copy(fd_in, fd_out, -1, &err) < 0)
		ohshit(_("%s: pass-through copy error: %s"), desc, err.str);
}
Exemplo n.º 4
0
void
showcopyright(const struct cmdinfo *cip, const char *value)
{
	int fd;

	fd = open(COPYINGFILE, O_RDONLY);
	if (fd < 0)
		ohshite(_("cannot open GPL file"));
	fd_fd_copy(fd, 1, -1, "showcopyright");
	exit(0);
}
Exemplo n.º 5
0
Arquivo: ar.c Projeto: pexip/os-dpkg
void
dpkg_ar_member_put_file(const char *ar_name, int ar_fd,
                        const char *name, int fd, off_t size)
{
    if (size <= 0) {
        struct stat st;

        if (fstat(fd, &st))
            ohshite(_("failed to fstat ar member file (%s)"), name);
        size = st.st_size;
    }

    dpkg_ar_member_put_header(ar_name, ar_fd, name, size);

    /* Copy data contents. */
    fd_fd_copy(fd, ar_fd, size, _("ar member file (%s)"), name);

    if (size & 1)
        if (fd_write(ar_fd, "\n", 1) < 0)
            ohshite(_("unable to write file '%s'"), ar_name);
}
Exemplo n.º 6
0
static void
file_copy(const char *src, const char *dst)
{
	struct dpkg_error err;
	char *tmp;
	int srcfd, dstfd;

	srcfd = open(src, O_RDONLY);
	if (srcfd < 0)
		ohshite(_("unable to open file '%s'"), src);

	tmp = str_fmt("%s%s", dst, ".dpkg-divert.tmp");
	dstfd = creat(tmp, 0600);
	if (dstfd < 0)
		ohshite(_("unable to create file '%s'"), tmp);

	push_cleanup(cu_filename, ~ehflag_normaltidy, 1, tmp);

	if (fd_fd_copy(srcfd, dstfd, -1, &err) < 0)
		ohshit(_("cannot copy '%s' to '%s': %s"), src, tmp, err.str);

	close(srcfd);

	if (fsync(dstfd))
		ohshite(_("unable to sync file '%s'"), tmp);
	if (close(dstfd))
		ohshite(_("unable to close file '%s'"), tmp);

	file_copy_perms(src, tmp);

	if (rename(tmp, dst) != 0)
		ohshite(_("cannot rename '%s' to '%s'"), tmp, dst);

	free(tmp);

	pop_cleanup(ehflag_normaltidy);
}
Exemplo n.º 7
0
/**
 * Overly complex function that builds a .deb file.
 */
int
do_build(const char *const *argv)
{
  struct compress_params control_compress_params;
  struct dpkg_error err;
  struct dpkg_ar *ar;
  time_t timestamp;
  const char *timestamp_str;
  const char *dir, *dest;
  char *ctrldir;
  char *debar;
  char *tfbuf;
  int gzfd;

  /* Decode our arguments. */
  dir = *argv++;
  if (!dir)
    badusage(_("--%s needs a <directory> argument"), cipaction->olong);

  dest = *argv++;
  if (dest && *argv)
    badusage(_("--%s takes at most two arguments"), cipaction->olong);

  debar = gen_dest_pathname(dir, dest);
  ctrldir = str_fmt("%s/%s", dir, BUILDCONTROLDIR);

  /* Perform some sanity checks on the to-be-build package. */
  if (nocheckflag) {
    if (debar == NULL)
      ohshit(_("target is directory - cannot skip control file check"));
    warning(_("not checking contents of control area"));
    info(_("building an unknown package in '%s'."), debar);
  } else {
    struct pkginfo *pkg;

    pkg = check_control_area(ctrldir, dir);
    if (debar == NULL)
      debar = gen_dest_pathname_from_pkg(dest, pkg);
    info(_("building package '%s' in '%s'."), pkg->set->name, debar);
  }
  m_output(stdout, _("<standard output>"));

  timestamp_str = getenv("SOURCE_DATE_EPOCH");
  if (timestamp_str)
    timestamp = parse_timestamp(timestamp_str);
  else
    timestamp = time(NULL);

  /* Now that we have verified everything its time to actually
   * build something. Let's start by making the ar-wrapper. */
  ar = dpkg_ar_create(debar, 0644);

  dpkg_ar_set_mtime(ar, timestamp);

  unsetenv("TAR_OPTIONS");

  /* Create a temporary file to store the control data in. Immediately
   * unlink our temporary file so others can't mess with it. */
  tfbuf = path_make_temp_template("dpkg-deb");
  gzfd = mkstemp(tfbuf);
  if (gzfd == -1)
    ohshite(_("failed to make temporary file (%s)"), _("control member"));
  /* Make sure it's gone, the fd will remain until we close it. */
  if (unlink(tfbuf))
    ohshit(_("failed to unlink temporary file (%s), %s"), _("control member"),
           tfbuf);
  free(tfbuf);

  /* Select the compressor to use for our control archive. */
  if (opt_uniform_compression) {
    control_compress_params = compress_params;
  } else {
    control_compress_params.type = COMPRESSOR_TYPE_GZIP;
    control_compress_params.strategy = COMPRESSOR_STRATEGY_NONE;
    control_compress_params.level = -1;
    if (!compressor_check_params(&control_compress_params, &err))
      internerr("invalid control member compressor params: %s", err.str);
  }

  /* Fork a tar to package the control-section of the package. */
  tarball_pack(ctrldir, control_treewalk_feed, timestamp,
               &control_compress_params, gzfd);

  free(ctrldir);

  if (lseek(gzfd, 0, SEEK_SET))
    ohshite(_("failed to rewind temporary file (%s)"), _("control member"));

  /* We have our first file for the ar-archive. Write a header for it
   * to the package and insert it. */
  if (deb_format.major == 0) {
    struct stat controlstab;
    char versionbuf[40];

    if (fstat(gzfd, &controlstab))
      ohshite(_("failed to stat temporary file (%s)"), _("control member"));
    sprintf(versionbuf, "%-8s\n%jd\n", OLDARCHIVEVERSION,
            (intmax_t)controlstab.st_size);
    if (fd_write(ar->fd, versionbuf, strlen(versionbuf)) < 0)
      ohshite(_("error writing '%s'"), debar);
    if (fd_fd_copy(gzfd, ar->fd, -1, &err) < 0)
      ohshit(_("cannot copy '%s' into archive '%s': %s"), _("control member"),
             ar->name, err.str);
  } else if (deb_format.major == 2) {
    const char deb_magic[] = ARCHIVEVERSION "\n";
    char adminmember[16 + 1];

    sprintf(adminmember, "%s%s", ADMINMEMBER,
            compressor_get_extension(control_compress_params.type));

    dpkg_ar_put_magic(ar);
    dpkg_ar_member_put_mem(ar, DEBMAGIC, deb_magic, strlen(deb_magic));
    dpkg_ar_member_put_file(ar, adminmember, gzfd, -1);
  } else {
    internerr("unknown deb format version %d.%d", deb_format.major, deb_format.minor);
  }

  close(gzfd);

  /* Control is done, now we need to archive the data. */
  if (deb_format.major == 0) {
    /* In old format, the data member is just concatenated after the
     * control member, so we do not need a temporary file and can use
     * the compression file descriptor. */
    gzfd = ar->fd;
  } else if (deb_format.major == 2) {
    /* Start by creating a new temporary file. Immediately unlink the
     * temporary file so others can't mess with it. */
    tfbuf = path_make_temp_template("dpkg-deb");
    gzfd = mkstemp(tfbuf);
    if (gzfd == -1)
      ohshite(_("failed to make temporary file (%s)"), _("data member"));
    /* Make sure it's gone, the fd will remain until we close it. */
    if (unlink(tfbuf))
      ohshit(_("failed to unlink temporary file (%s), %s"), _("data member"),
             tfbuf);
    free(tfbuf);
  } else {
    internerr("unknown deb format version %d.%d", deb_format.major, deb_format.minor);
  }

  /* Pack the directory into a tarball, feeding files from the callback. */
  tarball_pack(dir, file_treewalk_feed, timestamp, &compress_params, gzfd);

  /* Okay, we have data.tar as well now, add it to the ar wrapper. */
  if (deb_format.major == 2) {
    char datamember[16 + 1];

    sprintf(datamember, "%s%s", DATAMEMBER,
            compressor_get_extension(compress_params.type));

    if (lseek(gzfd, 0, SEEK_SET))
      ohshite(_("failed to rewind temporary file (%s)"), _("data member"));

    dpkg_ar_member_put_file(ar, datamember, gzfd, -1);

    close(gzfd);
  }
  if (fsync(ar->fd))
    ohshite(_("unable to sync file '%s'"), ar->name);

  dpkg_ar_close(ar);

  free(debar);

  return 0;
}
Exemplo n.º 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);
  }
}
Exemplo n.º 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;
}
Exemplo n.º 10
0
/**
 * Overly complex function that builds a .deb file.
 */
int
do_build(const char *const *argv)
{
  struct dpkg_error err;
  const char *debar, *dir;
  bool subdir;
  char *tfbuf;
  int arfd;
  int p1[2], p2[2], gzfd;
  pid_t c1, c2;

  /* Decode our arguments. */
  dir = *argv++;
  if (!dir)
    badusage(_("--%s needs a <directory> argument"), cipaction->olong);
  subdir = false;
  debar = *argv++;
  if (debar != NULL) {
    struct stat debarstab;

    if (*argv)
      badusage(_("--%s takes at most two arguments"), cipaction->olong);

    if (stat(debar, &debarstab)) {
      if (errno != ENOENT)
        ohshite(_("unable to check for existence of archive `%.250s'"), debar);
    } else if (S_ISDIR(debarstab.st_mode)) {
      subdir = true;
    }
  } else {
    char *m;

    m= m_malloc(strlen(dir) + sizeof(DEBEXT));
    strcpy(m, dir);
    path_trim_slash_slashdot(m);
    strcat(m, DEBEXT);
    debar= m;
  }

  /* Perform some sanity checks on the to-be-build package. */
  if (nocheckflag) {
    if (subdir)
      ohshit(_("target is directory - cannot skip control file check"));
    warning(_("not checking contents of control area"));
    printf(_("dpkg-deb: building an unknown package in '%s'.\n"), debar);
  } else {
    struct pkginfo *pkg;

    pkg = check_new_pkg(dir);
    if (subdir)
      debar = pkg_get_pathname(debar, pkg);
    printf(_("dpkg-deb: building package `%s' in `%s'.\n"),
           pkg->set->name, debar);
  }
  m_output(stdout, _("<standard output>"));

  /* Now that we have verified everything its time to actually
   * build something. Let's start by making the ar-wrapper. */
  arfd = creat(debar, 0644);
  if (arfd < 0)
    ohshite(_("unable to create `%.255s'"), debar);
  /* Fork a tar to package the control-section of the package. */
  unsetenv("TAR_OPTIONS");
  m_pipe(p1);
  c1 = subproc_fork();
  if (!c1) {
    m_dup2(p1[1],1); close(p1[0]); close(p1[1]);
    if (chdir(dir))
      ohshite(_("failed to chdir to `%.255s'"), dir);
    if (chdir(BUILDCONTROLDIR))
      ohshite(_("failed to chdir to `%.255s'"), ".../DEBIAN");
    execlp(TAR, "tar", "-cf", "-", "--format=gnu", ".", NULL);
    ohshite(_("unable to execute %s (%s)"), "tar -cf", TAR);
  }
  close(p1[1]);
  /* Create a temporary file to store the control data in. Immediately
   * unlink our temporary file so others can't mess with it. */
  tfbuf = path_make_temp_template("dpkg-deb");
  gzfd = mkstemp(tfbuf);
  if (gzfd == -1)
    ohshite(_("failed to make temporary file (%s)"), _("control member"));
  /* Make sure it's gone, the fd will remain until we close it. */
  if (unlink(tfbuf))
    ohshit(_("failed to unlink temporary file (%s), %s"), _("control member"),
           tfbuf);
  free(tfbuf);

  /* And run gzip to compress our control archive. */
  c2 = subproc_fork();
  if (!c2) {
    struct compress_params params;

    params.type = compressor_type_gzip;
    params.strategy = compressor_strategy_none;
    params.level = 9;

    compress_filter(&params, p1[0], gzfd, _("compressing control member"));
    exit(0);
  }
  close(p1[0]);
  subproc_wait_check(c2, "gzip -9c", 0);
  subproc_wait_check(c1, "tar -cf", 0);

  if (lseek(gzfd, 0, SEEK_SET))
    ohshite(_("failed to rewind temporary file (%s)"), _("control member"));

  /* We have our first file for the ar-archive. Write a header for it
   * to the package and insert it. */
  if (deb_format.major == 0) {
    struct stat controlstab;
    char versionbuf[40];

    if (fstat(gzfd, &controlstab))
      ohshite(_("failed to stat temporary file (%s)"), _("control member"));
    sprintf(versionbuf, "%-8s\n%jd\n", OLDARCHIVEVERSION,
            (intmax_t)controlstab.st_size);
    if (fd_write(arfd, versionbuf, strlen(versionbuf)) < 0)
      ohshite(_("error writing `%s'"), debar);
    if (fd_fd_copy(gzfd, arfd, -1, &err) < 0)
      ohshit(_("cannot copy '%s' into archive '%s': %s"), _("control member"),
             debar, err.str);
  } else if (deb_format.major == 2) {
    const char deb_magic[] = ARCHIVEVERSION "\n";

    dpkg_ar_put_magic(debar, arfd);
    dpkg_ar_member_put_mem(debar, arfd, DEBMAGIC, deb_magic, strlen(deb_magic));
    dpkg_ar_member_put_file(debar, arfd, ADMINMEMBER, gzfd, -1);
  } else {
    internerr("unknown deb format version %d.%d", deb_format.major, deb_format.minor);
  }

  close(gzfd);

  /* Control is done, now we need to archive the data. */
  if (deb_format.major == 0) {
    /* In old format, the data member is just concatenated after the
     * control member, so we do not need a temporary file and can use
     * the compression file descriptor. */
    gzfd = arfd;
  } else if (deb_format.major == 2) {
    /* Start by creating a new temporary file. Immediately unlink the
     * temporary file so others can't mess with it. */
    tfbuf = path_make_temp_template("dpkg-deb");
    gzfd = mkstemp(tfbuf);
    if (gzfd == -1)
      ohshite(_("failed to make temporary file (%s)"), _("data member"));
    /* Make sure it's gone, the fd will remain until we close it. */
    if (unlink(tfbuf))
      ohshit(_("failed to unlink temporary file (%s), %s"), _("data member"),
             tfbuf);
    free(tfbuf);
  } else {
    internerr("unknown deb format version %d.%d", deb_format.major, deb_format.minor);
  }
  /* Fork off a tar. We will feed it a list of filenames on stdin later. */
  m_pipe(p1);
  m_pipe(p2);
  c1 = subproc_fork();
  if (!c1) {
    m_dup2(p1[0],0); close(p1[0]); close(p1[1]);
    m_dup2(p2[1],1); close(p2[0]); close(p2[1]);
    if (chdir(dir))
      ohshite(_("failed to chdir to `%.255s'"), dir);
    execlp(TAR, "tar", "-cf", "-", "--format=gnu", "--null", "-T", "-", "--no-recursion", NULL);
    ohshite(_("unable to execute %s (%s)"), "tar -cf", TAR);
  }
  close(p1[0]);
  close(p2[1]);
  /* Of course we should not forget to compress the archive as well. */
  c2 = subproc_fork();
  if (!c2) {
    close(p1[1]);
    compress_filter(&compress_params, p2[0], gzfd, _("compressing data member"));
    exit(0);
  }
  close(p2[0]);

  /* All the pipes are set, now lets walk the tree, and start feeding
   * filenames to tar. */
  file_treewalk_feed(dir, p1[1]);

  /* All done, clean up wait for tar and gzip to finish their job. */
  close(p1[1]);
  subproc_wait_check(c2, _("<compress> from tar -cf"), 0);
  subproc_wait_check(c1, "tar -cf", 0);
  /* Okay, we have data.tar as well now, add it to the ar wrapper. */
  if (deb_format.major == 2) {
    char datamember[16 + 1];

    sprintf(datamember, "%s%s", DATAMEMBER,
            compressor_get_extension(compress_params.type));

    if (lseek(gzfd, 0, SEEK_SET))
      ohshite(_("failed to rewind temporary file (%s)"), _("data member"));

    dpkg_ar_member_put_file(debar, arfd, datamember, gzfd, -1);

    close(gzfd);
  }
  if (fsync(arfd))
    ohshite(_("unable to sync file '%s'"), debar);
  if (close(arfd))
    ohshite(_("unable to close file '%s'"), debar);

  return 0;
}
Exemplo n.º 11
0
/* Overly complex function that builds a .deb file
 */
void do_build(const char *const *argv) {
  static const char *const maintainerscripts[]= {
    PREINSTFILE, POSTINSTFILE, PRERMFILE, POSTRMFILE, NULL
  };
  
  char *m;
  const char *debar, *directory, *const *mscriptp, *versionstring, *arch;
  char *controlfile, *tfbuf;
  const char *envbuf;
  struct pkginfo *checkedinfo;
  struct arbitraryfield *field;
  FILE *ar, *cf;
  int p1[2],p2[2],p3[2], warns, errs, n, c, subdir, gzfd;
  pid_t c1,c2,c3;
  struct stat controlstab, datastab, mscriptstab, debarstab;
  char conffilename[MAXCONFFILENAME+1];
  time_t thetime= 0;
  struct file_info *fi;
  struct file_info *symlist = NULL;
  struct file_info *symlist_end = NULL;
  
/* Decode our arguments */
  directory = *argv++;
  if (!directory)
    badusage(_("--%s needs a <directory> argument"), cipaction->olong);
  /* template for our tempfiles */
  if ((envbuf= getenv("TMPDIR")) == NULL)
    envbuf= P_tmpdir;
  tfbuf = m_malloc(strlen(envbuf) + 13);
  strcpy(tfbuf,envbuf);
  strcat(tfbuf,"/dpkg.XXXXXX");
  subdir= 0;
  debar = *argv++;
  if (debar != NULL) {
    if (*argv) badusage(_("--build takes at most two arguments"));
    if (debar) {
      if (stat(debar,&debarstab)) {
        if (errno != ENOENT)
          ohshite(_("unable to check for existence of archive `%.250s'"),debar);
      } else if (S_ISDIR(debarstab.st_mode)) {
        subdir= 1;
      }
    }
  } else {
    m= m_malloc(strlen(directory) + sizeof(DEBEXT));
    strcpy(m, directory);
    path_rtrim_slash_slashdot(m);
    strcat(m, DEBEXT);
    debar= m;
  }
    
  /* Perform some sanity checks on the to-be-build package.
   */
  if (nocheckflag) {
    if (subdir)
      ohshit(_("target is directory - cannot skip control file check"));
    warning(_("not checking contents of control area."));
    printf(_("dpkg-deb: building an unknown package in '%s'.\n"), debar);
  } else {
    controlfile= m_malloc(strlen(directory) + sizeof(BUILDCONTROLDIR) +
                          sizeof(CONTROLFILE) + sizeof(CONFFILESFILE) +
                          sizeof(POSTINSTFILE) + sizeof(PREINSTFILE) +
                          sizeof(POSTRMFILE) + sizeof(PRERMFILE) +
                          MAXCONFFILENAME + 5);
    /* Lets start by reading in the control-file so we can check its contents */
    strcpy(controlfile, directory);
    strcat(controlfile, "/" BUILDCONTROLDIR "/" CONTROLFILE);
    warns= 0; errs= 0;
    parsedb(controlfile, pdb_recordavailable|pdb_rejectstatus,
            &checkedinfo, stderr, &warns);
    assert(checkedinfo->available.valid);
    if (strspn(checkedinfo->name,
               "abcdefghijklmnopqrstuvwxyz0123456789+-.")
        != strlen(checkedinfo->name))
      ohshit(_("package name has characters that aren't lowercase alphanums or `-+.'"));
    if (checkedinfo->priority == pri_other) {
      warning(_("'%s' contains user-defined Priority value '%s'"),
              controlfile, checkedinfo->otherpriority);
      warns++;
    }
    for (field= checkedinfo->available.arbs; field; field= field->next) {
      if (known_arbitrary_field(field))
        continue;

      warning(_("'%s' contains user-defined field '%s'"),
              controlfile, field->name);
      warns++;
    }
    checkversion(checkedinfo->available.version.version,
                 _("(upstream) version"), &errs);
    checkversion(checkedinfo->available.version.revision,
                 _("Debian revision"), &errs);
    if (errs) ohshit(_("%d errors in control file"),errs);

    if (subdir) {
      versionstring= versiondescribe(&checkedinfo->available.version,vdew_never);
      arch= checkedinfo->available.architecture; if (!arch) arch= "";
      m= m_malloc(sizeof(DEBEXT)+1+strlen(debar)+1+strlen(checkedinfo->name)+
                  strlen(versionstring)+1+strlen(arch));
      sprintf(m,"%s/%s_%s%s%s" DEBEXT,debar,checkedinfo->name,versionstring,
              arch[0] ? "_" : "", arch);
      debar= m;
    }
    printf(_("dpkg-deb: building package `%s' in `%s'.\n"), checkedinfo->name, debar);

    /* Check file permissions */
    strcpy(controlfile, directory);
    strcat(controlfile, "/" BUILDCONTROLDIR "/");
    if (lstat(controlfile, &mscriptstab))
      ohshite(_("unable to stat control directory"));
    if (!S_ISDIR(mscriptstab.st_mode))
      ohshit(_("control directory is not a directory"));
    if ((mscriptstab.st_mode & 07757) != 0755)
      ohshit(_("control directory has bad permissions %03lo (must be >=0755 "
             "and <=0775)"), (unsigned long)(mscriptstab.st_mode & 07777));

    for (mscriptp= maintainerscripts; *mscriptp; mscriptp++) {
      strcpy(controlfile, directory);
      strcat(controlfile, "/" BUILDCONTROLDIR "/");
      strcat(controlfile, *mscriptp);

      if (!lstat(controlfile,&mscriptstab)) {
        if (S_ISLNK(mscriptstab.st_mode)) continue;
        if (!S_ISREG(mscriptstab.st_mode))
          ohshit(_("maintainer script `%.50s' is not a plain file or symlink"),*mscriptp);
        if ((mscriptstab.st_mode & 07557) != 0555)
          ohshit(_("maintainer script `%.50s' has bad permissions %03lo "
                 "(must be >=0555 and <=0775)"),
                 *mscriptp, (unsigned long)(mscriptstab.st_mode & 07777));
      } else if (errno != ENOENT) {
        ohshite(_("maintainer script `%.50s' is not stattable"),*mscriptp);
      }
    }

    /* Check if conffiles contains sane information */
    strcpy(controlfile, directory);
    strcat(controlfile, "/" BUILDCONTROLDIR "/" CONFFILESFILE);
    if ((cf= fopen(controlfile,"r"))) {
      struct file_info *conffiles_head = NULL;
      struct file_info *conffiles_tail = NULL;

      while (fgets(conffilename,MAXCONFFILENAME+1,cf)) {
        n= strlen(conffilename);
        if (!n) ohshite(_("empty string from fgets reading conffiles"));
        if (conffilename[n-1] != '\n') {
          warning(_("conffile name '%.50s...' is too long, or missing final newline"),
		  conffilename);
          warns++;
          while ((c= getc(cf)) != EOF && c != '\n');
          continue;
        }
        conffilename[n - 1] = '\0';
        strcpy(controlfile, directory);
        strcat(controlfile, "/");
        strcat(controlfile, conffilename);
        if (lstat(controlfile,&controlstab)) {
	  if (errno == ENOENT) {
	    if((n > 1) && isspace(conffilename[n-2]))
	      warning(_("conffile filename '%s' contains trailing white spaces"),
	              conffilename);
	    ohshit(_("conffile `%.250s' does not appear in package"), conffilename);
	  } else
	    ohshite(_("conffile `%.250s' is not stattable"), conffilename);
        } else if (!S_ISREG(controlstab.st_mode)) {
          warning(_("conffile '%s' is not a plain file"), conffilename);
          warns++;
        }

        if (file_info_find_name(conffiles_head, conffilename))
          warning(_("conffile name '%s' is duplicated"), conffilename);
        else {
          struct file_info *conffile;

          conffile = file_info_new(conffilename);
          add_to_filist(&conffiles_head, &conffiles_tail, conffile);
        }
      }

      free_filist(conffiles_head);

      if (ferror(cf)) ohshite(_("error reading conffiles file"));
      fclose(cf);
    } else if (errno != ENOENT) {
      ohshite(_("error opening conffiles file"));
    }
    if (warns)
      warning(_("ignoring %d warnings about the control file(s)\n"), warns);
  }
  m_output(stdout, _("<standard output>"));
  
  /* Now that we have verified everything its time to actually
   * build something. Lets start by making the ar-wrapper.
   */
  if (!(ar=fopen(debar,"wb"))) ohshite(_("unable to create `%.255s'"),debar);
  if (setvbuf(ar, NULL, _IONBF, 0))
    ohshite(_("unable to unbuffer `%.255s'"), debar);
  /* Fork a tar to package the control-section of the package */
  unsetenv("TAR_OPTIONS");
  m_pipe(p1);
  if (!(c1= m_fork())) {
    m_dup2(p1[1],1); close(p1[0]); close(p1[1]);
    if (chdir(directory)) ohshite(_("failed to chdir to `%.255s'"),directory);
    if (chdir(BUILDCONTROLDIR)) ohshite(_("failed to chdir to .../DEBIAN"));
    execlp(TAR, "tar", "-cf", "-", "--format=gnu", ".", NULL);
    ohshite(_("failed to exec tar -cf"));
  }
  close(p1[1]);
  /* Create a temporary file to store the control data in. Immediately unlink
   * our temporary file so others can't mess with it.
   */
  if ((gzfd= mkstemp(tfbuf)) == -1) ohshite(_("failed to make tmpfile (control)"));
  /* make sure it's gone, the fd will remain until we close it */
  if (unlink(tfbuf)) ohshit(_("failed to unlink tmpfile (control), %s"),
      tfbuf);
  /* reset this, so we can use it elsewhere */
  strcpy(tfbuf,envbuf);
  strcat(tfbuf,"/dpkg.XXXXXX");
  /* And run gzip to compress our control archive */
  if (!(c2= m_fork())) {
    m_dup2(p1[0],0); m_dup2(gzfd,1); close(p1[0]); close(gzfd);
    compress_cat(compress_type_gzip, 0, 1, "9", _("control"));
  }
  close(p1[0]);
  waitsubproc(c2,"gzip -9c",0);
  waitsubproc(c1,"tar -cf",0);
  if (fstat(gzfd,&controlstab)) ohshite(_("failed to fstat tmpfile (control)"));
  /* We have our first file for the ar-archive. Write a header for it to the
   * package and insert it.
   */
  if (oldformatflag) {
    if (fprintf(ar, "%-8s\n%ld\n", OLDARCHIVEVERSION, (long)controlstab.st_size) == EOF)
      werr(debar);
  } else {
    thetime = time(NULL);
    if (fprintf(ar,
                "!<arch>\n"
                "debian-binary   %-12lu0     0     100644  %-10ld`\n"
                ARCHIVEVERSION "\n"
                "%s"
                ADMINMEMBER "%-12lu0     0     100644  %-10ld`\n",
                thetime,
                (long)sizeof(ARCHIVEVERSION),
                (sizeof(ARCHIVEVERSION)&1) ? "\n" : "",
                (unsigned long)thetime,
                (long)controlstab.st_size) == EOF)
      werr(debar);
  }                
                
  if (lseek(gzfd,0,SEEK_SET)) ohshite(_("failed to rewind tmpfile (control)"));
  fd_fd_copy(gzfd, fileno(ar), -1, _("control"));

  /* Control is done, now we need to archive the data. Start by creating
   * a new temporary file. Immediately unlink the temporary file so others
   * can't mess with it. */
  if (!oldformatflag) {
    close(gzfd);
    if ((gzfd= mkstemp(tfbuf)) == -1) ohshite(_("failed to make tmpfile (data)"));
    /* make sure it's gone, the fd will remain until we close it */
    if (unlink(tfbuf)) ohshit(_("failed to unlink tmpfile (data), %s"),
        tfbuf);
    /* reset these, in case we want to use the later */
    strcpy(tfbuf,envbuf);
    strcat(tfbuf,"/dpkg.XXXXXX");
  }
  /* Fork off a tar. We will feed it a list of filenames on stdin later.
   */
  m_pipe(p1);
  m_pipe(p2);
  if (!(c1= m_fork())) {
    m_dup2(p1[0],0); close(p1[0]); close(p1[1]);
    m_dup2(p2[1],1); close(p2[0]); close(p2[1]);
    if (chdir(directory)) ohshite(_("failed to chdir to `%.255s'"),directory);
    execlp(TAR, "tar", "-cf", "-", "--format=gnu", "--null", "-T", "-", "--no-recursion", NULL);
    ohshite(_("failed to exec tar -cf"));
  }
  close(p1[0]);
  close(p2[1]);
  /* Of course we should not forget to compress the archive as well.. */
  if (!(c2= m_fork())) {
    close(p1[1]);
    m_dup2(p2[0],0); close(p2[0]);
    m_dup2(oldformatflag ? fileno(ar) : gzfd,1);
    compress_cat(compress_type, 0, 1, compression, _("data"));
  }
  close(p2[0]);
  /* All the pipes are set, now lets run find, and start feeding
   * filenames to tar.
   */

  m_pipe(p3);
  if (!(c3= m_fork())) {
    m_dup2(p3[1],1); close(p3[0]); close(p3[1]);
    if (chdir(directory)) ohshite(_("failed to chdir to `%.255s'"),directory);
    execlp(FIND, "find", ".", "-path", "./" BUILDCONTROLDIR, "-prune", "-o",
           "-print0", NULL);
    ohshite(_("failed to exec find"));
  }
  close(p3[1]);
  /* We need to reorder the files so we can make sure that symlinks
   * will not appear before their target.
   */
  while ((fi=getfi(directory, p3[0]))!=NULL)
    if (S_ISLNK(fi->st.st_mode))
      add_to_filist(&symlist, &symlist_end, fi);
    else {
      if (write(p1[1], fi->fn, strlen(fi->fn)+1) ==- 1)
	ohshite(_("failed to write filename to tar pipe (data)"));
    }
  close(p3[0]);
  waitsubproc(c3,"find",0);

  for (fi= symlist;fi;fi= fi->next)
    if (write(p1[1], fi->fn, strlen(fi->fn)+1) == -1)
      ohshite(_("failed to write filename to tar pipe (data)"));
  /* All done, clean up wait for tar and gzip to finish their job */
  close(p1[1]);
  free_filist(symlist);
  waitsubproc(c2, _("<compress> from tar -cf"), 0);
  waitsubproc(c1,"tar -cf",0);
  /* Okay, we have data.tar.gz as well now, add it to the ar wrapper */
  if (!oldformatflag) {
    const char *datamember;
    switch (compress_type) {
    case compress_type_gzip:
      datamember = DATAMEMBER_GZ;
      break;
    case compress_type_bzip2:
      datamember = DATAMEMBER_BZ2;
      break;
    case compress_type_lzma:
      datamember = DATAMEMBER_LZMA;
      break;
    case compress_type_cat:
      datamember = DATAMEMBER_CAT;
      break;
    default:
      internerr("unkown compress_type '%i'", compress_type);
    }
    if (fstat(gzfd, &datastab))
      ohshite(_("failed to fstat tmpfile (data)"));
    if (fprintf(ar,
                "%s"
                "%s" "%-12lu0     0     100644  %-10ld`\n",
                (controlstab.st_size & 1) ? "\n" : "",
                datamember,
                (unsigned long)thetime,
                (long)datastab.st_size) == EOF)
      werr(debar);

    if (lseek(gzfd,0,SEEK_SET)) ohshite(_("failed to rewind tmpfile (data)"));
    fd_fd_copy(gzfd, fileno(ar), -1, _("cat (data)"));

    if (datastab.st_size & 1)
      if (putc('\n',ar) == EOF)
        werr(debar);
  }
  if (fclose(ar)) werr(debar);
                             
  exit(0);
}
Exemplo n.º 12
0
int
tarobject(void *ctx, struct tar_entry *ti)
{
  static struct varbuf conffderefn, hardlinkfn, symlinkfn;
  static int fd;
  const char *usename;
  struct filenamenode *usenode;
  struct filenamenode *linknode;

  struct conffile *conff;
  struct tarcontext *tc = ctx;
  bool existingdirectory, keepexisting;
  int statr;
  ssize_t r;
  struct stat stab, stabtmp;
  char databuf[TARBLKSZ];
  struct file_stat *st;
  struct fileinlist *nifd, **oldnifd;
  struct pkginfo *divpkg, *otherpkg;

  ensureobstackinit();

  /* Append to list of files.
   * The trailing ‘/’ put on the end of names in tarfiles has already
   * been stripped by tar_extractor(). */
  oldnifd= tc->newfilesp;
  nifd= addfiletolist(tc, findnamenode(ti->name, 0));
  nifd->namenode->flags |= fnnf_new_inarchive;

  debug(dbg_eachfile,
        "tarobject ti->name='%s' mode=%lo owner=%u.%u type=%d(%c)"
        " ti->linkname='%s' namenode='%s' flags=%o instead='%s'",
        ti->name, (long)ti->stat.mode,
        (unsigned)ti->stat.uid, (unsigned)ti->stat.gid,
        ti->type,
        ti->type >= '0' && ti->type <= '6' ? "-hlcbdp"[ti->type - '0'] : '?',
        ti->linkname,
        nifd->namenode->name, nifd->namenode->flags,
        nifd->namenode->divert && nifd->namenode->divert->useinstead
        ? nifd->namenode->divert->useinstead->name : "<none>");

  if (nifd->namenode->divert && nifd->namenode->divert->camefrom) {
    divpkg= nifd->namenode->divert->pkg;

    if (divpkg) {
      forcibleerr(fc_overwritediverted,
                  _("trying to overwrite `%.250s', which is the "
                    "diverted version of `%.250s' (package: %.100s)"),
                  nifd->namenode->name, nifd->namenode->divert->camefrom->name,
                  divpkg->name);
    } else {
      forcibleerr(fc_overwritediverted,
                  _("trying to overwrite `%.250s', which is the "
                    "diverted version of `%.250s'"),
                  nifd->namenode->name, nifd->namenode->divert->camefrom->name);
    }
  }

  if (nifd->namenode->statoverride)
    st = nifd->namenode->statoverride;
  else
    st = &ti->stat;

  usenode = namenodetouse(nifd->namenode, tc->pkg);
  usename = usenode->name + 1; /* Skip the leading '/'. */

  trig_file_activate(usenode, tc->pkg);

  if (nifd->namenode->flags & fnnf_new_conff) {
    /* If it's a conffile we have to extract it next to the installed
     * version (i.e. we do the usual link-following). */
    if (conffderef(tc->pkg, &conffderefn, usename))
      usename= conffderefn.buf;
    debug(dbg_conff,"tarobject fnnf_new_conff deref=`%s'",usename);
  }

  setupfnamevbs(usename);

  statr= lstat(fnamevb.buf,&stab);
  if (statr) {
    /* The lstat failed. */
    if (errno != ENOENT && errno != ENOTDIR)
      ohshite(_("unable to stat `%.255s' (which I was about to install)"),
              ti->name);
    /* OK, so it doesn't exist.
     * However, it's possible that we were in the middle of some other
     * backup/restore operation and were rudely interrupted.
     * So, we see if we have .dpkg-tmp, and if so we restore it. */
    if (rename(fnametmpvb.buf,fnamevb.buf)) {
      if (errno != ENOENT && errno != ENOTDIR)
        ohshite(_("unable to clean up mess surrounding `%.255s' before "
                  "installing another version"), ti->name);
      debug(dbg_eachfiledetail,"tarobject nonexistent");
    } else {
      debug(dbg_eachfiledetail,"tarobject restored tmp to main");
      statr= lstat(fnamevb.buf,&stab);
      if (statr) ohshite(_("unable to stat restored `%.255s' before installing"
                           " another version"), ti->name);
    }
  } else {
    debug(dbg_eachfiledetail,"tarobject already exists");
  }

  /* Check to see if it's a directory or link to one and we don't need to
   * do anything. This has to be done now so that we don't die due to
   * a file overwriting conflict. */
  existingdirectory = false;
  switch (ti->type) {
  case tar_filetype_symlink:
    /* If it's already an existing directory, do nothing. */
    if (!statr && S_ISDIR(stab.st_mode)) {
      debug(dbg_eachfiledetail, "tarobject symlink exists as directory");
      existingdirectory = true;
    } else if (!statr && S_ISLNK(stab.st_mode)) {
      if (linktosameexistingdir(ti, fnamevb.buf, &symlinkfn))
        existingdirectory = true;
    }
    break;
  case tar_filetype_dir:
    /* If it's already an existing directory, do nothing. */
    if (!stat(fnamevb.buf,&stabtmp) && S_ISDIR(stabtmp.st_mode)) {
      debug(dbg_eachfiledetail, "tarobject directory exists");
      existingdirectory = true;
    }
    break;
  case tar_filetype_file:
  case tar_filetype_chardev:
  case tar_filetype_blockdev:
  case tar_filetype_fifo:
  case tar_filetype_hardlink:
    break;
  default:
    ohshit(_("archive contained object `%.255s' of unknown type 0x%x"),
           ti->name, ti->type);
  }

  keepexisting = false;
  if (!existingdirectory) {
    struct filepackages_iterator *iter;

    iter = filepackages_iter_new(nifd->namenode);
    while ((otherpkg = filepackages_iter_next(iter))) {
      if (otherpkg == tc->pkg)
        continue;
      debug(dbg_eachfile, "tarobject ... found in %s", otherpkg->name);

      if (nifd->namenode->divert && nifd->namenode->divert->useinstead) {
        /* Right, so we may be diverting this file. This makes the conflict
         * OK iff one of us is the diverting package (we don't need to
         * check for both being the diverting package, obviously). */
        divpkg = nifd->namenode->divert->pkg;
        debug(dbg_eachfile, "tarobject ... diverted, divpkg=%s",
              divpkg ? divpkg->name : "<none>");
        if (otherpkg == divpkg || tc->pkg == divpkg)
          continue;
      }

      /* Nope? Hmm, file conflict, perhaps. Check Replaces. */
      switch (otherpkg->clientdata->replacingfilesandsaid) {
      case 2:
        keepexisting = true;
      case 1:
        continue;
      }

      /* Is the package with the conflicting file in the “config files only”
       * state? If so it must be a config file and we can silenty take it
       * over. */
      if (otherpkg->status == stat_configfiles)
        continue;

      /* Perhaps we're removing a conflicting package? */
      if (otherpkg->clientdata->istobe == itb_remove)
        continue;

      /* Is the file an obsolete conffile in the other package
       * and a conffile in the new package? */
      if ((nifd->namenode->flags & fnnf_new_conff) &&
          !statr && S_ISREG(stab.st_mode)) {
        for (conff = otherpkg->installed.conffiles;
             conff;
             conff = conff->next) {
          if (!conff->obsolete)
            continue;
          if (stat(conff->name, &stabtmp))
            if (errno == ENOENT || errno == ENOTDIR || errno == ELOOP)
              continue;
            if (stabtmp.st_dev == stab.st_dev &&
                stabtmp.st_ino == stab.st_ino)
              break;
        }
        if (conff) {
          debug(dbg_eachfiledetail, "tarobject other's obsolete conffile");
          /* process_archive() will have copied its hash already. */
          continue;
        }
      }

      if (does_replace(tc->pkg, &tc->pkg->available,
                       otherpkg, &otherpkg->installed)) {
        printf(_("Replacing files in old package %s ...\n"),otherpkg->name);
        otherpkg->clientdata->replacingfilesandsaid = 1;
      } else if (does_replace(otherpkg, &otherpkg->installed,
                              tc->pkg, &tc->pkg->available)) {
        printf(_("Replaced by files in installed package %s ...\n"),
               otherpkg->name);
        otherpkg->clientdata->replacingfilesandsaid = 2;
        nifd->namenode->flags &= ~fnnf_new_inarchive;
        keepexisting = true;
      } else {
        if (!statr && S_ISDIR(stab.st_mode)) {
          forcibleerr(fc_overwritedir,
                      _("trying to overwrite directory '%.250s' "
                        "in package %.250s %.250s with nondirectory"),
                      nifd->namenode->name, otherpkg->name,
                      versiondescribe(&otherpkg->installed.version,
                                      vdew_nonambig));
        } else {
          /* At this point we are replacing something without a Replaces.
           * If the new object is a directory and the previous object does
           * not exist assume it's also a directory and don't complain. */
          if (!(statr && ti->type == tar_filetype_dir))
            forcibleerr(fc_overwrite,
                        _("trying to overwrite '%.250s', "
                          "which is also in package %.250s %.250s"),
                        nifd->namenode->name, otherpkg->name,
                        versiondescribe(&otherpkg->installed.version,
                                        vdew_nonambig));
        }
      }
    }
    filepackages_iter_free(iter);
  }

  if (keepexisting) {
    remove_file_from_list(tc, ti, oldnifd, nifd);
    tarfile_skip_one_forward(tc, ti);
    return 0;
  }

  if (filter_should_skip(ti)) {
    nifd->namenode->flags &= ~fnnf_new_inarchive;
    nifd->namenode->flags |= fnnf_filtered;
    tarfile_skip_one_forward(tc, ti);

    return 0;
  }

  if (existingdirectory)
    return 0;

  /* Now, at this stage we want to make sure neither of .dpkg-new and
   * .dpkg-tmp are hanging around. */
  ensure_pathname_nonexisting(fnamenewvb.buf);
  ensure_pathname_nonexisting(fnametmpvb.buf);

  /* Now we start to do things that we need to be able to undo
   * if something goes wrong. Watch out for the CLEANUP comments to
   * keep an eye on what's installed on the disk at each point. */
  push_cleanup(cu_installnew, ~ehflag_normaltidy, NULL, 0, 1, (void *)nifd);

  /*
   * CLEANUP: Now we either have the old file on the disk, or not, in
   * its original filename.
   */

  /* Extract whatever it is as .dpkg-new ... */
  switch (ti->type) {
  case tar_filetype_file:
    /* We create the file with mode 0 to make sure nobody can do anything with
     * it until we apply the proper mode, which might be a statoverride. */
    fd= open(fnamenewvb.buf, (O_CREAT|O_EXCL|O_WRONLY), 0);
    if (fd < 0)
      ohshite(_("unable to create `%.255s' (while processing `%.255s')"),
              fnamenewvb.buf, ti->name);
    push_cleanup(cu_closefd, ehflag_bombout, NULL, 0, 1, &fd);
    debug(dbg_eachfiledetail, "tarobject file open size=%lu",
          (unsigned long)ti->size);
    { char fnamebuf[256];
    fd_fd_copy(tc->backendpipe, fd, ti->size,
               _("backend dpkg-deb during `%.255s'"),
               path_quote_filename(fnamebuf, ti->name, 256));
    }
    r = ti->size % TARBLKSZ;
    if (r > 0)
      if (safe_read(tc->backendpipe, databuf, TARBLKSZ - r) == -1)
        ohshite(_("error reading from dpkg-deb pipe"));

    fd_writeback_init(fd);

    if (nifd->namenode->statoverride)
      debug(dbg_eachfile, "tarobject ... stat override, uid=%d, gid=%d, mode=%04o",
			  nifd->namenode->statoverride->uid,
			  nifd->namenode->statoverride->gid,
			  nifd->namenode->statoverride->mode);
    if (fchown(fd, st->uid, st->gid))
      ohshite(_("error setting ownership of `%.255s'"), ti->name);
    if (fchmod(fd, st->mode & ~S_IFMT))
      ohshite(_("error setting permissions of `%.255s'"), ti->name);

    /* Postpone the fsync, to try to avoid massive I/O degradation. */
    if (!fc_unsafe_io)
      nifd->namenode->flags |= fnnf_deferred_fsync;

    pop_cleanup(ehflag_normaltidy); /* fd = open(fnamenewvb.buf) */
    if (close(fd))
      ohshite(_("error closing/writing `%.255s'"), ti->name);
    newtarobject_utime(fnamenewvb.buf, st);
    break;
  case tar_filetype_fifo:
    if (mkfifo(fnamenewvb.buf,0))
      ohshite(_("error creating pipe `%.255s'"), ti->name);
    debug(dbg_eachfiledetail, "tarobject fifo");
    newtarobject_allmodes(fnamenewvb.buf, st);
    break;
  case tar_filetype_chardev:
    if (mknod(fnamenewvb.buf, S_IFCHR, ti->dev))
      ohshite(_("error creating device `%.255s'"), ti->name);
    debug(dbg_eachfiledetail, "tarobject chardev");
    newtarobject_allmodes(fnamenewvb.buf, st);
    break;
  case tar_filetype_blockdev:
    if (mknod(fnamenewvb.buf, S_IFBLK, ti->dev))
      ohshite(_("error creating device `%.255s'"), ti->name);
    debug(dbg_eachfiledetail, "tarobject blockdev");
    newtarobject_allmodes(fnamenewvb.buf, st);
    break;
  case tar_filetype_hardlink:
    varbufreset(&hardlinkfn);
    varbufaddstr(&hardlinkfn,instdir); varbufaddc(&hardlinkfn,'/');
    varbufaddstr(&hardlinkfn, ti->linkname);
    linknode = findnamenode(ti->linkname, 0);
    if (linknode->flags & fnnf_deferred_rename)
      varbufaddstr(&hardlinkfn, DPKGNEWEXT);
    varbufaddc(&hardlinkfn, '\0');
    if (link(hardlinkfn.buf,fnamenewvb.buf))
      ohshite(_("error creating hard link `%.255s'"), ti->name);
    debug(dbg_eachfiledetail, "tarobject hardlink");
    newtarobject_allmodes(fnamenewvb.buf, st);
    break;
  case tar_filetype_symlink:
    /* We've already cheched for an existing directory. */
    if (symlink(ti->linkname, fnamenewvb.buf))
      ohshite(_("error creating symbolic link `%.255s'"), ti->name);
    debug(dbg_eachfiledetail, "tarobject symlink creating");
    if (lchown(fnamenewvb.buf, st->uid, st->gid))
      ohshite(_("error setting ownership of symlink `%.255s'"), ti->name);
    break;
  case tar_filetype_dir:
    /* We've already checked for an existing directory. */
    if (mkdir(fnamenewvb.buf,0))
      ohshite(_("error creating directory `%.255s'"), ti->name);
    debug(dbg_eachfiledetail, "tarobject directory creating");
    newtarobject_allmodes(fnamenewvb.buf, st);
    break;
  default:
    internerr("unknown tar type '%d', but already checked", ti->type);
  }

  set_selinux_path_context(fnamevb.buf, fnamenewvb.buf, st->mode);

  /*
   * CLEANUP: Now we have extracted the new object in .dpkg-new (or,
   * if the file already exists as a directory and we were trying to
   * extract a directory or symlink, we returned earlier, so we don't
   * need to worry about that here).
   *
   * The old file is still in the original filename,
   */

  /* First, check to see if it's a conffile. If so we don't install
   * it now - we leave it in .dpkg-new for --configure to take care of. */
  if (nifd->namenode->flags & fnnf_new_conff) {
    debug(dbg_conffdetail,"tarobject conffile extracted");
    nifd->namenode->flags |= fnnf_elide_other_lists;
    return 0;
  }

  /* Now we move the old file out of the way, the backup file will
   * be deleted later. */
  if (statr) {
    /* Don't try to back it up if it didn't exist. */
    debug(dbg_eachfiledetail,"tarobject new - no backup");
  } else {
    if (ti->type == tar_filetype_dir || S_ISDIR(stab.st_mode)) {
      /* One of the two is a directory - can't do atomic install. */
      debug(dbg_eachfiledetail,"tarobject directory, nonatomic");
      nifd->namenode->flags |= fnnf_no_atomic_overwrite;
      if (rename(fnamevb.buf,fnametmpvb.buf))
        ohshite(_("unable to move aside `%.255s' to install new version"),
                ti->name);
    } else if (S_ISLNK(stab.st_mode)) {
      /* We can't make a symlink with two hardlinks, so we'll have to
       * copy it. (Pretend that making a copy of a symlink is the same
       * as linking to it.) */
      varbufreset(&symlinkfn);
      varbuf_grow(&symlinkfn, stab.st_size + 1);
      r = readlink(fnamevb.buf, symlinkfn.buf, symlinkfn.size);
      if (r < 0)
        ohshite(_("unable to read link `%.255s'"), ti->name);
      assert(r == stab.st_size);
      varbuf_trunc(&symlinkfn, r);
      varbufaddc(&symlinkfn, '\0');
      if (symlink(symlinkfn.buf,fnametmpvb.buf))
        ohshite(_("unable to make backup symlink for `%.255s'"), ti->name);
      if (lchown(fnametmpvb.buf,stab.st_uid,stab.st_gid))
        ohshite(_("unable to chown backup symlink for `%.255s'"), ti->name);
      set_selinux_path_context(fnamevb.buf, fnametmpvb.buf, stab.st_mode);
    } else {
      debug(dbg_eachfiledetail,"tarobject nondirectory, `link' backup");
      if (link(fnamevb.buf,fnametmpvb.buf))
        ohshite(_("unable to make backup link of `%.255s' before installing new version"),
                ti->name);
    }
  }

  /*
   * CLEANUP: Now the old file is in .dpkg-tmp, and the new file is still
   * in .dpkg-new.
   */

  if (ti->type == tar_filetype_file || ti->type == tar_filetype_symlink) {
    nifd->namenode->flags |= fnnf_deferred_rename;

    debug(dbg_eachfiledetail, "tarobject done and installation deferred");
  } else {
    if (rename(fnamenewvb.buf, fnamevb.buf))
      ohshite(_("unable to install new version of `%.255s'"), ti->name);

    /*
     * CLEANUP: Now the new file is in the destination file, and the
     * old file is in .dpkg-tmp to be cleaned up later. We now need
     * to take a different attitude to cleanup, because we need to
     * remove the new file.
     */

    nifd->namenode->flags |= fnnf_placed_on_disk;
    nifd->namenode->flags |= fnnf_elide_other_lists;

    debug(dbg_eachfiledetail, "tarobject done and installed");
  }

  return 0;
}
Exemplo n.º 13
0
int
do_auto(const char *const *argv)
{
  const char *partfile;
  struct partinfo *refi, **partlist, *otherthispart;
  struct partqueue *pq;
  unsigned int i;
  int j;
  FILE *part;

  if (!opt_outputfile)
    badusage(_("--auto requires the use of the --output option"));
  if (!(partfile= *argv++) || *argv)
    badusage(_("--auto requires exactly one part file argument"));

  refi= nfmalloc(sizeof(struct partqueue));
  part= fopen(partfile,"r");
  if (!part) ohshite(_("unable to read part file `%.250s'"),partfile);
  if (!read_info(part,partfile,refi)) {
    if (!opt_npquiet)
      printf(_("File `%.250s' is not part of a multipart archive.\n"),partfile);
    m_output(stdout, _("<standard output>"));
    return 1;
  }
  fclose(part);
  scandepot();
  partlist= nfmalloc(sizeof(struct partinfo*)*refi->maxpartn);
  for (i = 0; i < refi->maxpartn; i++)
    partlist[i] = NULL;
  for (pq= queue; pq; pq= pq->nextinqueue) {
    struct partinfo *npi, *pi = &pq->info;

    if (!partmatches(pi,refi)) continue;
    npi= nfmalloc(sizeof(struct partinfo));
    mustgetpartinfo(pi->filename,npi);
    addtopartlist(partlist,npi,refi);
  }
  /* If we already have a copy of this version we ignore it and prefer the
   * new one, but we still want to delete the one in the depot, so we
   * save its partinfo (with the filename) for later. This also prevents
   * us from accidentally deleting the source file. */
  otherthispart= partlist[refi->thispartn-1];
  partlist[refi->thispartn-1]= refi;
  for (j=refi->maxpartn-1; j>=0 && partlist[j]; j--);

  if (j>=0) {
    int fd_src, fd_dst;
    int ap;
    char *p, *q;

    m_asprintf(&p, "%st.%lx", opt_depotdir, (long)getpid());
    m_asprintf(&q, "%s%s.%jx.%x.%x", opt_depotdir, refi->md5sum,
               (intmax_t)refi->maxpartlen, refi->thispartn, refi->maxpartn);

    fd_src = open(partfile, O_RDONLY);
    if (fd_src < 0)
      ohshite(_("unable to reopen part file `%.250s'"), partfile);
    fd_dst = creat(p, 0644);
    if (fd_dst < 0)
      ohshite(_("unable to open new depot file `%.250s'"), p);

    fd_fd_copy(fd_src, fd_dst, refi->filesize, _("extracting split part"));

    if (fsync(fd_dst))
      ohshite(_("unable to sync file '%s'"), p);
    if (close(fd_dst))
      ohshite(_("unable to close file '%s'"), p);
    close(fd_src);

    if (rename(p,q)) ohshite(_("unable to rename new depot file `%.250s' to `%.250s'"),p,q);
    free(q);
    free(p);

    printf(_("Part %d of package %s filed (still want "),refi->thispartn,refi->package);
    /* There are still some parts missing. */
    for (i=0, ap=0; i<refi->maxpartn; i++)
      if (!partlist[i])
        printf("%s%d", !ap++ ? "" : i == (unsigned int)j ? _(" and ") : ", ", i + 1);
    printf(").\n");

    dir_sync_path(opt_depotdir);
  } else {

    /* We have all the parts. */
    reassemble(partlist, opt_outputfile);

    /* OK, delete all the parts (except the new one, which we never copied). */
    partlist[refi->thispartn-1]= otherthispart;
    for (i=0; i<refi->maxpartn; i++)
      if (partlist[i])
        if (unlink(partlist[i]->filename))
          ohshite(_("unable to delete used-up depot file `%.250s'"),partlist[i]->filename);

  }

  m_output(stderr, _("<standard error>"));

  return 0;
}