Beispiel #1
0
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);
}
void
trigproc(struct pkginfo *pkg)
{
	static struct varbuf namesarg;

	struct trigpend *tp;
	struct pkginfo *gaveup;

	debug(dbg_triggers, "trigproc %s", pkg->name);

	if (pkg->clientdata->trigprocdeferred)
		pkg->clientdata->trigprocdeferred->pkg = NULL;
	pkg->clientdata->trigprocdeferred = NULL;

	if (pkg->trigpend_head) {
		assert(pkg->status == stat_triggerspending ||
		       pkg->status == stat_triggersawaited);

		gaveup = check_trigger_cycle(pkg);
		if (gaveup == pkg)
			return;

		printf(_("Processing triggers for %s ...\n"), pkg->name);
		log_action("trigproc", pkg);

		varbufreset(&namesarg);
		for (tp = pkg->trigpend_head; tp; tp = tp->next) {
			varbufaddc(&namesarg, ' ');
			varbufaddstr(&namesarg, tp->name);
		}
		varbufaddc(&namesarg, 0);

		/* Setting the status to halfconfigured
		 * causes modstatdb_note to clear pending triggers.
		 */
		pkg->status = stat_halfconfigured;
		modstatdb_note(pkg);

		if (!f_noact) {
			sincenothing = 0;
			maintainer_script_postinst(pkg, "triggered",
			                           namesarg.buf + 1, NULL);
		}

		/* This is to cope if the package triggers itself: */
		pkg->status = pkg->trigaw.head ? stat_triggersawaited :
		              pkg->trigpend_head ? stat_triggerspending :
		              stat_installed;

		post_postinst_tasks_core(pkg);
	} else {
		/* In other branch is done by modstatdb_note. */
		trig_clear_awaiters(pkg);
	}
}
const char *pkgadminfile(struct pkginfo *pkg, const char *whichfile) {
  static struct varbuf vb;
  varbufreset(&vb);
  varbufaddstr(&vb,admindir);
  varbufaddstr(&vb,"/" INFODIR);
  varbufaddstr(&vb,pkg->name);
  varbufaddc(&vb,'.');
  varbufaddstr(&vb,whichfile);
  varbufaddc(&vb,0);
  return vb.buf;
}
static void
save(diversion *diversions)
{
	diversion *d;
	static struct varbuf filename;
	static struct varbuf new_filename;
	static struct varbuf old_filename;
	FILE *new_file;

	if (testmode) return;

	varbufreset(&new_filename);
	varbufaddstr(&new_filename, admindir);
	varbufaddstr(&new_filename, "/" DIVERSIONSFILE "-new");
	varbufaddc(&new_filename, 0);
	new_file = fopen(new_filename.buf, "w");
	if (!new_file)
		ohshite(_("create diversions-new"));
	chmod(new_filename.buf, 0644);

	for (d = diversions; d; d = d->next)
	{
		if (fprintf(new_file, "%s\n%s\n%s\n",
			    d->contest,
			    d->altname,
			    d->package) < 0)
			ohshite(_("write diversions-new"));
	}

	if (fclose(new_file) == EOF)
		ohshite(_("close diversions-new"));

	varbufreset(&old_filename);
	varbufaddstr(&old_filename, admindir);
	varbufaddstr(&old_filename, "/" DIVERSIONSFILE "-old");
	varbufaddc(&old_filename, 0);

	if (unlink(old_filename.buf) != 0 && errno != ENOENT)
		ohshite(_("remove old diversions-old"));

	varbufreset(&filename);
	varbufaddstr(&filename, admindir);
	varbufaddstr(&filename, "/" DIVERSIONSFILE);
	varbufaddc(&filename, 0);

	if (link(filename.buf, old_filename.buf) != 0 && errno != ENOENT)
		ohshite(_("create new diversions-old"));

	if (rename(new_filename.buf, filename.buf) != 0)
		ohshite(_("install new diversions"));
}
static void
infol(const char *file, const char *divertto, const char *package,
      struct varbuf *ret)
{
	if (package) {
		if (strcmp(package, ":") == 0)
			varbufaddstr(ret, "local ");
	}
	else
		varbufaddstr(ret, "any ");

	varbufaddstr(ret, "diversion of ");
	varbufaddstr(ret, file);

	if (divertto) {
		varbufaddstr(ret, " to ");
		varbufaddstr(ret, divertto);
	}

	if (package && strcmp(package, ":") != 0) {
		varbufaddstr(ret, " by ");
		varbufaddstr(ret, package);
	}

	varbufaddc(ret, 0);
}
Beispiel #6
0
void varbufversion
(struct varbuf *vb,
 const struct versionrevision *version,
 enum versiondisplayepochwhen vdew)
{
  switch (vdew) {
  case vdew_never:
    break;
  case vdew_nonambig:
    if (!version->epoch &&
        (!version->version || !strchr(version->version,':')) &&
        (!version->revision || !strchr(version->revision,':'))) break;
    /* Fall through. */
  case vdew_always:
    varbufprintf(vb,"%lu:",version->epoch);
    break;
  default:
    internerr("unknown versiondisplayepochwhen '%d'", vdew);
  }
  if (version->version) varbufaddstr(vb,version->version);
  if (version->revision && *version->revision) {
    varbufaddc(vb,'-');
    varbufaddstr(vb,version->revision);
  }
}
Beispiel #7
0
void setupfnamevbs(const char *filename) {
  varbuf_trunc(&fnamevb, fnameidlu);
  varbufaddstr(&fnamevb,filename);
  varbufaddc(&fnamevb,0);

  varbuf_trunc(&fnametmpvb, fnameidlu);
  varbufaddstr(&fnametmpvb,filename);
  varbufaddstr(&fnametmpvb,DPKGTEMPEXT);
  varbufaddc(&fnametmpvb,0);

  varbuf_trunc(&fnamenewvb, fnameidlu);
  varbufaddstr(&fnamenewvb,filename);
  varbufaddstr(&fnamenewvb,DPKGNEWEXT);
  varbufaddc(&fnamenewvb,0);

  debug(dbg_eachfiledetail, "setupvnamevbs main=`%s' tmp=`%s' new=`%s'",
        fnamevb.buf, fnametmpvb.buf, fnamenewvb.buf);
}
Beispiel #8
0
void check_breaks(struct dependency *dep, struct pkginfo *pkg,
                  const char *pfilename) {
  struct pkginfo *fixbydeconf;
  struct varbuf why = VARBUF_INIT;
  int ok;

  fixbydeconf = NULL;
  if (depisok(dep, &why, &fixbydeconf, false)) {
    varbuf_destroy(&why);
    return;
  }

  varbufaddc(&why, 0);

  if (fixbydeconf && f_autodeconf) {
    char action[512];

    ensure_package_clientdata(fixbydeconf);
    assert(fixbydeconf->clientdata->istobe == itb_normal);

    sprintf(action, _("installation of %.250s"), pkg->name);
    fprintf(stderr, _("dpkg: considering deconfiguration of %s,"
                      " which would be broken by %s ...\n"),
            fixbydeconf->name, action);

    ok= try_deconfigure_can(force_breaks, fixbydeconf, dep->list,
                            action, NULL, why.buf);
    if (ok == 1) {
      fprintf(stderr, _("dpkg: yes, will deconfigure %s (broken by %s).\n"),
              fixbydeconf->name, pkg->name);
    }
  } else {
    fprintf(stderr, _("dpkg: regarding %s containing %s:\n%s"),
            pfilename, pkg->name, why.buf);
    ok= 0;
  }
  varbuf_destroy(&why);
  if (ok > 0) return;

  if (force_breaks(dep->list)) {
    warning(_("ignoring breakage, may proceed anyway!"));
    return;
  }

  if (fixbydeconf && !f_autodeconf) {
    ohshit(_("installing %.250s would break %.250s, and\n"
             " deconfiguration is not permitted (--auto-deconfigure might help)"),
           pkg->name, fixbydeconf->name);
  } else {
    ohshit(_("installing %.250s would break existing software"),
           pkg->name);
  }
}
Beispiel #9
0
/**
 * Cycle breaking works recursively down the package dependency tree.
 *
 * ‘sofar’ is the list of packages we've descended down already - if we
 * encounter any of its packages again in a dependency we have found a cycle.
 */
static bool
findbreakcyclerecursive(struct pkginfo *pkg, struct cyclesofarlink *sofar)
{
  struct cyclesofarlink thislink, *sol;
  struct dependency *dep;
  struct deppossi *possi, *providelink;
  struct pkginfo *provider;

  if (pkg->clientdata->color == black)
    return false;
  pkg->clientdata->color = gray;

  if (f_debug & dbg_depcondetail) {
    struct varbuf str_pkgs = VARBUF_INIT;

    for (sol = sofar; sol; sol = sol->prev) {
      varbufaddstr(&str_pkgs, " <- ");
      varbufaddstr(&str_pkgs, sol->pkg->name);
    }
    varbufaddc(&str_pkgs, '\0');
    debug(dbg_depcondetail, "findbreakcyclerecursive %s %s", pkg->name,
          str_pkgs.buf);
    varbuf_destroy(&str_pkgs);
  }
  thislink.pkg= pkg;
  thislink.prev = sofar;
  thislink.possi = NULL;
  for (dep= pkg->installed.depends; dep; dep= dep->next) {
    if (dep->type != dep_depends && dep->type != dep_predepends) continue;
    for (possi= dep->list; possi; possi= possi->next) {
      /* Don't find the same cycles again. */
      if (possi->cyclebreak) continue;
      thislink.possi= possi;
      if (foundcyclebroken(&thislink, sofar, possi->ed,possi))
        return true;
      /* Right, now we try all the providers ... */
      for (providelink= possi->ed->installed.depended;
           providelink;
           providelink = providelink->rev_next) {
        if (providelink->up->type != dep_provides) continue;
        provider= providelink->up->up;
        if (provider->clientdata->istobe == itb_normal) continue;
        /* We don't break things at ‘provides’ links, so ‘possi’ is
         * still the one we use. */
        if (foundcyclebroken(&thislink, sofar, provider, possi))
          return true;
      }
    }
  }
  /* Nope, we didn't find a cycle to break. */
  pkg->clientdata->color = black;
  return false;
}
Beispiel #10
0
const char *versiondescribe
(const struct versionrevision *version,
 enum versiondisplayepochwhen vdew)
{
  static struct varbuf bufs[10];
  static int bufnum=0;

  struct varbuf *vb;

  if (!informativeversion(version)) return _("<none>");

  vb= &bufs[bufnum]; bufnum++; if (bufnum == 10) bufnum= 0;
  varbufreset(vb);
  varbufversion(vb,version,vdew);
  varbufaddc(vb,0);

  return vb->buf;
}
Beispiel #11
0
static bool
linktosameexistingdir(const struct tar_entry *ti, const char *fname,
                      struct varbuf *symlinkfn)
{
  struct stat oldstab, newstab;
  int statr;
  const char *lastslash;

  statr= stat(fname, &oldstab);
  if (statr) {
    if (!(errno == ENOENT || errno == ELOOP || errno == ENOTDIR))
      ohshite(_("failed to stat (dereference) existing symlink `%.250s'"),
              fname);
    return false;
  }
  if (!S_ISDIR(oldstab.st_mode))
    return false;

  /* But is it to the same dir? */
  varbufreset(symlinkfn);
  if (ti->linkname[0] == '/') {
    varbufaddstr(symlinkfn, instdir);
  } else {
    lastslash= strrchr(fname, '/');
    assert(lastslash);
    varbufaddbuf(symlinkfn, fname, (lastslash - fname) + 1);
  }
  varbufaddstr(symlinkfn, ti->linkname);
  varbufaddc(symlinkfn, 0);

