Ejemplo n.º 1
0
static void
test_version_compare(void)
{
	struct versionrevision a, b;

	blankversion(&a);
	blankversion(&b);
	test_fail(epochsdiffer(&a, &b));

	a.epoch = 1;
	b.epoch = 2;
	test_pass(epochsdiffer(&a, &b));

	/* Test for version equality. */
	a = b = version(0, "0", "0");
	test_pass(versioncompare(&a, &b) == 0);

	a = version(0, "0", "00");
	b = version(0, "00", "0");
	test_pass(versioncompare(&a, &b) == 0);

	a = b = version(1, "2", "3");
	test_pass(versioncompare(&a, &b) == 0);

	/* Test for epoch difference. */
	a = version(0, "0", "0");
	b = version(1, "0", "0");
	test_pass(versioncompare(&a, &b) < 0);
	test_pass(versioncompare(&b, &a) > 0);

	/* FIXME: Complete. */
}
Ejemplo n.º 2
0
int para_compare(keys_t *keys, const para_t *a, const para_t *b)
{
	int r = 0;
	for (size_t i = 0; i < keys->nks; i++) {
		char * af = get_field_as(a, keys->keys[i].field_inx);
		char * bf = get_field_as(b, keys->keys[i].field_inx);
		if (af == 0 || bf == 0) fatal_enomem(0);
		switch (keys->keys[i].type) {
		case FT_STRING:
			r = strcmp(af, bf);
			break;
		case FT_VERSION:
			;
			struct versionrevision ar, br;
			bool aok = parse_version(&ar, af, strlen(af));
			bool bok = parse_version(&br, bf, strlen(bf));
			if (!aok || !bok) {
				message(L_IMPORTANT, 0,
					_("Parse error in field."));
				free(af);
				free(bf);
				return aok ? (bok ? 0 : 1) : (bok ? -1 : 0);
			}
			r = versioncompare(&ar, &br);
			break;
		}
		debug("cmp: a = %s, b = %s, verdict is %d", af, bf, r);
		free(af);
		free(bf);
		if (keys->keys[i].reverse) r = -r;
		if (r != 0) break;
	}

	return r;
}
Ejemplo n.º 3
0
/**
 * Decide whether we want to install a new version of the package.
 *
 * ver is the version we might want to install. If saywhy is 1 then
 * if we skip the package we say what we are doing (and, if we are
 * selecting a previously deselected package, say so and actually do
 * the select). want_install returns 0 if the package should be
 * skipped and 1 if it should be installed.
 *
 * ver may be 0, in which case saywhy must be 0 and want_install may
 * also return -1 to mean it doesn't know because it would depend on
 * the version number.
 */
