2 * \file lib/transaction.c
7 #include <rpm/rpmlib.h> /* rpmMachineScore, rpmReadPackageFile */
8 #include <rpm/rpmmacro.h> /* XXX for rpmExpand */
9 #include <rpm/rpmlog.h>
10 #include <rpm/rpmdb.h>
11 #include <rpm/rpmds.h>
12 #include <rpm/rpmfileutil.h>
13 #include <rpm/rpmstring.h>
15 #include "lib/fprint.h"
17 #include "lib/rpmchroot.h"
18 #include "lib/rpmlock.h"
19 #include "lib/rpmfi_internal.h" /* only internal apis */
20 #include "lib/rpmte_internal.h" /* only internal apis */
21 #include "lib/rpmts_internal.h"
22 #include "rpmio/rpmhook.h"
24 /* XXX FIXME: merge with existing (broken?) tests in system.h */
25 /* portability fiddles */
26 #if STATFS_IN_SYS_STATVFS
27 #include <sys/statvfs.h>
30 # if STATFS_IN_SYS_VFS
33 # if STATFS_IN_SYS_MOUNT
34 # include <sys/mount.h>
36 # if STATFS_IN_SYS_STATFS
37 # include <sys/statfs.h>
45 struct diskspaceInfo_s {
46 char * mntPoint; /*!< File system mount point */
47 dev_t dev; /*!< File system device number. */
48 int64_t bneeded; /*!< No. of blocks needed. */
49 int64_t ineeded; /*!< No. of inodes needed. */
50 int64_t bsize; /*!< File system block size. */
51 int64_t bavail; /*!< No. of blocks available. */
52 int64_t iavail; /*!< No. of inodes available. */
53 int64_t obneeded; /*!< Bookkeeping to avoid duplicate reports */
54 int64_t oineeded; /*!< Bookkeeping to avoid duplicate reports */
57 /* Adjust for root only reserved space. On linux e2fs, this is 5%. */
58 #define adj_fs_blocks(_nb) (((_nb) * 21) / 20)
59 #define BLOCK_ROUND(size, block) (((size) + (block) - 1) / (block))
61 static int rpmtsInitDSI(const rpmts ts)
63 if (rpmtsFilterFlags(ts) & RPMPROB_FILTER_DISKSPACE)
65 ts->dsi = _free(ts->dsi);
66 ts->dsi = xcalloc(1, sizeof(*ts->dsi));
70 static rpmDiskSpaceInfo rpmtsCreateDSI(const rpmts ts, dev_t dev,
71 const char * dirName, int count)
76 char mntPoint[PATH_MAX];
79 #if STATFS_IN_SYS_STATVFS
81 memset(&sfb, 0, sizeof(sfb));
82 rc = statvfs(dirName, &sfb);
85 memset(&sfb, 0, sizeof(sfb));
87 /* This platform has the 4-argument version of the statfs call. The last two
88 * should be the size of struct statfs and 0, respectively. The 0 is the
89 * filesystem type, and is always 0 when statfs is called on a mounted
90 * filesystem, as we're doing.
92 rc = statfs(dirName, &sfb, sizeof(sfb), 0);
94 rc = statfs(dirName, &sfb);
100 rc = stat(dirName, &sb);
103 if (sb.st_dev != dev)
106 ts->dsi = xrealloc(ts->dsi, (count + 2) * sizeof(*ts->dsi));
107 dsi = ts->dsi + count;
108 memset(dsi, 0, 2 * sizeof(*dsi));
110 dsi->dev = sb.st_dev;
111 dsi->bsize = sfb.f_bsize;
113 dsi->bsize = 512; /* we need a bsize */
116 #ifdef STATFS_HAS_F_BAVAIL
117 dsi->bavail = (sfb.f_flag & ST_RDONLY) ? 0 : sfb.f_bavail;
119 /* FIXME: the statfs struct doesn't have a member to tell how many blocks are
120 * available for non-superusers. f_blocks - f_bfree is probably too big, but
121 * it's about all we can do.
123 dsi->bavail = sfb.f_blocks - sfb.f_bfree;
125 /* XXX Avoid FAT and other file systems that have not inodes. */
126 /* XXX assigning negative value to unsigned type */
127 dsi->iavail = !(sfb.f_ffree == 0 && sfb.f_files == 0)
130 /* Find mount point belonging to this device number */
131 resolved_path = realpath(dirName, mntPoint);
132 if (!resolved_path) {
133 strncpy(mntPoint, dirName, PATH_MAX);
134 mntPoint[PATH_MAX-1] = '\0';
137 while (end != mntPoint) {
138 end = strrchr(mntPoint, '/');
139 if (end == mntPoint) { /* reached "/" */
141 if (dsi->dev != sb.st_dev) {
142 dsi->mntPoint = xstrdup(mntPoint);
144 dsi->mntPoint = xstrdup("/");
149 } else { /* dirName doesn't start with / - should not happen */
150 dsi->mntPoint = xstrdup(dirName);
154 if (dsi->dev != sb.st_dev) {
156 dsi->mntPoint = xstrdup(mntPoint);
162 "0x%08x %8" PRId64 " %12" PRId64 " %12" PRId64" %s\n",
163 (unsigned) dsi->dev, dsi->bsize,
164 dsi->bavail, dsi->iavail,
169 static rpmDiskSpaceInfo rpmtsGetDSI(const rpmts ts, dev_t dev,
170 const char *dirName) {
171 rpmDiskSpaceInfo dsi;
174 while (dsi->bsize && dsi->dev != dev)
176 if (dsi->bsize == 0) {
177 /* create new entry */
178 dsi = rpmtsCreateDSI(ts, dev, dirName, dsi - ts->dsi);
184 static void rpmtsUpdateDSI(const rpmts ts, dev_t dev, const char *dirName,
185 rpm_loff_t fileSize, rpm_loff_t prevSize, rpm_loff_t fixupSize,
186 rpmFileAction action)
189 rpmDiskSpaceInfo dsi = rpmtsGetDSI(ts, dev, dirName);
193 bneeded = BLOCK_ROUND(fileSize, dsi->bsize);
200 dsi->bneeded += bneeded;
204 * FIXME: If two packages share a file (same md5sum), and
205 * that file is being replaced on disk, will dsi->bneeded get
206 * adjusted twice? Quite probably!
209 dsi->bneeded += bneeded;
210 dsi->bneeded -= BLOCK_ROUND(prevSize, dsi->bsize);
215 dsi->bneeded -= bneeded;
223 dsi->bneeded -= BLOCK_ROUND(fixupSize, dsi->bsize);
225 /* adjust bookkeeping when requirements shrink */
226 if (dsi->bneeded < dsi->obneeded) dsi->obneeded = dsi->bneeded;
227 if (dsi->ineeded < dsi->oineeded) dsi->oineeded = dsi->ineeded;
230 /* return DSI of the device the rpmdb lives on */
231 static rpmDiskSpaceInfo rpmtsDbDSI(const rpmts ts) {
232 const char *dbhome = rpmdbHome(rpmtsGetRdb(ts));
236 rc = stat(dbhome, &sb);
240 return rpmtsGetDSI(ts, sb.st_dev, dbhome);
243 /* Update DSI for changing size of the rpmdb */
244 static void rpmtsUpdateDSIrpmDBSize(const rpmte p,
245 rpmDiskSpaceInfo dsi) {
246 rpm_loff_t headerSize;
249 if (dsi==NULL) return;
251 headerSize = rpmteHeaderSize(p);
252 bneeded = BLOCK_ROUND(headerSize, dsi->bsize);
253 /* REMOVE doesn't neccessarily shrink the database */
254 if (rpmteType(p) == TR_ADDED) {
255 /* guessing that db grows 4 times more than the header size */
256 dsi->bneeded += (bneeded * 4);
261 static void rpmtsCheckDSIProblems(const rpmts ts, const rpmte te)
263 rpmDiskSpaceInfo dsi = ts->dsi;
265 if (dsi == NULL || !dsi->bsize)
267 if (rpmfiFC(rpmteFI(te)) <= 0)
270 for (; dsi->bsize; dsi++) {
272 if (dsi->bavail >= 0 && adj_fs_blocks(dsi->bneeded) > dsi->bavail) {
273 if (dsi->bneeded > dsi->obneeded) {
274 rpmteAddProblem(te, RPMPROB_DISKSPACE, NULL, dsi->mntPoint,
275 (adj_fs_blocks(dsi->bneeded) - dsi->bavail) * dsi->bsize);
276 dsi->obneeded = dsi->bneeded;
280 if (dsi->iavail >= 0 && adj_fs_blocks(dsi->ineeded) > dsi->iavail) {
281 if (dsi->ineeded > dsi->oineeded) {
282 rpmteAddProblem(te, RPMPROB_DISKNODES, NULL, dsi->mntPoint,
283 (adj_fs_blocks(dsi->ineeded) - dsi->iavail));
284 dsi->oineeded = dsi->ineeded;
290 static void rpmtsFreeDSI(rpmts ts){
291 rpmDiskSpaceInfo dsi;
295 while (dsi && dsi->bsize != 0) {
296 dsi->mntPoint = _free(dsi->mntPoint);
300 ts->dsi = _free(ts->dsi);
304 /* Calculate total number of files involved in transaction */
305 static uint64_t countFiles(rpmts ts)
308 rpmtsi pi = rpmtsiInit(ts);
310 while ((p = rpmtsiNext(pi, 0)) != NULL)
311 fc += rpmfiFC(rpmteFI(p));
317 * handleInstInstalledFiles.
318 * @param ts transaction set
319 * @param p current transaction element
320 * @param fi file info set
321 * @param shared shared file info
322 * @param sharedCount no. of shared elements
323 * @param reportConflicts
325 /* XXX only ts->{probs,rpmdb} modified */
326 static int handleInstInstalledFile(const rpmts ts, rpmte p, rpmfi fi,
327 Header otherHeader, rpmfi otherFi,
330 unsigned int fx = rpmfiFX(fi);
331 rpmfs fs = rpmteGetFileStates(p);
332 int isCfgFile = ((rpmfiFFlags(otherFi) | rpmfiFFlags(fi)) & RPMFILE_CONFIG);
334 if (XFA_SKIPPING(rpmfsGetAction(fs, fx)))
337 if (rpmfiCompare(otherFi, fi)) {
338 rpm_color_t tscolor = rpmtsColor(ts);
339 rpm_color_t prefcolor = rpmtsPrefColor(ts);
340 rpm_color_t FColor = rpmfiFColor(fi) & tscolor;
341 rpm_color_t oFColor = rpmfiFColor(otherFi) & tscolor;
344 rConflicts = !(beingRemoved || (rpmtsFilterFlags(ts) & RPMPROB_FILTER_REPLACEOLDFILES));
345 /* Resolve file conflicts to prefer Elf64 (if not forced). */
346 if (tscolor != 0 && FColor != 0 && FColor != oFColor) {
347 if (oFColor & prefcolor) {
348 rpmfsSetAction(fs, fx, FA_SKIPCOLOR);
350 } else if (FColor & prefcolor) {
351 rpmfsSetAction(fs, fx, FA_CREATE);
357 char *altNEVR = headerGetAsString(otherHeader, RPMTAG_NEVRA);
358 rpmteAddProblem(p, RPMPROB_FILE_CONFLICT, altNEVR, rpmfiFN(fi),
359 headerGetInstance(otherHeader));
363 /* Save file identifier to mark as state REPLACED. */
364 if ( !(isCfgFile || XFA_SKIPPING(rpmfsGetAction(fs, fx))) ) {
366 rpmfsAddReplaced(rpmteGetFileStates(p), rpmfiFX(fi),
367 headerGetInstance(otherHeader),
372 /* Determine config file dispostion, skipping missing files (if any). */
374 int skipMissing = ((rpmtsFlags(ts) & RPMTRANS_FLAG_ALLFILES) ? 0 : 1);
375 rpmFileAction action = rpmfiDecideFate(otherFi, fi, skipMissing);
376 rpmfsSetAction(fs, fx, action);
378 rpmfiSetFReplacedSize(fi, rpmfiFSize(otherFi));
384 * Update disk space needs on each partition for this package's files.
386 /* XXX only ts->{probs,di} modified */
387 static void handleOverlappedFiles(rpmts ts, rpmFpHash ht, rpmte p, rpmfi fi)
389 rpm_loff_t fixupSize = 0;
392 rpm_color_t tscolor = rpmtsColor(ts);
393 rpm_color_t prefcolor = rpmtsPrefColor(ts);
394 rpmfs fs = rpmteGetFileStates(p);
397 fi = rpmfiInit(fi, 0);
398 while ((i = rpmfiNext(fi)) >= 0) {
399 rpm_color_t oFColor, FColor;
400 struct fingerPrint_s * fiFps;
401 int otherPkgNum, otherFileNum;
406 struct rpmffi_s * recs;
409 if (XFA_SKIPPING(rpmfsGetAction(fs, i)))
413 fiFps = rpmfiFpsIndex(fi, i);
414 FFlags = rpmfiFFlags(fi);
415 FMode = rpmfiFMode(fi);
416 FColor = rpmfiFColor(fi);
422 * Retrieve all records that apply to this file. Note that the
423 * file info records were built in the same order as the packages
424 * will be installed and removed so the records for an overlapped
425 * files will be sorted in exactly the same order.
427 (void) rpmFpHashGetEntry(ht, fiFps, &recs, &numRecs, NULL);
430 * If this package is being added, look only at other packages
431 * being added -- removed packages dance to a different tune.
433 * If both this and the other package are being added, overlapped
434 * files must be identical (or marked as a conflict). The
435 * disposition of already installed config files leads to
436 * a small amount of extra complexity.
438 * If this package is being removed, then there are two cases that
439 * need to be worried about:
440 * If the other package is being added, then skip any overlapped files
441 * so that this package removal doesn't nuke the overlapped files
442 * that were just installed.
443 * If both this and the other package are being removed, then each
444 * file removal from preceding packages needs to be skipped so that
445 * the file removal occurs only on the last occurence of an overlapped
446 * file in the transaction set.
450 /* Locate this overlapped file in the set of added/removed packages. */
451 for (j = 0; j < numRecs && recs[j].p != p; j++)
454 /* Find what the previous disposition of this file was. */
455 otherFileNum = -1; /* keep gcc quiet */
460 for (otherPkgNum = j - 1; otherPkgNum >= 0; otherPkgNum--) {
461 otherTe = recs[otherPkgNum].p;
462 otherFi = rpmteFI(otherTe);
463 otherFileNum = recs[otherPkgNum].fileno;
464 otherFs = rpmteGetFileStates(otherTe);
466 /* Added packages need only look at other added packages. */
467 if (rpmteType(p) == TR_ADDED && rpmteType(otherTe) != TR_ADDED)
470 (void) rpmfiSetFX(otherFi, otherFileNum);
472 /* XXX Happens iff fingerprint for incomplete package install. */
473 if (rpmfsGetAction(otherFs, otherFileNum) != FA_UNKNOWN)
477 oFColor = rpmfiFColor(otherFi);
480 switch (rpmteType(p)) {
483 int reportConflicts =
484 !(rpmtsFilterFlags(ts) & RPMPROB_FILTER_REPLACENEWFILES);
487 if (otherPkgNum < 0) {
488 /* XXX is this test still necessary? */
489 rpmFileAction action;
490 if (rpmfsGetAction(fs, i) != FA_UNKNOWN)
492 if (rpmfiConfigConflict(fi)) {
493 /* Here is a non-overlapped pre-existing config file. */
494 action = (FFlags & RPMFILE_NOREPLACE) ?
495 FA_ALTNAME : FA_BACKUP;
499 rpmfsSetAction(fs, i, action);
503 assert(otherFi != NULL);
504 /* Mark added overlapped non-identical files as a conflict. */
505 if (rpmfiCompare(otherFi, fi)) {
508 rConflicts = reportConflicts;
509 /* Resolve file conflicts to prefer Elf64 (if not forced) ... */
511 if (FColor & prefcolor) {
512 /* ... last file of preferred colour is installed ... */
513 if (!XFA_SKIPPING(rpmfsGetAction(fs, i)))
514 rpmfsSetAction(otherFs, otherFileNum, FA_SKIPCOLOR);
515 rpmfsSetAction(fs, i, FA_CREATE);
518 if (oFColor & prefcolor) {
519 /* ... first file of preferred colour is installed ... */
520 if (XFA_SKIPPING(rpmfsGetAction(fs, i)))
521 rpmfsSetAction(otherFs, otherFileNum, FA_CREATE);
522 rpmfsSetAction(fs, i, FA_SKIPCOLOR);
528 rpmteAddProblem(p, RPMPROB_NEW_FILE_CONFLICT,
529 rpmteNEVRA(otherTe), fn, 0);
533 /* Try to get the disk accounting correct even if a conflict. */
534 fixupSize = rpmfiFSize(otherFi);
536 if (rpmfiConfigConflict(fi)) {
537 /* Here is an overlapped pre-existing config file. */
538 rpmFileAction action;
539 action = (FFlags & RPMFILE_NOREPLACE) ? FA_ALTNAME : FA_SKIP;
540 rpmfsSetAction(fs, i, action);
543 rpmfsSetAction(fs, i, FA_CREATE);
548 if (otherPkgNum >= 0) {
549 assert(otherFi != NULL);
550 /* Here is an overlapped added file we don't want to nuke. */
551 if (rpmfsGetAction(otherFs, otherFileNum) != FA_ERASE) {
552 /* On updates, don't remove files. */
553 rpmfsSetAction(fs, i, FA_SKIP);
556 /* Here is an overlapped removed file: skip in previous. */
557 rpmfsSetAction(otherFs, otherFileNum, FA_SKIP);
559 if (XFA_SKIPPING(rpmfsGetAction(fs, i)))
561 if (rpmfiFState(fi) != RPMFILE_STATE_NORMAL)
563 if (!(S_ISREG(FMode) && (FFlags & RPMFILE_CONFIG))) {
564 rpmfsSetAction(fs, i, FA_ERASE);
568 /* Here is a pre-existing modified config file that needs saving. */
569 { pgpHashAlgo algo = 0;
571 const unsigned char *digest;
572 if ((digest = rpmfiFDigest(fi, &algo, &diglen))) {
573 unsigned char fdigest[diglen];
574 if (!rpmDoDigest(algo, fn, 0, fdigest, NULL) &&
575 memcmp(digest, fdigest, diglen)) {
576 rpmfsSetAction(fs, i, FA_BACKUP);
581 rpmfsSetAction(fs, i, FA_ERASE);
585 /* Update disk space info for a file. */
586 rpmtsUpdateDSI(ts, fiFps->entry->dev, fiFps->entry->dirName,
587 rpmfiFSize(fi), rpmfiFReplacedSize(fi),
588 fixupSize, rpmfsGetAction(fs, i));
594 * Ensure that current package is newer than installed package.
595 * @param p current transaction element
596 * @param h installed header
597 * @param ps problem set
599 static void ensureOlder(const rpmte p, const Header h)
601 rpmsenseFlags reqFlags = (RPMSENSE_LESS | RPMSENSE_EQUAL);
604 req = rpmdsSingle(RPMTAG_REQUIRENAME, rpmteN(p), rpmteEVR(p), reqFlags);
605 if (rpmdsNVRMatchesDep(h, req, _rpmds_nopromote) == 0) {
606 char * altNEVR = headerGetAsString(h, RPMTAG_NEVRA);
607 rpmteAddProblem(p, RPMPROB_OLDPACKAGE, altNEVR, NULL,
608 headerGetInstance(h));
615 * Check if the curent file in the file iterator is in the
616 * netshardpath and though should be excluded.
617 * @param ts transaction set
618 * @param fi file info set
619 * @returns pointer to matching path or NULL
621 static char ** matchNetsharedpath(const rpmts ts, rpmfi fi)
624 const char * dn, * bn;
631 for (nsp = ts->netsharedPaths; nsp && *nsp; nsp++) {
636 if (!rstreqn(dn, *nsp, len))
638 /* Only directories or complete file paths can be net shared */
639 if (!(dn[len] == '/' || dn[len] == '\0'))
642 if (len < (dnlen + bnlen))
644 if (!rstreqn(dn, *nsp, dnlen))
646 /* Insure that only the netsharedpath basename is compared. */
647 if ((s = strchr((*nsp) + dnlen, '/')) != NULL && s[1] != '\0')
649 if (!rstreqn(bn, (*nsp) + dnlen, bnlen))
652 /* Only directories or complete file paths can be net shared */
653 if (!((*nsp)[len] == '/' || (*nsp)[len] == '\0'))
662 static void skipEraseFiles(const rpmts ts, rpmte p)
664 rpmfi fi = rpmteFI(p);
665 rpmfs fs = rpmteGetFileStates(p);
669 * Skip net shared paths.
670 * Net shared paths are not relative to the current root (though
671 * they do need to take package relocations into account).
673 fi = rpmfiInit(fi, 0);
674 while ((i = rpmfiNext(fi)) >= 0)
676 nsp = matchNetsharedpath(ts, fi);
678 rpmfsSetAction(fs, i, FA_SKIPNETSHARED);
685 * Skip any files that do not match install policies.
686 * @param ts transaction set
687 * @param fi file info set
689 static void skipInstallFiles(const rpmts ts, rpmte p)
691 rpm_color_t tscolor = rpmtsColor(ts);
693 int noConfigs = (rpmtsFlags(ts) & RPMTRANS_FLAG_NOCONFIGS);
694 int noDocs = (rpmtsFlags(ts) & RPMTRANS_FLAG_NODOCS);
695 const char * dn, * bn;
701 rpmfi fi = rpmteFI(p);
702 rpmfs fs = rpmteGetFileStates(p);
705 noDocs = rpmExpandNumeric("%{_excludedocs}");
707 /* Compute directory refcount, skip directory if now empty. */
709 drc = xcalloc(dc, sizeof(*drc));
710 dff = xcalloc(dc, sizeof(*dff));
712 fi = rpmfiInit(fi, 0);
713 while ((i = rpmfiNext(fi)) >= 0)
726 /* Don't bother with skipped files */
727 if (XFA_SKIPPING(rpmfsGetAction(fs, i))) {
728 drc[ix]--; dff[ix] = 1;
732 /* Ignore colored files not in our rainbow. */
733 FColor = rpmfiFColor(fi);
734 if (tscolor && FColor && !(tscolor & FColor)) {
735 drc[ix]--; dff[ix] = 1;
736 rpmfsSetAction(fs, i, FA_SKIPCOLOR);
741 * Skip net shared paths.
742 * Net shared paths are not relative to the current root (though
743 * they do need to take package relocations into account).
745 nsp = matchNetsharedpath(ts, fi);
747 drc[ix]--; dff[ix] = 1;
748 rpmfsSetAction(fs, i, FA_SKIPNETSHARED);
753 * Skip i18n language specific files.
755 flangs = (ts->installLangs != NULL) ? rpmfiFLangs(fi) : NULL;
756 if (flangs != NULL && *flangs != '\0') {
759 for (lang = ts->installLangs; *lang != NULL; lang++) {
760 for (l = flangs; *l != '\0'; l = le) {
761 for (le = l; *le != '\0' && *le != '|'; le++)
763 if ((le-l) > 0 && rstreqn(*lang, l, (le-l)))
765 if (*le == '|') le++; /* skip over | */
771 drc[ix]--; dff[ix] = 1;
772 rpmfsSetAction(fs, i, FA_SKIPNSTATE);
778 * Skip config files if requested.
780 if (noConfigs && (rpmfiFFlags(fi) & RPMFILE_CONFIG)) {
781 drc[ix]--; dff[ix] = 1;
782 rpmfsSetAction(fs, i, FA_SKIPNSTATE);
787 * Skip documentation if requested.
789 if (noDocs && (rpmfiFFlags(fi) & RPMFILE_DOC)) {
790 drc[ix]--; dff[ix] = 1;
791 rpmfsSetAction(fs, i, FA_SKIPNSTATE);
796 /* Skip (now empty) directories that had skipped files. */
798 if (fi != NULL) /* XXX can't happen */
799 for (j = 0; j < dc; j++)
801 if ((fi = rpmfiInitD(fi)) != NULL)
802 while (j = rpmfiNextD(fi) >= 0)
806 if (drc[j]) continue; /* dir still has files. */
807 if (!dff[j]) continue; /* dir was not emptied here. */
809 /* Find parent directory and basename. */
810 dn = rpmfiDNIndex(fi, j); dnlen = strlen(dn) - 1;
811 bn = dn + dnlen; bnlen = 0;
812 while (bn > dn && bn[-1] != '/') {
818 /* If explicitly included in the package, skip the directory. */
819 fi = rpmfiInit(fi, 0);
820 while ((i = rpmfiNext(fi)) >= 0) {
821 const char * fdn, * fbn;
824 if (XFA_SKIPPING(rpmfsGetAction(fs, i)))
827 fFMode = rpmfiFMode(fi);
829 if (rpmfiWhatis(fFMode) != XDIR)
832 if (strlen(fdn) != dnlen)
834 if (!rstreqn(fdn, dn, dnlen))
837 if (strlen(fbn) != bnlen)
839 if (!rstreqn(fbn, bn, bnlen))
841 rpmlog(RPMLOG_DEBUG, "excluding directory %s\n", dn);
842 rpmfsSetAction(fs, i, FA_SKIPNSTATE);
855 #define HASHTYPE rpmStringSet
856 #define HTKEYTYPE const char *
857 #include "lib/rpmhash.H"
858 #include "lib/rpmhash.C"
860 /* Get a rpmdbMatchIterator containing all files in
861 * the rpmdb that share the basename with one from
863 * @param ts transaction set
864 * @return rpmdbMatchIterator sorted
865 by (package, fileNum)
868 rpmdbMatchIterator rpmFindBaseNamesInDB(rpmts ts, uint64_t fileCount)
870 tsMembers tsmem = rpmtsMembers(ts);
873 rpmdbMatchIterator mi;
876 const char * baseName;
878 rpmStringSet baseNames = rpmStringSetCreate(fileCount,
879 hashFunctionString, strcmp, NULL);
881 mi = rpmdbInitIterator(rpmtsGetRdb(ts), RPMDBI_BASENAMES, NULL, 0);
884 while ((p = rpmtsiNext(pi, 0)) != NULL) {
885 (void) rpmdbCheckSignals();
887 rpmtsNotify(ts, NULL, RPMCALLBACK_TRANS_PROGRESS, oc++, tsmem->orderCount);
889 /* Gather all installed headers with matching basename's. */
890 fi = rpmfiInit(rpmteFI(p), 0);
891 while (rpmfiNext(fi) >= 0) {
893 baseName = rpmfiBN(fi);
894 if (rpmStringSetHasEntry(baseNames, baseName))
897 keylen = strlen(baseName);
899 keylen++; /* XXX "/" fixup. */
900 xx = rpmdbExtendIterator(mi, baseName, keylen);
901 rpmStringSetAddEntry(baseNames, baseName);
905 rpmStringSetFree(baseNames);
907 rpmdbSortIterator(mi);
908 /* iterator is now sorted by (recnum, filenum) */
912 /* Check files in the transactions against the rpmdb
913 * Lookup all files with the same basename in the rpmdb
914 * and then check for matching finger prints
915 * @param ts transaction set
916 * @param fpc global finger print cache
919 void checkInstalledFiles(rpmts ts, uint64_t fileCount, rpmFpHash ht, fingerPrintCache fpc)
921 tsMembers tsmem = rpmtsMembers(ts);
928 unsigned int fileNum;
931 rpmdbMatchIterator mi;
936 rpmlog(RPMLOG_DEBUG, "computing file dispositions\n");
938 mi = rpmFindBaseNamesInDB(ts, fileCount);
940 /* For all installed headers with matching basename's ... */
944 if (rpmdbGetIteratorCount(mi) == 0) {
945 mi = rpmdbFreeIterator(mi);
949 /* Loop over all packages from the rpmdb */
950 h = newheader = rpmdbNextIterator(mi);
952 headerGetFlags hgflags = HEADERGET_MINMEM;
953 struct rpmtd_s bnames, dnames, dindexes, ostates;
955 unsigned int installedPkg;
957 /* Is this package being removed? */
958 installedPkg = rpmdbGetIteratorOffset(mi);
959 beingRemoved = intHashHasEntry(tsmem->removedPackages, installedPkg);
962 headerGet(h, RPMTAG_BASENAMES, &bnames, hgflags);
963 headerGet(h, RPMTAG_DIRNAMES, &dnames, hgflags);
964 headerGet(h, RPMTAG_DIRINDEXES, &dindexes, hgflags);
965 headerGet(h, RPMTAG_FILESTATES, &ostates, hgflags);
968 /* loop over all interesting files in that package */
971 struct rpmffi_s * recs;
973 const char * dirName;
974 const char * baseName;
976 fileNum = rpmdbGetIteratorFileNum(mi);
977 rpmtdSetIndex(&bnames, fileNum);
978 rpmtdSetIndex(&dindexes, fileNum);
979 rpmtdSetIndex(&dnames, *rpmtdGetUint32(&dindexes));
980 rpmtdSetIndex(&ostates, fileNum);
982 dirName = rpmtdGetString(&dnames);
983 baseName = rpmtdGetString(&bnames);
985 /* lookup finger print for this file */
986 if ( dirName == oldDir) {
987 /* directory is the same as last round */
988 fp.baseName = baseName;
990 fp = fpLookup(fpc, dirName, baseName, 1);
993 /* search for files in the transaction with same finger print */
994 gotRecs = rpmFpHashGetEntry(ht, &fp, &recs, &numRecs, NULL);
996 for (j=0; (j<numRecs)&&gotRecs; j++) {
999 fs = rpmteGetFileStates(p);
1001 /* Determine the fate of each file. */
1002 switch (rpmteType(p)) {
1005 otherFi = rpmfiNew(ts, h, RPMTAG_BASENAMES, RPMFI_KEEPHEADER);
1007 rpmfiSetFX(fi, recs[j].fileno);
1008 rpmfiSetFX(otherFi, fileNum);
1009 xx = handleInstInstalledFile(ts, p, fi, h, otherFi, beingRemoved);
1012 if (!beingRemoved) {
1013 rpmfiSetFX(fi, recs[j].fileno);
1014 if (*rpmtdGetChar(&ostates) == RPMFILE_STATE_NORMAL)
1015 rpmfsSetAction(fs, recs[j].fileno, FA_SKIP);
1021 newheader = rpmdbNextIterator(mi);
1023 } while (newheader==h);
1025 otherFi = rpmfiFree(otherFi);
1026 rpmtdFreeData(&ostates);
1027 rpmtdFreeData(&bnames);
1028 rpmtdFreeData(&dnames);
1029 rpmtdFreeData(&dindexes);
1034 mi = rpmdbFreeIterator(mi);
1037 #define badArch(_a) (rpmMachineScore(RPM_MACHTABLE_INSTARCH, (_a)) == 0)
1038 #define badOs(_a) (rpmMachineScore(RPM_MACHTABLE_INSTOS, (_a)) == 0)
1041 * For packages being installed:
1042 * - verify package arch/os.
1043 * - verify package epoch:version-release is newer.
1045 static rpmps checkProblems(rpmts ts)
1047 rpm_color_t tscolor = rpmtsColor(ts);
1048 rpmprobFilterFlags probFilter = rpmtsFilterFlags(ts);
1049 rpmtsi pi = rpmtsiInit(ts);
1052 /* The ordering doesn't matter here */
1053 /* XXX Only added packages need be checked. */
1054 rpmlog(RPMLOG_DEBUG, "sanity checking %d elements\n", rpmtsNElements(ts));
1055 while ((p = rpmtsiNext(pi, TR_ADDED)) != NULL) {
1056 rpmdbMatchIterator mi;
1058 if (!(probFilter & RPMPROB_FILTER_IGNOREARCH) && badArch(rpmteA(p)))
1059 rpmteAddProblem(p, RPMPROB_BADARCH, rpmteA(p), NULL, 0);
1061 if (!(probFilter & RPMPROB_FILTER_IGNOREOS) && badOs(rpmteO(p)))
1062 rpmteAddProblem(p, RPMPROB_BADOS, rpmteO(p), NULL, 0);
1064 if (!(probFilter & RPMPROB_FILTER_OLDPACKAGE)) {
1066 mi = rpmtsInitIterator(ts, RPMDBI_NAME, rpmteN(p), 0);
1067 while ((h = rpmdbNextIterator(mi)) != NULL)
1069 mi = rpmdbFreeIterator(mi);
1072 if (!(probFilter & RPMPROB_FILTER_REPLACEPKG)) {
1074 mi = rpmtsInitIterator(ts, RPMDBI_NAME, rpmteN(p), 0);
1075 rpmdbSetIteratorRE(mi, RPMTAG_EPOCH, RPMMIRE_STRCMP, rpmteE(p));
1076 rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_STRCMP, rpmteV(p));
1077 rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_STRCMP, rpmteR(p));
1079 rpmdbSetIteratorRE(mi, RPMTAG_ARCH, RPMMIRE_STRCMP, rpmteA(p));
1080 rpmdbSetIteratorRE(mi, RPMTAG_OS, RPMMIRE_STRCMP, rpmteO(p));
1083 if ((h = rpmdbNextIterator(mi)) != NULL) {
1084 rpmteAddProblem(p, RPMPROB_PKG_INSTALLED, NULL, NULL,
1085 headerGetInstance(h));
1087 mi = rpmdbFreeIterator(mi);
1090 pi = rpmtsiFree(pi);
1091 return rpmtsProblems(ts);
1095 * Run pre/post transaction scripts for transaction set
1096 * param ts Transaction set
1097 * param goal PKG_PRETRANS/PKG_POSTTRANS
1098 * return 0 on success
1100 static int runTransScripts(rpmts ts, pkgGoal goal)
1103 rpmtsi pi = rpmtsiInit(ts);
1104 while ((p = rpmtsiNext(pi, TR_ADDED)) != NULL) {
1105 rpmteProcess(p, goal);
1107 pi = rpmtsiFree(pi);
1108 return 0; /* what to do about failures? */
1111 static int rpmtsSetupCollections(rpmts ts)
1113 /* seenCollectionsPost and TEs are basically a key-value pair. each item in
1114 * seenCollectionsPost is a collection that has been seen from any package,
1115 * and the associated index in the TEs is the last transaction element
1116 * where that collection was seen. */
1117 ARGV_t seenCollectionsPost = NULL;
1119 int numSeenPost = 0;
1121 /* seenCollectionsPre is a list of collections that have been seen from
1122 * only removed packages */
1123 ARGV_t seenCollectionsPre = NULL;
1126 ARGV_const_t collname;
1131 rpmtsi pi = rpmtsiInit(ts);
1132 while ((p = rpmtsiNext(pi, 0)) != NULL) {
1133 /* detect when we switch from installing to removing packages, and
1134 * update the lastInCollectionAdd lists */
1135 if (installing && rpmteType(p) == TR_REMOVED) {
1137 for (i = 0; i < numSeenPost; i++) {
1138 rpmteAddToLastInCollectionAdd(TEs[i], seenCollectionsPost[i]);
1142 rpmteSetupCollectionPlugins(p);
1144 for (collname = rpmteCollections(p); collname && *collname; collname++) {
1145 /* figure out if we've seen this collection in post before */
1146 for (i = 0; i < numSeenPost && strcmp(*collname, seenCollectionsPost[i]); i++) {
1148 if (i < numSeenPost) {
1149 /* we've seen the collection, update the index */
1152 /* haven't seen the collection yet, add it */
1153 argvAdd(&seenCollectionsPost, *collname);
1154 TEs = xrealloc(TEs, sizeof(*TEs) * (numSeenPost + 1));
1155 TEs[numSeenPost] = p;
1159 /* figure out if we've seen this collection in pre remove before */
1160 if (installing == 0) {
1161 for (i = 0; i < numSeenPre && strcmp(*collname, seenCollectionsPre[i]); i++) {
1163 if (i >= numSeenPre) {
1164 /* haven't seen this collection, add it */
1165 rpmteAddToFirstInCollectionRemove(p, *collname);
1166 argvAdd(&seenCollectionsPre, *collname);
1172 pi = rpmtsiFree(pi);
1174 /* we've looked at all the rpmte's, update the lastInCollectionAny lists */
1175 for (i = 0; i < numSeenPost; i++) {
1176 rpmteAddToLastInCollectionAny(TEs[i], seenCollectionsPost[i]);
1177 if (installing == 1) {
1178 /* lastInCollectionAdd is only updated above if packages were
1179 * removed. if nothing is removed in the transaction, we need to
1180 * update that list here */
1181 rpmteAddToLastInCollectionAdd(TEs[i], seenCollectionsPost[i]);
1185 argvFree(seenCollectionsPost);
1186 argvFree(seenCollectionsPre);
1192 /* Add fingerprint for each file not skipped. */
1193 static void addFingerprints(rpmts ts, uint64_t fileCount, rpmFpHash ht, fingerPrintCache fpc)
1200 rpmFpHash symlinks = rpmFpHashCreate(fileCount/16+16, fpHashFunction, fpEqual, NULL, NULL);
1202 pi = rpmtsiInit(ts);
1203 while ((p = rpmtsiNext(pi, 0)) != NULL) {
1204 (void) rpmdbCheckSignals();
1206 if ((fi = rpmteFI(p)) == NULL)
1207 continue; /* XXX can't happen */
1209 (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_FINGERPRINT), 0);
1210 rpmfiFpLookup(fi, fpc);
1211 /* collect symbolic links */
1212 fi = rpmfiInit(fi, 0);
1213 while ((i = rpmfiNext(fi)) >= 0) {
1214 struct rpmffi_s ffi;
1215 char const *linktarget;
1216 linktarget = rpmfiFLink(fi);
1217 if (!(linktarget && *linktarget != '\0'))
1219 if (XFA_SKIPPING(rpmfsGetAction(rpmteGetFileStates(p), i)))
1223 rpmFpHashAddEntry(symlinks, rpmfiFpsIndex(fi, i), ffi);
1225 (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_FINGERPRINT), rpmfiFC(fi));
1228 pi = rpmtsiFree(pi);
1230 /* ===============================================
1231 * Check fingerprints if they contain symlinks
1232 * and add them to the hash table
1235 pi = rpmtsiInit(ts);
1236 while ((p = rpmtsiNext(pi, 0)) != NULL) {
1237 (void) rpmdbCheckSignals();
1239 fi = rpmfiInit(rpmteFI(p), 0);
1240 (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_FINGERPRINT), 0);
1241 while ((i = rpmfiNext(fi)) >= 0) {
1242 if (XFA_SKIPPING(rpmfsGetAction(rpmteGetFileStates(p), i)))
1244 fpLookupSubdir(symlinks, ht, fpc, p, i);
1246 (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_FINGERPRINT), 0);
1248 pi = rpmtsiFree(pi);
1250 rpmFpHashFree(symlinks);
1253 static int rpmtsSetup(rpmts ts, rpmprobFilterFlags ignoreSet)
1255 rpm_tid_t tid = (rpm_tid_t) time(NULL);
1256 int dbmode = (rpmtsFlags(ts) & RPMTRANS_FLAG_TEST) ? O_RDONLY : (O_RDWR|O_CREAT);
1258 if (rpmtsFlags(ts) & RPMTRANS_FLAG_NOSCRIPTS)
1259 (void) rpmtsSetFlags(ts, (rpmtsFlags(ts) | _noTransScripts | _noTransTriggers));
1260 if (rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERS)
1261 (void) rpmtsSetFlags(ts, (rpmtsFlags(ts) | _noTransTriggers));
1263 if (rpmtsFlags(ts) & RPMTRANS_FLAG_JUSTDB)
1264 (void) rpmtsSetFlags(ts, (rpmtsFlags(ts) | _noTransScripts | _noTransTriggers));
1266 /* if SELinux isn't enabled, init fails or test run, don't bother... */
1267 if (!is_selinux_enabled() || (rpmtsFlags(ts) & RPMTRANS_FLAG_TEST)) {
1268 rpmtsSetFlags(ts, (rpmtsFlags(ts) | RPMTRANS_FLAG_NOCONTEXTS));
1271 if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOCONTEXTS)) {
1272 rpmtsSELabelInit(ts, selinux_file_context_path());
1275 /* XXX Make sure the database is open RDWR for package install/erase. */
1276 if (rpmtsOpenDB(ts, dbmode) || rpmChrootSet(rpmtsRootDir(ts)))
1279 ts->ignoreSet = ignoreSet;
1280 (void) rpmtsSetTid(ts, tid);
1282 /* Get available space on mounted file systems. */
1283 (void) rpmtsInitDSI(ts);
1288 static int rpmtsFinish(rpmts ts)
1290 if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOCONTEXTS)) {
1291 rpmtsSELabelFini(ts);
1293 return rpmChrootSet(NULL);
1296 static int rpmtsPrepare(rpmts ts)
1298 tsMembers tsmem = rpmtsMembers(ts);
1303 uint64_t fileCount = countFiles(ts);
1305 fingerPrintCache fpc = fpCacheCreate(fileCount/2 + 10001);
1306 rpmFpHash ht = rpmFpHashCreate(fileCount/2+1, fpHashFunction, fpEqual,
1308 rpmDiskSpaceInfo dsi;
1310 rpmlog(RPMLOG_DEBUG, "computing %" PRIu64 " file fingerprints\n", fileCount);
1312 /* Skip netshared paths, not our i18n files, and excluded docs */
1313 pi = rpmtsiInit(ts);
1314 while ((p = rpmtsiNext(pi, 0)) != NULL) {
1315 if (rpmfiFC(rpmteFI(p)) == 0)
1317 if (rpmteType(p) == TR_ADDED) {
1318 skipInstallFiles(ts, p);
1320 skipEraseFiles(ts, p);
1323 pi = rpmtsiFree(pi);
1325 /* Open rpmdb & enter chroot for fingerprinting if necessary */
1326 if (rpmdbOpenAll(ts->rdb) || rpmChrootIn()) {
1331 rpmtsNotify(ts, NULL, RPMCALLBACK_TRANS_START, 6, tsmem->orderCount);
1332 addFingerprints(ts, fileCount, ht, fpc);
1333 /* check against files in the rpmdb */
1334 checkInstalledFiles(ts, fileCount, ht, fpc);
1336 dsi = rpmtsDbDSI(ts);
1338 pi = rpmtsiInit(ts);
1339 while ((p = rpmtsiNext(pi, 0)) != NULL) {
1340 if ((fi = rpmteFI(p)) == NULL)
1341 continue; /* XXX can't happen */
1343 (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_FINGERPRINT), 0);
1344 /* check files in ts against each other and update disk space
1345 needs on each partition for this package. */
1346 handleOverlappedFiles(ts, ht, p, fi);
1348 rpmtsUpdateDSIrpmDBSize(p, dsi);
1350 /* Check added package has sufficient space on each partition used. */
1351 if (rpmteType(p) == TR_ADDED) {
1352 rpmtsCheckDSIProblems(ts, p);
1354 (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_FINGERPRINT), 0);
1356 pi = rpmtsiFree(pi);
1357 rpmtsNotify(ts, NULL, RPMCALLBACK_TRANS_STOP, 6, tsmem->orderCount);
1359 /* return from chroot if done earlier */
1363 /* File info sets, fp caches etc not needed beyond here, free 'em up. */
1364 pi = rpmtsiInit(ts);
1365 while ((p = rpmtsiNext(pi, 0)) != NULL) {
1366 rpmteSetFI(p, NULL);
1368 pi = rpmtsiFree(pi);
1371 ht = rpmFpHashFree(ht);
1372 fpc = fpCacheFree(fpc);
1378 * Transaction main loop: install and remove packages
1380 static int rpmtsProcess(rpmts ts)
1385 pi = rpmtsiInit(ts);
1386 while ((p = rpmtsiNext(pi, 0)) != NULL) {
1389 rpmlog(RPMLOG_DEBUG, "========== +++ %s %s-%s 0x%x\n",
1390 rpmteNEVR(p), rpmteA(p), rpmteO(p), rpmteColor(p));
1392 failed = rpmteProcess(p, rpmteType(p));
1394 rpmlog(RPMLOG_ERR, "%s: %s %s\n", rpmteNEVRA(p),
1395 rpmteTypeString(p), failed > 1 ? _("skipped") : _("failed"));
1398 (void) rpmdbSync(rpmtsGetRdb(ts));
1400 pi = rpmtsiFree(pi);
1404 int rpmtsRun(rpmts ts, rpmps okProbs, rpmprobFilterFlags ignoreSet)
1406 int rc = -1; /* assume failure */
1407 rpmlock lock = NULL;
1408 rpmps tsprobs = NULL;
1410 /* XXX programmer error segfault avoidance. */
1411 if (rpmtsNElements(ts) <= 0) {
1415 /* If we are in test mode, then there's no need for transaction lock. */
1416 if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_TEST)) {
1417 if (!(lock = rpmtsAcquireLock(ts))) {
1422 /* Setup flags and such, open the DB */
1423 if (rpmtsSetup(ts, ignoreSet)) {
1427 rpmtsSetupCollections(ts);
1429 /* Check package set for problems */
1430 tsprobs = checkProblems(ts);
1432 /* Run pre-transaction scripts, but only if there are no known
1433 * problems up to this point and not disabled otherwise. */
1434 if (!((rpmtsFlags(ts) & (RPMTRANS_FLAG_BUILD_PROBS|RPMTRANS_FLAG_NOPRE))
1435 || (rpmpsNumProblems(tsprobs)))) {
1436 rpmlog(RPMLOG_DEBUG, "running pre-transaction scripts\n");
1437 runTransScripts(ts, PKG_PRETRANS);
1439 tsprobs = rpmpsFree(tsprobs);
1441 /* Compute file disposition for each package in transaction set. */
1442 if (rpmtsPrepare(ts)) {
1445 /* Check again for problems (now including file conflicts, duh */
1446 tsprobs = rpmtsProblems(ts);
1448 /* If unfiltered problems exist, free memory and return. */
1449 if ((rpmtsFlags(ts) & RPMTRANS_FLAG_BUILD_PROBS) || (rpmpsNumProblems(tsprobs))) {
1450 tsMembers tsmem = rpmtsMembers(ts);
1451 rc = tsmem->orderCount;
1455 /* Free up memory taken by problem sets */
1456 tsprobs = rpmpsFree(tsprobs);
1457 rpmtsCleanProblems(ts);
1459 /* Actually install and remove packages, get final exit code */
1460 rc = rpmtsProcess(ts) ? -1 : 0;
1462 /* Run post-transaction scripts unless disabled */
1463 if (!(rpmtsFlags(ts) & (RPMTRANS_FLAG_NOPOST))) {
1464 rpmlog(RPMLOG_DEBUG, "running post-transaction scripts\n");
1465 runTransScripts(ts, PKG_POSTTRANS);
1469 (void) rpmtsFinish(ts);
1472 tsprobs = rpmpsFree(tsprobs);