static rpmpsm rpmpsmNew(rpmts ts, rpmte te, pkgGoal goal) { rpmpsm psm = xcalloc(1, sizeof(*psm)); psm->ts = rpmtsLink(ts); psm->files = rpmteFiles(te); psm->te = te; /* XXX rpmte not refcounted yet */ if (!rpmteIsSource(te)) { /* * When we run scripts, we pass an argument which is the number of * versions of this package that will be installed when we are * finished. */ int npkgs_installed = rpmdbCountPackages(rpmtsGetRdb(ts), rpmteN(te)); switch (goal) { case PKG_INSTALL: case PKG_PRETRANS: psm->scriptArg = npkgs_installed + 1; psm->countCorrection = 0; break; case PKG_ERASE: psm->scriptArg = npkgs_installed - 1; psm->countCorrection = -1; break; case PKG_VERIFY: case PKG_POSTTRANS: psm->scriptArg = npkgs_installed; psm->countCorrection = 0; break; default: break; } } if (goal == PKG_INSTALL) { Header h = rpmteHeader(te); psm->total = headerGetNumber(h, RPMTAG_LONGARCHIVESIZE); headerFree(h); } else if (goal == PKG_ERASE) { psm->total = rpmfilesFC(psm->files); } /* Fake up something for packages with no files */ if (psm->total == 0) psm->total = 100; rpmlog(RPMLOG_DEBUG, "%s: %s has %d files\n", pkgGoalString(goal), rpmteNEVRA(psm->te), rpmfilesFC(psm->files)); return psm; }
static void rpmalAddFiles(rpmal al, rpmalNum pkgNum, rpmfiles fi) { struct availableIndexFileEntry_s fileEntry; int fc = rpmfilesFC(fi); rpm_color_t ficolor; int skipdoc = (al->tsflags & RPMTRANS_FLAG_NODOCS); int skipconf = (al->tsflags & RPMTRANS_FLAG_NOCONFIGS); fileEntry.pkgNum = pkgNum; for (int i = 0; i < fc; i++) { /* Ignore colored provides not in our rainbow. */ ficolor = rpmfilesFColor(fi, i); if (al->tscolor && ficolor && !(al->tscolor & ficolor)) continue; /* Ignore files that wont be installed */ if (skipdoc && (rpmfilesFFlags(fi, i) & RPMFILE_DOC)) continue; if (skipconf && (rpmfilesFFlags(fi, i) & RPMFILE_CONFIG)) continue; fileEntry.dirName = rpmfilesDNId(fi, rpmfilesDI(fi, i)); fileEntry.entryIx = i; rpmalFileHashAddEntry(al->fileHash, rpmfilesBNId(fi, i), fileEntry); } }
/* Calculate total number of files involved in transaction */ static uint64_t countFiles(rpmts ts) { uint64_t fc = 0; rpmtsi pi = rpmtsiInit(ts); rpmte p; rpmfiles files; while ((p = rpmtsiNext(pi, 0)) != NULL) { files = rpmteFiles(p); fc += rpmfilesFC(files); rpmfilesFree(files); } rpmtsiFree(pi); return fc; }
static void rpmalMakeFileIndex(rpmal al) { availablePackage alp; int i, fileCnt = 0; for (i = 0; i < al->size; i++) { alp = al->list + i; if (alp->fi != NULL) fileCnt += rpmfilesFC(alp->fi); } al->fileHash = rpmalFileHashCreate(fileCnt/4+128, sidHash, sidCmp, NULL, NULL); for (i = 0; i < al->size; i++) { alp = al->list + i; rpmalAddFiles(al, i, alp->fi); } }
static rpmRC rpmpsmUnpack(rpmpsm psm) { char *failedFile = NULL; int fsmrc = 0; int saved_errno = 0; rpmRC rc = RPMRC_OK; rpmpsmNotify(psm, RPMCALLBACK_INST_START, 0); /* make sure first progress call gets made */ rpmpsmNotify(psm, RPMCALLBACK_INST_PROGRESS, 0); if (!(rpmtsFlags(psm->ts) & RPMTRANS_FLAG_JUSTDB)) { if (rpmfilesFC(psm->files) > 0) { fsmrc = rpmPackageFilesInstall(psm->ts, psm->te, psm->files, psm, &failedFile); saved_errno = errno; } } /* XXX make sure progress reaches 100% */ rpmpsmNotify(psm, RPMCALLBACK_INST_PROGRESS, psm->total); rpmpsmNotify(psm, RPMCALLBACK_INST_STOP, psm->total); if (fsmrc) { char *emsg; errno = saved_errno; emsg = rpmfileStrerror(fsmrc); rpmlog(RPMLOG_ERR, _("unpacking of archive failed%s%s: %s\n"), (failedFile != NULL ? _(" on file ") : ""), (failedFile != NULL ? failedFile : ""), emsg); free(emsg); rc = RPMRC_FAIL; /* XXX notify callback on error. */ rpmtsNotify(psm->ts, psm->te, RPMCALLBACK_UNPACK_ERROR, 0, 0); } free(failedFile); return rc; }
static rpmRC rpmpsmRemove(rpmpsm psm) { char *failedFile = NULL; int fsmrc = 0; rpmpsmNotify(psm, RPMCALLBACK_UNINST_START, 0); /* make sure first progress call gets made */ rpmpsmNotify(psm, RPMCALLBACK_UNINST_PROGRESS, 0); /* XXX should't we log errors from here? */ if (!(rpmtsFlags(psm->ts) & RPMTRANS_FLAG_JUSTDB)) { if (rpmfilesFC(psm->files) > 0) { fsmrc = rpmPackageFilesRemove(psm->ts, psm->te, psm->files, psm, &failedFile); } } /* XXX make sure progress reaches 100% */ rpmpsmNotify(psm, RPMCALLBACK_UNINST_PROGRESS, psm->total); rpmpsmNotify(psm, RPMCALLBACK_UNINST_STOP, psm->total); free(failedFile); return (fsmrc == 0) ? RPMRC_OK : RPMRC_FAIL; }
/* XXX only ts->{probs,di} modified */ static void handleOverlappedFiles(rpmts ts, fingerPrintCache fpc, rpmte p, rpmfiles fi) { rpm_loff_t fixupSize = 0; int i, j; rpmfs fs = rpmteGetFileStates(p); rpmfs otherFs; rpm_count_t fc = rpmfilesFC(fi); int reportConflicts = !(rpmtsFilterFlags(ts) & RPMPROB_FILTER_REPLACENEWFILES); fingerPrint * fpList = rpmfilesFps(fi); for (i = 0; i < fc; i++) { struct fingerPrint_s * fiFps; int otherPkgNum, otherFileNum; rpmfiles otherFi; rpmte otherTe; rpmfileAttrs FFlags; struct rpmffi_s * recs; int numRecs; if (XFA_SKIPPING(rpmfsGetAction(fs, i))) continue; FFlags = rpmfilesFFlags(fi, i); fixupSize = 0; /* * Retrieve all records that apply to this file. Note that the * file info records were built in the same order as the packages * will be installed and removed so the records for an overlapped * files will be sorted in exactly the same order. */ fiFps = fpCacheGetByFp(fpc, fpList, i, &recs, &numRecs); /* * If this package is being added, look only at other packages * being added -- removed packages dance to a different tune. * * If both this and the other package are being added, overlapped * files must be identical (or marked as a conflict). The * disposition of already installed config files leads to * a small amount of extra complexity. * * If this package is being removed, then there are two cases that * need to be worried about: * If the other package is being added, then skip any overlapped files * so that this package removal doesn't nuke the overlapped files * that were just installed. * If both this and the other package are being removed, then each * file removal from preceding packages needs to be skipped so that * the file removal occurs only on the last occurrence of an overlapped * file in the transaction set. * */ /* * Locate this overlapped file in the set of added/removed packages, * including the package owning it: a package can have self-conflicting * files when directory symlinks are present. Don't compare a file * with itself though... */ for (j = 0; j < numRecs && !(recs[j].p == p && recs[j].fileno == i); j++) {}; /* Find what the previous disposition of this file was. */ otherFileNum = -1; /* keep gcc quiet */ otherFi = NULL; otherTe = NULL; otherFs = NULL; for (otherPkgNum = j - 1; otherPkgNum >= 0; otherPkgNum--) { otherTe = recs[otherPkgNum].p; otherFileNum = recs[otherPkgNum].fileno; otherFs = rpmteGetFileStates(otherTe); /* Added packages need only look at other added packages. */ if (rpmteType(p) == TR_ADDED && rpmteType(otherTe) != TR_ADDED) continue; /* XXX Happens iff fingerprint for incomplete package install. */ if (rpmfsGetAction(otherFs, otherFileNum) != FA_UNKNOWN) { otherFi = rpmteFiles(otherTe); break; } } switch (rpmteType(p)) { case TR_ADDED: if (otherPkgNum < 0) { /* XXX is this test still necessary? */ rpmFileAction action; if (rpmfsGetAction(fs, i) != FA_UNKNOWN) break; if (rpmfilesConfigConflict(fi, i)) { /* Here is a non-overlapped pre-existing config file. */ action = (FFlags & RPMFILE_NOREPLACE) ? FA_ALTNAME : FA_BACKUP; } else { action = FA_CREATE; } rpmfsSetAction(fs, i, action); break; } assert(otherFi != NULL); /* Mark added overlapped non-identical files as a conflict. */ if (rpmfilesCompare(otherFi, otherFileNum, fi, i)) { int rConflicts; /* If enabled, resolve colored conflicts to preferred type */ rConflicts = handleColorConflict(ts, fs, fi, i, otherFs, otherFi, otherFileNum); if (rConflicts && reportConflicts) { char *fn = rpmfilesFN(fi, i); rpmteAddProblem(p, RPMPROB_NEW_FILE_CONFLICT, rpmteNEVRA(otherTe), fn, 0); free(fn); } } else { /* Skip create on all but the first instance of a shared file */ rpmFileAction oaction = rpmfsGetAction(otherFs, otherFileNum); if (oaction != FA_UNKNOWN && !XFA_SKIPPING(oaction)) { rpmfileAttrs oflags; /* ...but ghosts aren't really created so... */ oflags = rpmfilesFFlags(otherFi, otherFileNum); if (!(oflags & RPMFILE_GHOST)) { rpmfsSetAction(fs, i, FA_SKIP); } /* if the other file is color skipped then skip this file too */ } else if (oaction == FA_SKIPCOLOR) { rpmfsSetAction(fs, i, FA_SKIPCOLOR); } } /* Skipped files dont need fixup size or backups, %config or not */ if (XFA_SKIPPING(rpmfsGetAction(fs, i))) break; /* Try to get the disk accounting correct even if a conflict. */ fixupSize = rpmfilesFSize(otherFi, otherFileNum); if (rpmfilesConfigConflict(fi, i)) { /* Here is an overlapped pre-existing config file. */ rpmFileAction action; action = (FFlags & RPMFILE_NOREPLACE) ? FA_ALTNAME : FA_SKIP; rpmfsSetAction(fs, i, action); } else { /* If not decided yet, create it */ if (rpmfsGetAction(fs, i) == FA_UNKNOWN) rpmfsSetAction(fs, i, FA_CREATE); } break; case TR_REMOVED: if (otherPkgNum >= 0) { assert(otherFi != NULL); /* Here is an overlapped added file we don't want to nuke. */ if (rpmfsGetAction(otherFs, otherFileNum) != FA_ERASE) { /* On updates, don't remove files. */ rpmfsSetAction(fs, i, FA_SKIP); break; } /* Here is an overlapped removed file: skip in previous. */ rpmfsSetAction(otherFs, otherFileNum, FA_SKIP); } if (XFA_SKIPPING(rpmfsGetAction(fs, i))) break; if (rpmfilesFState(fi, i) != RPMFILE_STATE_NORMAL) break; /* Pre-existing modified config files need to be saved. */ if (rpmfilesConfigConflict(fi, i)) { rpmfsSetAction(fs, i, FA_SAVE); break; } /* Otherwise, we can just erase. */ rpmfsSetAction(fs, i, FA_ERASE); break; } rpmfilesFree(otherFi); /* Update disk space info for a file. */ rpmtsUpdateDSI(ts, fpEntryDev(fpc, fiFps), fpEntryDir(fpc, fiFps), rpmfilesFSize(fi, i), rpmfilesFReplacedSize(fi, i), fixupSize, rpmfsGetAction(fs, i)); } }
static int rpmtsPrepare(rpmts ts) { tsMembers tsmem = rpmtsMembers(ts); rpmtsi pi; rpmte p; int rc = 0; uint64_t fileCount = countFiles(ts); const char *dbhome = NULL; struct stat dbstat; fingerPrintCache fpc = fpCacheCreate(fileCount/2 + 10001, rpmtsPool(ts)); rpmlog(RPMLOG_DEBUG, "computing %" PRIu64 " file fingerprints\n", fileCount); /* Reset actions, set skip for netshared paths and excluded files */ pi = rpmtsiInit(ts); while ((p = rpmtsiNext(pi, 0)) != NULL) { rpmfiles files = rpmteFiles(p); if (rpmfilesFC(files) > 0) { rpmfs fs = rpmteGetFileStates(p); /* Ensure clean state, this could get called more than once. */ rpmfsResetActions(fs); if (rpmteType(p) == TR_ADDED) { skipInstallFiles(ts, files, fs); } else { skipEraseFiles(ts, files, fs); } } rpmfilesFree(files); } rpmtsiFree(pi); /* Open rpmdb & enter chroot for fingerprinting if necessary */ if (rpmdbOpenAll(ts->rdb) || rpmChrootIn()) { rc = -1; goto exit; } rpmtsNotify(ts, NULL, RPMCALLBACK_TRANS_START, 6, tsmem->orderCount); /* Add fingerprint for each file not skipped. */ fpCachePopulate(fpc, ts, fileCount); /* check against files in the rpmdb */ checkInstalledFiles(ts, fileCount, fpc); dbhome = rpmdbHome(rpmtsGetRdb(ts)); /* If we can't stat, ignore db growth. Probably not right but... */ if (dbhome && stat(dbhome, &dbstat)) dbhome = NULL; pi = rpmtsiInit(ts); while ((p = rpmtsiNext(pi, 0)) != NULL) { rpmfiles files = rpmteFiles(p);; if (files == NULL) continue; /* XXX can't happen */ (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_FINGERPRINT), 0); /* check files in ts against each other and update disk space needs on each partition for this package. */ handleOverlappedFiles(ts, fpc, p, files); /* Check added package has sufficient space on each partition used. */ if (rpmteType(p) == TR_ADDED) { /* * Try to estimate space needed for rpmdb growth: guess that the * db grows 4 times the header size (indexes and all). */ if (dbhome) { int64_t hsize = rpmteHeaderSize(p) * 4; rpmtsUpdateDSI(ts, dbstat.st_dev, dbhome, hsize, 0, 0, FA_CREATE); } rpmtsCheckDSIProblems(ts, p); } (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_FINGERPRINT), 0); rpmfilesFree(files); } rpmtsiFree(pi); rpmtsNotify(ts, NULL, RPMCALLBACK_TRANS_STOP, 6, tsmem->orderCount); /* return from chroot if done earlier */ if (rpmChrootOut()) rc = -1; /* On actual transaction, file info sets are not needed after this */ if (!(rpmtsFlags(ts) & (RPMTRANS_FLAG_TEST|RPMTRANS_FLAG_BUILD_PROBS))) { pi = rpmtsiInit(ts); while ((p = rpmtsiNext(pi, 0)) != NULL) { rpmteCleanFiles(p); } rpmtsiFree(pi); } exit: fpCacheFree(fpc); rpmtsFreeDSI(ts); return rc; }
/** \ingroup payload * Create directory name iterator. * @param fi file info set * @param fs file state set * @param reverse traverse directory names in reverse order? * @return directory name iterator */ static DNLI_t dnlInitIterator(rpmfiles fi, rpmfs fs, int reverse) { DNLI_t dnli; int i, j; int dc; if (fi == NULL) return NULL; dc = rpmfilesDC(fi); dnli = xcalloc(1, sizeof(*dnli)); dnli->fi = fi; dnli->reverse = reverse; dnli->i = (reverse ? dc : 0); if (dc) { dnli->active = xcalloc(dc, sizeof(*dnli->active)); int fc = rpmfilesFC(fi); /* Identify parent directories not skipped. */ for (i = 0; i < fc; i++) if (!XFA_SKIPPING(rpmfsGetAction(fs, i))) dnli->active[rpmfilesDI(fi, i)] = 1; /* Exclude parent directories that are explicitly included. */ for (i = 0; i < fc; i++) { int dil; size_t dnlen, bnlen; if (!S_ISDIR(rpmfilesFMode(fi, i))) continue; dil = rpmfilesDI(fi, i); dnlen = strlen(rpmfilesDN(fi, dil)); bnlen = strlen(rpmfilesBN(fi, i)); for (j = 0; j < dc; j++) { const char * dnl; size_t jlen; if (!dnli->active[j] || j == dil) continue; dnl = rpmfilesDN(fi, j); jlen = strlen(dnl); if (jlen != (dnlen+bnlen+1)) continue; if (!rstreqn(dnl, rpmfilesDN(fi, dil), dnlen)) continue; if (!rstreqn(dnl+dnlen, rpmfilesBN(fi, i), bnlen)) continue; if (dnl[dnlen+bnlen] != '/' || dnl[dnlen+bnlen+1] != '\0') continue; /* This directory is included in the package. */ dnli->active[j] = 0; break; } } /* Print only once per package. */ if (!reverse) { j = 0; for (i = 0; i < dc; i++) { if (!dnli->active[i]) continue; if (j == 0) { j = 1; rpmlog(RPMLOG_DEBUG, "========== Directories not explicitly included in package:\n"); } rpmlog(RPMLOG_DEBUG, "%10d %s\n", i, rpmfilesDN(fi, i)); } if (j) rpmlog(RPMLOG_DEBUG, "==========\n"); } } return dnli; }