2 * \file lib/transaction.c
9 #include <rpmmacro.h> /* XXX for rpmExpand */
12 #include "legacy.h" /* XXX mdfile */
13 #include "misc.h" /* XXX stripTrailingChar, splitString, currentDirectory */
16 /*@-redecl -exportheadervar@*/
18 extern const char * chroot_prefix;
19 /*@=redecl =exportheadervar@*/
21 /* XXX FIXME: merge with existing (broken?) tests in system.h */
22 /* portability fiddles */
23 #if STATFS_IN_SYS_STATVFS
25 # include <sys/statvfs.h>
26 #if defined(__LCLINT__)
27 /*@-declundef -exportheader -protoparammatch @*/ /* LCL: missing annotation */
28 extern int statvfs (const char * file, /*@out@*/ struct statvfs * buf)
29 /*@globals fileSystem @*/
30 /*@modifies *buf, fileSystem @*/;
31 /*@=declundef =exportheader =protoparammatch @*/
35 # if STATFS_IN_SYS_VFS
38 # if STATFS_IN_SYS_MOUNT
39 # include <sys/mount.h>
41 # if STATFS_IN_SYS_STATFS
42 # include <sys/statfs.h>
50 /*@access FD_t@*/ /* XXX compared with NULL */
51 /*@access Header@*/ /* XXX compared with NULL */
52 /*@access rpmProblemSet@*/ /* XXX need rpmProblemSetOK() */
53 /*@access dbiIndexSet@*/
55 /*@access rpmTransactionSet@*/
60 /*@access availablePackage@*/
63 /*@access transactionElement@*/
67 struct diskspaceInfo {
68 dev_t dev; /*!< file system device number. */
69 signed long bneeded; /*!< no. of blocks needed. */
70 signed long ineeded; /*!< no. of inodes needed. */
71 int bsize; /*!< file system block size. */
72 signed long bavail; /*!< no. of blocks available. */
73 signed long iavail; /*!< no. of inodes available. */
77 * Adjust for root only reserved space. On linux e2fs, this is 5%.
79 #define adj_fs_blocks(_nb) (((_nb) * 21) / 20)
81 /* argon thought a shift optimization here was a waste of time... he's
83 #define BLOCK_ROUND(size, block) (((size) + (block) - 1) / (block))
87 static /*@null@*/ void * freeFl(rpmTransactionSet ts,
88 /*@only@*/ /*@null@*/ TFI_t flList)
95 /*@-usereleased -onlytrans @*/ /* FIX: fi needs to be only */
96 for (oc = 0, fi = flList; oc < ts->orderCount; oc++, fi++)
98 flList = _free(flList);
99 /*@=usereleased =onlytrans @*/
104 void rpmtransSetScriptFd(rpmTransactionSet ts, FD_t fd)
106 /*@-type@*/ /* FIX: cast? */
107 ts->scriptFd = (fd ? fdLink(fd, "rpmtransSetScriptFd") : NULL);
111 int rpmtransGetKeys(const rpmTransactionSet ts, const void *** ep, int * nep)
115 if (nep) *nep = ts->orderCount;
120 *ep = e = xmalloc(ts->orderCount * sizeof(*e));
121 for (oc = 0; oc < ts->orderCount; oc++, e++) {
122 switch (ts->order[oc].type) {
124 *e = alGetKey(ts->addedPackages, ts->order[oc].u.addedIndex);
125 /*@switchbreak@*/ break;
128 /*@-mods@*/ /* FIX: double indirection. */
131 /*@switchbreak@*/ break;
140 static int archOkay(Header h)
146 /* make sure we're trying to install this on the proper architecture */
147 (void) headerGetEntry(h, RPMTAG_ARCH, &type, (void **) &pkgArch, &count);
149 if (type == RPM_INT8_TYPE) {
153 /* old arch handling */
154 rpmGetArchInfo(NULL, &archNum);
155 pkgArchNum = pkgArch;
156 if (archNum != *pkgArchNum) {
162 /* new arch handling */
163 if (!rpmMachineScore(RPM_MACHTABLE_INSTARCH, pkgArch)) {
173 static int osOkay(Header h)
179 /* make sure we're trying to install this on the proper os */
180 (void) headerGetEntry(h, RPMTAG_OS, &type, (void **) &pkgOs, &count);
182 if (type == RPM_INT8_TYPE) {
183 /* v1 packages and v2 packages both used improper OS numbers, so just
184 deal with it hope things work */
189 /* new os handling */
190 if (!rpmMachineScore(RPM_MACHTABLE_INSTOS, pkgOs)) {
200 static int sharedCmp(const void * one, const void * two)
203 const struct sharedFileInfo * a = one;
204 const struct sharedFileInfo * b = two;
206 if (a->otherPkg < b->otherPkg)
208 else if (a->otherPkg > b->otherPkg)
216 static fileAction decideFileFate(const char * dirName,
217 const char * baseName, short dbMode,
218 const char * dbMd5, const char * dbLink, short newMode,
219 const char * newMd5, const char * newLink, int newFlags,
220 rpmtransFlags transFlags)
221 /*@globals fileSystem @*/
222 /*@modifies fileSystem @*/
225 const char * dbAttr, * newAttr;
226 fileTypes dbWhat, newWhat, diskWhat;
229 int save = (newFlags & RPMFILE_NOREPLACE) ? FA_ALTNAME : FA_SAVE;
230 char * filespec = alloca(strlen(dirName) + strlen(baseName) + 1);
232 (void) stpcpy( stpcpy(filespec, dirName), baseName);
234 if (lstat(filespec, &sb)) {
236 * The file doesn't exist on the disk. Create it unless the new
237 * package has marked it as missingok, or allfiles is requested.
239 if (!(transFlags & RPMTRANS_FLAG_ALLFILES) &&
240 (newFlags & RPMFILE_MISSINGOK)) {
241 rpmMessage(RPMMESS_DEBUG, _("%s skipped due to missingok flag\n"),
249 diskWhat = whatis(sb.st_mode);
250 dbWhat = whatis(dbMode);
251 newWhat = whatis(newMode);
253 /* RPM >= 2.3.10 shouldn't create config directories -- we'll ignore
254 them in older packages as well */
255 if (newWhat == XDIR) {
259 if (diskWhat != newWhat) {
261 } else if (newWhat != dbWhat && diskWhat != dbWhat) {
263 } else if (dbWhat != newWhat) {
265 } else if (dbWhat != LINK && dbWhat != REG) {
270 rc = mdfile(filespec, buffer);
273 /* assume the file has been removed, don't freak */
278 } else /* dbWhat == LINK */ {
279 memset(buffer, 0, sizeof(buffer));
280 i = readlink(filespec, buffer, sizeof(buffer) - 1);
282 /* assume the file has been removed, don't freak */
289 /* this order matters - we'd prefer to CREATE the file if at all
290 possible in case something else (like the timestamp) has changed */
292 if (!strcmp(dbAttr, buffer)) {
293 /* this config file has never been modified, so just replace it */
297 if (!strcmp(dbAttr, newAttr)) {
298 /* this file is the same in all versions of this package */
303 * The config file on the disk has been modified, but
304 * the ones in the two packages are different. It would
305 * be nice if RPM was smart enough to at least try and
306 * merge the difference ala CVS, but...
313 static int filecmp(short mode1, const char * md51, const char * link1,
314 short mode2, const char * md52, const char * link2)
317 fileTypes what1 = whatis(mode1);
318 fileTypes what2 = whatis(mode2);
320 if (what1 != what2) return 1;
323 return strcmp(link1, link2);
324 else if (what1 == REG)
325 return strcmp(md51, md52);
332 /* XXX only ts->{probs,rpmdb} modified */
333 static int handleInstInstalledFiles(const rpmTransactionSet ts, TFI_t fi,
334 struct sharedFileInfo * shared,
335 int sharedCount, int reportConflicts)
336 /*@globals fileSystem @*/
337 /*@modifies ts, fi, fileSystem @*/
340 HFD_t hfd = (fi->hfd ? fi->hfd : headerFreeData);
341 rpmtransFlags transFlags = ts->transFlags;
342 rpmTagType oltype, omtype;
345 const char ** otherMd5s;
346 const char ** otherLinks;
347 const char * otherStates;
348 uint_32 * otherFlags;
349 uint_32 * otherSizes;
350 uint_16 * otherModes;
354 rpmdbMatchIterator mi;
356 mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES, &shared->otherPkg, sizeof(shared->otherPkg));
357 h = rpmdbNextIterator(mi);
359 mi = rpmdbFreeIterator(mi);
363 xx = hge(h, RPMTAG_FILEMD5S, &omtype, (void **) &otherMd5s, NULL);
364 xx = hge(h, RPMTAG_FILELINKTOS, &oltype, (void **) &otherLinks, NULL);
365 xx = hge(h, RPMTAG_FILESTATES, NULL, (void **) &otherStates, NULL);
366 xx = hge(h, RPMTAG_FILEMODES, NULL, (void **) &otherModes, NULL);
367 xx = hge(h, RPMTAG_FILEFLAGS, NULL, (void **) &otherFlags, NULL);
368 xx = hge(h, RPMTAG_FILESIZES, NULL, (void **) &otherSizes, NULL);
370 fi->replaced = xmalloc(sharedCount * sizeof(*fi->replaced));
372 for (i = 0; i < sharedCount; i++, shared++) {
373 int otherFileNum, fileNum;
374 otherFileNum = shared->otherFileNum;
375 fileNum = shared->pkgFileNum;
377 /* XXX another tedious segfault, assume file state normal. */
378 if (otherStates && otherStates[otherFileNum] != RPMFILE_STATE_NORMAL)
381 if (XFA_SKIPPING(fi->actions[fileNum]))
384 if (filecmp(otherModes[otherFileNum],
385 otherMd5s[otherFileNum],
386 otherLinks[otherFileNum],
389 fi->flinks[fileNum])) {
390 /*@-compdef@*/ /* FIX: *fi->replaced undefined */
392 rpmProblemSetAppend(ts->probs, RPMPROB_FILE_CONFLICT,
393 fiGetNVR(fi), fi->key,
394 fi->dnl[fi->dil[fileNum]], fi->bnl[fileNum],
398 if (!(otherFlags[otherFileNum] | fi->fflags[fileNum])
401 if (!shared->isRemoved)
402 fi->replaced[numReplaced++] = *shared;
407 if ((otherFlags[otherFileNum] | fi->fflags[fileNum]) & RPMFILE_CONFIG) {
408 fi->actions[fileNum] = decideFileFate(
409 fi->dnl[fi->dil[fileNum]],
411 otherModes[otherFileNum],
412 otherMd5s[otherFileNum],
413 otherLinks[otherFileNum],
421 fi->replacedSizes[fileNum] = otherSizes[otherFileNum];
424 otherMd5s = hfd(otherMd5s, omtype);
425 otherLinks = hfd(otherLinks, oltype);
426 mi = rpmdbFreeIterator(mi);
428 fi->replaced = xrealloc(fi->replaced, /* XXX memory leak */
429 sizeof(*fi->replaced) * (numReplaced + 1));
430 fi->replaced[numReplaced].otherPkg = 0;
437 /* XXX only ts->rpmdb modified */
438 static int handleRmvdInstalledFiles(const rpmTransactionSet ts, TFI_t fi,
439 struct sharedFileInfo * shared, int sharedCount)
440 /*@globals fileSystem @*/
441 /*@modifies fi, fileSystem @*/
445 const char * otherStates;
448 rpmdbMatchIterator mi;
450 mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES,
451 &shared->otherPkg, sizeof(shared->otherPkg));
452 h = rpmdbNextIterator(mi);
454 mi = rpmdbFreeIterator(mi);
458 xx = hge(h, RPMTAG_FILESTATES, NULL, (void **) &otherStates, NULL);
460 for (i = 0; i < sharedCount; i++, shared++) {
461 int otherFileNum, fileNum;
462 otherFileNum = shared->otherFileNum;
463 fileNum = shared->pkgFileNum;
465 if (otherStates[otherFileNum] != RPMFILE_STATE_NORMAL)
468 fi->actions[fileNum] = FA_SKIP;
471 mi = rpmdbFreeIterator(mi);
477 * Update disk space needs on each partition for this package.
479 /* XXX only ts->{probs,di} modified */
480 static void handleOverlappedFiles(const rpmTransactionSet ts, TFI_t fi)
481 /*@globals fileSystem @*/
482 /*@modifies ts, fi, fileSystem @*/
484 struct diskspaceInfo * ds = NULL;
485 uint_32 fixupSize = 0;
486 char * filespec = NULL;
487 int fileSpecAlloced = 0;
490 for (i = 0; i < fi->fc; i++) {
491 int otherPkgNum, otherFileNum;
495 if (XFA_SKIPPING(fi->actions[i]))
498 j = strlen(fi->dnl[fi->dil[i]]) + strlen(fi->bnl[i]) + 1;
500 if (j > fileSpecAlloced) {
501 fileSpecAlloced = j * 2;
502 filespec = xrealloc(filespec, fileSpecAlloced);
506 (void) stpcpy( stpcpy( filespec, fi->dnl[fi->dil[i]]), fi->bnl[i]);
510 while (ds->bsize && ds->dev != fi->fps[i].entry->dev) ds++;
511 if (!ds->bsize) ds = NULL;
516 * Retrieve all records that apply to this file. Note that the
517 * file info records were built in the same order as the packages
518 * will be installed and removed so the records for an overlapped
519 * files will be sorted in exactly the same order.
521 (void) htGetEntry(ts->ht, &fi->fps[i],
522 (const void ***) &recs, &numRecs, NULL);
525 * If this package is being added, look only at other packages
526 * being added -- removed packages dance to a different tune.
527 * If both this and the other package are being added, overlapped
528 * files must be identical (or marked as a conflict). The
529 * disposition of already installed config files leads to
530 * a small amount of extra complexity.
532 * If this package is being removed, then there are two cases that
533 * need to be worried about:
534 * If the other package is being added, then skip any overlapped files
535 * so that this package removal doesn't nuke the overlapped files
536 * that were just installed.
537 * If both this and the other package are being removed, then each
538 * file removal from preceding packages needs to be skipped so that
539 * the file removal occurs only on the last occurence of an overlapped
540 * file in the transaction set.
544 /* Locate this overlapped file in the set of added/removed packages. */
545 for (j = 0; j < numRecs && recs[j] != fi; j++)
548 /* Find what the previous disposition of this file was. */
549 otherFileNum = -1; /* keep gcc quiet */
550 for (otherPkgNum = j - 1; otherPkgNum >= 0; otherPkgNum--) {
551 /* Added packages need only look at other added packages. */
552 if (fi->type == TR_ADDED && recs[otherPkgNum]->type != TR_ADDED)
553 /*@innercontinue@*/ continue;
555 /* TESTME: there are more efficient searches in the world... */
556 for (otherFileNum = 0; otherFileNum < recs[otherPkgNum]->fc;
559 /* If the addresses are the same, so are the values. */
560 if ((fi->fps + i) == (recs[otherPkgNum]->fps + otherFileNum))
561 /*@innerbreak@*/ break;
563 /* Otherwise, compare fingerprints by value. */
564 /*@-nullpass@*/ /* LCL: looks good to me */
565 if (FP_EQUAL(fi->fps[i], recs[otherPkgNum]->fps[otherFileNum]))
566 /*@innerbreak@*/ break;
570 /* XXX is this test still necessary? */
571 if (recs[otherPkgNum]->actions[otherFileNum] != FA_UNKNOWN)
572 /*@innerbreak@*/ break;
578 if (otherPkgNum < 0) {
579 /* XXX is this test still necessary? */
580 if (fi->actions[i] != FA_UNKNOWN)
581 /*@switchbreak@*/ break;
582 if ((fi->fflags[i] & RPMFILE_CONFIG) &&
583 !lstat(filespec, &sb)) {
584 /* Here is a non-overlapped pre-existing config file. */
585 fi->actions[i] = (fi->fflags[i] & RPMFILE_NOREPLACE)
586 ? FA_ALTNAME : FA_BACKUP;
588 fi->actions[i] = FA_CREATE;
590 /*@switchbreak@*/ break;
593 /* Mark added overlapped non-identical files as a conflict. */
594 if ((ts->ignoreSet & RPMPROB_FILTER_REPLACENEWFILES)
595 && filecmp(recs[otherPkgNum]->fmodes[otherFileNum],
596 recs[otherPkgNum]->fmd5s[otherFileNum],
597 recs[otherPkgNum]->flinks[otherFileNum],
602 rpmProblemSetAppend(ts->probs, RPMPROB_NEW_FILE_CONFLICT,
603 fiGetNVR(fi), fi->key,
605 fiGetNVR(recs[otherPkgNum]),
609 /* Try to get the disk accounting correct even if a conflict. */
610 fixupSize = recs[otherPkgNum]->fsizes[otherFileNum];
612 if ((fi->fflags[i] & RPMFILE_CONFIG) && !lstat(filespec, &sb)) {
613 /* Here is an overlapped pre-existing config file. */
614 fi->actions[i] = (fi->fflags[i] & RPMFILE_NOREPLACE)
615 ? FA_ALTNAME : FA_SKIP;
617 fi->actions[i] = FA_CREATE;
619 } /*@switchbreak@*/ break;
621 if (otherPkgNum >= 0) {
622 /* Here is an overlapped added file we don't want to nuke. */
623 if (recs[otherPkgNum]->actions[otherFileNum] != FA_ERASE) {
624 /* On updates, don't remove files. */
625 fi->actions[i] = FA_SKIP;
626 /*@switchbreak@*/ break;
628 /* Here is an overlapped removed file: skip in previous. */
629 recs[otherPkgNum]->actions[otherFileNum] = FA_SKIP;
631 if (XFA_SKIPPING(fi->actions[i]))
632 /*@switchbreak@*/ break;
633 if (fi->fstates && fi->fstates[i] != RPMFILE_STATE_NORMAL)
634 /*@switchbreak@*/ break;
635 if (!(S_ISREG(fi->fmodes[i]) && (fi->fflags[i] & RPMFILE_CONFIG))) {
636 fi->actions[i] = FA_ERASE;
637 /*@switchbreak@*/ break;
640 /* Here is a pre-existing modified config file that needs saving. */
642 if (!mdfile(filespec, mdsum) && strcmp(fi->fmd5s[i], mdsum)) {
643 fi->actions[i] = FA_BACKUP;
644 /*@switchbreak@*/ break;
647 fi->actions[i] = FA_ERASE;
648 /*@switchbreak@*/ break;
652 uint_32 s = BLOCK_ROUND(fi->fsizes[i], ds->bsize);
654 switch (fi->actions[i]) {
660 /*@switchbreak@*/ break;
663 * FIXME: If two packages share a file (same md5sum), and
664 * that file is being replaced on disk, will ds->bneeded get
665 * decremented twice? Quite probably!
669 ds->bneeded -= BLOCK_ROUND(fi->replacedSizes[i], ds->bsize);
670 /*@switchbreak@*/ break;
675 /*@switchbreak@*/ break;
678 /*@switchbreak@*/ break;
681 ds->bneeded -= BLOCK_ROUND(fixupSize, ds->bsize);
684 filespec = _free(filespec);
689 static int ensureOlder(rpmTransactionSet ts,
690 const Header h, /*@null@*/ const Header old,
691 /*@dependent@*/ /*@null@*/ const void * key)
696 if (old == NULL) return 1;
698 result = rpmVersionCompare(old, h);
701 else if (result > 0) {
703 /*@-evalorder@*/ /* LCL: is confused */
704 rpmProblemSetAppend(ts->probs, RPMPROB_OLDPACKAGE,
705 hGetNVR(h, NULL), key,
717 static void skipFiles(const rpmTransactionSet ts, TFI_t fi)
718 /*@globals rpmGlobalMacroContext @*/
719 /*@modifies fi, rpmGlobalMacroContext @*/
721 int noDocs = (ts->transFlags & RPMTRANS_FLAG_NODOCS);
722 char ** netsharedPaths = NULL;
723 const char ** languages;
724 const char * dn, * bn;
725 int dnlen, bnlen, ix;
732 noDocs = rpmExpandNumeric("%{_excludedocs}");
734 { const char *tmpPath = rpmExpand("%{_netsharedpath}", NULL);
736 if (tmpPath && *tmpPath != '%')
737 netsharedPaths = splitString(tmpPath, strlen(tmpPath), ':');
739 tmpPath = _free(tmpPath);
742 s = rpmExpand("%{_install_langs}", NULL);
744 if (!(s && *s != '%'))
747 languages = (const char **) splitString(s, strlen(s), ':');
753 /* Compute directory refcount, skip directory if now empty. */
754 drc = alloca(fi->dc * sizeof(*drc));
755 memset(drc, 0, fi->dc * sizeof(*drc));
756 dff = alloca(fi->dc * sizeof(*dff));
757 memset(dff, 0, fi->dc * sizeof(*dff));
759 for (i = 0; i < fi->fc; i++) {
770 /* Don't bother with skipped files */
771 if (XFA_SKIPPING(fi->actions[i])) {
777 * Skip net shared paths.
778 * Net shared paths are not relative to the current root (though
779 * they do need to take package relocations into account).
781 for (nsp = netsharedPaths; nsp && *nsp; nsp++) {
786 if (strncmp(dn, *nsp, len))
787 /*@innercontinue@*/ continue;
788 /* Only directories or complete file paths can be net shared */
789 if (!(dn[len] == '/' || dn[len] == '\0'))
790 /*@innercontinue@*/ continue;
792 if (len < (dnlen + bnlen))
793 /*@innercontinue@*/ continue;
794 if (strncmp(dn, *nsp, dnlen))
795 /*@innercontinue@*/ continue;
796 if (strncmp(bn, (*nsp) + dnlen, bnlen))
797 /*@innercontinue@*/ continue;
799 /* Only directories or complete file paths can be net shared */
800 if (!((*nsp)[len] == '/' || (*nsp)[len] == '\0'))
801 /*@innercontinue@*/ continue;
804 /*@innerbreak@*/ break;
808 drc[ix]--; dff[ix] = 1;
809 fi->actions[i] = FA_SKIPNETSHARED;
814 * Skip i18n language specific files.
816 if (fi->flangs && languages && *fi->flangs[i]) {
817 const char **lang, *l, *le;
818 for (lang = languages; *lang != NULL; lang++) {
819 if (!strcmp(*lang, "all"))
820 /*@innerbreak@*/ break;
821 for (l = fi->flangs[i]; *l != '\0'; l = le) {
822 for (le = l; *le != '\0' && *le != '|'; le++)
824 if ((le-l) > 0 && !strncmp(*lang, l, (le-l)))
825 /*@innerbreak@*/ break;
826 if (*le == '|') le++; /* skip over | */
829 /*@innerbreak@*/ break;
832 drc[ix]--; dff[ix] = 1;
833 fi->actions[i] = FA_SKIPNSTATE;
839 * Skip documentation if requested.
841 if (noDocs && (fi->fflags[i] & RPMFILE_DOC)) {
842 drc[ix]--; dff[ix] = 1;
843 fi->actions[i] = FA_SKIPNSTATE;
848 /* Skip (now empty) directories that had skipped files. */
849 for (j = 0; j < fi->dc; j++) {
851 if (drc[j]) continue; /* dir still has files. */
852 if (!dff[j]) continue; /* dir was not emptied here. */
854 /* Find parent directory and basename. */
855 dn = fi->dnl[j]; dnlen = strlen(dn) - 1;
856 bn = dn + dnlen; bnlen = 0;
857 while (bn > dn && bn[-1] != '/') {
863 /* If explicitly included in the package, skip the directory. */
864 for (i = 0; i < fi->fc; i++) {
867 if (XFA_SKIPPING(fi->actions[i]))
868 /*@innercontinue@*/ continue;
869 if (whatis(fi->fmodes[i]) != XDIR)
870 /*@innercontinue@*/ continue;
871 dir = fi->dnl[fi->dil[i]];
872 if (strlen(dir) != dnlen)
873 /*@innercontinue@*/ continue;
874 if (strncmp(dir, dn, dnlen))
875 /*@innercontinue@*/ continue;
876 if (strlen(fi->bnl[i]) != bnlen)
877 /*@innercontinue@*/ continue;
878 if (strncmp(fi->bnl[i], bn, bnlen))
879 /*@innercontinue@*/ continue;
880 rpmMessage(RPMMESS_DEBUG, _("excluding directory %s\n"), dn);
881 fi->actions[i] = FA_SKIPNSTATE;
882 /*@innerbreak@*/ break;
886 if (netsharedPaths) freeSplitString(netsharedPaths);
887 #ifdef DYING /* XXX freeFi will deal with this later. */
888 fi->flangs = _free(fi->flangs);
890 if (languages) freeSplitString((char **)languages);
894 * Iterator across transaction elements, forward on install, backward on erase.
896 struct tsIterator_s {
897 /*@refcounted@*/ rpmTransactionSet ts; /*!< transaction set. */
898 int reverse; /*!< reversed traversal? */
899 int ocsave; /*!< last returned iterator index. */
900 int oc; /*!< iterator index. */
904 * Return transaction element order count.
905 * @param a transaction element iterator
906 * @return element order count
908 static int tsGetOc(void * a)
911 struct tsIterator_s * iter = a;
912 int oc = iter->ocsave;
917 * Destroy transaction element iterator.
918 * @param a transaction element iterator
919 * @return NULL always
921 static /*@null@*/ void * tsFreeIterator(/*@only@*//*@null@*/ void * a)
924 struct tsIterator_s * iter = a;
926 iter->ts = rpmtsUnlink(iter->ts, "tsIterator");
931 * Create transaction element iterator.
932 * @param ts transaction set
933 * @return transaction element iterator
935 static void * tsInitIterator(rpmTransactionSet ts)
938 struct tsIterator_s * iter = NULL;
940 iter = xcalloc(1, sizeof(*iter));
941 iter->ts = rpmtsLink(ts, "tsIterator");
942 iter->reverse = ((ts->transFlags & RPMTRANS_FLAG_REVERSE) ? 1 : 0);
943 iter->oc = (iter->reverse ? (ts->orderCount - 1) : 0);
944 iter->ocsave = iter->oc;
949 * Return next transaction element's file info.
950 * @param a file info iterator
951 * @return next index, -1 on termination
953 static TFI_t tsNextIterator(void * a)
956 struct tsIterator_s * iter = a;
961 if (iter->oc >= 0) oc = iter->oc--;
963 if (iter->oc < iter->ts->orderCount) oc = iter->oc++;
967 fi = iter->ts->flList + oc;
971 #define NOTIFY(_ts, _al) if ((_ts)->notify) (void) (_ts)->notify _al
973 int rpmRunTransactions( rpmTransactionSet ts,
974 rpmCallbackFunction notify, rpmCallbackData notifyData,
975 rpmProblemSet okProbs, rpmProblemSet * newProbs,
976 rpmtransFlags transFlags, rpmprobFilterFlags ignoreSet)
980 int totalFileCount = 0;
982 struct diskspaceInfo * dip;
983 struct sharedFileInfo * shared, * sharedList;
988 fingerPrintCache fpc;
993 int keep_header = 1; /* XXX rpmProblemSetAppend prevents dumping headers. */
995 /* FIXME: what if the same package is included in ts twice? */
997 ts->transFlags = transFlags;
998 if (ts->transFlags & RPMTRANS_FLAG_NOSCRIPTS)
999 ts->transFlags |= (_noTransScripts | _noTransTriggers);
1000 if (ts->transFlags & RPMTRANS_FLAG_NOTRIGGERS)
1001 ts->transFlags |= _noTransTriggers;
1003 /* XXX MULTILIB is broken, as packages can and do execute /sbin/ldconfig. */
1004 if (ts->transFlags & (RPMTRANS_FLAG_JUSTDB | RPMTRANS_FLAG_MULTILIB))
1005 ts->transFlags |= (_noTransScripts | _noTransTriggers);
1007 ts->notify = notify;
1008 ts->notifyData = notifyData;
1010 ts->probs = *newProbs = rpmProblemSetCreate();
1012 ts->ignoreSet = ignoreSet;
1013 ts->currDir = _free(ts->currDir);
1014 ts->currDir = currentDirectory();
1016 if (ts->rpmdb) ts->rpmdb->db_chrootDone = 0;
1017 ts->id = (int_32) time(NULL);
1019 memset(psm, 0, sizeof(*psm));
1021 psm->ts = rpmtsLink(ts, "tsRun");
1024 /* Get available space on mounted file systems. */
1025 if (!(ts->ignoreSet & RPMPROB_FILTER_DISKSPACE) &&
1026 !rpmGetFilesystemList(&ts->filesystems, &ts->filesystemCount)) {
1029 ts->di = _free(ts->di);
1030 dip = ts->di = xcalloc((ts->filesystemCount + 1), sizeof(*ts->di));
1032 for (i = 0; (i < ts->filesystemCount) && dip; i++) {
1033 #if STATFS_IN_SYS_STATVFS
1035 memset(&sfb, 0, sizeof(sfb));
1036 if (statvfs(ts->filesystems[i], &sfb))
1040 /* This platform has the 4-argument version of the statfs call. The last two
1041 * should be the size of struct statfs and 0, respectively. The 0 is the
1042 * filesystem type, and is always 0 when statfs is called on a mounted
1043 * filesystem, as we're doing.
1045 memset(&sfb, 0, sizeof(sfb));
1046 if (statfs(ts->filesystems[i], &sfb, sizeof(sfb), 0))
1048 memset(&sfb, 0, sizeof(sfb));
1049 if (statfs(ts->filesystems[i], &sfb))
1055 ts->di[i].bsize = sfb.f_bsize;
1056 ts->di[i].bneeded = 0;
1057 ts->di[i].ineeded = 0;
1058 #ifdef STATFS_HAS_F_BAVAIL
1059 ts->di[i].bavail = sfb.f_bavail;
1061 /* FIXME: the statfs struct doesn't have a member to tell how many blocks are
1062 * available for non-superusers. f_blocks - f_bfree is probably too big, but
1063 * it's about all we can do.
1065 ts->di[i].bavail = sfb.f_blocks - sfb.f_bfree;
1067 /* XXX Avoid FAT and other file systems that have not inodes. */
1068 ts->di[i].iavail = !(sfb.f_ffree == 0 && sfb.f_files == 0)
1071 xx = stat(ts->filesystems[i], &sb);
1072 ts->di[i].dev = sb.st_dev;
1076 if (dip) ts->di[i].bsize = 0;
1079 /* ===============================================
1080 * For packages being installed:
1081 * - verify package arch/os.
1082 * - verify package epoch:version-release is newer.
1084 * For packages being removed:
1087 /* The ordering doesn't matter here */
1088 for (i = 0; i < alGetSize(ts->addedPackages); i++) {
1089 const char * n, * v, * r;
1091 rpmdbMatchIterator mi;
1094 h = alGetHeader(ts->addedPackages, i, 0);
1095 if (h == NULL) /* XXX can't happen */
1098 (void) headerNVR(h, &n, &v, &r);
1099 key = alGetKey(ts->addedPackages, i);
1101 if (!archOkay(h) && !(ts->ignoreSet & RPMPROB_FILTER_IGNOREARCH))
1102 rpmProblemSetAppend(ts->probs, RPMPROB_BADARCH,
1103 hGetNVR(h, NULL), key,
1104 NULL, NULL, NULL, 0);
1106 if (!osOkay(h) && !(ts->ignoreSet & RPMPROB_FILTER_IGNOREOS))
1107 rpmProblemSetAppend(ts->probs, RPMPROB_BADOS,
1108 hGetNVR(h, NULL), key,
1109 NULL, NULL, NULL, 0);
1111 if (!(ts->ignoreSet & RPMPROB_FILTER_OLDPACKAGE)) {
1113 mi = rpmtsInitIterator(ts, RPMTAG_NAME, n, 0);
1114 while ((oldH = rpmdbNextIterator(mi)) != NULL)
1115 xx = ensureOlder(ts, h, oldH, key);
1116 mi = rpmdbFreeIterator(mi);
1119 /* XXX multilib should not display "already installed" problems */
1120 if (!(ts->ignoreSet & RPMPROB_FILTER_REPLACEPKG)
1121 && !alGetMultiLib(ts->addedPackages, i))
1123 mi = rpmtsInitIterator(ts, RPMTAG_NAME, n, 0);
1124 xx = rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_DEFAULT, v);
1125 xx = rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_DEFAULT, r);
1127 while (rpmdbNextIterator(mi) != NULL) {
1128 rpmProblemSetAppend(ts->probs, RPMPROB_PKG_INSTALLED,
1129 hGetNVR(h, NULL), key,
1130 NULL, NULL, NULL, 0);
1131 /*@innerbreak@*/ break;
1133 mi = rpmdbFreeIterator(mi);
1136 totalFileCount += alGetFilesCount(ts->addedPackages, i);
1142 /* FIXME: it seems a bit silly to read in all of these headers twice */
1143 /* The ordering doesn't matter here */
1144 if (ts->numRemovedPackages > 0) {
1145 rpmdbMatchIterator mi;
1149 mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES, NULL, 0);
1150 xx = rpmdbAppendIterator(mi, ts->removedPackages, ts->numRemovedPackages);
1151 while ((h = rpmdbNextIterator(mi)) != NULL) {
1152 if (headerGetEntry(h, RPMTAG_BASENAMES, NULL, NULL, &fileCount))
1153 totalFileCount += fileCount;
1155 mi = rpmdbFreeIterator(mi);
1158 /* ===============================================
1159 * Initialize transaction element file info for package:
1161 ts->flEntries = alGetSize(ts->addedPackages) + ts->numRemovedPackages;
1162 ts->flList = xcalloc(ts->flEntries, sizeof(*ts->flList));
1165 * FIXME?: we'd be better off assembling one very large file list and
1166 * calling fpLookupList only once. I'm not sure that the speedup is
1167 * worth the trouble though.
1169 tsi = tsInitIterator(ts);
1170 while ((fi = tsNextIterator(tsi)) != NULL) {
1172 fi->magic = TFIMAGIC;
1174 fi->type = ts->order[oc].type;
1181 i = ts->order[oc].u.addedIndex;
1183 fi->h = alGetHeader(ts->addedPackages, i, 1);
1184 fi->multiLib = alGetMultiLib(ts->addedPackages, i);
1186 fi->key = alGetKey(ts->addedPackages, i);
1188 fi->relocs = alGetRelocs(ts->addedPackages, i);
1189 fi->fd = alGetFd(ts->addedPackages, i);
1191 /* XXX availablePackage can be dumped here XXX */
1193 /* XXX header arg unused. */
1194 loadFi(ts, fi, fi->h, keep_header);
1199 /* Skip netshared paths, not our i18n files, and excluded docs */
1201 /*@switchbreak@*/ break;
1203 fi->record = ts->order[oc].u.removed.dboffset;
1204 /* Retrieve erased package header from the database. */
1205 { rpmdbMatchIterator mi;
1207 mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES,
1208 &fi->record, sizeof(fi->record));
1209 if ((fi->h = rpmdbNextIterator(mi)) != NULL)
1210 fi->h = headerLink(fi->h);
1211 mi = rpmdbFreeIterator(mi);
1213 if (fi->h == NULL) {
1217 /* XXX header arg unused. */
1218 loadFi(ts, fi, fi->h, 0);
1219 /*@switchbreak@*/ break;
1224 fi->fps = xmalloc(fi->fc * sizeof(*fi->fps));
1226 tsi = tsFreeIterator(tsi);
1228 if (!ts->chrootDone) {
1230 /*@-superuser -noeffect @*/
1231 xx = chroot(ts->rootDir);
1232 /*@=superuser =noeffect @*/
1234 if (ts->rpmdb) ts->rpmdb->db_chrootDone = 1;
1237 chroot_prefix = ts->rootDir;
1242 ts->ht = htCreate(totalFileCount * 2, 0, 0, fpHashFunction, fpEqual);
1243 fpc = fpCacheCreate(totalFileCount);
1245 /* ===============================================
1246 * Add fingerprint for each file not skipped.
1248 tsi = tsInitIterator(ts);
1249 while ((fi = tsNextIterator(tsi)) != NULL) {
1250 fpLookupList(fpc, fi->dnl, fi->bnl, fi->dil, fi->fc, fi->fps);
1251 for (i = 0; i < fi->fc; i++) {
1252 if (XFA_SKIPPING(fi->actions[i]))
1253 /*@innercontinue@*/ continue;
1254 /*@-dependenttrans@*/
1255 htAddEntry(ts->ht, fi->fps + i, fi);
1256 /*@=dependenttrans@*/
1259 tsi = tsFreeIterator(tsi);
1261 /*@-noeffectuncon @*/ /* FIX: check rc */
1262 NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_START, 6, ts->flEntries,
1263 NULL, ts->notifyData));
1264 /*@=noeffectuncon@*/
1266 /* ===============================================
1267 * Compute file disposition for each package in transaction set.
1269 tsi = tsInitIterator(ts);
1270 while ((fi = tsNextIterator(tsi)) != NULL) {
1271 dbiIndexSet * matches;
1274 /*@-noeffectuncon @*/ /* FIX: check rc */
1275 NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_PROGRESS, (fi - ts->flList),
1276 ts->flEntries, NULL, ts->notifyData));
1277 /*@=noeffectuncon@*/
1279 if (fi->fc == 0) continue;
1281 /* Extract file info for all files in this package from the database. */
1282 matches = xcalloc(fi->fc, sizeof(*matches));
1283 if (rpmdbFindFpList(ts->rpmdb, fi->fps, matches, fi->fc)) {
1284 psm->ts = rpmtsUnlink(ts, "tsRun (rpmFindFpList fail)");
1285 return 1; /* XXX WTFO? */
1289 for (i = 0; i < fi->fc; i++)
1290 numShared += dbiIndexSetCount(matches[i]);
1292 /* Build sorted file info list for this package. */
1293 shared = sharedList = xcalloc((numShared + 1), sizeof(*sharedList));
1294 for (i = 0; i < fi->fc; i++) {
1296 * Take care not to mark files as replaced in packages that will
1297 * have been removed before we will get here.
1299 for (j = 0; j < dbiIndexSetCount(matches[i]); j++) {
1301 ro = dbiIndexRecordOffset(matches[i], j);
1303 for (k = 0; ro != knownBad && k < ts->orderCount; k++) {
1304 switch (ts->order[k].type) {
1306 if (ts->order[k].u.removed.dboffset == ro)
1308 /*@switchbreak@*/ break;
1310 /*@switchbreak@*/ break;
1314 shared->pkgFileNum = i;
1315 shared->otherPkg = dbiIndexRecordOffset(matches[i], j);
1316 shared->otherFileNum = dbiIndexRecordFileNumber(matches[i], j);
1317 shared->isRemoved = (knownBad == ro);
1320 matches[i] = dbiFreeIndexSet(matches[i]);
1322 numShared = shared - sharedList;
1323 shared->otherPkg = -1;
1324 matches = _free(matches);
1326 /* Sort file info by other package index (otherPkg) */
1327 qsort(sharedList, numShared, sizeof(*shared), sharedCmp);
1329 /* For all files from this package that are in the database ... */
1330 for (i = 0; i < numShared; i = nexti) {
1333 shared = sharedList + i;
1335 /* Find the end of the files in the other package. */
1336 for (nexti = i + 1; nexti < numShared; nexti++) {
1337 if (sharedList[nexti].otherPkg != shared->otherPkg)
1338 /*@innerbreak@*/ break;
1341 /* Is this file from a package being removed? */
1343 for (j = 0; j < ts->numRemovedPackages; j++) {
1344 if (ts->removedPackages[j] != shared->otherPkg)
1345 /*@innercontinue@*/ continue;
1347 /*@innerbreak@*/ break;
1350 /* Determine the fate of each file. */
1353 xx = handleInstInstalledFiles(ts, fi, shared, nexti - i,
1354 !(beingRemoved || (ts->ignoreSet & RPMPROB_FILTER_REPLACEOLDFILES)));
1355 /*@switchbreak@*/ break;
1358 xx = handleRmvdInstalledFiles(ts, fi, shared, nexti - i);
1359 /*@switchbreak@*/ break;
1365 /* Update disk space needs on each partition for this package. */
1366 handleOverlappedFiles(ts, fi);
1368 /* Check added package has sufficient space on each partition used. */
1371 if (!(ts->di && fi->fc))
1372 /*@switchbreak@*/ break;
1373 for (i = 0; i < ts->filesystemCount; i++) {
1377 /* XXX Avoid FAT and other file systems that have not inodes. */
1378 if (dip->iavail <= 0)
1379 /*@innercontinue@*/ continue;
1381 if (adj_fs_blocks(dip->bneeded) > dip->bavail)
1382 rpmProblemSetAppend(ts->probs, RPMPROB_DISKSPACE,
1383 fiGetNVR(fi), fi->key,
1384 ts->filesystems[i], NULL, NULL,
1385 (adj_fs_blocks(dip->bneeded) - dip->bavail) * dip->bsize);
1387 if (adj_fs_blocks(dip->ineeded) > dip->iavail)
1388 rpmProblemSetAppend(ts->probs, RPMPROB_DISKNODES,
1389 fiGetNVR(fi), fi->key,
1390 ts->filesystems[i], NULL, NULL,
1391 (adj_fs_blocks(dip->ineeded) - dip->iavail));
1393 /*@switchbreak@*/ break;
1395 /*@switchbreak@*/ break;
1398 tsi = tsFreeIterator(tsi);
1400 if (ts->chrootDone) {
1401 /*@-superuser -noeffect @*/
1403 /*@=superuser =noeffect @*/
1405 if (ts->rpmdb) ts->rpmdb->db_chrootDone = 0;
1407 chroot_prefix = NULL;
1409 xx = chdir(ts->currDir);
1412 /*@-noeffectuncon @*/ /* FIX: check rc */
1413 NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_STOP, 6, ts->flEntries,
1414 NULL, ts->notifyData));
1415 /*@=noeffectuncon @*/
1417 /* ===============================================
1418 * Free unused memory as soon as possible.
1421 tsi = tsInitIterator(ts);
1422 while ((fi = tsNextIterator(tsi)) != NULL) {
1425 fi->fps = _free(fi->fps);
1427 tsi = tsFreeIterator(tsi);
1433 /* ===============================================
1434 * If unfiltered problems exist, free memory and return.
1436 if ((ts->transFlags & RPMTRANS_FLAG_BUILD_PROBS)
1437 || (ts->probs->numProblems &&
1438 (okProbs != NULL || rpmProblemSetTrim(ts->probs, okProbs)))
1441 *newProbs = ts->probs;
1443 ts->flList = freeFl(ts, ts->flList);
1445 if (psm->ts != NULL)
1446 psm->ts = rpmtsUnlink(psm->ts, "tsRun (problems)");
1447 /*@-nullstate@*/ /* FIX: ts->flList may be NULL */
1448 return ts->orderCount;
1452 /* ===============================================
1453 * Save removed files before erasing.
1455 if (ts->transFlags & (RPMTRANS_FLAG_DIRSTASH | RPMTRANS_FLAG_REPACKAGE)) {
1456 tsi = tsInitIterator(ts);
1457 while ((fi = tsNextIterator(tsi)) != NULL) {
1460 /*@switchbreak@*/ break;
1462 if (ts->transFlags & RPMTRANS_FLAG_REPACKAGE) {
1463 psm->fi = rpmfiLink(fi, "tsRepackage");
1464 xx = psmStage(psm, PSM_PKGSAVE);
1465 (void) rpmfiUnlink(fi, "tsRepackage");
1468 /*@switchbreak@*/ break;
1471 tsi = tsFreeIterator(tsi);
1474 /* ===============================================
1475 * Install and remove packages.
1478 lastFailed = -2; /* erased packages have -1 */
1479 tsi = tsInitIterator(ts);
1480 /*@-branchstate@*/ /* FIX: fi reload needs work */
1481 while ((fi = tsNextIterator(tsi)) != NULL) {
1487 psm->fi = rpmfiLink(fi, "tsInstall");
1491 i = ts->order[oc].u.addedIndex;
1493 rpmMessage(RPMMESS_DEBUG, "========== +++ %s-%s-%s\n",
1494 fi->name, fi->version, fi->release);
1495 h = (fi->h ? headerLink(fi->h) : NULL);
1497 if (fi->fd == NULL) {
1498 /*@-noeffectuncon @*/ /* FIX: ??? */
1499 fi->fd = ts->notify(fi->h, RPMCALLBACK_INST_OPEN_FILE, 0, 0,
1500 fi->key, ts->notifyData);
1501 /*@=noeffectuncon @*/
1502 if (fi->fd != NULL) {
1507 /*@-mustmod@*/ /* LCL: segfault */
1508 rpmrc = rpmReadPackageFile(ts, fi->fd,
1509 "rpmRunTransactions", &h);
1512 if (!(rpmrc == RPMRC_OK || rpmrc == RPMRC_BADSIZE)) {
1513 /*@-noeffectuncon @*/ /* FIX: check rc */
1514 (void) ts->notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE,
1516 fi->key, ts->notifyData);
1517 /*@=noeffectuncon @*/
1520 } else if (fi->h != NULL) {
1521 Header foo = relocateFileList(ts, fi, h, NULL);
1523 h = headerLink(foo);
1524 foo = headerFree(foo);
1526 if (fi->fd != NULL) gotfd = 1;
1531 if (fi->fd != NULL) {
1532 Header hsave = NULL;
1535 hsave = headerLink(fi->h);
1536 fi->h = headerFree(fi->h);
1537 fi->h = headerLink(h);
1539 char * fstates = fi->fstates;
1540 fileAction * actions = fi->actions;
1541 uint_32 multiLib = fi->multiLib;
1542 const void * key = fi->key;
1543 rpmRelocation * relocs = fi->relocs;
1553 fi->magic = TFIMAGIC;
1554 fi->type = ts->order[oc].type;
1556 loadFi(ts, fi, h, 1);
1557 fi->fstates = _free(fi->fstates);
1558 fi->fstates = fstates;
1559 fi->actions = _free(fi->actions);
1560 fi->actions = actions;
1561 fi->multiLib = multiLib;
1563 fi->relocs = relocs;
1565 /*@i@*/ fi->fd = fd;
1570 ts->transFlags |= RPMTRANS_FLAG_MULTILIB;
1572 if (psmStage(psm, PSM_PKGINSTALL)) {
1576 fi->h = headerFree(fi->h);
1578 fi->h = headerLink(hsave);
1579 hsave = headerFree(hsave);
1589 /*@-noeffectuncon @*/ /* FIX: check rc */
1590 (void)ts->notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE, 0, 0,
1591 fi->key, ts->notifyData);
1592 /*@=noeffectuncon @*/
1596 /*@switchbreak@*/ break;
1598 rpmMessage(RPMMESS_DEBUG, "========== --- %s-%s-%s\n",
1599 fi->name, fi->version, fi->release);
1601 /* If install failed, then we shouldn't erase. */
1602 if (ts->order[oc].u.removed.dependsOnIndex != lastFailed) {
1603 if (psmStage(psm, PSM_PKGERASE))
1607 /*@switchbreak@*/ break;
1609 xx = rpmdbSync(ts->rpmdb);
1610 (void) rpmfiUnlink(fi, "tsInstall");
1614 tsi = tsFreeIterator(tsi);
1616 ts->flList = freeFl(ts, ts->flList);
1619 psm->ts = rpmtsUnlink(psm->ts, "tsRun");
1621 /*@-nullstate@*/ /* FIX: ts->flList may be NULL */