/** \ingroup rpmdep * \file lib/rpmte.c * Routine(s) to handle an "rpmte" transaction element. */ #include "system.h" #include #include /* RPM_MACHTABLE_* */ #include #include #include #include #include "lib/rpmte_internal.h" #include "debug.h" int _rpmte_debug = 0; /** \ingroup rpmte * A single package instance to be installed/removed atomically. */ struct rpmte_s { rpmElementType type; /*!< Package disposition (installed/removed). */ Header h; /*!< Package header. */ char * NEVR; /*!< Package name-version-release. */ char * NEVRA; /*!< Package name-version-release.arch. */ char * name; /*!< Name: */ char * epoch; char * version; /*!< Version: */ char * release; /*!< Release: */ char * arch; /*!< Architecture hint. */ char * os; /*!< Operating system hint. */ int archScore; /*!< (TR_ADDED) Arch score. */ int osScore; /*!< (TR_ADDED) Os score. */ int isSource; /*!< (TR_ADDED) source rpm? */ rpmte depends; /*!< Package updated by this package (ERASE te) */ rpmte parent; /*!< Parent transaction element. */ int degree; /*!< No. of immediate children. */ int npreds; /*!< No. of predecessors. */ int tree; /*!< Tree index. */ int depth; /*!< Depth in dependency tree. */ int breadth; /*!< Breadth in dependency tree. */ unsigned int db_instance; /*!< Database instance (of removed pkgs) */ tsortInfo tsi; /*!< Dependency ordering chains. */ rpmds this; /*!< This package's provided NEVR. */ rpmds provides; /*!< Provides: dependencies. */ rpmds requires; /*!< Requires: dependencies. */ rpmds conflicts; /*!< Conflicts: dependencies. */ rpmds obsoletes; /*!< Obsoletes: dependencies. */ rpmfi fi; /*!< File information. */ rpmps probs; /*!< Problems (relocations) */ rpm_color_t color; /*!< Color bit(s) from package dependencies. */ rpm_loff_t pkgFileSize; /*!< No. of bytes in package file (approx). */ fnpyKey key; /*!< (TR_ADDED) Retrieval key. */ rpmRelocation * relocs; /*!< (TR_ADDED) Payload file relocations. */ int nrelocs; /*!< (TR_ADDED) No. of relocations. */ FD_t fd; /*!< (TR_ADDED) Payload file descriptor. */ #define RPMTE_HAVE_PRETRANS (1 << 0) #define RPMTE_HAVE_POSTTRANS (1 << 1) int transscripts; /*!< pre/posttrans script existence */ int failed; /*!< (parent) install/erase failed */ rpmfs fs; }; void rpmteCleanDS(rpmte te) { te->this = rpmdsFree(te->this); te->provides = rpmdsFree(te->provides); te->requires = rpmdsFree(te->requires); te->conflicts = rpmdsFree(te->conflicts); te->obsoletes = rpmdsFree(te->obsoletes); } /** * Destroy transaction element data. * @param p transaction element */ static void delTE(rpmte p) { if (p->relocs) { for (int i = 0; i < p->nrelocs; i++) { free(p->relocs[i].oldPath); free(p->relocs[i].newPath); } free(p->relocs); } rpmteCleanDS(p); p->fi = rpmfiFree(p->fi); if (p->fd != NULL) p->fd = fdFree(p->fd, RPMDBG_M("delTE")); p->os = _free(p->os); p->arch = _free(p->arch); p->epoch = _free(p->epoch); p->name = _free(p->name); p->version = _free(p->version); p->release = _free(p->release); p->NEVR = _free(p->NEVR); p->NEVRA = _free(p->NEVRA); p->h = headerFree(p->h); p->fs = rpmfsFree(p->fs); p->probs = rpmpsFree(p->probs); memset(p, 0, sizeof(*p)); /* XXX trash and burn */ /* FIX: p->{NEVR,name} annotations */ return; } static rpmfi getFI(rpmte p, rpmts ts, Header h) { rpmfiFlags fiflags; fiflags = (p->type == TR_ADDED) ? (RPMFI_NOHEADER | RPMFI_FLAGS_INSTALL) : (RPMFI_NOHEADER | RPMFI_FLAGS_ERASE); /* relocate stuff in header if necessary */ if (rpmteType(p) == TR_ADDED && rpmfsFC(p->fs) > 0) { if (!headerIsSource(h) && !headerIsEntry(h, RPMTAG_ORIGBASENAMES)) { rpmRelocateFileList(p->relocs, p->nrelocs, p->fs, h); } } return rpmfiNew(ts, h, RPMTAG_BASENAMES, fiflags); } /* stupid bubble sort, but it's probably faster here */ static void sortRelocs(rpmRelocation *relocations, int numRelocations) { for (int i = 0; i < numRelocations; i++) { int madeSwap = 0; for (int j = 1; j < numRelocations; j++) { rpmRelocation tmpReloc; if (relocations[j - 1].oldPath == NULL || /* XXX can't happen */ relocations[j ].oldPath == NULL || /* XXX can't happen */ strcmp(relocations[j - 1].oldPath, relocations[j].oldPath) <= 0) continue; tmpReloc = relocations[j - 1]; relocations[j - 1] = relocations[j]; relocations[j] = tmpReloc; madeSwap = 1; } if (!madeSwap) break; } } static void buildRelocs(rpmts ts, rpmte p, Header h, rpmRelocation *relocs) { int i; struct rpmtd_s validRelocs; for (rpmRelocation *r = relocs; r->oldPath || r->newPath; r++) p->nrelocs++; headerGet(h, RPMTAG_PREFIXES, &validRelocs, HEADERGET_MINMEM); p->relocs = xmalloc(sizeof(*p->relocs) * (p->nrelocs+1)); /* Build sorted relocation list from raw relocations. */ for (i = 0; i < p->nrelocs; i++) { char * t; /* * Default relocations (oldPath == NULL) are handled in the UI, * not rpmlib. */ if (relocs[i].oldPath == NULL) continue; /* XXX can't happen */ /* FIXME: Trailing /'s will confuse us greatly. Internal ones will too, but those are more trouble to fix up. :-( */ t = xstrdup(relocs[i].oldPath); p->relocs[i].oldPath = (t[0] == '/' && t[1] == '\0') ? t : stripTrailingChar(t, '/'); /* An old path w/o a new path is valid, and indicates exclusion */ if (relocs[i].newPath) { int valid = 0; const char *validprefix; t = xstrdup(relocs[i].newPath); p->relocs[i].newPath = (t[0] == '/' && t[1] == '\0') ? t : stripTrailingChar(t, '/'); /* FIX: relocations[i].oldPath == NULL */ /* Verify that the relocation's old path is in the header. */ rpmtdInit(&validRelocs); while ((validprefix = rpmtdNextString(&validRelocs))) { if (rstreq(validprefix, p->relocs[i].oldPath)) { valid = 1; break; } } if (!valid) { if (p->probs == NULL) { p->probs = rpmpsCreate(); } rpmpsAppend(p->probs, RPMPROB_BADRELOCATE, rpmteNEVRA(p), rpmteKey(p), p->relocs[i].oldPath, NULL, NULL, 0); } } else { p->relocs[i].newPath = NULL; } } p->relocs[i].oldPath = NULL; p->relocs[i].newPath = NULL; sortRelocs(p->relocs, p->nrelocs); rpmtdFreeData(&validRelocs); } /** * Initialize transaction element data from header. * @param ts transaction set * @param p transaction element * @param h header * @param key (TR_ADDED) package retrieval key (e.g. file name) * @param relocs (TR_ADDED) package file relocations */ static void addTE(rpmts ts, rpmte p, Header h, fnpyKey key, rpmRelocation * relocs) { p->name = headerGetAsString(h, RPMTAG_NAME); p->version = headerGetAsString(h, RPMTAG_VERSION); p->release = headerGetAsString(h, RPMTAG_RELEASE); p->epoch = headerGetAsString(h, RPMTAG_EPOCH); p->arch = headerGetAsString(h, RPMTAG_ARCH); p->archScore = p->arch ? rpmMachineScore(RPM_MACHTABLE_INSTARCH, p->arch) : 0; p->os = headerGetAsString(h, RPMTAG_OS); p->osScore = p->os ? rpmMachineScore(RPM_MACHTABLE_INSTOS, p->os) : 0; p->isSource = headerIsSource(h); p->NEVR = headerGetAsString(h, RPMTAG_NEVR); p->NEVRA = headerGetAsString(h, RPMTAG_NEVRA); p->nrelocs = 0; p->relocs = NULL; if (relocs != NULL) buildRelocs(ts, p, h, relocs); p->db_instance = headerGetInstance(h); p->key = key; p->fd = NULL; p->pkgFileSize = 0; p->this = rpmdsThis(h, RPMTAG_PROVIDENAME, RPMSENSE_EQUAL); p->provides = rpmdsNew(h, RPMTAG_PROVIDENAME, 0); p->requires = rpmdsNew(h, RPMTAG_REQUIRENAME, 0); p->conflicts = rpmdsNew(h, RPMTAG_CONFLICTNAME, 0); p->obsoletes = rpmdsNew(h, RPMTAG_OBSOLETENAME, 0); { // get number of files by hand as rpmfiNew needs p->fs struct rpmtd_s bnames; headerGet(h, RPMTAG_BASENAMES, &bnames, HEADERGET_MINMEM); p->fs = rpmfsNew(rpmtdCount(&bnames), p->type); rpmtdFreeData(&bnames); } p->fi = getFI(p, ts, h); /* See if we have pre/posttrans scripts. */ p->transscripts |= (headerIsEntry(h, RPMTAG_PRETRANS) && headerIsEntry(h, RPMTAG_PRETRANSPROG)) ? RPMTE_HAVE_PRETRANS : 0; p->transscripts |= (headerIsEntry(h, RPMTAG_POSTTRANS) && headerIsEntry(h, RPMTAG_POSTTRANSPROG)) ? RPMTE_HAVE_POSTTRANS : 0; rpmteColorDS(p, RPMTAG_PROVIDENAME); rpmteColorDS(p, RPMTAG_REQUIRENAME); return; } rpmte rpmteFree(rpmte te) { if (te != NULL) { delTE(te); te = _free(te); } return NULL; } rpmte rpmteNew(const rpmts ts, Header h, rpmElementType type, fnpyKey key, rpmRelocation * relocs, int dboffset) { rpmte p = xcalloc(1, sizeof(*p)); uint32_t *ep; struct rpmtd_s size; p->type = type; addTE(ts, p, h, key, relocs); switch (type) { case TR_ADDED: headerGet(h, RPMTAG_SIGSIZE, &size, HEADERGET_DEFAULT); if ((ep = rpmtdGetUint32(&size))) { p->pkgFileSize += 96 + 256 + *ep; } break; case TR_REMOVED: /* nothing to do */ break; } return p; } unsigned int rpmteDBInstance(rpmte te) { return (te != NULL ? te->db_instance : 0); } void rpmteSetDBInstance(rpmte te, unsigned int instance) { if (te != NULL) te->db_instance = instance; } Header rpmteHeader(rpmte te) { return (te != NULL && te->h != NULL ? headerLink(te->h) : NULL); } Header rpmteSetHeader(rpmte te, Header h) { if (te != NULL) { te->h = headerFree(te->h); if (h != NULL) te->h = headerLink(h); } return NULL; } rpmElementType rpmteType(rpmte te) { /* XXX returning negative for unsigned type */ return (te != NULL ? te->type : -1); } const char * rpmteN(rpmte te) { return (te != NULL ? te->name : NULL); } const char * rpmteE(rpmte te) { return (te != NULL ? te->epoch : NULL); } const char * rpmteV(rpmte te) { return (te != NULL ? te->version : NULL); } const char * rpmteR(rpmte te) { return (te != NULL ? te->release : NULL); } const char * rpmteA(rpmte te) { return (te != NULL ? te->arch : NULL); } const char * rpmteO(rpmte te) { return (te != NULL ? te->os : NULL); } int rpmteIsSource(rpmte te) { return (te != NULL ? te->isSource : 0); } rpm_color_t rpmteColor(rpmte te) { return (te != NULL ? te->color : 0); } rpm_color_t rpmteSetColor(rpmte te, rpm_color_t color) { rpm_color_t ocolor = 0; if (te != NULL) { ocolor = te->color; te->color = color; } return ocolor; } rpm_loff_t rpmtePkgFileSize(rpmte te) { return (te != NULL ? te->pkgFileSize : 0); } int rpmteDepth(rpmte te) { return (te != NULL ? te->depth : 0); } int rpmteSetDepth(rpmte te, int ndepth) { int odepth = 0; if (te != NULL) { odepth = te->depth; te->depth = ndepth; } return odepth; } int rpmteBreadth(rpmte te) { return (te != NULL ? te->depth : 0); } int rpmteSetBreadth(rpmte te, int nbreadth) { int obreadth = 0; if (te != NULL) { obreadth = te->breadth; te->breadth = nbreadth; } return obreadth; } int rpmteNpreds(rpmte te) { return (te != NULL ? te->npreds : 0); } int rpmteSetNpreds(rpmte te, int npreds) { int opreds = 0; if (te != NULL) { opreds = te->npreds; te->npreds = npreds; } return opreds; } int rpmteTree(rpmte te) { return (te != NULL ? te->tree : 0); } int rpmteSetTree(rpmte te, int ntree) { int otree = 0; if (te != NULL) { otree = te->tree; te->tree = ntree; } return otree; } rpmte rpmteParent(rpmte te) { return (te != NULL ? te->parent : NULL); } rpmte rpmteSetParent(rpmte te, rpmte pte) { rpmte opte = NULL; if (te != NULL) { opte = te->parent; te->parent = pte; } return opte; } int rpmteDegree(rpmte te) { return (te != NULL ? te->degree : 0); } int rpmteSetDegree(rpmte te, int ndegree) { int odegree = 0; if (te != NULL) { odegree = te->degree; te->degree = ndegree; } return odegree; } tsortInfo rpmteTSI(rpmte te) { return te->tsi; } void rpmteFreeTSI(rpmte te) { relation rel; if (te == NULL || rpmteTSI(te) == NULL) return; while (te->tsi->tsi_relations != NULL) { rel = te->tsi->tsi_relations; te->tsi->tsi_relations = te->tsi->tsi_relations->rel_next; rel = _free(rel); } while (te->tsi->tsi_forward_relations != NULL) { rel = te->tsi->tsi_forward_relations; te->tsi->tsi_forward_relations = \ te->tsi->tsi_forward_relations->rel_next; rel = _free(rel); } te->tsi = _free(te->tsi); } void rpmteNewTSI(rpmte te) { if (te != NULL) { rpmteFreeTSI(te); te->tsi = xcalloc(1, sizeof(*te->tsi)); memset(te->tsi, 0, sizeof(*te->tsi)); } } void rpmteSetDependsOn(rpmte te, rpmte depends) { te->depends = depends; } rpmte rpmteDependsOn(rpmte te) { return te->depends; } int rpmteDBOffset(rpmte te) { return rpmteDBInstance(te); } const char * rpmteEVR(rpmte te) { return (te != NULL ? te->NEVR + strlen(te->name) + 1 : NULL); } const char * rpmteNEVR(rpmte te) { return (te != NULL ? te->NEVR : NULL); } const char * rpmteNEVRA(rpmte te) { return (te != NULL ? te->NEVRA : NULL); } FD_t rpmteSetFd(rpmte te, FD_t fd) { if (te != NULL) { if (te->fd != NULL) te->fd = fdFree(te->fd, RPMDBG_M("rpmteSetFd")); if (fd != NULL) te->fd = fdLink(fd, RPMDBG_M("rpmteSetFd")); } return NULL; } FD_t rpmteFd(rpmte te) { return (te != NULL ? te->fd : NULL); } fnpyKey rpmteKey(rpmte te) { return (te != NULL ? te->key : NULL); } rpmds rpmteDS(rpmte te, rpmTag tag) { if (te == NULL) return NULL; if (tag == RPMTAG_NAME) return te->this; else if (tag == RPMTAG_PROVIDENAME) return te->provides; else if (tag == RPMTAG_REQUIRENAME) return te->requires; else if (tag == RPMTAG_CONFLICTNAME) return te->conflicts; else if (tag == RPMTAG_OBSOLETENAME) return te->obsoletes; else return NULL; } rpmfi rpmteSetFI(rpmte te, rpmfi fi) { if (te != NULL) { te->fi = rpmfiFree(te->fi); if (fi != NULL) te->fi = rpmfiLink(fi, __FUNCTION__); } return NULL; } rpmfi rpmteFI(rpmte te) { if (te == NULL) return NULL; return te->fi; /* XXX take fi reference here? */ } void rpmteColorDS(rpmte te, rpmTag tag) { rpmfi fi = rpmteFI(te); rpmds ds = rpmteDS(te, tag); char deptype = 'R'; char mydt; const uint32_t * ddict; rpm_color_t * colors; int32_t * refs; rpm_color_t val; int Count; size_t nb; unsigned ix; int ndx, i; if (!(te && (Count = rpmdsCount(ds)) > 0 && rpmfiFC(fi) > 0)) return; switch (tag) { default: return; break; case RPMTAG_PROVIDENAME: deptype = 'P'; break; case RPMTAG_REQUIRENAME: deptype = 'R'; break; } colors = xcalloc(Count, sizeof(*colors)); nb = Count * sizeof(*refs); refs = memset(xmalloc(nb), -1, nb); /* Calculate dependency color and reference count. */ fi = rpmfiInit(fi, 0); if (fi != NULL) while (rpmfiNext(fi) >= 0) { val = rpmfiFColor(fi); ddict = NULL; ndx = rpmfiFDepends(fi, &ddict); if (ddict != NULL) while (ndx-- > 0) { ix = *ddict++; mydt = ((ix >> 24) & 0xff); if (mydt != deptype) continue; ix &= 0x00ffffff; assert (ix < Count); colors[ix] |= val; refs[ix]++; } } /* Set color/refs values in dependency set. */ ds = rpmdsInit(ds); while ((i = rpmdsNext(ds)) >= 0) { val = colors[i]; te->color |= val; (void) rpmdsSetColor(ds, val); val = refs[i]; if (val >= 0) val++; (void) rpmdsSetRefs(ds, val); } free(colors); free(refs); } int rpmtsiOc(rpmtsi tsi) { return tsi->ocsave; } rpmtsi rpmtsiFree(rpmtsi tsi) { /* XXX watchout: a funky recursion segfaults here iff nrefs is wrong. */ if (tsi) tsi->ts = rpmtsFree(tsi->ts); return _free(tsi); } rpmtsi rpmtsiInit(rpmts ts) { rpmtsi tsi = NULL; tsi = xcalloc(1, sizeof(*tsi)); tsi->ts = rpmtsLink(ts, RPMDBG_M("rpmtsi")); tsi->reverse = ((rpmtsFlags(ts) & RPMTRANS_FLAG_REVERSE) ? 1 : 0); tsi->oc = (tsi->reverse ? (rpmtsNElements(ts) - 1) : 0); tsi->ocsave = tsi->oc; return tsi; } /** * Return next transaction element. * @param tsi transaction element iterator * @return transaction element, NULL on termination */ static rpmte rpmtsiNextElement(rpmtsi tsi) { rpmte te = NULL; int oc = -1; if (tsi == NULL || tsi->ts == NULL || rpmtsNElements(tsi->ts) <= 0) return te; if (tsi->reverse) { if (tsi->oc >= 0) oc = tsi->oc--; } else { if (tsi->oc < rpmtsNElements(tsi->ts)) oc = tsi->oc++; } tsi->ocsave = oc; if (oc != -1) te = rpmtsElement(tsi->ts, oc); return te; } rpmte rpmtsiNext(rpmtsi tsi, rpmElementType type) { rpmte te; while ((te = rpmtsiNextElement(tsi)) != NULL) { if (type == 0 || (te->type & type) != 0) break; } return te; } static Header rpmteDBHeader(rpmts ts, unsigned int rec) { Header h = NULL; rpmdbMatchIterator mi; mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES, &rec, sizeof(rec)); /* iterator returns weak refs, grab hold of header */ if ((h = rpmdbNextIterator(mi))) h = headerLink(h); mi = rpmdbFreeIterator(mi); return h; } static Header rpmteFDHeader(rpmts ts, rpmte te) { Header h = NULL; te->fd = rpmtsNotify(ts, te, RPMCALLBACK_INST_OPEN_FILE, 0, 0); if (te->fd != NULL) { rpmVSFlags ovsflags; rpmRC pkgrc; ovsflags = rpmtsSetVSFlags(ts, rpmtsVSFlags(ts) | RPMVSF_NEEDPAYLOAD); pkgrc = rpmReadPackageFile(ts, rpmteFd(te), rpmteNEVRA(te), &h); rpmtsSetVSFlags(ts, ovsflags); switch (pkgrc) { default: rpmteClose(te, ts, 1); break; case RPMRC_NOTTRUSTED: case RPMRC_NOKEY: case RPMRC_OK: break; } } return h; } int rpmteOpen(rpmte te, rpmts ts, int reload_fi) { Header h = NULL; unsigned int instance; if (te == NULL || ts == NULL) goto exit; instance = rpmteDBInstance(te); rpmteSetHeader(te, NULL); switch (rpmteType(te)) { case TR_ADDED: h = instance ? rpmteDBHeader(ts, instance) : rpmteFDHeader(ts, te); break; case TR_REMOVED: h = rpmteDBHeader(ts, instance); break; } if (h != NULL) { if (reload_fi) { te->fi = getFI(te, ts, h); } rpmteSetHeader(te, h); headerFree(h); } exit: return (h != NULL); } int rpmteClose(rpmte te, rpmts ts, int reset_fi) { if (te == NULL || ts == NULL) return 0; switch (te->type) { case TR_ADDED: if (te->fd) { rpmtsNotify(ts, te, RPMCALLBACK_INST_CLOSE_FILE, 0, 0); te->fd = NULL; } break; case TR_REMOVED: /* eventually we'll want notifications for erase open too */ break; } rpmteSetHeader(te, NULL); if (reset_fi) { rpmteSetFI(te, NULL); } return 1; } int rpmteMarkFailed(rpmte te, rpmts ts) { rpmtsi pi = rpmtsiInit(ts); int rc = 0; rpmte p; te->failed = 1; /* XXX we can do a much better here than this... */ while ((p = rpmtsiNext(pi, TR_REMOVED))) { if (rpmteDependsOn(p) == te) { p->failed = 1; } } rpmtsiFree(pi); return rc; } int rpmteFailed(rpmte te) { return (te != NULL) ? te->failed : -1; } int rpmteHaveTransScript(rpmte te, rpmTag tag) { int rc = 0; if (tag == RPMTAG_PRETRANS) { rc = (te->transscripts & RPMTE_HAVE_PRETRANS); } else if (tag == RPMTAG_POSTTRANS) { rc = (te->transscripts & RPMTE_HAVE_POSTTRANS); } return rc; } rpmps rpmteProblems(rpmte te) { return te ? te->probs : NULL; } rpmfs rpmteGetFileStates(rpmte te) { return te->fs; } rpmfs rpmfsNew(unsigned int fc, rpmElementType type) { rpmfs fs = xmalloc(sizeof(*fs)); fs->fc = fc; fs->replaced = NULL; fs->states = NULL; if (type == TR_ADDED) { fs->states = xmalloc(sizeof(*fs->states) * fs->fc); memset(fs->states, RPMFILE_STATE_NORMAL, fs->fc); } fs->actions = xmalloc(fc * sizeof(*fs->actions)); memset(fs->actions, FA_UNKNOWN, fc * sizeof(*fs->actions)); fs->numReplaced = fs->allocatedReplaced = 0; return fs; } rpmfs rpmfsFree(rpmfs fs) { fs->replaced = _free(fs->replaced); fs->states = _free(fs->states); fs->actions = _free(fs->actions); fs = _free(fs); return fs; } rpm_count_t rpmfsFC(rpmfs fs) { return fs->fc; } void rpmfsAddReplaced(rpmfs fs, int pkgFileNum, int otherPkg, int otherFileNum) { if (!fs->replaced) { fs->replaced = xcalloc(3, sizeof(*fs->replaced)); fs->allocatedReplaced = 3; } if (fs->numReplaced>=fs->allocatedReplaced) { fs->allocatedReplaced += (fs->allocatedReplaced>>1) + 2; fs->replaced = xrealloc(fs->replaced, fs->allocatedReplaced*sizeof(*fs->replaced)); } fs->replaced[fs->numReplaced].pkgFileNum = pkgFileNum; fs->replaced[fs->numReplaced].otherPkg = otherPkg; fs->replaced[fs->numReplaced].otherFileNum = otherFileNum; fs->numReplaced++; } sharedFileInfo rpmfsGetReplaced(rpmfs fs) { if (fs && fs->numReplaced) return fs->replaced; else return NULL; } sharedFileInfo rpmfsNextReplaced(rpmfs fs , sharedFileInfo replaced) { if (fs && replaced) { replaced++; if (replaced - fs->replaced < fs->numReplaced) return replaced; } return NULL; } void rpmfsSetState(rpmfs fs, unsigned int ix, rpmfileState state) { assert(ix < fs->fc); fs->states[ix] = state; } rpmfileState rpmfsGetState(rpmfs fs, unsigned int ix) { assert(ix < fs->fc); if (fs->states) return fs->states[ix]; return RPMFILE_STATE_MISSING; } rpm_fstate_t * rpmfsGetStates(rpmfs fs) { return fs->states; } rpmFileAction rpmfsGetAction(rpmfs fs, unsigned int ix) { rpmFileAction action; if (fs->actions != NULL && ix < fs->fc) { action = fs->actions[ix]; } else { action = FA_UNKNOWN; } return action; } void rpmfsSetAction(rpmfs fs, unsigned int ix, rpmFileAction action) { if (fs->actions != NULL && ix < fs->fc) { fs->actions[ix] = action; } }