  statr= stat(symlinkfn->buf, &newstab);
  if (statr) {
    if (!(errno == ENOENT || errno == ELOOP || errno == ENOTDIR))
      ohshite(_("failed to stat (dereference) proposed new symlink target"
                " `%.250s' for symlink `%.250s'"), symlinkfn->buf, fname);
    return false;
  }
  if (!S_ISDIR(newstab.st_mode))
    return false;
  if (newstab.st_dev != oldstab.st_dev ||
      newstab.st_ino != oldstab.st_ino)
    return false;
  return true;
}
Beispiel #12
0
static char *
deb_field(const char *filename, const char *field)
{
	pid_t pid;
	int p[2];
	struct varbuf buf = VARBUF_INIT;
	char *end;

	m_pipe(p);

	pid = subproc_fork();
	if (pid == 0) {
		/* Child writes to pipe. */
		m_dup2(p[1], 1);
		close(p[0]);
		close(p[1]);

		execlp(BACKEND, BACKEND, "--field", filename, field, NULL);
		ohshite(_("failed to exec dpkg-deb to extract field value"));
	}
	close(p[1]);

	/* Parant reads from pipe. */
	varbufreset(&buf);
	fd_vbuf_copy(p[0], &buf, -1, _("dpkg-deb field extraction"));
	varbufaddc(&buf, '\0');

	close(p[0]);

	subproc_wait_check(pid, _("dpkg-deb field extraction"), PROCPIPE);

	/* Trim down trailing junk. */
	for (end = buf.buf + strlen(buf.buf) - 1; end - buf.buf >= 1; end--)
		if (isspace(*end))
			*end = '\0';
		else
			break;

	return varbuf_detach(&buf);
}
static diversion *
read_diversions()
{
	diversion *diversions = NULL;
	char linebuf[MAXDIVERTFILENAME];

	static struct varbuf vb;
	varbufreset(&vb);
	varbufaddstr(&vb, admindir);
	varbufaddstr(&vb, "/" DIVERSIONSFILE);
	varbufaddc(&vb, 0);

	FILE *file = fopen(vb.buf, "r");
	if (!file)
		ohshite(_("failed to open diversions file"));

	for (;;)
	{
		diversion *next = nfmalloc(sizeof(diversion));

		if (fgets_checked(linebuf, sizeof(linebuf), file, vb.buf) < 0)
			break;
		chomp(linebuf);
		next->contest = strdup(linebuf);
		fgets_must(linebuf, sizeof(linebuf), file, vb.buf);
		chomp(linebuf);
		next->altname = strdup(linebuf);
		fgets_must(linebuf, sizeof(linebuf), file, vb.buf);
		chomp(linebuf);
		next->package = strdup(linebuf);

		next->next = diversions;
		diversions = next;
	}

	fclose(file);

	return invert_diversions_list(diversions);
}
Beispiel #14
0
void describedepcon(struct varbuf *addto, struct dependency *dep) {
  const char *fmt;
  struct varbuf depstr = VARBUF_INIT;

  switch (dep->type) {
  case dep_depends:
    fmt = _("%s depends on %s");
    break;
  case dep_predepends:
    fmt = _("%s pre-depends on %s");
    break;
  case dep_recommends:
    fmt = _("%s recommends %s");
    break;
  case dep_suggests:
    fmt = _("%s suggests %s");
    break;
  case dep_breaks:
    fmt = _("%s breaks %s");
    break;
  case dep_conflicts:
    fmt = _("%s conflicts with %s");
    break;
  case dep_enhances:
    fmt = _("%s enhances %s");
    break;
  default:
    internerr("unknown deptype '%d'", dep->type);
  }

  varbufdependency(&depstr, dep);
  varbufaddc(&depstr, 0);

  varbufprintf(addto, fmt, dep->up->name, depstr.buf);
  varbuf_destroy(&depstr);
}
Beispiel #15
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;
}
Beispiel #16
0
void
pkg_format_show(const struct pkg_format_node *head,
                struct pkginfo *pkg, struct pkginfoperfile *pif)
{
	struct varbuf vb = VARBUF_INIT, fb = VARBUF_INIT, wb = VARBUF_INIT;

	while (head) {
		bool ok;
		char fmt[16];

		ok = false;

		if (head->width > 0)
			snprintf(fmt, 16, "%%%s%zds",
			         ((head->pad) ? "-" : ""), head->width);
		else
			strcpy(fmt, "%s");

		if (head->type == string) {
			varbufprintf(&fb, fmt, head->data);
			ok = true;
		} else if (head->type == field) {
			const struct fieldinfo *fip;

			for (fip = fieldinfos; fip->name; fip++)
				if (strcasecmp(head->data, fip->name) == 0) {
					fip->wcall(&wb, pkg, pif, 0, fip);

					varbufaddc(&wb, '\0');
					varbufprintf(&fb, fmt, wb.buf);
					varbufreset(&wb);
					ok = true;
					break;
				}

			if (!fip->name) {
				const struct arbitraryfield *afp;

				for (afp = pif->arbs; afp; afp = afp->next)
					if (strcasecmp(head->data, afp->name) == 0) {
						varbufprintf(&fb, fmt, afp->value);
						ok = true;
						break;
					}
			}
		}

		if (ok) {
			size_t len = strlen(fb.buf);
			if ((head->width > 0) && (len > head->width))
				len = head->width;
			varbufaddbuf(&vb, fb.buf, len);
		}

		varbufreset(&fb);
		head = head->next;
	}

	if (vb.buf) {
		varbufaddc(&vb, '\0');
		fputs(vb.buf, stdout);
	}

