/* * libdpkg - Debian packaging suite library routines * dump.c - code to write in-core database to a file * * Copyright © 1995 Ian Jackson * Copyright © 2001 Wichert Akkerman * Copyright © 2006,2008-2014 Guillem Jover * Copyright © 2011 Linaro Limited * Copyright © 2011 Raphaël Hertzog * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static inline void varbuf_add_fieldname(struct varbuf *vb, const struct fieldinfo *fip) { varbuf_add_str(vb, fip->name); varbuf_add_str(vb, ": "); } void w_name(struct varbuf *vb, const struct pkginfo *pkg, const struct pkgbin *pkgbin, enum fwriteflags flags, const struct fieldinfo *fip) { if (pkg->set->name == NULL) internerr("pkgset has no name"); if (flags&fw_printheader) varbuf_add_str(vb, "Package: "); varbuf_add_str(vb, pkg->set->name); if (flags&fw_printheader) varbuf_add_char(vb, '\n'); } void w_version(struct varbuf *vb, const struct pkginfo *pkg, const struct pkgbin *pkgbin, enum fwriteflags flags, const struct fieldinfo *fip) { if (!dpkg_version_is_informative(&pkgbin->version)) return; if (flags&fw_printheader) varbuf_add_str(vb, "Version: "); varbufversion(vb, &pkgbin->version, vdew_nonambig); if (flags&fw_printheader) varbuf_add_char(vb, '\n'); } void w_configversion(struct varbuf *vb, const struct pkginfo *pkg, const struct pkgbin *pkgbin, enum fwriteflags flags, const struct fieldinfo *fip) { if (pkgbin != &pkg->installed) return; if (!dpkg_version_is_informative(&pkg->configversion)) return; if (pkg->status == PKG_STAT_INSTALLED || pkg->status == PKG_STAT_NOTINSTALLED || pkg->status == PKG_STAT_TRIGGERSPENDING) return; if (flags&fw_printheader) varbuf_add_str(vb, "Config-Version: "); varbufversion(vb, &pkg->configversion, vdew_nonambig); if (flags&fw_printheader) varbuf_add_char(vb, '\n'); } void w_null(struct varbuf *vb, const struct pkginfo *pkg, const struct pkgbin *pkgbin, enum fwriteflags flags, const struct fieldinfo *fip) { } void w_section(struct varbuf *vb, const struct pkginfo *pkg, const struct pkgbin *pkgbin, enum fwriteflags flags, const struct fieldinfo *fip) { const char *value = pkg->section; if (str_is_unset(value)) return; if (flags&fw_printheader) varbuf_add_str(vb, "Section: "); varbuf_add_str(vb, value); if (flags&fw_printheader) varbuf_add_char(vb, '\n'); } void w_charfield(struct varbuf *vb, const struct pkginfo *pkg, const struct pkgbin *pkgbin, enum fwriteflags flags, const struct fieldinfo *fip) { const char *value = STRUCTFIELD(pkgbin, fip->integer, const char *); if (str_is_unset(value)) return; if (flags & fw_printheader) varbuf_add_fieldname(vb, fip); varbuf_add_str(vb, value); if (flags&fw_printheader) varbuf_add_char(vb, '\n'); } void w_archives(struct varbuf *vb, const struct pkginfo *pkg, const struct pkgbin *pkgbin, enum fwriteflags flags, const struct fieldinfo *fip) { struct archivedetails *archive; if (pkgbin != &pkg->available) return; archive = pkg->archives; if (!archive || !STRUCTFIELD(archive, fip->integer, const char *)) return; if (flags&fw_printheader) { varbuf_add_str(vb, fip->name); varbuf_add_char(vb, ':'); } while (archive) { varbuf_add_char(vb, ' '); varbuf_add_str(vb, STRUCTFIELD(archive, fip->integer, const char *)); archive = archive->next; } if (flags&fw_printheader) varbuf_add_char(vb, '\n'); } void w_booleandefno(struct varbuf *vb, const struct pkginfo *pkg, const struct pkgbin *pkgbin, enum fwriteflags flags, const struct fieldinfo *fip) { bool value = STRUCTFIELD(pkgbin, fip->integer, bool); if ((flags & fw_printheader) && !value) return; if (flags & fw_printheader) varbuf_add_fieldname(vb, fip); varbuf_add_str(vb, value ? "yes" : "no"); if (flags & fw_printheader) varbuf_add_char(vb, '\n'); } void w_multiarch(struct varbuf *vb, const struct pkginfo *pkg, const struct pkgbin *pkgbin, enum fwriteflags flags, const struct fieldinfo *fip) { int value = STRUCTFIELD(pkgbin, fip->integer, int); if ((flags & fw_printheader) && !value) return; if (flags & fw_printheader) varbuf_add_fieldname(vb, fip); varbuf_add_str(vb, multiarchinfos[value].name); if (flags & fw_printheader) varbuf_add_char(vb, '\n'); } void w_architecture(struct varbuf *vb, const struct pkginfo *pkg, const struct pkgbin *pkgbin, enum fwriteflags flags, const struct fieldinfo *fip) { if (!pkgbin->arch) return; if (pkgbin->arch->type == DPKG_ARCH_NONE) return; if (pkgbin->arch->type == DPKG_ARCH_EMPTY) return; if (flags & fw_printheader) varbuf_add_fieldname(vb, fip); varbuf_add_str(vb, pkgbin->arch->name); if (flags & fw_printheader) varbuf_add_char(vb, '\n'); } void w_priority(struct varbuf *vb, const struct pkginfo *pkg, const struct pkgbin *pkgbin, enum fwriteflags flags, const struct fieldinfo *fip) { if (pkg->priority == PKG_PRIO_UNKNOWN) return; if (pkg->priority > PKG_PRIO_UNKNOWN) internerr("package %s has out-of-range priority %d", pkgbin_name_const(pkg, pkgbin, pnaw_always), pkg->priority); if (flags&fw_printheader) varbuf_add_str(vb, "Priority: "); varbuf_add_str(vb, pkg_priority_name(pkg)); if (flags&fw_printheader) varbuf_add_char(vb, '\n'); } void w_status(struct varbuf *vb, const struct pkginfo *pkg, const struct pkgbin *pkgbin, enum fwriteflags flags, const struct fieldinfo *fip) { if (pkgbin != &pkg->installed) return; if (pkg->want > PKG_WANT_PURGE) internerr("package %s has unknown want state %d", pkgbin_name_const(pkg, pkgbin, pnaw_always), pkg->want); if (pkg->eflag > PKG_EFLAG_REINSTREQ) internerr("package %s has unknown error state %d", pkgbin_name_const(pkg, pkgbin, pnaw_always), pkg->eflag); switch (pkg->status) { case PKG_STAT_NOTINSTALLED: case PKG_STAT_CONFIGFILES: if (pkg->trigpend_head || pkg->trigaw.head) internerr("package %s in state %s, has awaited or pending triggers", pkgbin_name_const(pkg, pkgbin, pnaw_always), pkg_status_name(pkg)); break; case PKG_STAT_HALFINSTALLED: case PKG_STAT_UNPACKED: case PKG_STAT_HALFCONFIGURED: if (pkg->trigpend_head) internerr("package %s in state %s, has pending triggers", pkgbin_name_const(pkg, pkgbin, pnaw_always), pkg_status_name(pkg)); break; case PKG_STAT_TRIGGERSAWAITED: if (pkg->trigaw.head == NULL) internerr("package %s in state %s, has no awaited triggers", pkgbin_name_const(pkg, pkgbin, pnaw_always), pkg_status_name(pkg)); break; case PKG_STAT_TRIGGERSPENDING: if (pkg->trigpend_head == NULL || pkg->trigaw.head) internerr("package %s in stata %s, has awaited or no pending triggers", pkgbin_name_const(pkg, pkgbin, pnaw_always), pkg_status_name(pkg)); break; case PKG_STAT_INSTALLED: if (pkg->trigpend_head || pkg->trigaw.head) internerr("package %s in state %s, has awaited or pending triggers", pkgbin_name_const(pkg, pkgbin, pnaw_always), pkg_status_name(pkg)); break; default: internerr("unknown package status '%d'", pkg->status); } if (flags&fw_printheader) varbuf_add_str(vb, "Status: "); varbuf_add_str(vb, pkg_want_name(pkg)); varbuf_add_char(vb, ' '); varbuf_add_str(vb, pkg_eflag_name(pkg)); varbuf_add_char(vb, ' '); varbuf_add_str(vb, pkg_status_name(pkg)); if (flags&fw_printheader) varbuf_add_char(vb, '\n'); } void varbufdependency(struct varbuf *vb, struct dependency *dep) { struct deppossi *dop; const char *possdel; possdel= ""; for (dop= dep->list; dop; dop= dop->next) { if (dop->up != dep) internerr("dependency and deppossi not linked properly"); varbuf_add_str(vb, possdel); possdel = " | "; varbuf_add_str(vb, dop->ed->name); if (!dop->arch_is_implicit) varbuf_add_archqual(vb, dop->arch); if (dop->verrel != DPKG_RELATION_NONE) { varbuf_add_str(vb, " ("); switch (dop->verrel) { case DPKG_RELATION_EQ: varbuf_add_char(vb, '='); break; case DPKG_RELATION_GE: varbuf_add_str(vb, ">="); break; case DPKG_RELATION_LE: varbuf_add_str(vb, "<="); break; case DPKG_RELATION_GT: varbuf_add_str(vb, ">>"); break; case DPKG_RELATION_LT: varbuf_add_str(vb, "<<"); break; default: internerr("unknown dpkg_relation %d", dop->verrel); } varbuf_add_char(vb, ' '); varbufversion(vb,&dop->version,vdew_nonambig); varbuf_add_char(vb, ')'); } } } void w_dependency(struct varbuf *vb, const struct pkginfo *pkg, const struct pkgbin *pkgbin, enum fwriteflags flags, const struct fieldinfo *fip) { struct dependency *dyp; bool dep_found = false; for (dyp = pkgbin->depends; dyp; dyp = dyp->next) { if (dyp->type != fip->integer) continue; if (dyp->up != pkg) internerr("dependency and package %s not linked properly", pkgbin_name_const(pkg, pkgbin, pnaw_always)); if (dep_found) { varbuf_add_str(vb, ", "); } else { if (flags & fw_printheader) varbuf_add_fieldname(vb, fip); dep_found = true; } varbufdependency(vb,dyp); } if ((flags & fw_printheader) && dep_found) varbuf_add_char(vb, '\n'); } void w_conffiles(struct varbuf *vb, const struct pkginfo *pkg, const struct pkgbin *pkgbin, enum fwriteflags flags, const struct fieldinfo *fip) { struct conffile *i; if (!pkgbin->conffiles || pkgbin == &pkg->available) return; if (flags&fw_printheader) varbuf_add_str(vb, "Conffiles:\n"); for (i = pkgbin->conffiles; i; i = i->next) { if (i != pkgbin->conffiles) varbuf_add_char(vb, '\n'); varbuf_add_char(vb, ' '); varbuf_add_str(vb, i->name); varbuf_add_char(vb, ' '); varbuf_add_str(vb, i->hash); if (i->obsolete) varbuf_add_str(vb, " obsolete"); if (i->remove_on_upgrade) varbuf_add_str(vb, " remove-on-upgrade"); } if (flags&fw_printheader) varbuf_add_char(vb, '\n'); } void w_trigpend(struct varbuf *vb, const struct pkginfo *pkg, const struct pkgbin *pkgbin, enum fwriteflags flags, const struct fieldinfo *fip) { struct trigpend *tp; if (pkgbin == &pkg->available || !pkg->trigpend_head) return; if (pkg->status < PKG_STAT_TRIGGERSAWAITED || pkg->status > PKG_STAT_TRIGGERSPENDING) internerr("package %s in non-trigger state %s, has pending triggers", pkgbin_name_const(pkg, pkgbin, pnaw_always), pkg_status_name(pkg)); if (flags & fw_printheader) varbuf_add_str(vb, "Triggers-Pending:"); for (tp = pkg->trigpend_head; tp; tp = tp->next) { varbuf_add_char(vb, ' '); varbuf_add_str(vb, tp->name); } if (flags & fw_printheader) varbuf_add_char(vb, '\n'); } void w_trigaw(struct varbuf *vb, const struct pkginfo *pkg, const struct pkgbin *pkgbin, enum fwriteflags flags, const struct fieldinfo *fip) { struct trigaw *ta; if (pkgbin == &pkg->available || !pkg->trigaw.head) return; if (pkg->status <= PKG_STAT_CONFIGFILES || pkg->status > PKG_STAT_TRIGGERSAWAITED) internerr("package %s in state %s, has awaited triggers", pkgbin_name_const(pkg, pkgbin, pnaw_always), pkg_status_name(pkg)); if (flags & fw_printheader) varbuf_add_str(vb, "Triggers-Awaited:"); for (ta = pkg->trigaw.head; ta; ta = ta->sameaw.next) { varbuf_add_char(vb, ' '); varbuf_add_pkgbin_name(vb, ta->pend, &ta->pend->installed, pnaw_nonambig); } if (flags & fw_printheader) varbuf_add_char(vb, '\n'); } void varbuf_add_arbfield(struct varbuf *vb, const struct arbitraryfield *arbfield, enum fwriteflags flags) { if (flags & fw_printheader) { varbuf_add_str(vb, arbfield->name); varbuf_add_str(vb, ": "); } varbuf_add_str(vb, arbfield->value); if (flags & fw_printheader) varbuf_add_char(vb, '\n'); } void varbuf_stanza(struct varbuf *vb, const struct pkginfo *pkg, const struct pkgbin *pkgbin) { const struct fieldinfo *fip; const struct arbitraryfield *afp; for (fip= fieldinfos; fip->name; fip++) { fip->wcall(vb, pkg, pkgbin, fw_printheader, fip); } for (afp = pkgbin->arbs; afp; afp = afp->next) { varbuf_add_arbfield(vb, afp, fw_printheader); } } void write_stanza(FILE *file, const char *filename, const struct pkginfo *pkg, const struct pkgbin *pkgbin) { struct varbuf vb = VARBUF_INIT; varbuf_stanza(&vb, pkg, pkgbin); varbuf_end_str(&vb); if (fputs(vb.buf, file) < 0) ohshite(_("failed to write details of '%.50s' to '%.250s'"), pkgbin_name_const(pkg, pkgbin, pnaw_nonambig), filename); varbuf_destroy(&vb); } void writedb_stanzas(FILE *fp, const char *filename, enum writedb_flags flags) { static char writebuf[8192]; struct pkg_array array; struct pkginfo *pkg; struct pkgbin *pkgbin; const char *which; struct varbuf vb = VARBUF_INIT; int i; which = (flags & wdb_dump_available) ? "available" : "status"; if (setvbuf(fp, writebuf, _IOFBF, sizeof(writebuf))) ohshite(_("unable to set buffering on %s database file"), which); pkg_array_init_from_hash(&array); pkg_array_sort(&array, pkg_sorter_by_nonambig_name_arch); for (i = 0; i < array.n_pkgs; i++) { pkg = array.pkgs[i]; pkgbin = (flags & wdb_dump_available) ? &pkg->available : &pkg->installed; /* Don't dump stanzas which have no useful content. */ if (!pkg_is_informative(pkg, pkgbin)) continue; varbuf_stanza(&vb, pkg, pkgbin); varbuf_add_char(&vb, '\n'); varbuf_end_str(&vb); if (fputs(vb.buf, fp) < 0) ohshite(_("failed to write %s database stanza about '%s' to '%s'"), which, pkgbin_name(pkg, pkgbin, pnaw_nonambig), filename); varbuf_reset(&vb); } pkg_array_destroy(&array); varbuf_destroy(&vb); } void writedb(const char *filename, enum writedb_flags flags) { struct atomic_file *file; enum atomic_file_flags atomic_flags = ATOMIC_FILE_BACKUP; if (flags & wdb_dump_available) atomic_flags = 0; file = atomic_file_new(filename, atomic_flags); atomic_file_open(file); writedb_stanzas(file->fp, filename, flags); if (flags & wdb_must_sync) atomic_file_sync(file); atomic_file_close(file); atomic_file_commit(file); atomic_file_free(file); if (flags & wdb_must_sync) dir_sync_path_parent(filename); }