int
wanttoinstall(struct pkginfo *pkg, const struct versionrevision *ver,
              bool saywhy)
{
  int r;

  if (pkg->want != want_install && pkg->want != want_hold) {
    if (f_alsoselect) {
      if (saywhy) {
   printf(_("Selecting previously deselected package %s.\n"),pkg->name);
   pkg->want= want_install;
      }
      return 1;
    } else {
      if (saywhy) printf(_("Skipping deselected package %s.\n"),pkg->name);
      return 0;
    }
  }

  if (!(pkg->status == stat_installed ||
        pkg->status == stat_triggersawaited ||
        pkg->status == stat_triggerspending))
    return 1;
  if (!ver) return -1;

  r= versioncompare(ver,&pkg->installed.version);
  if (r > 0) {
    return 1;
  } else if (r == 0) {
    /* Same version fully installed. */
    if (f_skipsame) {
      if (saywhy) fprintf(stderr, _("Version %.250s of %.250s already installed, "
             "skipping.\n"),
             versiondescribe(&pkg->installed.version, vdew_nonambig),
             pkg->name);
      return 0;
    } else {
      return 1;
    }
  } else {
    if (fc_downgrade) {
      if (saywhy)
        warning(_("downgrading %.250s from %.250s to %.250s."), pkg->name,
             versiondescribe(&pkg->installed.version, vdew_nonambig),
             versiondescribe(&pkg->available.version, vdew_nonambig));
      return 1;
    } else {
      if (saywhy) fprintf(stderr, _("Will not downgrade %.250s from version %.250s "
             "to %.250s, skipping.\n"), pkg->name,
             versiondescribe(&pkg->installed.version, vdew_nonambig),
             versiondescribe(&pkg->available.version, vdew_nonambig));
      return 0;
    }
  }
}
Ejemplo n.º 4
0
bool
versionsatisfied3(const struct versionrevision *it,
                  const struct versionrevision *ref,
                  enum depverrel verrel)
{
  int r;
  if (verrel == dvr_none)
    return true;
  r= versioncompare(it,ref);
  switch (verrel) {
  case dvr_earlierequal:   return r <= 0;
  case dvr_laterequal:     return r >= 0;
  case dvr_earlierstrict:  return r < 0;
  case dvr_laterstrict:    return r > 0;
  case dvr_exact:          return r == 0;
  default:
    internerr("unknown depverrel '%d'", verrel);
  }
  return false;
}
Ejemplo n.º 5
0
int versionsatisfied3(const struct versionrevision *it,
                      const struct versionrevision *ref,
                      const char *op) 
{
  int r;
  r= versioncompare(it,ref);
  if (strcmp(op, "<=") == 0 || strcmp(op, "<") == 0)
    return r <= 0;
  if (strcmp(op, ">=") == 0 || strcmp(op, ">") == 0)
    return r >= 0;
  if (strcmp(op, "<<") == 0)
    return r < 0;
  if (strcmp(op, ">>") == 0)
    return r > 0;
  if (strcmp(op, "=") == 0)
    return r == 0;
  fprintf(stderr, "unknown operator: %s", op);

  exit(1);
}
Ejemplo n.º 6
0
int parsedb(const char *filename, enum parsedbflags flags,
            struct pkginfo **donep, FILE *warnto, int *warncount) {
  /* warnto, warncount and donep may be null.
   * If donep is not null only one package's information is expected.
   */
  
  static int fd;
  struct pkginfo newpig, *pigp;
  struct pkginfoperfile *newpifp, *pifp;
  struct arbitraryfield *arp, **larpp;
  struct trigaw *ta;
  int pdone;
  int fieldencountered[array_count(fieldinfos)];
  const struct fieldinfo *fip;
  const struct nickname *nick;
  char *data, *dataptr, *endptr;
  const char *fieldstart, *valuestart;
  char *value= NULL;
  int fieldlen= 0, valuelen= 0;
  int *ip, c;
  struct stat st;
  struct parsedb_state ps;

  ps.filename = filename;
  ps.flags = flags;
  ps.lno = 0;
  ps.warnto = warnto;
  ps.warncount = 0;

  newpifp= (flags & pdb_recordavailable) ? &newpig.available : &newpig.installed;
  fd= open(filename, O_RDONLY);
  if (fd == -1) ohshite(_("failed to open package info file `%.255s' for reading"),filename);

  push_cleanup(cu_closefd, ~ehflag_normaltidy, NULL, 0, 1, &fd);

  if (fstat(fd, &st) == -1)
    ohshite(_("can't stat package info file `%.255s'"),filename);

  if (st.st_size > 0) {
#ifdef USE_MMAP
    dataptr = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
    if (dataptr == MAP_FAILED)
      ohshite(_("can't mmap package info file `%.255s'"),filename);
#else
    dataptr = m_malloc(st.st_size);

    fd_buf_copy(fd, dataptr, st.st_size, _("copy info file `%.255s'"), filename);
#endif
    data= dataptr;
    endptr = dataptr + st.st_size;
  } else {
    data= dataptr= endptr= NULL;
  }

  pdone= 0;
#define EOF_mmap(dataptr, endptr)	(dataptr >= endptr)
#define getc_mmap(dataptr)		*dataptr++;
#define ungetc_mmap(c, dataptr, data)	dataptr--;

  for (;;) { /* loop per package */
    memset(fieldencountered, 0, sizeof(fieldencountered));
    blankpackage(&newpig);

/* Skip adjacent new lines */
    while(!EOF_mmap(dataptr, endptr)) {
      c= getc_mmap(dataptr); if (c!='\n' && c!=MSDOS_EOF_CHAR ) break;
      ps.lno++;
    }
    if (EOF_mmap(dataptr, endptr)) break;
    for (;;) { /* loop per field */
      fieldstart= dataptr - 1;
      while (!EOF_mmap(dataptr, endptr) && !isspace(c) && c!=':' && c!=MSDOS_EOF_CHAR)
        c= getc_mmap(dataptr);
      fieldlen= dataptr - fieldstart - 1;
      while (!EOF_mmap(dataptr, endptr) && c != '\n' && isspace(c)) c= getc_mmap(dataptr);
      if (EOF_mmap(dataptr, endptr))
        parse_error(&ps, &newpig,
                    _("EOF after field name `%.*s'"), fieldlen, fieldstart);
      if (c == '\n')
        parse_error(&ps, &newpig,
                    _("newline in field name `%.*s'"), fieldlen, fieldstart);
      if (c == MSDOS_EOF_CHAR)
        parse_error(&ps, &newpig,
                    _("MSDOS EOF (^Z) in field name `%.*s'"),
                    fieldlen, fieldstart);
      if (c != ':')
        parse_error(&ps, &newpig,
                    _("field name `%.*s' must be followed by colon"),
                    fieldlen, fieldstart);
/* Skip space after ':' but before value and eol */
      while(!EOF_mmap(dataptr, endptr)) {
        c= getc_mmap(dataptr);
        if (c == '\n' || !isspace(c)) break;
      }
      if (EOF_mmap(dataptr, endptr))
        parse_error(&ps, &newpig,
                    _("EOF before value of field `%.*s' (missing final newline)"),
                 fieldlen,fieldstart);
      if (c == MSDOS_EOF_CHAR)
        parse_error(&ps, &newpig,
                    _("MSDOS EOF char in value of field `%.*s' (missing newline?)"),
                    fieldlen,fieldstart);
      valuestart= dataptr - 1;
      for (;;) {
        if (c == '\n' || c == MSDOS_EOF_CHAR) {
          ps.lno++;
	  if (EOF_mmap(dataptr, endptr)) break;
          c= getc_mmap(dataptr);
/* Found double eol, or start of new field */
          if (EOF_mmap(dataptr, endptr) || c == '\n' || !isspace(c)) break;
          ungetc_mmap(c,dataptr, data);
          c= '\n';
        } else if (EOF_mmap(dataptr, endptr)) {
          parse_error(&ps, &newpig,
                      _("EOF during value of field `%.*s' (missing final newline)"),
                      fieldlen,fieldstart);
        }
        c= getc_mmap(dataptr);
      }
      valuelen= dataptr - valuestart - 1;
/* trim ending space on value */
      while (valuelen && isspace(*(valuestart+valuelen-1)))
 valuelen--;
      for (nick = nicknames;
           nick->nick && (strncasecmp(nick->nick, fieldstart, fieldlen) ||
                          nick->nick[fieldlen] != '\0'); nick++) ;
      if (nick->nick) {
	fieldstart= nick->canon;
	fieldlen= strlen(fieldstart);
      }
      for (fip= fieldinfos, ip= fieldencountered;
           fip->name && strncasecmp(fieldstart,fip->name, fieldlen);
           fip++, ip++);
      if (fip->name) {
        value = m_realloc(value, valuelen + 1);
	memcpy(value,valuestart,valuelen);
        *(value + valuelen) = '\0';
        if ((*ip)++)
          parse_error(&ps, &newpig,
                      _("duplicate value for `%s' field"), fip->name);
        fip->rcall(&newpig, newpifp, &ps, value, fip);
      } else {
        if (fieldlen<2)
          parse_error(&ps, &newpig,
                      _("user-defined field name `%.*s' too short"),
                      fieldlen, fieldstart);
        larpp= &newpifp->arbs;
        while ((arp= *larpp) != NULL) {
          if (!strncasecmp(arp->name,fieldstart,fieldlen))
            parse_error(&ps, &newpig,
                       _("duplicate value for user-defined field `%.*s'"),
                       fieldlen, fieldstart);
          larpp= &arp->next;
        }
        arp= nfmalloc(sizeof(struct arbitraryfield));
        arp->name= nfstrnsave(fieldstart,fieldlen);
        arp->value= nfstrnsave(valuestart,valuelen);
        arp->next= NULL;
        *larpp= arp;
      }
      if (EOF_mmap(dataptr, endptr) || c == '\n' || c == MSDOS_EOF_CHAR) break;
    } /* loop per field */
    if (pdone && donep)
      parse_error(&ps, &newpig,
                  _("several package info entries found, only one allowed"));
    parse_must_have_field(&ps, &newpig, newpig.name, "package name");
    if ((flags & pdb_recordavailable) || newpig.status != stat_notinstalled) {
      parse_ensure_have_field(&ps, &newpig,
                              &newpifp->description, "description");
      parse_ensure_have_field(&ps, &newpig,
                              &newpifp->maintainer, "maintainer");
      if (newpig.status != stat_halfinstalled)
        parse_must_have_field(&ps, &newpig,
                              newpifp->version.version, "version");
    }
    if (flags & pdb_recordavailable)
      parse_ensure_have_field(&ps, &newpig,
                              &newpifp->architecture, "architecture");

    /* Check the Config-Version information:
     * If there is a Config-Version it is definitely to be used, but
     * there shouldn't be one if the package is `installed' (in which case
     * the Version and/or Revision will be copied) or if the package is
     * `not-installed' (in which case there is no Config-Version).
     */
    if (!(flags & pdb_recordavailable)) {
      if (newpig.configversion.version) {
        if (newpig.status == stat_installed || newpig.status == stat_notinstalled)
          parse_error(&ps, &newpig,
                      _("Configured-Version for package with inappropriate Status"));
      } else {
        if (newpig.status == stat_installed) newpig.configversion= newpifp->version;
      }
    }

    if (newpig.trigaw.head &&
        (newpig.status <= stat_configfiles ||
         newpig.status >= stat_triggerspending))
      parse_error(&ps, &newpig,
                  _("package has status %s but triggers are awaited"),
                  statusinfos[newpig.status].name);
    else if (newpig.status == stat_triggersawaited && !newpig.trigaw.head)
      parse_error(&ps, &newpig,
                  _("package has status triggers-awaited but no triggers "
                    "awaited"));

    if (!(newpig.status == stat_triggerspending ||
          newpig.status == stat_triggersawaited) &&
        newpig.trigpend_head)
      parse_error(&ps, &newpig,
                  _("package has status %s but triggers are pending"),
                  statusinfos[newpig.status].name);
    else if (newpig.status == stat_triggerspending && !newpig.trigpend_head)
      parse_error(&ps, &newpig,
                  _("package has status triggers-pending but no triggers "
                    "pending"));

    /* FIXME: There was a bug that could make a not-installed package have
     * conffiles, so we check for them here and remove them (rather than
     * calling it an error, which will do at some point).
     */
    if (!(flags & pdb_recordavailable) &&
        newpig.status == stat_notinstalled &&
        newpifp->conffiles) {
      parse_warn(&ps, &newpig,
                 _("Package which in state not-installed has conffiles, "
                   "forgetting them"));
      newpifp->conffiles= NULL;
    }

    /* XXX: Mark not-installed leftover packages for automatic removal on
     * next database dump. This code can be removed after dpkg 1.16.x, when
     * there's guarantee that no leftover is found on the status file on
     * major distributions. */
    if (!(flags & pdb_recordavailable) &&
        newpig.status == stat_notinstalled &&
        newpig.eflag == eflag_ok &&
        (newpig.want == want_purge ||
         newpig.want == want_deinstall ||
         newpig.want == want_hold)) {
      newpig.want = want_unknown;
    }

    pigp= findpackage(newpig.name);
    pifp= (flags & pdb_recordavailable) ? &pigp->available : &pigp->installed;

    if ((flags & pdb_ignoreolder) &&
	versioncompare(&newpifp->version, &pifp->version) < 0)
      continue;

    /* Copy the priority and section across, but don't overwrite existing
     * values if the pdb_weakclassification flag is set.
     */
    if (newpig.section && *newpig.section &&
        !((flags & pdb_weakclassification) && pigp->section && *pigp->section))
      pigp->section= newpig.section;
    if (newpig.priority != pri_unknown &&
        !((flags & pdb_weakclassification) && pigp->priority != pri_unknown)) {
      pigp->priority= newpig.priority;
      if (newpig.priority == pri_other) pigp->otherpriority= newpig.otherpriority;
    }

    /* Sort out the dependency mess. */
    copy_dependency_links(pigp,&pifp->depends,newpifp->depends,
                          (flags & pdb_recordavailable) ? 1 : 0);
    /* Leave the `depended' pointer alone, we've just gone to such
     * trouble to get it right :-).  The `depends' pointer in
     * pifp was indeed also updated by copy_dependency_links,
     * but since the value was that from newpifp anyway there's
     * no need to copy it back.
     */
    newpifp->depended= pifp->depended;

    /* Copy across data */
    memcpy(pifp,newpifp,sizeof(struct pkginfoperfile));
    if (!(flags & pdb_recordavailable)) {
      pigp->want= newpig.want;
      pigp->eflag= newpig.eflag;
      pigp->status= newpig.status;
      pigp->configversion= newpig.configversion;
      pigp->files= NULL;

      pigp->trigpend_head = newpig.trigpend_head;
      pigp->trigaw = newpig.trigaw;
      for (ta = pigp->trigaw.head; ta; ta = ta->sameaw.next) {
        assert(ta->aw == &newpig);
        ta->aw = pigp;
        /* ->othertrigaw_head is updated by trig_note_aw in *(findpackage())
         * rather than in newpig */
      }

    } else if (!(flags & pdb_ignorefiles)) {
      pigp->files= newpig.files;
    }

    if (donep) *donep= pigp;
    pdone++;
    if (EOF_mmap(dataptr, endptr)) break;
    if (c == '\n')
      ps.lno++;
  }
  if (data != NULL) {
#ifdef USE_MMAP
    munmap(data, st.st_size);
#else
    free(data);
#endif
  }
  free(value);
  pop_cleanup(ehflag_normaltidy);
  if (close(fd)) ohshite(_("failed to close after read: `%.255s'"),filename);
  if (donep && !pdone) ohshit(_("no package information in `%.255s'"),filename);

  if (warncount)
    *warncount = ps.warncount;

  return pdone;
}
Ejemplo n.º 7
0
void cmpversions(const char *const *argv) {
  struct relationinfo {
    const char *string;
    /* These values are exit status codes, so 0 = true, 1 = false. */
    int if_lesser, if_equal, if_greater;
    int if_none_a, if_none_both, if_none_b;
  };

  static const struct relationinfo relationinfos[]= {
    /*             < = > !a!2!b  */
    { "le",        0,0,1, 0,0,1  },
    { "lt",        0,1,1, 0,1,1  },
    { "eq",        1,0,1, 1,0,1  },
    { "ne",        0,1,0, 0,1,0  },
    { "ge",        1,0,0, 1,0,0  },
    { "gt",        1,1,0, 1,1,0  },

    /* These treat an empty version as later than any version. */
    { "le-nl",     0,0,1, 1,0,0  },
    { "lt-nl",     0,1,1, 1,1,0  },
    { "ge-nl",     1,0,0, 0,0,1  },
    { "gt-nl",     1,1,0, 0,1,1  },

    /* For compatibility with dpkg control file syntax. */
    { "<",         0,0,1, 0,0,1  },
    { "<=",        0,0,1, 0,0,1  },
    { "<<",        0,1,1, 0,1,1  },
    { "=",         1,0,1, 1,0,1  },
    { ">",         1,0,0, 1,0,0  },
    { ">=",        1,0,0, 1,0,0  },
    { ">>",        1,1,0, 1,1,0  },
    { NULL                       }
  };

  const struct relationinfo *rip;
  const char *emsg;
  struct versionrevision a, b;
  int r;

  if (!argv[0] || !argv[1] || !argv[2] || argv[3])
    badusage(_("--compare-versions takes three arguments:"
             " <version> <relation> <version>"));

  for (rip=relationinfos; rip->string && strcmp(rip->string,argv[1]); rip++);

  if (!rip->string) badusage(_("--compare-versions bad relation"));

  if (*argv[0] && strcmp(argv[0],"<unknown>")) {
    emsg= parseversion(&a,argv[0]);
    if (emsg)
      ohshit(_("version '%s' has bad syntax: %s"), argv[0], emsg);
  } else {
    blankversion(&a);
  }
  if (*argv[2] && strcmp(argv[2],"<unknown>")) {
    emsg= parseversion(&b,argv[2]);
    if (emsg)
      ohshit(_("version '%s' has bad syntax: %s"), argv[2], emsg);
  } else {
    blankversion(&b);
  }
  if (!informativeversion(&a)) {
    exit(informativeversion(&b) ? rip->if_none_a : rip->if_none_both);
  } else if (!informativeversion(&b)) {
    exit(rip->if_none_b);
  }
  r= versioncompare(&a,&b);
  debug(dbg_general,"cmpversions a=`%s' b=`%s' r=%d",
        versiondescribe(&a,vdew_always),
        versiondescribe(&b,vdew_always),
        r);
  if (r>0) exit(rip->if_greater);
  else if (r<0) exit(rip->if_lesser);
  else exit(rip->if_equal);
}
Ejemplo n.º 8
0
static void
test_version_parse(void)
{
	struct versionrevision a, b;

	/* Test 0 versions. */
	blankversion(&a);
	b = version(0, "0", "");

	test_pass(parseversion(&a, "0") == NULL);
	test_pass(versioncompare(&a, &b) == 0);

	test_pass(parseversion(&a, "0:0") == NULL);
	test_pass(versioncompare(&a, &b) == 0);

	test_pass(parseversion(&a, "0:0-") == NULL);
	test_pass(versioncompare(&a, &b) == 0);

	b = version(0, "0", "0");
	test_pass(parseversion(&a, "0:0-0") == NULL);
	test_pass(versioncompare(&a, &b) == 0);

	b = version(0, "0.0", "0.0");
	test_pass(parseversion(&a, "0:0.0-0.0") == NULL);
	test_pass(versioncompare(&a, &b) == 0);

	/* Test epoched versions. */
	b = version(1, "0", "");
	test_pass(parseversion(&a, "1:0") == NULL);
	test_pass(versioncompare(&a, &b) == 0);

	b = version(5, "1", "");
	test_pass(parseversion(&a, "5:1") == NULL);
	test_pass(versioncompare(&a, &b) == 0);

	/* Test multiple dashes. */
	b = version(0, "0-0", "0");
	test_pass(parseversion(&a, "0:0-0-0") == NULL);
	test_pass(versioncompare(&a, &b) == 0);

	b = version(0, "0-0-0", "0");
	test_pass(parseversion(&a, "0:0-0-0-0") == NULL);
	test_pass(versioncompare(&a, &b) == 0);

	/* Test multiple colons. */
	b = version(0, "0:0", "0");
	test_pass(parseversion(&a, "0:0:0-0") == NULL);
	test_pass(versioncompare(&a, &b) == 0);

	b = version(0, "0:0:0", "0");
	test_pass(parseversion(&a, "0:0:0:0-0") == NULL);
	test_pass(versioncompare(&a, &b) == 0);

	/* Test multiple dashes and colons. */
	b = version(0, "0:0-0", "0");
	test_pass(parseversion(&a, "0:0:0-0-0") == NULL);
	test_pass(versioncompare(&a, &b) == 0);

	b = version(0, "0-0:0", "0");
	test_pass(parseversion(&a, "0:0-0:0-0") == NULL);
	test_pass(versioncompare(&a, &b) == 0);

	/* Test valid characters in upstream version. */
	b = version(0, "azAZ09.-+~:", "0");
	test_pass(parseversion(&a, "0:azAZ09.-+~:-0") == NULL);
	test_pass(versioncompare(&a, &b) == 0);

	/* Test valid characters in revision. */
	b = version(0, "0", "azAZ09.+~");
	test_pass(parseversion(&a, "0:0-azAZ09.+~") == NULL);
	test_pass(versioncompare(&a, &b) == 0);

	/* Test invalid characters in epoch. */
	test_fail(parseversion(&a, "a:0-0") == NULL);
	test_fail(parseversion(&a, "A:0-0") == NULL);

	/* FIXME: parseversion() should validate input! */
#if 0
	/* Test invalid characters in upstream version. */
	test_fail(parseversion(&a, "0:!#@$%&/|\\<>()[]{};,=*^'-0") == NULL);

	/* Test invalid characters in revision. */
	test_fail(parseversion(&a, "0:0-!#@$%&/|\\<>()[]{};,=*^'") == NULL);
#endif

	/* FIXME: Complete. */
}
Ejemplo n.º 9
0
void process_archive(const char *filename) {
  static const struct tar_operations tf = {
    .read = tarfileread,
    .extract_file = tarobject,
    .link = tarobject,
    .symlink = tarobject,
    .mkdir = tarobject,
    .mknod = 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 *cidir = NULL;
  static struct fileinlist *newconffiles, *newfileslist;
  static enum pkgstatus oldversionstatus;
  static struct varbuf depprobwhy;
  static struct tarcontext tc;

  int r, i;
  pid_t pid;
  struct pkgiterator *it;
  struct pkginfo *pkg, *otherpkg;
  char *cidirrest, *p;
  char conffilenamebuf[MAXCONFFILENAME];
  char *psize;
  const char *pfilename;
  struct fileinlist *newconff, **newconffileslastp;
  struct fileinlist *cfile;
  struct reversefilelistiter rlistit;
  struct conffile *searchconff, **iconffileslastp, *newiconff;
  struct dependency *dsearch, *newdeplist, **newdeplistlastp;
  struct dependency *newdep, *dep, *providecheck;
  struct deppossi *psearch, **newpossilastp, *possi, *newpossi, *pdep;
  FILE *conff;
  struct filenamenode *namenode;
  struct stat stab, oldfs;
  struct pkg_deconf_list *deconpil, *deconpiltemp;
  struct pkginfo *fixbytrigaw;

  cleanup_pkg_failed= cleanup_conflictor_failed= 0;

  pfilename = summarize_filename(filename);

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

  /* We can't ‘tentatively-reassemble’ packages. */
  if (!f_noact) {
    if (!deb_reassemble(&filename, &pfilename))
      return;
  }

  /* Verify the package. */
  if (!f_nodebsig)
    deb_verify(filename);

  /* Get the control information directory. */
  cidir = get_control_dir(cidir);
  cidirrest = cidir + strlen(cidir);
  push_cleanup(cu_cidir, ~0, NULL, 0, 2, (void *)cidir, (void *)cidirrest);

  pid = subproc_fork();
  if (pid == 0) {
    cidirrest[-1] = '\0';
    execlp(BACKEND, BACKEND, "--control", filename, cidir, NULL);
    ohshite(_("unable to execute %s (%s)"),
            _("package control information extraction"), BACKEND);
  }
  subproc_wait_check(pid, BACKEND " --control", 0);

  /* We want to guarantee the extracted files are on the disk, so that the
   * subsequent renames to the info database do not end up with old or zero
   * length files in case of a system crash. As neither dpkg-deb nor tar do
   * explicit fsync()s, we have to do them here.
   * XXX: This could be avoided by switching to an internal tar extractor. */
  dir_sync_contents(cidir);

  strcpy(cidirrest,CONTROLFILE);

  parsedb(cidir, pdb_parse_binary | pdb_ignorefiles | parsedb_force_flags(),
          &pkg);
  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, "%jd", (intmax_t)stab.st_size);
  pkg->files->size = psize;

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

  if (pkg->available.arch->type != arch_all &&
      pkg->available.arch->type != arch_native)
    forcibleerr(fc_architecture,
                _("package architecture (%s) does not match system (%s)"),
                pkg->available.arch->name, dpkg_arch_get(arch_native)->name);

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

  if (!wanttoinstall(pkg)) {
      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. */
      for (psearch = dsearch->list->ed->depended.installed;
           psearch;
           psearch = psearch->rev_next) {
        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, &fixbytrigaw, true)) {
        if (fixbytrigaw) {
          while (fixbytrigaw->trigaw.head)
            trigproc(fixbytrigaw->trigaw.head->pend);
        } else {
          varbuf_end_str(&depprobwhy);
          fprintf(stderr, _("dpkg: regarding %s containing %s, pre-dependency problem:\n%s"),
                  pfilename, pkgbin_name(pkg, &pkg->available, pnaw_nonambig),
                  depprobwhy.buf);
          if (!force_depends(dsearch->list))
            ohshit(_("pre-dependency problem - not installing %.250s"),
                   pkgbin_name(pkg, &pkg->available, pnaw_nonambig));
          warning(_("ignoring pre-dependency problem!"));
        }
      }
    }
  }
  /* Look for things that conflict with us. */
  for (psearch = pkg->set->depended.installed; psearch; psearch = psearch->rev_next) {
    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(pkg, pnaw_nonambig),
           versiondescribe(&pkg->installed.version,vdew_nonambig),
           pfilename);
    log_action("upgrade", pkg, &pkg->installed);
  } else {
    printf(_("Unpacking %s (from %s) ...\n"),
           pkgbin_name(pkg, &pkg->available, pnaw_nonambig), pfilename);
    log_action("install", pkg, &pkg->available);
  }

  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, &pkg->available);

  /* 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_closestream, ehflag_bombout, NULL, 0, 1, (void *)conff);
    while (fgets(conffilenamebuf,MAXCONFFILENAME-2,conff)) {
      struct filepackages_iterator *iter;

      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;

      iter = filepackages_iter_new(newconff->namenode);
      while ((otherpkg = filepackages_iter_next(iter))) {
        debug(dbg_conffdetail,
              "process_archive conffile `%s' in package %s - conff ?",
              newconff->namenode->name, pkg_name(otherpkg, pnaw_nonambig));
        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, pkg_name(otherpkg, pnaw_nonambig),
                searchconff->name);
        if (searchconff) {
          debug(dbg_conff,
                "process_archive conffile `%s' package=%s %s hash=%s",
                newconff->namenode->name, pkg_name(otherpkg, pnaw_nonambig),
                otherpkg == pkg ? "same" : "different!",
                searchconff->hash);
          if (otherpkg == pkg)
            break;
        }
      }
      filepackages_iter_free(iter);

      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_set_eflags(pkg, eflag_reinstreq);
    pkg_set_status(pkg, stat_halfconfigured);
    modstatdb_note(pkg);
    push_cleanup(cu_prermupgrade, ~ehflag_normaltidy, NULL, 0, 1, (void *)pkg);
    if (versioncompare(&pkg->available.version,
                       &pkg->installed.version) >= 0) /* Upgrade or reinstall */
      maintainer_script_alternative(pkg, PRERMFILE, "pre-removal", cidir, cidirrest,
                                    "upgrade", "failed-upgrade");
    else /* Downgrade => no fallback */
      maintainer_script_installed(pkg, PRERMFILE, "pre-removal", "upgrade",
                                  versiondescribe(&pkg->available.version,
                                                  vdew_nonambig), NULL);
    pkg_set_status(pkg, 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"),
             pkg_name(deconpil->pkg, pnaw_nonambig),
             pkg_name(removing, pnaw_nonambig));
    else
      printf(_("De-configuring %s ...\n"),
             pkg_name(deconpil->pkg, pnaw_nonambig));

    trig_activate_packageprocessing(deconpil->pkg);
    pkg_set_status(deconpil->pkg, 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->set->name,
                                  versiondescribe(&pkg->available.version,
                                                  vdew_nonambig),
                                  "removing", removing->set->name,
                                  versiondescribe(&removing->installed.version,
                                                  vdew_nonambig),
                                  NULL);
    } else {
      maintainer_script_installed(deconpil->pkg, PRERMFILE, "pre-removal",
                                  "deconfigure", "in-favour", pkg->set->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]);
    pkg_set_status(conflictor[i], 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->set->name,
                                versiondescribe(&pkg->available.version,
                                                vdew_nonambig),
                                NULL);
    pkg_set_status(conflictor[i], stat_halfinstalled);
    modstatdb_note(conflictor[i]);
  }

  pkg_set_eflags(pkg, eflag_reinstreq);
  if (pkg->status == stat_notinstalled)
    pkg->installed.version= pkg->available.version;
  pkg_set_status(pkg, 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"),
           pkgbin_name(pkg, &pkg->available, pnaw_nonambig));
  }

  /*
   * 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]);
  pid = subproc_fork();
  if (pid == 0) {
    m_dup2(p1[1],1); close(p1[0]); close(p1[1]);
    execlp(BACKEND, BACKEND, "--fsys-tarfile", filename, NULL);
    ohshite(_("unable to execute %s (%s)"),
            _("package filesystem archive extraction"), BACKEND);
  }
  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 = tar_extractor(&tc, &tf);
  if (r) {
    if (errno) {
      ohshite(_("error reading dpkg-deb tar output"));
    } else {
      ohshit(_("corrupted filesystem tarfile - corrupted package archive"));
    }
  }
  fd_skip(p1[0], -1, _("dpkg-deb: zap possible trailing zeros"));
  close(p1[0]);
  p1[0] = -1;
  subproc_wait_check(pid, BACKEND " --fsys-tarfile", PROCPIPE);

  tar_deferred_extract(newfileslist, pkg);

  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_set_status(pkg, 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, &pkg->installed);
    trig_file_activate(usenode, pkg);

    varbuf_trunc(&fnamevb, fnameidlu);
    varbuf_add_str(&fnamevb, usenode->name);
    varbuf_end_str(&fnamevb);

    if (!stat(namenode->name,&stab) && S_ISDIR(stab.st_mode)) {
      debug(dbg_eachfiledetail, "process_archive: %s is a directory",
	    namenode->name);
      if (dir_is_used_by_others(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 {
      struct fileinlist *sameas = NULL;
      static struct stat empty_stat;
      struct varbuf cfilename = VARBUF_INIT;

      /*
       * 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.
       */

      /* 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 the file has been filtered then treat it as if it didn't exist
	 * on the file system. */
	if (cfile->namenode->flags & fnnf_filtered)
	  continue;
	if (!cfile->namenode->filestat) {
	  struct stat tmp_stat;

	  varbuf_reset(&cfilename);
	  varbuf_add_str(&cfilename, instdir);
	  varbuf_add_char(&cfilename, '/');
	  varbuf_add_str(&cfilename, cfile->namenode->name);
	  varbuf_end_str(&cfilename);

	  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);
	}
      }

      varbuf_destroy(&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, &pkg->available, 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, &pkg->installed, TRIGGERSCIFILE),
                trig_cicb_interest_delete, NULL, pkg, &pkg->installed);
  trig_parse_ci(cidir, trig_cicb_interest_add, NULL, pkg, &pkg->available);
  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");
  pkg_infodb_update(pkg, cidir, cidirrest);

  /*
   * 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->rev_next = newpossi->rev_prev = NULL;
      newpossi->arch_is_implicit = possi->arch_is_implicit;
      newpossi->arch = possi->arch;
      newpossi->verrel= possi->verrel;
      if (possi->verrel != dvr_none)
        newpossi->version= possi->version;
      else
        blankversion(&newpossi->version);
      newpossi->cyclebreak = false;
      *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);

  /* We copy the text fields. */
  pkg->installed.essential= pkg->available.essential;
  pkg->installed.multiarch = pkg->available.multiarch;
  pkg->installed.description= pkg->available.description;
  pkg->installed.maintainer= pkg->available.maintainer;
  pkg->installed.source= pkg->available.source;
  pkg->installed.arch = pkg->available.arch;
  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 = pkg_db_iter_new();
  while ((otherpkg = pkg_db_iter_next_pkg(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",
          pkg_name(otherpkg, pnaw_nonambig));
    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->set->depended.installed;
         pdep;
         pdep = pdep->rev_next) {
      if (pdep->up->type != dep_depends && pdep->up->type != dep_predepends &&
          pdep->up->type != dep_recommends) continue;
      if (depisok(pdep->up, &depprobwhy, NULL, NULL, false))
        continue;
      varbuf_end_str(&depprobwhy);
      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->depended.installed;
             pdep;
             pdep = pdep->rev_next) {
          if (pdep->up->type != dep_depends && pdep->up->type != dep_predepends &&
              pdep->up->type != dep_recommends)
            continue;
          if (depisok(pdep->up, &depprobwhy, NULL, NULL, false))
            continue;
          varbuf_end_str(&depprobwhy);
          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;

    /* 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. */
    pkg_disappear(otherpkg, pkg);
  } /* while (otherpkg= ... */
  pkg_db_iter_free(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) {
    struct filepackages_iterator *iter;
    struct pkgset *divpkgset;

    if (!(cfile->namenode->flags & fnnf_elide_other_lists)) continue;
    if (cfile->namenode->divert && cfile->namenode->divert->useinstead) {
      divpkgset = cfile->namenode->divert->pkgset;
      if (divpkgset == pkg->set) {
        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, divpkgset ? divpkgset->name : "<local>");
      }
    } else {
      divpkgset = NULL;
      debug(dbg_eachfile, "process_archive looking for overwriting `%s'",
            cfile->namenode->name);
    }
    iter = filepackages_iter_new(cfile->namenode);
    while ((otherpkg = filepackages_iter_next(iter))) {
      debug(dbg_eachfiledetail, "process_archive ... found in %s",
            pkg_name(otherpkg, pnaw_nonambig));
      /* 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->set == divpkgset) {
        debug(dbg_eachfiledetail, "process_archive ... diverted, skipping");
        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->installed,
                            otherpkg->clientdata->files, fnnf_elide_other_lists);
      ensure_package_clientdata(otherpkg);
      debug(dbg_veryverbose, "process_archive overwrote from %s",
            pkg_name(otherpkg, pnaw_nonambig));
    }
    filepackages_iter_free(iter);
  }

  /* 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_set_status(pkg, 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;
    varbuf_trunc(&fnametmpvb, fnameidlu);
    varbuf_add_str(&fnametmpvb,
                   namenodetouse(cfile->namenode, pkg, &pkg->installed)->name);
    varbuf_add_str(&fnametmpvb, DPKGTEMPEXT);
    varbuf_end_str(&fnametmpvb);
    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_reset_eflags(pkg);
  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_int == act_install)
    add_to_queue(pkg);
}
Ejemplo n.º 10
0
/**
 * Parse an RFC-822 style file.
 *
 * donep may be NULL.
 * If donep is not NULL only one package's information is expected.
 */
int parsedb(const char *filename, enum parsedbflags flags,
            struct pkginfo **donep)
{
  struct pkgset tmp_set;
  struct pkginfo *new_pkg, *db_pkg;
  struct pkgbin *new_pkgbin, *db_pkgbin;
  struct pkg_parse_object pkg_obj;
  int fieldencountered[array_count(fieldinfos)];
  int pdone;
  struct parsedb_state ps;
  struct field_state fs;

  memset(&fs, 0, sizeof(fs));
  fs.fieldencountered = fieldencountered;

  parse_open(&ps, filename, flags);

  new_pkg = &tmp_set.pkg;
  if (flags & pdb_recordavailable)
    new_pkgbin = &new_pkg->available;
  else
    new_pkgbin = &new_pkg->installed;

  ps.pkg = new_pkg;
  ps.pkgbin = new_pkgbin;

  pkg_obj.pkg = new_pkg;
  pkg_obj.pkgbin = new_pkgbin;

  pdone= 0;

  /* Loop per package. */
  for (;;) {
    memset(fieldencountered, 0, sizeof(fieldencountered));
    pkgset_blank(&tmp_set);

    if (!parse_stanza(&ps, &fs, pkg_parse_field, &pkg_obj))
      break;

    if (pdone && donep)
      parse_error(&ps,
                  _("several package info entries found, only one allowed"));

    pkg_parse_verify(&ps, new_pkg, new_pkgbin);

    db_pkg = pkg_db_find_pkg(new_pkg->set->name, new_pkgbin->arch);
    if (flags & pdb_recordavailable)
      db_pkgbin = &db_pkg->available;
    else
      db_pkgbin = &db_pkg->installed;

    if ((flags & pdb_ignoreolder) &&
        versioncompare(&new_pkgbin->version, &db_pkgbin->version) < 0)
      continue;

    pkg_parse_copy(&ps, db_pkg, db_pkgbin, new_pkg, new_pkgbin);

    if (donep)
      *donep = db_pkg;
    pdone++;
    if (parse_EOF(&ps))
      break;
  }

  parse_close(&ps);

  varbuf_destroy(&fs.value);
  if (donep && !pdone) ohshit(_("no package information in `%.255s'"),filename);

  return pdone;
}
Ejemplo n.º 11
0
static bool atom_field_verify(struct atom * atom, FSAF * fp,
                              size_t start, size_t end)
{
	size_t len = end - start;
	struct fsaf_read_rv r = fsaf_read(fp, start, len);
	assert(r.len == len);
	switch (atom->mode) {
	case M_EXACT:
		if (len != atom->patlen) return false;
		if (atom->ignore_case) {
			return strncasecmp(atom->pat, r.b, len) == 0;
		} else {
			return strncmp(atom->pat, r.b, len) == 0;
		}
	case M_SUBSTR: {
#if 0
		if (atom->ignore_case) {
			return strncasestr(r.b, atom->pat, len);
		} else {
			return strnstr(r.b, atom->pat, len);
		}
#else
		bool rv;
		char * s = strndup(r.b, len);
		if (s == 0) fatal_enomem(0);
		if (atom->ignore_case) {
			rv = strcasestr(s, atom->pat) != 0;
		} else {
			rv = strstr(s, atom->pat) != 0;
		}
		free(s);
		return rv;
#endif
	}
	case M_REGEX: case M_EREGEX: {
		char * s = strndup(r.b, len);
		if (s == 0) fatal_enomem(0);
		int regex_errcode = regexec(&atom->regex, s, 0, 0, 0);
		free(s);
		if (regex_errcode == 0 || regex_errcode == REG_NOMATCH) {
			return (regex_errcode == 0);
		}
		/* Error handling be here. */
		assert(regex_errcode != 0 && regex_errcode != REG_NOMATCH);
		s = get_regerror (regex_errcode, &atom->regex);
		if (s == 0) { enomem (0); return false; }
		message(L_IMPORTANT, 0, "%s", s);
		free(s);
		return false;
	}
	case M_VER_EQ:case M_VER_LT:case M_VER_LE:case M_VER_GT:case M_VER_GE:
		;
		char *pats = strndup(atom->pat, atom->patlen);
		char *cands = strndup(r.b, len);
		struct versionrevision pat, cand;
		if (!parse_version(&pat, pats, atom->patlen)) {
			free(pats);
			free(cands);
			return false;
		}
		if (!parse_version(&cand, cands, len)) {
			free(pats);
			free(cands);
			return false;
		}
		int res = versioncompare(&cand, &pat);
		free(pats);
		free(cands);
		switch (atom->mode) {
		case M_VER_EQ:
			return res == 0;
		case M_VER_LT:
			return res < 0;
		case M_VER_LE:
			return res <= 0;
		case M_VER_GT:
			return res > 0;
		case M_VER_GE:
			return res >= 0;
		default:
			assert(0);
		}
	}
	assert(0);
}