	varbuf_destroy(&wb);
	varbuf_destroy(&fb);
	varbuf_destroy(&vb);
}
void process_archive(const char *filename) {
  static const struct TarFunctions tf = {
    tarfileread,
    tarobject, tarobject, tarobject, tarobject, tarobject
  };

  /* These need to be static so that we can pass their addresses to
   * push_cleanup as arguments to the cu_xxx routines; if an error occurs
   * we unwind the stack before processing the cleanup list, and these
   * variables had better still exist ...
   */
  static int p1[2];
  static char cidirtmpnambuf[L_tmpnam+100];
  static char *cidirbuf = NULL, *reasmbuf = NULL;
  static struct fileinlist *newconffiles, *newfileslist;
  static enum pkgstatus oldversionstatus;
  static struct varbuf infofnvb, fnvb, depprobwhy;
  static struct tarcontext tc;
  
  int c1, r, admindirlen, i, infodirlen, infodirbaseused, status;
  struct pkgiterator *it;
  struct pkginfo *pkg, *otherpkg, *divpkg;
  char *cidir, *cidirrest, *p;
  char *pfilenamebuf, conffilenamebuf[MAXCONFFILENAME];
  char *psize;
  const char *pfilename, *newinfofilename;
  struct fileinlist *newconff, **newconffileslastp;
  struct fileinlist *cfile;
  struct reversefilelistiter rlistit;
  struct conffile *searchconff, **iconffileslastp, *newiconff;
  struct filepackages *packageslump;
  struct dependency *dsearch, *newdeplist, **newdeplistlastp;
  struct dependency *newdep, *dep, *providecheck;
  struct deppossi *psearch, **newpossilastp, *possi, *newpossi, *pdep;
  FILE *conff;
  DIR *dsd;
  struct filenamenode *namenode;
  struct dirent *de;
  struct stat stab, oldfs;
  struct pkg_deconf_list *deconpil, *deconpiltemp;
  
  cleanup_pkg_failed= cleanup_conflictor_failed= 0;
  admindirlen= strlen(admindir);

  for (pfilename= filename ; pfilename && strlen(pfilename) > 30 &&
      strchr(pfilename,'/') != NULL ; pfilename++ )
    pfilename= strchr(pfilename,'/');
  if (pfilename && pfilename != filename) {
    pfilenamebuf= (char *)nfmalloc(strlen(pfilename)+5);
    strcpy(pfilenamebuf,".../");
    strcat(pfilenamebuf,pfilename);
    pfilename= pfilenamebuf;
  } else {
    pfilename= filename;
  }

  if (stat(filename,&stab)) ohshite(_("cannot access archive"));

  if (!f_noact) {
    /* We can't `tentatively-reassemble' packages. */
    if (!reasmbuf) {
      reasmbuf= m_malloc(admindirlen+sizeof(REASSEMBLETMP)+5);
      strcpy(reasmbuf,admindir);
      strcat(reasmbuf,"/" REASSEMBLETMP);
    }
    if (unlink(reasmbuf) && errno != ENOENT)
      ohshite(_("error ensuring `%.250s' doesn't exist"),reasmbuf);
    push_cleanup(cu_pathname, ~0, NULL, 0, 1, (void *)reasmbuf);
    c1= m_fork();
    if (!c1) {
      execlp(SPLITTER, SPLITTER, "-Qao", reasmbuf, filename, NULL);
      ohshite(_("failed to exec dpkg-split to see if it's part of a multiparter"));
    }
    while ((r= waitpid(c1,&status,0)) == -1 && errno == EINTR);
    if (r != c1) { onerr_abort++; ohshite(_("wait for dpkg-split failed")); }
    switch (WIFEXITED(status) ? WEXITSTATUS(status) : -1) {
    case 0:
      /* It was a part - is it complete ? */
      if (!stat(reasmbuf,&stab)) { /* Yes. */
        filename= reasmbuf;
        pfilename= _("reassembled package file");
        break;
      } else if (errno == ENOENT) { /* No.  That's it, we skip it. */
        return;
      }
    case 1:
      /* No, it wasn't a part. */
      break;
    default:
      checksubprocerr(status,SPLITTER,0);
    }
  }
  
  /* Verify the package. */
  if (!f_nodebsig && (stat(DEBSIGVERIFY, &stab)==0)) {
    printf(_("Authenticating %s ...\n"), filename);
    fflush(stdout);
    c1 = m_fork();
    if (!c1) {
      execl(DEBSIGVERIFY, DEBSIGVERIFY, "-q", filename, NULL);
      ohshite(_("failed to execl debsig-verify"));
    } else {
      int status;
      waitpid(c1, &status, 0);
      if (!(WIFEXITED(status) && WEXITSTATUS(status) == 0)) {
	if (! fc_badverify) {
	  ohshit(_("Verification on package %s failed!"), filename);
	} else {
	  fprintf(stderr, _("Verification on package %s failed,\nbut installing anyway as you requested.\n"), filename);
	}
      } else {
	printf(_("passed\n"));
      }
    }
  }
    

  if (f_noact) {
    cidir= cidirtmpnambuf;
    /* We use tmpnam here, not to get a unique filename, but to get a unique directory. */
    if (!tmpnam(cidir)) ohshite(_("unable to get unique filename for control info"));
    strcat(cidir,"/");
  } else {
    /* We want it to be on the same filesystem so that we can
     * use rename(2) to install the postinst &c.
     */
    if (!cidirbuf)
      cidirbuf= m_malloc(admindirlen+sizeof(CONTROLDIRTMP)+MAXCONTROLFILENAME+10);
    cidir= cidirbuf;
    strcpy(cidir,admindir);
    strcat(cidir, "/" CONTROLDIRTMP);
  }
  cidirrest= cidir + strlen(cidir);

  assert(*cidir && cidirrest[-1] == '/');
  cidirrest[-1] = '\0';
  ensure_pathname_nonexisting(cidir); cidirrest[-1]= '/';
  
  push_cleanup(cu_cidir, ~0, NULL, 0, 2, (void *)cidir, (void *)cidirrest);
  c1= m_fork();
  if (!c1) {
    cidirrest[-1] = '\0';
    execlp(BACKEND, BACKEND, "--control", filename, cidir, NULL);
    ohshite(_("failed to exec dpkg-deb to extract control information"));
  }
  waitsubproc(c1,BACKEND " --control",0);
  strcpy(cidirrest,CONTROLFILE);

  parsedb(cidir, pdb_recordavailable | pdb_rejectstatus | pdb_ignorefiles,
          &pkg,NULL,NULL);
  if (!pkg->files) {
    pkg->files= nfmalloc(sizeof(struct filedetails));
    pkg->files->next = NULL;
    pkg->files->name = pkg->files->msdosname = pkg->files->md5sum = NULL;
  }
  /* Always nfmalloc.  Otherwise, we may overwrite some other field(like md5sum). */
  psize = nfmalloc(30);
  sprintf(psize, "%lu", (unsigned long)stab.st_size);
  pkg->files->size = psize;

  if (cipaction->arg == act_avail) {
    printf(_("Recorded info about %s from %s.\n"),pkg->name,pfilename);
    pop_cleanup(ehflag_normaltidy);
    return;
  }

  if (pkg->available.architecture && *pkg->available.architecture &&
      strcmp(pkg->available.architecture,"all") &&
      strcmp(pkg->available.architecture,architecture))
    forcibleerr(fc_architecture,
                _("package architecture (%s) does not match system (%s)"),
                pkg->available.architecture,architecture);
    
  if (!pkg->installed.valid) blankpackageperfile(&pkg->installed);
  assert(pkg->available.valid);

  for (deconpil= deconfigure;
       deconpil;
       deconpil= deconpiltemp) {
    deconpiltemp= deconpil->next;
    free(deconpil);
  }
  deconfigure = NULL;
  clear_istobes();

  if (!wanttoinstall(pkg,&pkg->available.version,1)) {
      pop_cleanup(ehflag_normaltidy);
      return;
  }

  /* Check if anything is installed that we conflict with, or not installed
   * that we need */
  pkg->clientdata->istobe= itb_installnew;

  for (dsearch= pkg->available.depends; dsearch; dsearch= dsearch->next) {
    switch (dsearch->type) {
    case dep_conflicts:
      /* Look for things we conflict with. */
      check_conflict(dsearch, pkg, pfilename);
      break;
    case dep_breaks:
      /* Look for things we break. */
      check_breaks(dsearch, pkg, pfilename);
      break;
    case dep_provides:
      /* Look for things that conflict with what we provide. */
      if (dsearch->list->ed->installed.valid) {
        for (psearch= dsearch->list->ed->installed.depended;
             psearch;
             psearch= psearch->nextrev) {
          if (psearch->up->type != dep_conflicts) continue;
          check_conflict(psearch->up, pkg, pfilename);
        }
      }
      break;
    case dep_suggests:
    case dep_recommends:
    case dep_depends:
    case dep_replaces:
    case dep_enhances:
      /* Ignore these here. */
      break;
    case dep_predepends:
      if (!depisok(dsearch, &depprobwhy, NULL, 1)) {
        varbufaddc(&depprobwhy,0);
        fprintf(stderr, _("dpkg: regarding %s containing %s, pre-dependency problem:\n%s"),
                pfilename, pkg->name, depprobwhy.buf);
        if (!force_depends(dsearch->list))
          ohshit(_("pre-dependency problem - not installing %.250s"),pkg->name);
        warning(_("ignoring pre-dependency problem!"));
      }
    }
  }
  /* Look for things that conflict with us. */
  for (psearch= pkg->installed.depended; psearch; psearch= psearch->nextrev) {
    if (psearch->up->type != dep_conflicts) continue;
    check_conflict(psearch->up, pkg, pfilename);
  }
  
  ensure_allinstfiles_available();
  filesdbinit();
  trig_file_interests_ensure();

  if (pkg->status != stat_notinstalled && pkg->status != stat_configfiles) {
    printf(_("Preparing to replace %s %s (using %s) ...\n"),
           pkg->name,
           versiondescribe(&pkg->installed.version,vdew_nonambig),
           pfilename);
    log_action("upgrade", pkg);
  } else {
    printf(_("Unpacking %s (from %s) ...\n"),pkg->name,pfilename);
    log_action("install", pkg);
  }

  if (f_noact) {
    pop_cleanup(ehflag_normaltidy);
    return;
  }

  /*
   * OK, we're going ahead.
   */

  trig_activate_packageprocessing(pkg);
  strcpy(cidirrest, TRIGGERSCIFILE);
  trig_parse_ci(cidir, NULL, trig_cicb_statuschange_activate, pkg);

  /* Read the conffiles, and copy the hashes across. */
  newconffiles = NULL;
  newconffileslastp = &newconffiles;
  push_cleanup(cu_fileslist, ~0, NULL, 0, 0);
  strcpy(cidirrest,CONFFILESFILE);
  conff= fopen(cidir,"r");
  if (conff) {
    push_cleanup(cu_closefile, ehflag_bombout, NULL, 0, 1, (void *)conff);
    while (fgets(conffilenamebuf,MAXCONFFILENAME-2,conff)) {
      p= conffilenamebuf + strlen(conffilenamebuf);
      assert(p != conffilenamebuf);
      if (p[-1] != '\n')
        ohshit(_("name of conffile (starting `%.250s') is too long (>%d characters)"),
               conffilenamebuf, MAXCONFFILENAME);
      while (p > conffilenamebuf && isspace(p[-1])) --p;
      if (p == conffilenamebuf) continue;
      *p = '\0';
      namenode= findnamenode(conffilenamebuf, 0);
      namenode->oldhash= NEWCONFFILEFLAG;
      newconff= newconff_append(&newconffileslastp, namenode);
      
      /* Let's see if any packages have this file.  If they do we
       * check to see if they listed it as a conffile, and if they did
       * we copy the hash across.  Since (for plain file conffiles,
       * which is the only kind we are supposed to have) there will
       * only be one package which `has' the file, this will usually
       * mean we only look in the package which we're installing now.
       * The `conffiles' data in the status file is ignored when a
       * package isn't also listed in the file ownership database as
       * having that file.  If several packages are listed as owning
       * the file we pick one at random.
       */
      searchconff = NULL;
      for (packageslump= newconff->namenode->packages;
           packageslump;
           packageslump= packageslump->more) {
        for (i=0; i < PERFILEPACKAGESLUMP && packageslump->pkgs[i]; i++) {
          otherpkg= packageslump->pkgs[i];
          debug(dbg_conffdetail,"process_archive conffile `%s' in package %s - conff ?",
                newconff->namenode->name,otherpkg->name);
          for (searchconff= otherpkg->installed.conffiles;
               searchconff && strcmp(newconff->namenode->name,searchconff->name);
               searchconff= searchconff->next)
            debug(dbg_conffdetail,
                  "process_archive conffile `%s' in package %s - conff ? not `%s'",
                  newconff->namenode->name,otherpkg->name,searchconff->name);
          if (searchconff) {
            debug(dbg_conff,"process_archive conffile `%s' package=%s %s hash=%s",
                  newconff->namenode->name,otherpkg->name,
                  otherpkg == pkg ? "same" : "different!",
                  searchconff->hash);
            if (otherpkg == pkg) goto xit_conff_hashcopy_srch;
          }
        }
      }
    xit_conff_hashcopy_srch:
      if (searchconff) {
        newconff->namenode->oldhash= searchconff->hash;
	/* we don't copy `obsolete'; it's not obsolete in the new package */
      } else {
        debug(dbg_conff,"process_archive conffile `%s' no package, no hash",
              newconff->namenode->name);
      }
      newconff->namenode->flags |= fnnf_new_conff;
    }
    if (ferror(conff)) ohshite(_("read error in %.250s"),cidir);
    pop_cleanup(ehflag_normaltidy); /* conff= fopen() */
    if (fclose(conff)) ohshite(_("error closing %.250s"),cidir);
  } else {
    if (errno != ENOENT) ohshite(_("error trying to open %.250s"),cidir);
  }

  /* All the old conffiles are marked with a flag, so that we don't delete
   * them if they seem to disappear completely.
   */
  oldconffsetflags(pkg->installed.conffiles);
  for (i = 0 ; i < cflict_index ; i++) {
    oldconffsetflags(conflictor[i]->installed.conffiles);
  }
  
  oldversionstatus= pkg->status;

  assert(oldversionstatus <= stat_installed);
  debug(dbg_general,"process_archive oldversionstatus=%s",
        statusstrings[oldversionstatus]);
  
  if (oldversionstatus == stat_halfconfigured ||
      oldversionstatus == stat_triggersawaited ||
      oldversionstatus == stat_triggerspending ||
      oldversionstatus == stat_installed) {
    pkg->eflag |= eflag_reinstreq;
    pkg->status= stat_halfconfigured;
    modstatdb_note(pkg);
    push_cleanup(cu_prermupgrade, ~ehflag_normaltidy, NULL, 0, 1, (void *)pkg);
    maintainer_script_alternative(pkg, PRERMFILE, "pre-removal", cidir, cidirrest,
                                  "upgrade", "failed-upgrade");
    pkg->status= stat_unpacked;
    oldversionstatus= stat_unpacked;
    modstatdb_note(pkg);
  }

  for (deconpil= deconfigure; deconpil; deconpil= deconpil->next) {
    struct pkginfo *removing = deconpil->pkg_removal;

    if (removing)
      printf(_("De-configuring %s, to allow removal of %s ...\n"),
             deconpil->pkg->name, removing->name);
    else
      printf(_("De-configuring %s ...\n"), deconpil->pkg->name);

    trig_activate_packageprocessing(deconpil->pkg);
    deconpil->pkg->status= stat_halfconfigured;
    modstatdb_note(deconpil->pkg);

    /* This means that we *either* go and run postinst abort-deconfigure,
     * *or* queue the package for later configure processing, depending
     * on which error cleanup route gets taken.
     */
    push_cleanup(cu_prermdeconfigure, ~ehflag_normaltidy,
                 ok_prermdeconfigure, ehflag_normaltidy,
                 3, (void*)deconpil->pkg, (void*)removing, (void*)pkg);

    if (removing) {
      maintainer_script_installed(deconpil->pkg, PRERMFILE, "pre-removal",
                                  "deconfigure", "in-favour", pkg->name,
                                  versiondescribe(&pkg->available.version,
                                                  vdew_nonambig),
                                  "removing", removing->name,
                                  versiondescribe(&removing->installed.version,
                                                  vdew_nonambig),
                                  NULL);
    } else {
      maintainer_script_installed(deconpil->pkg, PRERMFILE, "pre-removal",
                                  "deconfigure", "in-favour", pkg->name,
                                  versiondescribe(&pkg->available.version,
                                                  vdew_nonambig),
                                  NULL);
    }
  }

  for (i = 0 ; i < cflict_index; i++) {
    if (!(conflictor[i]->status == stat_halfconfigured ||
          conflictor[i]->status == stat_triggersawaited ||
          conflictor[i]->status == stat_triggerspending ||
          conflictor[i]->status == stat_installed)) continue;
    trig_activate_packageprocessing(conflictor[i]);
    conflictor[i]->status= stat_halfconfigured;
    modstatdb_note(conflictor[i]);
    push_cleanup(cu_prerminfavour, ~ehflag_normaltidy, NULL, 0,
                 2,(void*)conflictor[i],(void*)pkg);
    maintainer_script_installed(conflictor[i], PRERMFILE, "pre-removal",
                                "remove", "in-favour", pkg->name,
                                versiondescribe(&pkg->available.version,
                                                vdew_nonambig),
                                NULL);
    conflictor[i]->status= stat_halfinstalled;
    modstatdb_note(conflictor[i]);
  }

  pkg->eflag |= eflag_reinstreq;
  if (pkg->status == stat_notinstalled)
    pkg->installed.version= pkg->available.version;
  pkg->status= stat_halfinstalled;
  modstatdb_note(pkg);
  if (oldversionstatus == stat_notinstalled) {
    push_cleanup(cu_preinstverynew, ~ehflag_normaltidy, NULL, 0,
                 3,(void*)pkg,(void*)cidir,(void*)cidirrest);
    maintainer_script_new(pkg, PREINSTFILE, "pre-installation", cidir, cidirrest,
                          "install", NULL);
  } else if (oldversionstatus == stat_configfiles) {
    push_cleanup(cu_preinstnew, ~ehflag_normaltidy, NULL, 0,
                 3,(void*)pkg,(void*)cidir,(void*)cidirrest);
    maintainer_script_new(pkg, PREINSTFILE, "pre-installation", cidir, cidirrest,
                          "install", versiondescribe(&pkg->installed.version,
                                                     vdew_nonambig),
                          NULL);
  } else {
    push_cleanup(cu_preinstupgrade, ~ehflag_normaltidy, NULL, 0,
                 4,(void*)pkg,(void*)cidir,(void*)cidirrest,(void*)&oldversionstatus);
    maintainer_script_new(pkg, PREINSTFILE, "pre-installation", cidir, cidirrest,
                          "upgrade", versiondescribe(&pkg->installed.version,
                                                     vdew_nonambig),
                          NULL);
    printf(_("Unpacking replacement %.250s ...\n"),pkg->name);
  }
  
  /*
   * Now we unpack the archive, backing things up as we go.
   * For each file, we check to see if it already exists.
   * There are several possibilities:
   * + We are trying to install a non-directory ...
   *  - It doesn't exist.  In this case we simply extract it.
   *  - It is a plain file, device, symlink, &c.  We do an `atomic
   *    overwrite' using link() and rename(), but leave a backup copy.
   *    Later, when we delete the backup, we remove it from any other
   *    packages' lists.
   * -  It is a directory.  In this case it depends on whether we're
   *    trying to install a symlink or something else.
   *   = If we're not trying to install a symlink we move the directory
   *     aside and extract the node.  Later, when we recursively remove
   *     the backed-up directory, we remove it from any other packages'
   *     lists.
   *   = If we are trying to install a symlink we do nothing - ie,
   *     dpkg will never replace a directory tree with a symlink.  This
   *     is to avoid embarrassing effects such as replacing a directory
   *     tree with a link to a link to the original directory tree.
   * + We are trying to install a directory ...
   *  - It doesn't exist.  We create it with the appropriate modes.
   *  - It exists as a directory or a symlink to one.  We do nothing.
   *  - It is a plain file or a symlink (other than to a directory).
   *    We move it aside and create the directory.  Later, when we
   *    delete the backup, we remove it from any other packages' lists.
   *
   *                   Install non-dir   Install symlink   Install dir
   *  Exists not               X               X                X
   *  File/node/symlink       LXR             LXR              BXR
   *  Directory               BXR              -                -
   *
   *    X: extract file/node/link/directory
   *   LX: atomic overwrite leaving backup
   *    B: ordinary backup
   *    R: later remove from other packages' lists
   *    -: do nothing
   * 
   * After we've done this we go through the remaining things in the
   * lists of packages we're trying to remove (including the old
   * version of the current package).  This happens in reverse order,
   * so that we process files before the directories (or symlinks-to-
   * directories) containing them.
   * + If the thing is a conffile then we leave it alone for the purge
   *   operation.
   * + Otherwise, there are several possibilities too:
   *  - The listed thing does not exist.  We ignore it.
   *  - The listed thing is a directory or a symlink to a directory.
   *    We delete it only if it isn't listed in any other package.
   *  - The listed thing is not a directory, but was part of the package
   *    that was upgraded, we check to make sure the files aren't the
   *    same ones from the old package by checking dev/inode
   *  - The listed thing is not a directory or a symlink to one (ie,
   *    it's a plain file, device, pipe, &c, or a symlink to one, or a
   *    dangling symlink).  We delete it.
   * The removed packages' list becomes empty (of course, the new
   * version of the package we're installing will have a new list,
   * which replaces the old version's list).
   *
   * If at any stage we remove a file from a package's list, and the
   * package isn't one we're already processing, and the package's
   * list becomes empty as a result, we `vanish' the package.  This
   * means that we run its postrm with the `disappear' argument, and
   * put the package in the `not-installed' state.  If it had any
   * conffiles, their hashes and ownership will have been transferred
   * already, so we just ignore those and forget about them from the
   * point of view of the disappearing package.
   *
   * NOTE THAT THE OLD POSTRM IS RUN AFTER THE NEW PREINST, since the
   * files get replaced `as we go'.
   */

  m_pipe(p1);
  push_cleanup(cu_closepipe, ehflag_bombout, NULL, 0, 1, (void *)&p1[0]);
  c1= m_fork();
  if (!c1) {
    m_dup2(p1[1],1); close(p1[0]); close(p1[1]);
    execlp(BACKEND, BACKEND, "--fsys-tarfile", filename, NULL);
    ohshite(_("unable to exec dpkg-deb to get filesystem archive"));
  }
  close(p1[1]);
  p1[1] = -1;

  newfileslist = NULL;
  tc.newfilesp = &newfileslist;
  push_cleanup(cu_fileslist, ~0, NULL, 0, 0);
  tc.pkg= pkg;
  tc.backendpipe= p1[0];

  r= TarExtractor((void*)&tc, &tf);
  if (r) {
    if (errno) {
      ohshite(_("error reading dpkg-deb tar output"));
    } else {
      ohshit(_("corrupted filesystem tarfile - corrupted package archive"));
    }
  }
  fd_null_copy(p1[0], -1, _("dpkg-deb: zap possible trailing zeros"));
  close(p1[0]);
  p1[0] = -1;
  waitsubproc(c1,BACKEND " --fsys-tarfile",PROCPIPE);

  if (oldversionstatus == stat_halfinstalled || oldversionstatus == stat_unpacked) {
    /* Packages that were in `installed' and `postinstfailed' have been reduced
     * to `unpacked' by now, by the running of the prerm script.
     */
    pkg->status= stat_halfinstalled;
    modstatdb_note(pkg);
    push_cleanup(cu_postrmupgrade, ~ehflag_normaltidy, NULL, 0, 1, (void *)pkg);
    maintainer_script_alternative(pkg, POSTRMFILE, "post-removal", cidir, cidirrest,
                                  "upgrade", "failed-upgrade");
  }

  /* If anything goes wrong while tidying up it's a bit late to do
   * anything about it.  However, we don't install the new status
   * info yet, so that a future dpkg installation will put everything
   * right (we hope).
   *
   * If something does go wrong later the `conflictor' package will be
   * left in the `removal_failed' state.  Removing or installing it
   * will be impossible if it was required because of the conflict with
   * the package we're installing now and (presumably) the dependency
   * by other packages.  This means that the files it contains in
   * common with this package will hang around until we successfully
   * get this package installed, after which point we can trust the
   * conflicting package's file list, which will have been updated to
   * remove any files in this package.
   */
  push_checkpoint(~ehflag_bombout, ehflag_normaltidy);
  
  /* Now we delete all the files that were in the old version of
   * the package only, except (old or new) conffiles, which we leave
   * alone.
   */
  reversefilelist_init(&rlistit,pkg->clientdata->files);
  while ((namenode= reversefilelist_next(&rlistit))) {
    struct filenamenode *usenode;

    if ((namenode->flags & fnnf_new_conff) ||
        (namenode->flags & fnnf_new_inarchive))
      continue;

    usenode = namenodetouse(namenode, pkg);
    trig_file_activate(usenode, pkg);

    fnamevb.used= fnameidlu;
    varbufaddstr(&fnamevb, usenode->name);
    varbufaddc(&fnamevb,0);

    if (!stat(namenode->name,&stab) && S_ISDIR(stab.st_mode)) {
      debug(dbg_eachfiledetail, "process_archive: %s is a directory",
	    namenode->name);
      if (isdirectoryinuse(namenode,pkg)) continue;
    }

    if (lstat(fnamevb.buf, &oldfs)) {
      if (!(errno == ENOENT || errno == ELOOP || errno == ENOTDIR))
	warning(_("could not stat old file '%.250s' so not deleting it: %s"),
	        fnamevb.buf, strerror(errno));
      continue;
    }
    if (S_ISDIR(oldfs.st_mode)) {
      if (rmdir(fnamevb.buf)) {
	warning(_("unable to delete old directory '%.250s': %s"),
	        namenode->name, strerror(errno));
      } else if ((namenode->flags & fnnf_old_conff)) {
	warning(_("old conffile '%.250s' was an empty directory "
	          "(and has now been deleted)"), namenode->name);
      }
    } else {
      /* Ok, it's an old file, but is it really not in the new package?
       * It might be known by a different name because of symlinks.
       *
       * We need to check to make sure, so we stat the file, then compare
       * it to the new list. If we find a dev/inode match, we assume they
       * are the same file, and leave it alone. NOTE: we don't check in
       * other packages for sanity reasons (we don't want to stat _all_
       * the files on the system).
       *
       * We run down the list of _new_ files in this package. This keeps
       * the process a little leaner. We are only worried about new ones
       * since ones that stayed the same don't really apply here.
       */
      struct fileinlist *sameas = NULL;
      static struct stat empty_stat;
      struct varbuf cfilename = VARBUF_INIT;

      /* If we can't stat the old or new file, or it's a directory,
       * we leave it up to the normal code
       */
      debug(dbg_eachfile, "process_archive: checking %s for same files on "
	    "upgrade/downgrade", fnamevb.buf);

      for (cfile= newfileslist; cfile; cfile= cfile->next) {
	if (!cfile->namenode->filestat) {
	  struct stat tmp_stat;

	  varbufreset(&cfilename);
	  varbufaddstr(&cfilename, instdir);
	  varbufaddc(&cfilename, '/');
	  varbufaddstr(&cfilename, cfile->namenode->name);
	  varbufaddc(&cfilename, '\0');

	  if (lstat(cfilename.buf, &tmp_stat) == 0) {
	    cfile->namenode->filestat = nfmalloc(sizeof(struct stat));
	    memcpy(cfile->namenode->filestat, &tmp_stat, sizeof(struct stat));
	  } else {
	    if (!(errno == ENOENT || errno == ELOOP || errno == ENOTDIR))
	      ohshite(_("unable to stat other new file `%.250s'"),
		      cfile->namenode->name);
	    cfile->namenode->filestat = &empty_stat;
	    continue;
	  }
	}
	if (cfile->namenode->filestat == &empty_stat)
	  continue;
	if (oldfs.st_dev == cfile->namenode->filestat->st_dev &&
	    oldfs.st_ino == cfile->namenode->filestat->st_ino) {
	  if (sameas)
	    warning(_("old file '%.250s' is the same as several new files! "
	              "(both '%.250s' and '%.250s')"), fnamevb.buf,
		    sameas->namenode->name, cfile->namenode->name);
	  sameas= cfile;
	  debug(dbg_eachfile, "process_archive: not removing %s,"
		" since it matches %s", fnamevb.buf, cfile->namenode->name);
	}
      }

      varbuffree(&cfilename);

      if ((namenode->flags & fnnf_old_conff)) {
	if (sameas) {
	  if (sameas->namenode->flags & fnnf_new_conff) {
	    if (!strcmp(sameas->namenode->oldhash, NEWCONFFILEFLAG)) {
	      sameas->namenode->oldhash= namenode->oldhash;
	      debug(dbg_eachfile, "process_archive: old conff %s"
		    " is same as new conff %s, copying hash",
		    namenode->name, sameas->namenode->name);
	    } else {
	      debug(dbg_eachfile, "process_archive: old conff %s"
		    " is same as new conff %s but latter already has hash",
		    namenode->name, sameas->namenode->name);
	    }
	  }
	} else {
	  debug(dbg_eachfile, "process_archive: old conff %s"
		" is disappearing", namenode->name);
	  namenode->flags |= fnnf_obs_conff;
	  newconff_append(&newconffileslastp, namenode);
	  addfiletolist(&tc, namenode);
	}
	continue;
      }
      
      if (sameas)
	continue;

      if (secure_unlink_statted(fnamevb.buf, &oldfs)) {
        warning(_("unable to securely remove old file '%.250s': %s"),
                namenode->name, strerror(errno));
      }

    } /* !S_ISDIR */
  }

  /* OK, now we can write the updated files-in-this package list,
   * since we've done away (hopefully) with all the old junk.
   */
  write_filelist_except(pkg,newfileslist,0);

  /* Trigger interests may have changed.
   * Firstly we go through the old list of interests deleting them.
   * Then we go through the new list adding them.
   */
  strcpy(cidirrest, TRIGGERSCIFILE);
  trig_parse_ci(pkgadminfile(pkg, TRIGGERSCIFILE),
                trig_cicb_interest_delete, NULL, pkg);
  trig_parse_ci(cidir, trig_cicb_interest_add, NULL, pkg);
  trig_file_interests_save();

  /* We also install the new maintainer scripts, and any other
   * cruft that may have come along with the package.  First
   * we go through the existing scripts replacing or removing
   * them as appropriate; then we go through the new scripts
   * (any that are left) and install them.
   */
  debug(dbg_general, "process_archive updating info directory");
  varbufreset(&infofnvb);
  varbufaddstr(&infofnvb,admindir);
  varbufaddstr(&infofnvb, "/" INFODIR);
  infodirlen= infofnvb.used;
  varbufaddc(&infofnvb,0);
  dsd= opendir(infofnvb.buf);
  if (!dsd) ohshite(_("cannot read info directory"));
  push_cleanup(cu_closedir, ~0, NULL, 0, 1, (void *)dsd);
  while ((de = readdir(dsd)) != NULL) {
    debug(dbg_veryverbose, "process_archive info file `%s'", de->d_name);
    if (de->d_name[0] == '.') continue; /* ignore dotfiles, including `.' and `..' */
    p= strrchr(de->d_name,'.'); if (!p) continue; /* ignore anything odd */
    if (strlen(pkg->name) != (size_t)(p-de->d_name) ||
        strncmp(de->d_name,pkg->name,p-de->d_name)) continue;
    debug(dbg_stupidlyverbose, "process_archive info this pkg");
    /* Right do we have one ? */
    p++; /* skip past the full stop */
    if (!strcmp(p,LISTFILE)) continue; /* We do the list separately */
    if (strlen(p) > MAXCONTROLFILENAME)
      ohshit(_("old version of package has overly-long info file name starting `%.250s'"),
             de->d_name);
    infofnvb.used= infodirlen;
    varbufaddstr(&infofnvb,de->d_name);
    varbufaddc(&infofnvb,0);
    strcpy(cidirrest,p);
    if (!rename(cidir,infofnvb.buf)) {
      debug(dbg_scripts, "process_archive info installed %s as %s",
            cidir, infofnvb.buf);
    } else if (errno == ENOENT) {
      /* Right, no new version. */
      if (unlink(infofnvb.buf))
        ohshite(_("unable to remove obsolete info file `%.250s'"),infofnvb.buf);
      debug(dbg_scripts, "process_archive info unlinked %s",infofnvb.buf);
    } else {
      ohshite(_("unable to install (supposed) new info file `%.250s'"),cidir);
    }
  }
  pop_cleanup(ehflag_normaltidy); /* closedir */
  
  *cidirrest = '\0'; /* the directory itself */
  dsd= opendir(cidir);
  if (!dsd) ohshite(_("unable to open temp control directory"));
  push_cleanup(cu_closedir, ~0, NULL, 0, 1, (void *)dsd);
  while ((de= readdir(dsd))) {
    if (strchr(de->d_name,'.')) {
      debug(dbg_scripts,"process_archive tmp.ci script/file `%s' contains dot",
            de->d_name);
      continue;
    }
    if (strlen(de->d_name) > MAXCONTROLFILENAME)
      ohshit(_("package contains overly-long control info file name (starting `%.50s')"),
             de->d_name);
    strcpy(cidirrest,de->d_name);
    /* First we check it's not a directory. */
    if (!rmdir(cidir))
      ohshit(_("package control info contained directory `%.250s'"),cidir);
    else if (errno != ENOTDIR)
      ohshite(_("package control info rmdir of `%.250s' didn't say not a dir"),de->d_name);
    if (!strcmp(de->d_name,CONTROLFILE)) {
      debug(dbg_scripts,"process_archive tmp.ci script/file `%s' is control",cidir);
      continue; /* ignore the control file */
    }
    if (!strcmp(de->d_name,LISTFILE)) {
      warning(_("package %s contained list as info file"), pkg->name);
      continue;
    }
    /* Right, install it */
    newinfofilename= pkgadminfile(pkg,de->d_name);
    if (rename(cidir,newinfofilename))
      ohshite(_("unable to install new info file `%.250s' as `%.250s'"),
              cidir,newinfofilename);
    debug(dbg_scripts,"process_archive tmp.ci script/file `%s' installed as `%s'",
          cidir, newinfofilename);
  }
  pop_cleanup(ehflag_normaltidy); /* closedir */

  /* Update the status database.
   * This involves copying each field across from the `available'
   * to the `installed' half of the pkg structure.
   * For some of the fields we have to do a complicated construction
   * operation; for others we can just copy the value.
   * We tackle the fields in the order they appear, so that
   * we don't miss any out :-).
   * At least we don't have to copy any strings that are referred
   * to, because these are never modified and never freed.
   */

  /* The dependencies are the most difficult.  We have to build
   * a whole new forward dependency tree.  At least the reverse
   * links (linking our deppossi's into the reverse chains)
   * can be done by copy_dependency_links.
   */
  newdeplist = NULL;
  newdeplistlastp = &newdeplist;
  for (dep= pkg->available.depends; dep; dep= dep->next) {
    newdep= nfmalloc(sizeof(struct dependency));
    newdep->up= pkg;
    newdep->next = NULL;
    newdep->list = NULL;
    newpossilastp = &newdep->list;
    for (possi= dep->list; possi; possi= possi->next) {
      newpossi= nfmalloc(sizeof(struct deppossi));
      newpossi->up= newdep;
      newpossi->ed= possi->ed;
      newpossi->next = NULL;
      newpossi->nextrev = newpossi->backrev = NULL;
      newpossi->verrel= possi->verrel;
      if (possi->verrel != dvr_none)
        newpossi->version= possi->version;
      else
        blankversion(&newpossi->version);
      newpossi->cyclebreak= 0;
      *newpossilastp= newpossi;
      newpossilastp= &newpossi->next;
    }
    newdep->type= dep->type;
    *newdeplistlastp= newdep;
    newdeplistlastp= &newdep->next;
  }
  /* Right, now we've replicated the forward tree, we
   * get copy_dependency_links to remove all the old dependency
   * structures from the reverse links and add the new dependency
   * structures in instead.  It also copies the new dependency
   * structure pointer for this package into the right field.
   */
  copy_dependency_links(pkg,&pkg->installed.depends,newdeplist,0);

  /* The `depended' pointer in the structure doesn't represent anything
   * that is actually specified by this package - it's there so we
   * can find out what other packages refer to this one.  So,
   * we don't copy it.  We go straight on to copy the text fields.
   */
  pkg->installed.essential= pkg->available.essential;
  pkg->installed.description= pkg->available.description;
  pkg->installed.maintainer= pkg->available.maintainer;
  pkg->installed.source= pkg->available.source;
  pkg->installed.architecture= pkg->available.architecture;
  pkg->installed.installedsize= pkg->available.installedsize;
  pkg->installed.version= pkg->available.version;
  pkg->installed.origin = pkg->available.origin;                               
  pkg->installed.bugs = pkg->available.bugs;                                   

  /* We have to generate our own conffiles structure. */
  pkg->installed.conffiles = NULL;
  iconffileslastp = &pkg->installed.conffiles;
  for (cfile= newconffiles; cfile; cfile= cfile->next) {
    newiconff= nfmalloc(sizeof(struct conffile));
    newiconff->next = NULL;
    newiconff->name= nfstrsave(cfile->namenode->name);
    newiconff->hash= nfstrsave(cfile->namenode->oldhash);
    newiconff->obsolete= !!(cfile->namenode->flags & fnnf_obs_conff);
    *iconffileslastp= newiconff;
    iconffileslastp= &newiconff->next;
  }

  /* We can just copy the arbitrary fields list, because it is
   * never even rearranged. Phew!
   */
  pkg->installed.arbs= pkg->available.arbs;

  /* Check for disappearing packages:
   * We go through all the packages on the system looking for ones
   * whose files are entirely part of the one we've just unpacked
   * (and which actually *have* some files!).
   *
   * Any that we find are removed - we run the postrm with `disappear'
   * as an argument, and remove their info/... files and status info.
   * Conffiles are ignored (the new package had better do something
   * with them !).
   */
  it= iterpkgstart();
  while ((otherpkg = iterpkgnext(it)) != NULL) {
    ensure_package_clientdata(otherpkg);
    if (otherpkg == pkg ||
        otherpkg->status == stat_notinstalled ||
        otherpkg->status == stat_configfiles ||
	otherpkg->clientdata->istobe == itb_remove ||
        !otherpkg->clientdata->files) continue;
    debug(dbg_veryverbose, "process_archive checking disappearance %s",otherpkg->name);
    assert(otherpkg->clientdata->istobe == itb_normal ||
           otherpkg->clientdata->istobe == itb_deconfigure);
    for (cfile= otherpkg->clientdata->files;
         cfile && !strcmp(cfile->namenode->name,"/.");
         cfile= cfile->next);
    if (!cfile) {
      debug(dbg_stupidlyverbose, "process_archive no non-root, no disappear");
      continue;
    }
    for (cfile= otherpkg->clientdata->files;
         cfile && !filesavespackage(cfile,otherpkg,pkg);
         cfile= cfile->next);
    if (cfile) continue;

    /* So dependency things will give right answers ... */
    otherpkg->clientdata->istobe= itb_remove; 
    debug(dbg_veryverbose, "process_archive disappear checking dependencies");
    for (pdep= otherpkg->installed.depended;
         pdep;
         pdep= pdep->nextrev) {
      if (pdep->up->type != dep_depends && pdep->up->type != dep_predepends &&
          pdep->up->type != dep_recommends) continue;
      if (depisok(pdep->up, &depprobwhy, NULL, 0))
        continue;
      varbufaddc(&depprobwhy,0);
      debug(dbg_veryverbose,"process_archive cannot disappear: %s",depprobwhy.buf);
      break;
    }
    if (!pdep) {
      /* If we haven't found a reason not to yet, let's look some more. */
      for (providecheck= otherpkg->installed.depends;
           providecheck;
           providecheck= providecheck->next) {
        if (providecheck->type != dep_provides) continue;
        for (pdep= providecheck->list->ed->installed.depended;
             pdep;
             pdep= pdep->nextrev) {
          if (pdep->up->type != dep_depends && pdep->up->type != dep_predepends &&
              pdep->up->type != dep_recommends)
            continue;
          if (depisok(pdep->up, &depprobwhy, NULL, 0))
            continue;
          varbufaddc(&depprobwhy,0);
          debug(dbg_veryverbose,"process_archive cannot disappear (provides %s): %s",
                providecheck->list->ed->name, depprobwhy.buf);
          goto break_from_both_loops_at_once;
        }
      }
    break_from_both_loops_at_once:;
    }
    otherpkg->clientdata->istobe= itb_normal;
    if (pdep) continue;

    printf(_("(Noting disappearance of %s, which has been completely replaced.)\n"),
           otherpkg->name);
    debug(dbg_general, "process_archive disappearing %s",otherpkg->name);
    /* No, we're disappearing it.  This is the wrong time to go and
     * run maintainer scripts and things, as we can't back out.  But
     * what can we do ?  It has to be run this late.
     */
    trig_activate_packageprocessing(otherpkg);
    maintainer_script_installed(otherpkg, POSTRMFILE,
                                "post-removal script (for disappearance)",
                                "disappear", pkg->name, 
                                versiondescribe(&pkg->available.version,
                                                vdew_nonambig),
                                NULL);

    /* OK, now we delete all the stuff in the `info' directory .. */
    varbufreset(&fnvb);
    varbufaddstr(&fnvb,admindir);
    varbufaddstr(&fnvb,"/" INFODIR);
    infodirbaseused= fnvb.used;
    varbufaddc(&fnvb,0);
    dsd= opendir(fnvb.buf); if (!dsd) ohshite(_("cannot read info directory"));
    push_cleanup(cu_closedir, ~0, NULL, 0, 1, (void *)dsd);

    debug(dbg_general, "process_archive disappear cleaning info directory");
      
    while ((de = readdir(dsd)) != NULL) {
      debug(dbg_veryverbose, "process_archive info file `%s'", de->d_name);
      if (de->d_name[0] == '.') continue;
      p= strrchr(de->d_name,'.'); if (!p) continue;
      if (strlen(otherpkg->name) != (size_t)(p-de->d_name) ||
          strncmp(de->d_name,otherpkg->name,p-de->d_name)) continue;
      debug(dbg_stupidlyverbose, "process_archive info this pkg");
      fnvb.used= infodirbaseused;
      varbufaddstr(&fnvb,de->d_name);
      varbufaddc(&fnvb,0);
      if (unlink(fnvb.buf))
        ohshite(_("unable to delete disappearing control info file `%.250s'"),fnvb.buf);
      debug(dbg_scripts, "process_archive info unlinked %s",fnvb.buf);
    }
    pop_cleanup(ehflag_normaltidy); /* closedir */
    
    otherpkg->status= stat_notinstalled;
    otherpkg->want = want_unknown;
    otherpkg->eflag = eflag_ok;

    blankversion(&otherpkg->configversion);
    blankpackageperfile(&otherpkg->installed);

    otherpkg->clientdata->fileslistvalid= 0;

    modstatdb_note(otherpkg);

  } /* while (otherpkg= ... */
  iterpkgend(it);
  
  /* Delete files from any other packages' lists.
   * We have to do this before we claim this package is in any
   * sane kind of state, as otherwise we might delete by mistake
   * a file that we overwrote, when we remove the package which
   * had the version we overwrote.  To prevent this we make
   * sure that we don't claim this package is OK until we
   * have claimed `ownership' of all its files.
   */
  for (cfile= newfileslist; cfile; cfile= cfile->next) {
    if (!(cfile->namenode->flags & fnnf_elide_other_lists)) continue;
    if (cfile->namenode->divert && cfile->namenode->divert->useinstead) {
      divpkg= cfile->namenode->divert->pkg;
      if (divpkg == pkg) {
        debug(dbg_eachfile,
              "process_archive not overwriting any `%s' (overriding, `%s')",
              cfile->namenode->name, cfile->namenode->divert->useinstead->name);
        continue;
      } else {
        debug(dbg_eachfile,
              "process_archive looking for overwriting `%s' (overridden by %s)",
              cfile->namenode->name, divpkg ? divpkg->name : "<local>");
      }        
    } else {
      divpkg = NULL;
      debug(dbg_eachfile, "process_archive looking for overwriting `%s'",
            cfile->namenode->name);
    }
    for (packageslump= cfile->namenode->packages;
         packageslump;
         packageslump= packageslump->more) {
      for (i=0; i < PERFILEPACKAGESLUMP && packageslump->pkgs[i]; i++) {
        otherpkg= packageslump->pkgs[i];
        debug(dbg_eachfiledetail, "process_archive ... found in %s\n",otherpkg->name);
        /* If !fileslistvalid then it's one of the disappeared packages above
         * and we don't bother with it here, clearly.
         */
        if (otherpkg == pkg || !otherpkg->clientdata->fileslistvalid) continue;
        if (otherpkg == divpkg) {
          debug(dbg_eachfiledetail, "process_archive ... diverted, skipping\n");
          continue;
        }

        /* Found one.  We delete remove the list entry for this file,
         * (and any others in the same package) and then mark the package
         * as requiring a reread.
         */
        write_filelist_except(otherpkg, otherpkg->clientdata->files, 1);
        ensure_package_clientdata(otherpkg);
        debug(dbg_veryverbose, "process_archive overwrote from %s",otherpkg->name);
      }
    }
  }

  /* Right, the package we've unpacked is now in a reasonable state.
   * The only thing that we have left to do with it is remove
   * backup files, and we can leave the user to fix that if and when
   * it happens (we leave the reinstall required flag, of course).
   */
  pkg->status= stat_unpacked;
  modstatdb_note(pkg);
  
  /* Now we delete all the backup files that we made when
   * extracting the archive - except for files listed as conffiles
   * in the new package.
   * This time we count it as an error if something goes wrong.
   *
   * Note that we don't ever delete things that were in the old
   * package as a conffile and don't appear at all in the new.
   * They stay recorded as obsolete conffiles and will eventually
   * (if not taken over by another package) be forgotten.
   */
  for (cfile= newfileslist; cfile; cfile= cfile->next) {
    if (cfile->namenode->flags & fnnf_new_conff) continue;
    fnametmpvb.used= fnameidlu;
    varbufaddstr(&fnametmpvb,namenodetouse(cfile->namenode,pkg)->name);
    varbufaddstr(&fnametmpvb,DPKGTEMPEXT);
    varbufaddc(&fnametmpvb,0);
    ensure_pathname_nonexisting(fnametmpvb.buf);
  }

  /* OK, we're now fully done with the main package.
   * This is quite a nice state, so we don't unwind past here.
   */
  pkg->eflag = eflag_ok;
  modstatdb_note(pkg);
  push_checkpoint(~ehflag_bombout, ehflag_normaltidy);

  /* Only the removal of the conflictor left to do.
   * The files list for the conflictor is still a little inconsistent in-core,
   * as we have not yet updated the filename->packages mappings; however,
   * the package->filenames mapping is 
   */
  for (i = 0 ; i < cflict_index ; i++) {
    /* We need to have the most up-to-date info about which files are what ... */
    ensure_allinstfiles_available();
    removal_bulk(conflictor[i]);
  }

  if (cipaction->arg == act_install) add_to_queue(pkg);
}
void
show1package(const struct lstitem *head, struct pkginfo *pkg)
{
	struct varbuf vb = VARBUF_INIT, fb = VARBUF_INIT, wb = VARBUF_INIT;

	/* Make sure we have package info available, even if it's all empty. */
	if (!pkg->installed.valid)
		blankpackageperfile(&pkg->installed);

	while (head) {
		int ok;
		char fmt[16];

		ok = 0;

		if (head->width > 0)
			snprintf(fmt, 16, "%%%s%zds",
			         ((head->pad) ? "-" : ""), head->width);
		else
			strcpy(fmt, "%s");

		if (head->type == string) {
			varbufprintf(&fb, fmt, head->data);
			ok = 1;
		} else if (head->type == field) {
			const struct fieldinfo *fip;

			for (fip = fieldinfos; fip->name; fip++)
				if (strcasecmp(head->data, fip->name) == 0) {
					fip->wcall(&wb, pkg, &pkg->installed, 0, fip);

					varbufaddc(&wb, '\0');
					varbufprintf(&fb, fmt, wb.buf);
					varbufreset(&wb);
					ok = 1;
					break;
				}

			if (!fip->name && pkg->installed.valid) {
				const struct arbitraryfield *afp;

				for (afp = pkg->installed.arbs; afp; afp = afp->next)
					if (strcasecmp(head->data, afp->name) == 0) {
						varbufprintf(&fb, fmt, afp->value);
						ok = 1;
						break;
					}
			}
		}

		if (ok) {
			size_t len = strlen(fb.buf);
			if ((head->width > 0) && (len > head->width))
				len = head->width;
			varbufaddbuf(&vb, fb.buf, len);
		}

		varbufreset(&fb);
		head = head->next;
	}

	if (vb.buf) {
		varbufaddc(&vb, '\0');
		fputs(vb.buf, stdout);
	}

	varbuffree(&wb);
	varbuffree(&fb);
	varbuffree(&vb);
}
Beispiel #19
0
void setselections(const char *const *argv) {
  const struct namevalue *nvp;
  struct pkginfo *pkg;
  const char *e;
  int c, lno;
  struct varbuf namevb = VARBUF_INIT;
  struct varbuf selvb = VARBUF_INIT;

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

  modstatdb_init(admindir,msdbrw_write);

  lno= 1;
  for (;;) {
    varbufreset(&namevb);
    varbufreset(&selvb);
    do { c= getchar(); if (c == '\n') lno++; } while (c != EOF && isspace(c));
    if (c == EOF) break;
    if (c == '#') {
      do { c= getchar(); if (c == '\n') lno++; } while (c != EOF && c != '\n');
      continue;
    }
    while (!isspace(c)) {
      varbufaddc(&namevb,c);
      c= getchar();
      if (c == EOF) ohshit(_("unexpected eof in package name at line %d"),lno);
      if (c == '\n') ohshit(_("unexpected end of line in package name at line %d"),lno);
    }
    while (c != EOF && isspace(c)) {
      c= getchar();
      if (c == EOF) ohshit(_("unexpected eof after package name at line %d"),lno);
      if (c == '\n') ohshit(_("unexpected end of line after package name at line %d"),lno);
    }
    while (c != EOF && !isspace(c)) {
      varbufaddc(&selvb,c);
      c= getchar();
    }
    while (c != EOF && c != '\n') {
      c= getchar();
      if (!isspace(c))
        ohshit(_("unexpected data after package and selection at line %d"),lno);
    }
    varbufaddc(&namevb,0);
    varbufaddc(&selvb,0);
    e = pkg_name_is_illegal(namevb.buf, NULL);
    if (e) ohshit(_("illegal package name at line %d: %.250s"),lno,e);

    nvp = namevalue_find_by_name(wantinfos, selvb.buf);
    if (nvp == NULL)
      ohshit(_("unknown wanted status at line %d: %.250s"), lno, selvb.buf);
    pkg = pkg_db_find(namevb.buf);
    pkg->want= nvp->value;
    if (c == EOF) break;
    lno++;
  }
  if (ferror(stdin)) ohshite(_("read error on standard input"));
  modstatdb_shutdown();
  varbuf_destroy(&namevb);
  varbuf_destroy(&selvb);
}
Beispiel #20
0
void check_conflict(struct dependency *dep, struct pkginfo *pkg,
                    const char *pfilename) {
  struct pkginfo *fixbyrm;
  struct deppossi *pdep, flagdeppossi;
  struct varbuf conflictwhy = VARBUF_INIT, removalwhy = VARBUF_INIT;
  struct dependency *providecheck;

  fixbyrm = NULL;
  if (depisok(dep, &conflictwhy, &fixbyrm, false)) {
    varbuf_destroy(&conflictwhy);
    varbuf_destroy(&removalwhy);
    return;
  }
  if (fixbyrm) {
    ensure_package_clientdata(fixbyrm);
    if (fixbyrm->clientdata->istobe == itb_installnew) {
      fixbyrm= dep->up;
      ensure_package_clientdata(fixbyrm);
    }
    if (((pkg->available.essential && fixbyrm->installed.essential) ||
         (((fixbyrm->want != want_install && fixbyrm->want != want_hold) ||
           does_replace(pkg, &pkg->available, fixbyrm, &fixbyrm->installed)) &&
          (!fixbyrm->installed.essential || fc_removeessential)))) {
      assert(fixbyrm->clientdata->istobe == itb_normal || fixbyrm->clientdata->istobe == itb_deconfigure);
      fixbyrm->clientdata->istobe= itb_remove;
      fprintf(stderr, _("dpkg: considering removing %s in favour of %s ...\n"),
              fixbyrm->name, pkg->name);
      if (!(fixbyrm->status == stat_installed ||
            fixbyrm->status == stat_triggerspending ||
            fixbyrm->status == stat_triggersawaited)) {
        fprintf(stderr,
                _("%s is not properly installed - ignoring any dependencies on it.\n"),
                fixbyrm->name);
        pdep = NULL;
      } else {
        for (pdep= fixbyrm->installed.depended;
             pdep;
             pdep = pdep->rev_next) {
          if (pdep->up->type != dep_depends && pdep->up->type != dep_predepends)
            continue;
          if (depisok(pdep->up, &removalwhy, NULL, false))
            continue;
          varbufaddc(&removalwhy,0);
          if (!try_remove_can(pdep,fixbyrm,removalwhy.buf))
            break;
        }
        if (!pdep) {
          /* If we haven't found a reason not to yet, let's look some more. */
          for (providecheck= fixbyrm->installed.depends;
               providecheck;
               providecheck= providecheck->next) {
            if (providecheck->type != dep_provides) continue;
            for (pdep= providecheck->list->ed->installed.depended;
                 pdep;
                 pdep = pdep->rev_next) {
              if (pdep->up->type != dep_depends && pdep->up->type != dep_predepends)
                continue;
              if (depisok(pdep->up, &removalwhy, NULL, false))
                continue;
              varbufaddc(&removalwhy,0);
              fprintf(stderr, _("dpkg"
                      ": may have trouble removing %s, as it provides %s ...\n"),
                      fixbyrm->name, providecheck->list->ed->name);
              if (!try_remove_can(pdep,fixbyrm,removalwhy.buf))
                goto break_from_both_loops_at_once;
            }
          }
        break_from_both_loops_at_once:;
        }
      }
      if (!pdep && skip_due_to_hold(fixbyrm)) {
        pdep= &flagdeppossi;
      }
      if (!pdep && (fixbyrm->eflag & eflag_reinstreq)) {
        if (fc_removereinstreq) {
          fprintf(stderr, _("dpkg: package %s requires reinstallation, but will"
                  " remove anyway as you requested.\n"), fixbyrm->name);
        } else {
          fprintf(stderr, _("dpkg: package %s requires reinstallation, "
                  "will not remove.\n"), fixbyrm->name);
          pdep= &flagdeppossi;
        }
      }
      if (!pdep) {
	if (cflict_index >= MAXCONFLICTORS)
	  ohshit(_("package %s has too many Conflicts/Replaces pairs"),
		 pkg->name);

        /* This conflict is OK - we'll remove the conflictor. */
	conflictor[cflict_index++]= fixbyrm;
        varbuf_destroy(&conflictwhy); varbuf_destroy(&removalwhy);
        fprintf(stderr, _("dpkg: yes, will remove %s in favour of %s.\n"),
                fixbyrm->name, pkg->name);
        return;
      }
      /* Put it back. */
      fixbyrm->clientdata->istobe = itb_normal;
    }
  }
  varbufaddc(&conflictwhy,0);
  fprintf(stderr, _("dpkg: regarding %s containing %s:\n%s"),
          pfilename, pkg->name, conflictwhy.buf);
  if (!force_conflicts(dep->list))
    ohshit(_("conflicting packages - not installing %.250s"),pkg->name);
  warning(_("ignoring conflict, may proceed anyway!"));
  varbuf_destroy(&conflictwhy);

  return;
}
Beispiel #21
0
void archivefiles(const char *const *argv) {
  const char *volatile thisarg;
  const char *const *volatile argp;
  jmp_buf ejbuf;
  int pi[2], fc, nfiles, c, i, r;
  FILE *pf;
  static struct varbuf findoutput;
  const char **arglist;
  char *p;

  trigproc_install_hooks();

  modstatdb_init(admindir,
                 f_noact ?                     msdbrw_readonly
               : cipaction->arg == act_avail ? msdbrw_write
               : fc_nonroot ?                  msdbrw_write
               :                               msdbrw_needsuperuser);

  checkpath();
  log_message("startup archives %s", cipaction->olong);

  if (f_recursive) {
    if (!*argv)
      badusage(_("--%s --recursive needs at least one path argument"),cipaction->olong);

    m_pipe(pi);
    fc = subproc_fork();
    if (!fc) {
      struct command cmd;
      const char *const *ap;

      m_dup2(pi[1],1); close(pi[0]); close(pi[1]);

      command_init(&cmd, FIND, _("find for dpkg --recursive"));
      command_add_args(&cmd, FIND, "-L", NULL);

      for (ap = argv; *ap; ap++) {
        if (strchr(FIND_EXPRSTARTCHARS,(*ap)[0])) {
          char *a;
          a= m_malloc(strlen(*ap)+10);
          strcpy(a,"./");
          strcat(a,*ap);
          command_add_arg(&cmd, a);
        } else {
          command_add_arg(&cmd, (const char *)*ap);
        }
      }

      command_add_args(&cmd, "-name", "*.deb", "-type", "f", "-print0", NULL);

      command_exec(&cmd);
    }
    close(pi[1]);

    nfiles= 0;
    pf= fdopen(pi[0],"r");  if (!pf) ohshite(_("failed to fdopen find's pipe"));
    varbufreset(&findoutput);
    while ((c= fgetc(pf)) != EOF) {
      varbufaddc(&findoutput,c);
      if (!c) nfiles++;
    }
    if (ferror(pf)) ohshite(_("error reading find's pipe"));
    if (fclose(pf)) ohshite(_("error closing find's pipe"));
    r = subproc_wait_check(fc, "find", PROCNOERR);
    if (r != 0)
      ohshit(_("find for --recursive returned unhandled error %i"),r);

    if (!nfiles)
      ohshit(_("searched, but found no packages (files matching *.deb)"));

    varbufaddc(&findoutput,0);
    varbufaddc(&findoutput,0);

    arglist= m_malloc(sizeof(char*)*(nfiles+1));
    p= findoutput.buf; i=0;
    while (*p) {
      arglist[i++]= p;
      while (*p++ != '\0') ;
    }
    arglist[i] = NULL;
    argp= arglist;
  } else {
    if (!*argv) badusage(_("--%s needs at least one package archive file argument"),
                         cipaction->olong);
    argp= argv;
  }

  currenttime = time(NULL);

  /* Initialize fname variables contents. */

  varbufreset(&fnamevb);
  varbufreset(&fnametmpvb);
  varbufreset(&fnamenewvb);

  varbufaddstr(&fnamevb,instdir); varbufaddc(&fnamevb,'/');
  varbufaddstr(&fnametmpvb,instdir); varbufaddc(&fnametmpvb,'/');
  varbufaddstr(&fnamenewvb,instdir); varbufaddc(&fnamenewvb,'/');
  fnameidlu= fnamevb.used;

  ensure_diversions();
  ensure_statoverrides();

  while ((thisarg = *argp++) != NULL) {
    if (setjmp(ejbuf)) {
      pop_error_context(ehflag_bombout);
      if (abort_processing)
        break;
      continue;
    }
    push_error_context_jump(&ejbuf, print_error_perpackage, thisarg);

    process_archive(thisarg);
    onerr_abort++;
    m_output(stdout, _("<standard output>"));
    m_output(stderr, _("<standard error>"));
    onerr_abort--;

    pop_error_context(ehflag_normaltidy);
  }

  switch (cipaction->arg) {
  case act_install:
  case act_configure:
  case act_triggers:
  case act_remove:
  case act_purge:
    process_queue();
  case act_unpack:
  case act_avail:
    break;
  default:
    internerr("unknown action '%d'", cipaction->arg);
  }

  trigproc_run_deferred();
  modstatdb_shutdown();
}
static void
checkrename(const char *rsrc, const char *rdest)
{
	struct stat ssrc;
	struct stat sdest;
	int has_src;
	int has_dest;
	static struct varbuf tmpfilename;
	int tmpfile;

	if (!dorename_) return;

	has_src = !lstat(rsrc, &ssrc);
	if (!has_src && errno != ENOENT)
		ohshite(_("cannot stat old name '%s'"), rsrc);
	has_dest = !lstat(rdest, &sdest);
	if (!has_dest && errno != ENOENT)
		ohshite(_("cannot state new name '%s'"), rdest);

	/* Unfortunately we have to check for write access in both places, just
	 * having +w is not enough, since people do mount things RO, and we need
	 * to fail before we start mucking around with things. So we open a file
	 * with the same name as the diversions but with an extension that
	 * (hopefully) wont overwrite anything. If it succeeds, we assume a
	 * writable filesystem.
	 */

	varbufreset(&tmpfilename);
	varbufaddstr(&tmpfilename, rsrc);
	varbufaddstr(&tmpfilename, ".dpkg-devert.tmp");
	varbufaddc(&tmpfilename, 0);

	tmpfile = open(tmpfilename.buf, O_WRONLY | O_CREAT);
	if (tmpfile != -1) {
		unlink(tmpfilename.buf);
		close(tmpfile);
	}
	else if (errno == ENOENT) {
		dorename_ = 0;
		/* If the source file is not present and we are not going to do
		   the rename anyway there's no point in checking the target. */
		return;
	}
	else
		ohshite(_("error checking '%s'"), rsrc);

	varbufreset(&tmpfilename);
	varbufaddstr(&tmpfilename, rdest);
	varbufaddstr(&tmpfilename, ".dpkg-devert.tmp");
	varbufaddc(&tmpfilename, 0);

	tmpfile = open(tmpfilename.buf, O_WRONLY | O_CREAT);
	if (tmpfile != -1) {
		unlink(tmpfilename.buf);
		close(tmpfile);
	}
	else
		ohshite(_("error checking '%s'"), rdest);

	if (has_src && has_dest &&
	   !(ssrc.st_dev == sdest.st_dev && ssrc.st_ino == sdest.st_ino)) {
		ohshite(_("rename involves overwriting '%s' with \n"
			  "  different file '%s', not allowed"), rdest, rsrc);
	}
}
Beispiel #23
0
void
ensure_diversions(void)
{
	static struct varbuf vb;

	struct stat stab1, stab2;
	char linebuf[MAXDIVERTFILENAME];
	FILE *file;
	struct diversion *ov, *oicontest, *oialtname;

	varbufreset(&vb);
	varbufaddstr(&vb, admindir);
	varbufaddstr(&vb, "/" DIVERSIONSFILE);
	varbufaddc(&vb, 0);

	onerr_abort++;

	file = fopen(vb.buf,"r");
	if (!file) {
		if (errno != ENOENT)
			ohshite(_("failed to open diversions file"));
		if (!diversionsfile) {
			onerr_abort--;
			return;
		}
	} else if (diversionsfile) {
		if (fstat(fileno(diversionsfile), &stab1))
			ohshite(_("failed to fstat previous diversions file"));
		if (fstat(fileno(file), &stab2))
			ohshite(_("failed to fstat diversions file"));
		if (stab1.st_dev == stab2.st_dev &&
		    stab1.st_ino == stab2.st_ino) {
			fclose(file);
			onerr_abort--;
			return;
		}
	}
	if (diversionsfile)
		fclose(diversionsfile);
	diversionsfile = file;
	setcloexec(fileno(diversionsfile), vb.buf);

	for (ov = diversions; ov; ov = ov->next) {
		ov->useinstead->divert->camefrom->divert = NULL;
		ov->useinstead->divert = NULL;
	}
	diversions = NULL;
	if (!file) {
		onerr_abort--;
		return;
	}

	while (fgets_checked(linebuf, sizeof(linebuf), file, vb.buf) >= 0) {
		oicontest = nfmalloc(sizeof(struct diversion));
		oialtname = nfmalloc(sizeof(struct diversion));

		oialtname->camefrom = findnamenode(linebuf, 0);
		oialtname->useinstead = NULL;

		fgets_must(linebuf, sizeof(linebuf), file, vb.buf);
		oicontest->useinstead = findnamenode(linebuf, 0);
		oicontest->camefrom = NULL;

		fgets_must(linebuf, sizeof(linebuf), file, vb.buf);
		oicontest->pkg = oialtname->pkg = strcmp(linebuf, ":") ?
		                                  pkg_db_find(linebuf) : NULL;

		if (oialtname->camefrom->divert ||
		    oicontest->useinstead->divert)
			ohshit(_("conflicting diversions involving `%.250s' or `%.250s'"),
			       oialtname->camefrom->name, oicontest->useinstead->name);

		oialtname->camefrom->divert = oicontest;
		oicontest->useinstead->divert = oialtname;

		oicontest->next = diversions;
		diversions = oicontest;
	}

	onerr_abort--;
}
Beispiel #24
0
void commandfd(const char *const *argv) {
  jmp_buf ejbuf;
  struct varbuf linevb = VARBUF_INIT;
  const char * pipein;
  const char **newargs = NULL;
  char *ptr, *endptr;
  FILE *in;
  unsigned long infd;
  int c, lno, i;
  bool skipchar;
  void (*actionfunction)(const char *const *argv);

  pipein = *argv++;
  if (pipein == NULL)
    badusage(_("--command-fd takes one argument, not zero"));
  if (*argv)
    badusage(_("--command-fd only takes one argument"));
  errno = 0;
  infd = strtoul(pipein, &endptr, 10);
  if (pipein == endptr || *endptr || infd > INT_MAX)
    ohshite(_("invalid integer for --%s: `%.250s'"), "command-fd", pipein);
  if ((in= fdopen(infd, "r")) == NULL)
    ohshite(_("couldn't open `%i' for stream"), (int) infd);

  if (setjmp(ejbuf)) { /* expect warning about possible clobbering of argv */
    error_unwind(ehflag_bombout); exit(2);
  }

  for (;;) {
    bool mode = false;
    int argc= 1;
    lno= 0;
    push_error_handler(&ejbuf, print_error_fatal, NULL);

    do { c= getc(in); if (c == '\n') lno++; } while (c != EOF && isspace(c));
    if (c == EOF) break;
    if (c == '#') {
      do { c= getc(in); if (c == '\n') lno++; } while (c != EOF && c != '\n');
      continue;
    }
    varbufreset(&linevb);
    do {
      varbufaddc(&linevb,c);
      c= getc(in);
      if (c == '\n') lno++;
      if (isspace(c)) argc++;  /* This isn't fully accurate, but overestimating can't hurt. */
    } while (c != EOF && c != '\n');
    if (c == EOF) ohshit(_("unexpected eof before end of line %d"),lno);
    if (!argc) continue;
    varbufaddc(&linevb,0);
    newargs = m_realloc(newargs, sizeof(const char *) * (argc + 1));
    argc= 1;
    ptr= linevb.buf;
    endptr= ptr + linevb.used;
    skipchar = false;
    while(ptr < endptr) {
      if (skipchar) {
	skipchar = false;
      } else if (*ptr == '\\') {
	memmove(ptr, (ptr+1), (linevb.used-(linevb.buf - ptr)-1));
	endptr--;
	skipchar = true;
	continue;
      } else if (isspace(*ptr)) {
	if (mode == true) {
	  *ptr = '\0';
	  mode = false;
	}
      } else {
	if (mode == false) {
	  newargs[argc]= ptr;
	  argc++;
	  mode = true;
	}
      }
      ptr++;
    }
    *ptr = '\0';
    newargs[argc++] = NULL;

/* We strdup each argument, but never free it, because the error messages
 * contain references back to these strings.  Freeing them, and reusing
 * the memory, would make those error messages confusing, to say the
 * least.
 */
    for(i=1;i<argc;i++)
      if (newargs[i])
        newargs[i] = m_strdup(newargs[i]);

    cipaction= NULL;
    myopt((const char *const**)&newargs,cmdinfos);
    if (!cipaction) badusage(_("need an action option"));

    actionfunction= (void (*)(const char* const*))cipaction->farg;
    actionfunction(newargs);
    set_error_display(NULL, NULL);
    error_unwind(ehflag_normaltidy);
  }
}
Beispiel #25
0
/**
 * Print a single package which:
 *  (a) is the target of one or more relevant predependencies.
 *  (b) has itself no unsatisfied pre-dependencies.
 *
 * If such a package is present output is the Packages file entry,
 * which can be massaged as appropriate.
 *
 * Exit status:
 *  0 = a package printed, OK
 *  1 = no suitable package available
 *  2 = error
 */
void predeppackage(const char *const *argv) {
  static struct varbuf vb;

  struct pkgiterator *it;
  struct pkginfo *pkg = NULL, *startpkg, *trypkg;
  struct dependency *dep;
  struct deppossi *possi, *provider;

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

  modstatdb_init(admindir,msdbrw_readonly);
  /* We use clientdata->istobe to detect loops. */
  clear_istobes();

  dep = NULL;
  it = pkg_db_iter_new();
  while (!dep && (pkg = pkg_db_iter_next(it))) {
    /* Ignore packages user doesn't want. */
    if (pkg->want != want_install)
      continue;
    /* Ignore packages not available. */
    if (!pkg->files)
      continue;
    pkg->clientdata->istobe= itb_preinstall;
    for (dep= pkg->available.depends; dep; dep= dep->next) {
      if (dep->type != dep_predepends) continue;
      if (depisok(dep, &vb, NULL, true))
        continue;
      /* This will leave dep non-NULL, and so exit the loop. */
      break;
    }
    pkg->clientdata->istobe= itb_normal;
    /* If dep is NULL we go and get the next package. */
  }
  pkg_db_iter_free(it);

  if (!dep)
    exit(1); /* Not found. */
  assert(pkg);
  startpkg= pkg;
  pkg->clientdata->istobe= itb_preinstall;

  /* OK, we have found an unsatisfied predependency.
   * Now go and find the first thing we need to install, as a first step
   * towards satisfying it. */
  do {
    /* We search for a package which would satisfy dep, and put it in pkg. */
    for (possi = dep->list, pkg = NULL;
         !pkg && possi;
         possi=possi->next) {
      trypkg= possi->ed;
      if (trypkg->files && versionsatisfied(&trypkg->available,possi)) {
        if (trypkg->clientdata->istobe == itb_normal) { pkg= trypkg; break; }
      }
      if (possi->verrel != dvr_none) continue;
      for (provider=possi->ed->available.depended;
           !pkg && provider;
           provider=provider->next) {
        if (provider->up->type != dep_provides) continue;
        trypkg= provider->up->up;
        if (!trypkg->files)
          continue;
        if (trypkg->clientdata->istobe == itb_normal) { pkg= trypkg; break; }
      }
    }
    if (!pkg) {
      varbufreset(&vb);
      describedepcon(&vb,dep);
      varbufaddc(&vb,0);
      fprintf(stderr, _("dpkg: cannot see how to satisfy pre-dependency:\n %s\n"),vb.buf);
      ohshit(_("cannot satisfy pre-dependencies for %.250s (wanted due to %.250s)"),
             dep->up->name,startpkg->name);
    }
    pkg->clientdata->istobe= itb_preinstall;
    for (dep= pkg->available.depends; dep; dep= dep->next) {
      if (dep->type != dep_predepends) continue;
      if (depisok(dep, &vb, NULL, true))
        continue;
      /* This will leave dep non-NULL, and so exit the loop. */
      break;
    }
  } while (dep);

  /* OK, we've found it - pkg has no unsatisfied pre-dependencies! */
  writerecord(stdout, _("<standard output>"), pkg, &pkg->available);

  m_output(stdout, _("<standard output>"));
}
Beispiel #26
0
/*
 * *whynot must already have been initialized; it need not be
 * empty though - it will be reset before use.
 *
 * If depisok returns false for ‘not OK’ it will contain a description,
 * newline-terminated BUT NOT NUL-TERMINATED, of the reason.
 *
 * If depisok returns true it will contain garbage.
 * allowunconfigd should be non-zero during the ‘Pre-Depends’ checking
 * before a package is unpacked, when it is sufficient for the package
 * to be unpacked provided that both the unpacked and previously-configured
 * versions are acceptable.
 *
 * On false return (‘not OK’), *canfixbyremove refers to a package which
 * if removed (dep_conflicts) or deconfigured (dep_breaks) will fix
 * the problem. Caller may pass NULL for canfixbyremove and need not
 * initialize *canfixbyremove.
 */
bool
depisok(struct dependency *dep, struct varbuf *whynot,
        struct pkginfo **canfixbyremove, bool allowunconfigd)
{
  struct deppossi *possi;
  struct deppossi *provider;
  int nconflicts;

  /* Use this buffer so that when internationalisation comes along we
   * don't have to rewrite the code completely, only redo the sprintf strings
   * (assuming we have the fancy argument-number-specifiers).
   * Allow 250x3 for package names, versions, &c, + 250 for ourselves. */
  char linebuf[1024];

  assert(dep->type == dep_depends || dep->type == dep_predepends ||
	 dep->type == dep_breaks || dep->type == dep_conflicts ||
	 dep->type == dep_recommends || dep->type == dep_suggests ||
	 dep->type == dep_enhances);

  if (canfixbyremove)
    *canfixbyremove = NULL;

  /* The dependency is always OK if we're trying to remove the depend*ing*
   * package. */
  switch (dep->up->clientdata->istobe) {
  case itb_remove: case itb_deconfigure:
    return true;
  case itb_normal:
    /* Only installed packages can be make dependency problems. */
    switch (dep->up->status) {
    case stat_installed:
    case stat_triggerspending:
    case stat_triggersawaited:
      break;
    case stat_notinstalled: case stat_configfiles: case stat_halfinstalled:
    case stat_halfconfigured: case stat_unpacked:
      return true;
    default:
      internerr("unknown status depending '%d'", dep->up->status);
    }
    break;
  case itb_installnew: case itb_preinstall:
    break;
  default:
    internerr("unknown istobe depending '%d'", dep->up->clientdata->istobe);
  }

  /* Describe the dependency, in case we have to moan about it. */
  varbufreset(whynot);
  varbufaddc(whynot, ' ');
  describedepcon(whynot, dep);
  varbufaddc(whynot,'\n');

  /* TODO: Check dep_enhances as well. */
  if (dep->type == dep_depends || dep->type == dep_predepends ||
      dep->type == dep_recommends || dep->type == dep_suggests ) {
    /* Go through the alternatives. As soon as we find one that
     * we like, we return ‘true’ straight away. Otherwise, when we get to
     * the end we'll have accumulated all the reasons in whynot and
     * can return ‘false’. */

    for (possi= dep->list; possi; possi= possi->next) {
      switch (possi->ed->clientdata->istobe) {
      case itb_remove:
        sprintf(linebuf,_("  %.250s is to be removed.\n"),possi->ed->name);
        break;
      case itb_deconfigure:
        sprintf(linebuf,_("  %.250s is to be deconfigured.\n"),possi->ed->name);
        break;
      case itb_installnew:
        if (versionsatisfied(&possi->ed->available, possi))
          return true;
        sprintf(linebuf,_("  %.250s is to be installed, but is version %.250s.\n"),
                possi->ed->name,
                versiondescribe(&possi->ed->available.version,vdew_nonambig));
        break;
      case itb_normal: case itb_preinstall:
        switch (possi->ed->status) {
        case stat_installed:
        case stat_triggerspending:
          if (versionsatisfied(&possi->ed->installed, possi))
            return true;
          sprintf(linebuf,_("  %.250s is installed, but is version %.250s.\n"),
                  possi->ed->name,
                  versiondescribe(&possi->ed->installed.version,vdew_nonambig));
          break;
        case stat_notinstalled:
          /* Don't say anything about this yet - it might be a virtual package.
           * Later on, if nothing has put anything in linebuf, we know that it
           * isn't and issue a diagnostic then. */
          *linebuf = '\0';
          break;
        case stat_unpacked:
        case stat_halfconfigured:
        case stat_triggersawaited:
          if (allowunconfigd) {
            if (!informativeversion(&possi->ed->configversion)) {
              sprintf(linebuf, _("  %.250s is unpacked, but has never been configured.\n"),
                      possi->ed->name);
              break;
            } else if (!versionsatisfied(&possi->ed->installed, possi)) {
              sprintf(linebuf, _("  %.250s is unpacked, but is version %.250s.\n"),
                      possi->ed->name,
                      versiondescribe(&possi->ed->available.version,vdew_nonambig));
              break;
            } else if (!versionsatisfied3(&possi->ed->configversion,
                                          &possi->version,possi->verrel)) {
              sprintf(linebuf, _("  %.250s latest configured version is %.250s.\n"),
                      possi->ed->name,
                      versiondescribe(&possi->ed->configversion,vdew_nonambig));
              break;
            } else {
              return true;
            }
          }
          /* Fall through. */
        default:
          sprintf(linebuf, _("  %.250s is %s.\n"),
                  possi->ed->name, gettext(statusstrings[possi->ed->status]));
          break;
        }
        break;
      default:
        internerr("unknown istobe depended '%d'", possi->ed->clientdata->istobe);
      }
      varbufaddstr(whynot, linebuf);

      /* If there was no version specified we try looking for Providers. */
      if (possi->verrel == dvr_none) {
        /* See if the package we're about to install Provides it. */
        for (provider= possi->ed->available.depended;
             provider;
             provider = provider->rev_next) {
          if (provider->up->type != dep_provides) continue;
          if (provider->up->up->clientdata->istobe == itb_installnew)
            return true;
        }

        /* Now look at the packages already on the system. */
        for (provider= possi->ed->installed.depended;
             provider;
             provider = provider->rev_next) {
          if (provider->up->type != dep_provides) continue;

          switch (provider->up->up->clientdata->istobe) {
          case itb_installnew:
            /* Don't pay any attention to the Provides field of the
             * currently-installed version of the package we're trying
             * to install. We dealt with that by using the available
             * information above. */
            continue;
          case itb_remove:
            sprintf(linebuf, _("  %.250s provides %.250s but is to be removed.\n"),
                    provider->up->up->name, possi->ed->name);
            break;
          case itb_deconfigure:
            sprintf(linebuf, _("  %.250s provides %.250s but is to be deconfigured.\n"),
                    provider->up->up->name, possi->ed->name);
            break;
          case itb_normal: case itb_preinstall:
            if (provider->up->up->status == stat_installed)
              return true;
            sprintf(linebuf, _("  %.250s provides %.250s but is %s.\n"),
                    provider->up->up->name, possi->ed->name,
                    gettext(statusstrings[provider->up->up->status]));
            break;
          default:
            internerr("unknown istobe provider '%d'",
                      provider->up->up->clientdata->istobe);
          }
          varbufaddstr(whynot, linebuf);
        }

        if (!*linebuf) {
          /* If the package wasn't installed at all, and we haven't said
           * yet why this isn't satisfied, we should say so now. */
          sprintf(linebuf, _("  %.250s is not installed.\n"), possi->ed->name);
          varbufaddstr(whynot, linebuf);
        }
      }
    }

    return false;
  } else {
    /* It's conflicts or breaks. There's only one main alternative,
     * but we also have to consider Providers. We return ‘false’ as soon
     * as we find something that matches the conflict, and only describe
     * it then. If we get to the end without finding anything we return
     * ‘true’. */

    possi= dep->list;
    nconflicts= 0;

    if (possi->ed != possi->up->up) {
      /* If the package conflicts with or breaks itself it must mean
       * other packages which provide the same virtual name. We
       * therefore don't look at the real package and go on to the
       * virtual ones. */

      switch (possi->ed->clientdata->istobe) {
      case itb_remove:
        break;
      case itb_installnew:
        if (!versionsatisfied(&possi->ed->available, possi)) break;
        sprintf(linebuf, _("  %.250s (version %.250s) is to be installed.\n"),
                possi->ed->name,
                versiondescribe(&possi->ed->available.version,vdew_nonambig));
        varbufaddstr(whynot, linebuf);
        if (!canfixbyremove)
          return false;
        nconflicts++;
        *canfixbyremove= possi->ed;
        break;
      case itb_deconfigure:
        if (dep->type == dep_breaks)
          break; /* Already deconfiguring this. */
        /* Fall through. */
      case itb_normal: case itb_preinstall:
        switch (possi->ed->status) {
        case stat_notinstalled: case stat_configfiles:
          break;
        case stat_halfinstalled: case stat_unpacked:
        case stat_halfconfigured:
          if (dep->type == dep_breaks)
            break; /* No problem. */
        case stat_installed:
        case stat_triggerspending:
        case stat_triggersawaited:
          if (!versionsatisfied(&possi->ed->installed, possi)) break;
          sprintf(linebuf, _("  %.250s (version %.250s) is present and %s.\n"),
                  possi->ed->name,
                  versiondescribe(&possi->ed->installed.version,vdew_nonambig),
                  gettext(statusstrings[possi->ed->status]));
          varbufaddstr(whynot, linebuf);
          if (!canfixbyremove)
            return false;
          nconflicts++;
          *canfixbyremove= possi->ed;
        }
        break;
      default:
        internerr("unknown istobe conflict '%d'",
                  possi->ed->clientdata->istobe);
      }
    }

    /* If there was no version specified we try looking for Providers. */
    if (possi->verrel == dvr_none) {
      /* See if the package we're about to install Provides it. */
      for (provider= possi->ed->available.depended;
           provider;
           provider = provider->rev_next) {
        if (provider->up->type != dep_provides) continue;
        if (provider->up->up->clientdata->istobe != itb_installnew) continue;
        if (provider->up->up == dep->up)
          continue; /* Conflicts and provides the same. */
        sprintf(linebuf, _("  %.250s provides %.250s and is to be installed.\n"),
                provider->up->up->name, possi->ed->name);
        varbufaddstr(whynot, linebuf);
        /* We can't remove the one we're about to install: */
        if (canfixbyremove)
          *canfixbyremove = NULL;
        return false;
      }

      /* Now look at the packages already on the system. */
      for (provider= possi->ed->installed.depended;
           provider;
           provider = provider->rev_next) {
        if (provider->up->type != dep_provides) continue;

        if (provider->up->up == dep->up)
          continue; /* Conflicts and provides the same. */

        switch (provider->up->up->clientdata->istobe) {
        case itb_installnew:
          /* Don't pay any attention to the Provides field of the
           * currently-installed version of the package we're trying
           * to install. We dealt with that package by using the
           * available information above. */
          continue;
        case itb_remove:
          continue;
        case itb_deconfigure:
          if (dep->type == dep_breaks)
            continue; /* Already deconfiguring. */
        case itb_normal: case itb_preinstall:
          switch (provider->up->up->status) {
          case stat_notinstalled: case stat_configfiles:
            continue;
          case stat_halfinstalled: case stat_unpacked:
          case stat_halfconfigured:
            if (dep->type == dep_breaks)
              break; /* No problem. */
          case stat_installed:
          case stat_triggerspending:
          case stat_triggersawaited:
            sprintf(linebuf,
                    _("  %.250s provides %.250s and is present and %s.\n"),
                    provider->up->up->name, possi->ed->name,
                    gettext(statusstrings[provider->up->up->status]));
            varbufaddstr(whynot, linebuf);
            if (!canfixbyremove)
              return false;
            nconflicts++;
            *canfixbyremove= provider->up->up;
            break;
          }
          break;
        default:
          internerr("unknown istobe conflict provider '%d'",
                    provider->up->up->clientdata->istobe);
        }
      }
    }

    if (!nconflicts)
      return true;
    if (nconflicts > 1)
      *canfixbyremove = NULL;
    return false;

  } /* if (dependency) {...} else {...} */
}
Beispiel #27
0
void
ensure_statoverrides(void)
{
	static struct varbuf vb;

	struct stat stab1, stab2;
	FILE *file;
	char *loaded_list, *loaded_list_end, *thisline, *nextline, *ptr;
	struct file_stat *fso;
	struct filenamenode *fnn;

	varbufreset(&vb);
	varbufaddstr(&vb, admindir);
	varbufaddstr(&vb, "/" STATOVERRIDEFILE);
	varbufaddc(&vb, 0);

	onerr_abort++;

	file = fopen(vb.buf,"r");
	if (!file) {
		if (errno != ENOENT)
			ohshite(_("failed to open statoverride file"));
		if (!statoverridefile) {
			onerr_abort--;
			return;
		}
	} else {
		if (fstat(fileno(file), &stab2))
			ohshite(_("failed to fstat statoverride file"));
		if (statoverridefile) {
			if (fstat(fileno(statoverridefile), &stab1))
				ohshite(_("failed to fstat previous statoverride file"));
			if (stab1.st_dev == stab2.st_dev &&
			    stab1.st_ino == stab2.st_ino) {
				fclose(file);
				onerr_abort--;
				return;
			}
		}
	}
	if (statoverridefile)
		fclose(statoverridefile);
	statoverridefile = file;
	setcloexec(fileno(statoverridefile), vb.buf);

	/* If the statoverride list is empty we don't need to bother
	 * reading it. */
	if (!stab2.st_size) {
		onerr_abort--;
		return;
	}

	loaded_list = nfmalloc(stab2.st_size);
	loaded_list_end = loaded_list + stab2.st_size;

	fd_buf_copy(fileno(file), loaded_list, stab2.st_size,
	            _("statoverride file `%.250s'"), vb.buf);

	thisline = loaded_list;
	while (thisline < loaded_list_end) {
		fso = nfmalloc(sizeof(struct file_stat));

		if (!(ptr = memchr(thisline, '\n', loaded_list_end - thisline)))
			ohshit(_("statoverride file is missing final newline"));
		/* Where to start next time around. */
		nextline = ptr + 1;
		if (ptr == thisline)
			ohshit(_("statoverride file contains empty line"));
		*ptr = '\0';

		/* Extract the uid. */
		if (!(ptr = memchr(thisline, ' ', nextline - thisline)))
			ohshit(_("syntax error in statoverride file"));
		*ptr = '\0';

		fso->uid = statdb_parse_uid(thisline);

		/* Move to the next bit */
		thisline = ptr + 1;
		if (thisline >= loaded_list_end)
			ohshit(_("unexpected end of line in statoverride file"));

		/* Extract the gid */
		if (!(ptr = memchr(thisline, ' ', nextline - thisline)))
			ohshit(_("syntax error in statoverride file"));
		*ptr = '\0';

		fso->gid = statdb_parse_gid(thisline);

		/* Move to the next bit */
		thisline = ptr + 1;
		if (thisline >= loaded_list_end)
			ohshit(_("unexpected end of line in statoverride file"));

		/* Extract the mode */
		if (!(ptr = memchr(thisline, ' ', nextline - thisline)))
			ohshit(_("syntax error in statoverride file"));
		*ptr = '\0';

		fso->mode = statdb_parse_mode(thisline);

		/* Move to the next bit */
		thisline = ptr + 1;
		if (thisline >= loaded_list_end)
			ohshit(_("unexpected end of line in statoverride file"));

		fnn = findnamenode(thisline, 0);
		if (fnn->statoverride)
			ohshit(_("multiple statusoverrides present for file '%.250s'"),
			       thisline);
		fnn->statoverride = fso;

		/* Moving on.. */
		thisline = nextline;
	}

	onerr_abort--;
}