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@*/
59 /*@access availablePackage@*/
60 /*@access availableList@*/
61 /*@access transactionElement@*/
65 struct diskspaceInfo {
66 dev_t dev; /*!< file system device number. */
67 signed long bneeded; /*!< no. of blocks needed. */
68 signed long ineeded; /*!< no. of inodes needed. */
69 int bsize; /*!< file system block size. */
70 signed long bavail; /*!< no. of blocks available. */
71 signed long iavail; /*!< no. of inodes available. */
75 * Adjust for root only reserved space. On linux e2fs, this is 5%.
77 #define adj_fs_blocks(_nb) (((_nb) * 21) / 20)
79 /* argon thought a shift optimization here was a waste of time... he's
81 #define BLOCK_ROUND(size, block) (((size) + (block) - 1) / (block))
85 static /*@null@*/ void * freeFl(rpmTransactionSet ts,
86 /*@only@*/ /*@null@*/ TFI_t flList)
93 /*@-usereleased -onlytrans @*/ /* FIX: fi needs to be only */
94 for (oc = 0, fi = flList; oc < ts->orderCount; oc++, fi++)
96 flList = _free(flList);
97 /*@=usereleased =onlytrans @*/
102 void rpmtransSetScriptFd(rpmTransactionSet ts, FD_t fd)
104 /*@-type@*/ /* FIX: cast? */
105 ts->scriptFd = (fd ? fdLink(fd, "rpmtransSetScriptFd") : NULL);
109 int rpmtransGetKeys(const rpmTransactionSet ts, const void *** ep, int * nep)
113 if (nep) *nep = ts->orderCount;
118 *ep = e = xmalloc(ts->orderCount * sizeof(*e));
119 for (oc = 0; oc < ts->orderCount; oc++, e++) {
120 switch (ts->order[oc].type) {
122 if (ts->addedPackages->list) {
123 availablePackage alp;
124 alp = ts->addedPackages->list + ts->order[oc].u.addedIndex;
126 /*@switchbreak@*/ break;
131 /*@-mods@*/ /* FIX: double indirection. */
134 /*@switchbreak@*/ break;
143 static int archOkay(Header h)
149 /* make sure we're trying to install this on the proper architecture */
150 (void) headerGetEntry(h, RPMTAG_ARCH, &type, (void **) &pkgArch, &count);
152 if (type == RPM_INT8_TYPE) {
156 /* old arch handling */
157 rpmGetArchInfo(NULL, &archNum);
158 pkgArchNum = pkgArch;
159 if (archNum != *pkgArchNum) {
165 /* new arch handling */
166 if (!rpmMachineScore(RPM_MACHTABLE_INSTARCH, pkgArch)) {
176 static int osOkay(Header h)
182 /* make sure we're trying to install this on the proper os */
183 (void) headerGetEntry(h, RPMTAG_OS, &type, (void **) &pkgOs, &count);
185 if (type == RPM_INT8_TYPE) {
186 /* v1 packages and v2 packages both used improper OS numbers, so just
187 deal with it hope things work */
192 /* new os handling */
193 if (!rpmMachineScore(RPM_MACHTABLE_INSTOS, pkgOs)) {
203 static int sharedCmp(const void * one, const void * two)
206 const struct sharedFileInfo * a = one;
207 const struct sharedFileInfo * b = two;
209 if (a->otherPkg < b->otherPkg)
211 else if (a->otherPkg > b->otherPkg)
219 static fileAction decideFileFate(const char * dirName,
220 const char * baseName, short dbMode,
221 const char * dbMd5, const char * dbLink, short newMode,
222 const char * newMd5, const char * newLink, int newFlags,
223 rpmtransFlags transFlags)
224 /*@globals fileSystem @*/
225 /*@modifies fileSystem @*/
228 const char * dbAttr, * newAttr;
229 fileTypes dbWhat, newWhat, diskWhat;
232 int save = (newFlags & RPMFILE_NOREPLACE) ? FA_ALTNAME : FA_SAVE;
233 char * filespec = alloca(strlen(dirName) + strlen(baseName) + 1);
235 (void) stpcpy( stpcpy(filespec, dirName), baseName);
237 if (lstat(filespec, &sb)) {
239 * The file doesn't exist on the disk. Create it unless the new
240 * package has marked it as missingok, or allfiles is requested.
242 if (!(transFlags & RPMTRANS_FLAG_ALLFILES) &&
243 (newFlags & RPMFILE_MISSINGOK)) {
244 rpmMessage(RPMMESS_DEBUG, _("%s skipped due to missingok flag\n"),
252 diskWhat = whatis(sb.st_mode);
253 dbWhat = whatis(dbMode);
254 newWhat = whatis(newMode);
256 /* RPM >= 2.3.10 shouldn't create config directories -- we'll ignore
257 them in older packages as well */
258 if (newWhat == XDIR) {
262 if (diskWhat != newWhat) {
264 } else if (newWhat != dbWhat && diskWhat != dbWhat) {
266 } else if (dbWhat != newWhat) {
268 } else if (dbWhat != LINK && dbWhat != REG) {
273 rc = mdfile(filespec, buffer);
276 /* assume the file has been removed, don't freak */
281 } else /* dbWhat == LINK */ {
282 memset(buffer, 0, sizeof(buffer));
283 i = readlink(filespec, buffer, sizeof(buffer) - 1);
285 /* assume the file has been removed, don't freak */
292 /* this order matters - we'd prefer to CREATE the file if at all
293 possible in case something else (like the timestamp) has changed */
295 if (!strcmp(dbAttr, buffer)) {
296 /* this config file has never been modified, so just replace it */
300 if (!strcmp(dbAttr, newAttr)) {
301 /* this file is the same in all versions of this package */
306 * The config file on the disk has been modified, but
307 * the ones in the two packages are different. It would
308 * be nice if RPM was smart enough to at least try and
309 * merge the difference ala CVS, but...
316 static int filecmp(short mode1, const char * md51, const char * link1,
317 short mode2, const char * md52, const char * link2)
320 fileTypes what1 = whatis(mode1);
321 fileTypes what2 = whatis(mode2);
323 if (what1 != what2) return 1;
326 return strcmp(link1, link2);
327 else if (what1 == REG)
328 return strcmp(md51, md52);
335 /* XXX only ts->{probs,rpmdb} modified */
336 static int handleInstInstalledFiles(const rpmTransactionSet ts, TFI_t fi,
337 struct sharedFileInfo * shared,
338 int sharedCount, int reportConflicts)
339 /*@globals fileSystem @*/
340 /*@modifies ts, fi, fileSystem @*/
343 HFD_t hfd = (fi->hfd ? fi->hfd : headerFreeData);
344 rpmtransFlags transFlags = ts->transFlags;
345 rpmTagType oltype, omtype;
348 const char ** otherMd5s;
349 const char ** otherLinks;
350 const char * otherStates;
351 uint_32 * otherFlags;
352 uint_32 * otherSizes;
353 uint_16 * otherModes;
357 rpmdbMatchIterator mi;
359 mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES, &shared->otherPkg, sizeof(shared->otherPkg));
360 h = rpmdbNextIterator(mi);
362 mi = rpmdbFreeIterator(mi);
366 xx = hge(h, RPMTAG_FILEMD5S, &omtype, (void **) &otherMd5s, NULL);
367 xx = hge(h, RPMTAG_FILELINKTOS, &oltype, (void **) &otherLinks, NULL);
368 xx = hge(h, RPMTAG_FILESTATES, NULL, (void **) &otherStates, NULL);
369 xx = hge(h, RPMTAG_FILEMODES, NULL, (void **) &otherModes, NULL);
370 xx = hge(h, RPMTAG_FILEFLAGS, NULL, (void **) &otherFlags, NULL);
371 xx = hge(h, RPMTAG_FILESIZES, NULL, (void **) &otherSizes, NULL);
373 fi->replaced = xmalloc(sharedCount * sizeof(*fi->replaced));
375 for (i = 0; i < sharedCount; i++, shared++) {
376 int otherFileNum, fileNum;
377 otherFileNum = shared->otherFileNum;
378 fileNum = shared->pkgFileNum;
380 /* XXX another tedious segfault, assume file state normal. */
381 if (otherStates && otherStates[otherFileNum] != RPMFILE_STATE_NORMAL)
384 if (XFA_SKIPPING(fi->actions[fileNum]))
387 if (filecmp(otherModes[otherFileNum],
388 otherMd5s[otherFileNum],
389 otherLinks[otherFileNum],
392 fi->flinks[fileNum])) {
394 rpmProblemSetAppend(ts->probs, RPMPROB_FILE_CONFLICT, fi->ap,
395 fi->dnl[fi->dil[fileNum]], fi->bnl[fileNum], h, 0);
396 if (!(otherFlags[otherFileNum] | fi->fflags[fileNum])
399 if (!shared->isRemoved)
400 fi->replaced[numReplaced++] = *shared;
405 if ((otherFlags[otherFileNum] | fi->fflags[fileNum]) & RPMFILE_CONFIG) {
406 fi->actions[fileNum] = decideFileFate(
407 fi->dnl[fi->dil[fileNum]],
409 otherModes[otherFileNum],
410 otherMd5s[otherFileNum],
411 otherLinks[otherFileNum],
419 fi->replacedSizes[fileNum] = otherSizes[otherFileNum];
422 otherMd5s = hfd(otherMd5s, omtype);
423 otherLinks = hfd(otherLinks, oltype);
424 mi = rpmdbFreeIterator(mi);
426 fi->replaced = xrealloc(fi->replaced, /* XXX memory leak */
427 sizeof(*fi->replaced) * (numReplaced + 1));
428 fi->replaced[numReplaced].otherPkg = 0;
435 /* XXX only ts->rpmdb modified */
436 static int handleRmvdInstalledFiles(const rpmTransactionSet ts, TFI_t fi,
437 struct sharedFileInfo * shared, int sharedCount)
438 /*@globals fileSystem @*/
439 /*@modifies fi, fileSystem @*/
443 const char * otherStates;
446 rpmdbMatchIterator mi;
448 mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES,
449 &shared->otherPkg, sizeof(shared->otherPkg));
450 h = rpmdbNextIterator(mi);
452 mi = rpmdbFreeIterator(mi);
456 xx = hge(h, RPMTAG_FILESTATES, NULL, (void **) &otherStates, NULL);
458 for (i = 0; i < sharedCount; i++, shared++) {
459 int otherFileNum, fileNum;
460 otherFileNum = shared->otherFileNum;
461 fileNum = shared->pkgFileNum;
463 if (otherStates[otherFileNum] != RPMFILE_STATE_NORMAL)
466 fi->actions[fileNum] = FA_SKIP;
469 mi = rpmdbFreeIterator(mi);
475 * Update disk space needs on each partition for this package.
477 /* XXX only ts->{probs,di} modified */
478 static void handleOverlappedFiles(const rpmTransactionSet ts, TFI_t fi)
479 /*@globals fileSystem @*/
480 /*@modifies ts, fi, fileSystem @*/
482 struct diskspaceInfo * ds = NULL;
483 uint_32 fixupSize = 0;
484 char * filespec = NULL;
485 int fileSpecAlloced = 0;
488 for (i = 0; i < fi->fc; i++) {
489 int otherPkgNum, otherFileNum;
493 if (XFA_SKIPPING(fi->actions[i]))
496 j = strlen(fi->dnl[fi->dil[i]]) + strlen(fi->bnl[i]) + 1;
498 if (j > fileSpecAlloced) {
499 fileSpecAlloced = j * 2;
500 filespec = xrealloc(filespec, fileSpecAlloced);
504 (void) stpcpy( stpcpy( filespec, fi->dnl[fi->dil[i]]), fi->bnl[i]);
508 while (ds->bsize && ds->dev != fi->fps[i].entry->dev) ds++;
509 if (!ds->bsize) ds = NULL;
514 * Retrieve all records that apply to this file. Note that the
515 * file info records were built in the same order as the packages
516 * will be installed and removed so the records for an overlapped
517 * files will be sorted in exactly the same order.
519 (void) htGetEntry(ts->ht, &fi->fps[i],
520 (const void ***) &recs, &numRecs, NULL);
523 * If this package is being added, look only at other packages
524 * being added -- removed packages dance to a different tune.
525 * If both this and the other package are being added, overlapped
526 * files must be identical (or marked as a conflict). The
527 * disposition of already installed config files leads to
528 * a small amount of extra complexity.
530 * If this package is being removed, then there are two cases that
531 * need to be worried about:
532 * If the other package is being added, then skip any overlapped files
533 * so that this package removal doesn't nuke the overlapped files
534 * that were just installed.
535 * If both this and the other package are being removed, then each
536 * file removal from preceding packages needs to be skipped so that
537 * the file removal occurs only on the last occurence of an overlapped
538 * file in the transaction set.
542 /* Locate this overlapped file in the set of added/removed packages. */
543 for (j = 0; j < numRecs && recs[j] != fi; j++)
546 /* Find what the previous disposition of this file was. */
547 otherFileNum = -1; /* keep gcc quiet */
548 for (otherPkgNum = j - 1; otherPkgNum >= 0; otherPkgNum--) {
549 /* Added packages need only look at other added packages. */
550 if (fi->type == TR_ADDED && recs[otherPkgNum]->type != TR_ADDED)
551 /*@innercontinue@*/ continue;
553 /* TESTME: there are more efficient searches in the world... */
554 for (otherFileNum = 0; otherFileNum < recs[otherPkgNum]->fc;
557 /* If the addresses are the same, so are the values. */
558 if ((fi->fps + i) == (recs[otherPkgNum]->fps + otherFileNum))
559 /*@innerbreak@*/ break;
561 /* Otherwise, compare fingerprints by value. */
562 /*@-nullpass@*/ /* LCL: looks good to me */
563 if (FP_EQUAL(fi->fps[i], recs[otherPkgNum]->fps[otherFileNum]))
564 /*@innerbreak@*/ break;
568 /* XXX is this test still necessary? */
569 if (recs[otherPkgNum]->actions[otherFileNum] != FA_UNKNOWN)
570 /*@innerbreak@*/ break;
576 if (otherPkgNum < 0) {
577 /* XXX is this test still necessary? */
578 if (fi->actions[i] != FA_UNKNOWN)
579 /*@switchbreak@*/ break;
580 if ((fi->fflags[i] & RPMFILE_CONFIG) &&
581 !lstat(filespec, &sb)) {
582 /* Here is a non-overlapped pre-existing config file. */
583 fi->actions[i] = (fi->fflags[i] & RPMFILE_NOREPLACE)
584 ? FA_ALTNAME : FA_BACKUP;
586 fi->actions[i] = FA_CREATE;
588 /*@switchbreak@*/ break;
591 /* Mark added overlapped non-identical files as a conflict. */
592 if ((ts->ignoreSet & RPMPROB_FILTER_REPLACENEWFILES)
593 && filecmp(recs[otherPkgNum]->fmodes[otherFileNum],
594 recs[otherPkgNum]->fmd5s[otherFileNum],
595 recs[otherPkgNum]->flinks[otherFileNum],
599 rpmProblemSetAppend(ts->probs, RPMPROB_NEW_FILE_CONFLICT, fi->ap,
600 filespec, NULL, recs[otherPkgNum]->ap->h, 0);
603 /* Try to get the disk accounting correct even if a conflict. */
604 fixupSize = recs[otherPkgNum]->fsizes[otherFileNum];
606 if ((fi->fflags[i] & RPMFILE_CONFIG) && !lstat(filespec, &sb)) {
607 /* Here is an overlapped pre-existing config file. */
608 fi->actions[i] = (fi->fflags[i] & RPMFILE_NOREPLACE)
609 ? FA_ALTNAME : FA_SKIP;
611 fi->actions[i] = FA_CREATE;
613 } /*@switchbreak@*/ break;
615 if (otherPkgNum >= 0) {
616 /* Here is an overlapped added file we don't want to nuke. */
617 if (recs[otherPkgNum]->actions[otherFileNum] != FA_ERASE) {
618 /* On updates, don't remove files. */
619 fi->actions[i] = FA_SKIP;
620 /*@switchbreak@*/ break;
622 /* Here is an overlapped removed file: skip in previous. */
623 recs[otherPkgNum]->actions[otherFileNum] = FA_SKIP;
625 if (XFA_SKIPPING(fi->actions[i]))
626 /*@switchbreak@*/ break;
627 if (fi->fstates && fi->fstates[i] != RPMFILE_STATE_NORMAL)
628 /*@switchbreak@*/ break;
629 if (!(S_ISREG(fi->fmodes[i]) && (fi->fflags[i] & RPMFILE_CONFIG))) {
630 fi->actions[i] = FA_ERASE;
631 /*@switchbreak@*/ break;
634 /* Here is a pre-existing modified config file that needs saving. */
636 if (!mdfile(filespec, mdsum) && strcmp(fi->fmd5s[i], mdsum)) {
637 fi->actions[i] = FA_BACKUP;
638 /*@switchbreak@*/ break;
641 fi->actions[i] = FA_ERASE;
642 /*@switchbreak@*/ break;
646 uint_32 s = BLOCK_ROUND(fi->fsizes[i], ds->bsize);
648 switch (fi->actions[i]) {
654 /*@switchbreak@*/ break;
657 * FIXME: If two packages share a file (same md5sum), and
658 * that file is being replaced on disk, will ds->bneeded get
659 * decremented twice? Quite probably!
663 ds->bneeded -= BLOCK_ROUND(fi->replacedSizes[i], ds->bsize);
664 /*@switchbreak@*/ break;
669 /*@switchbreak@*/ break;
672 /*@switchbreak@*/ break;
675 ds->bneeded -= BLOCK_ROUND(fixupSize, ds->bsize);
678 if (filespec) free(filespec);
683 static int ensureOlder(rpmTransactionSet ts, availablePackage alp, Header old)
684 /*@modifies ts, alp @*/
688 if (old == NULL) return 1;
690 result = rpmVersionCompare(old, alp->h);
693 else if (result > 0) {
695 rpmProblemSetAppend(ts->probs, RPMPROB_OLDPACKAGE, alp,
704 static void skipFiles(const rpmTransactionSet ts, TFI_t fi)
705 /*@globals rpmGlobalMacroContext @*/
706 /*@modifies fi, rpmGlobalMacroContext @*/
708 int noDocs = (ts->transFlags & RPMTRANS_FLAG_NODOCS);
709 char ** netsharedPaths = NULL;
710 const char ** languages;
711 const char * dn, * bn;
712 int dnlen, bnlen, ix;
719 noDocs = rpmExpandNumeric("%{_excludedocs}");
721 { const char *tmpPath = rpmExpand("%{_netsharedpath}", NULL);
723 if (tmpPath && *tmpPath != '%')
724 netsharedPaths = splitString(tmpPath, strlen(tmpPath), ':');
726 tmpPath = _free(tmpPath);
729 s = rpmExpand("%{_install_langs}", NULL);
731 if (!(s && *s != '%'))
734 languages = (const char **) splitString(s, strlen(s), ':');
740 /* Compute directory refcount, skip directory if now empty. */
741 drc = alloca(fi->dc * sizeof(*drc));
742 memset(drc, 0, fi->dc * sizeof(*drc));
743 dff = alloca(fi->dc * sizeof(*dff));
744 memset(dff, 0, fi->dc * sizeof(*dff));
746 for (i = 0; i < fi->fc; i++) {
757 /* Don't bother with skipped files */
758 if (XFA_SKIPPING(fi->actions[i])) {
764 * Skip net shared paths.
765 * Net shared paths are not relative to the current root (though
766 * they do need to take package relocations into account).
768 for (nsp = netsharedPaths; nsp && *nsp; nsp++) {
773 if (strncmp(dn, *nsp, len))
774 /*@innercontinue@*/ continue;
775 /* Only directories or complete file paths can be net shared */
776 if (!(dn[len] == '/' || dn[len] == '\0'))
777 /*@innercontinue@*/ continue;
779 if (len < (dnlen + bnlen))
780 /*@innercontinue@*/ continue;
781 if (strncmp(dn, *nsp, dnlen))
782 /*@innercontinue@*/ continue;
783 if (strncmp(bn, (*nsp) + dnlen, bnlen))
784 /*@innercontinue@*/ continue;
786 /* Only directories or complete file paths can be net shared */
787 if (!((*nsp)[len] == '/' || (*nsp)[len] == '\0'))
788 /*@innercontinue@*/ continue;
791 /*@innerbreak@*/ break;
795 drc[ix]--; dff[ix] = 1;
796 fi->actions[i] = FA_SKIPNETSHARED;
801 * Skip i18n language specific files.
803 if (fi->flangs && languages && *fi->flangs[i]) {
804 const char **lang, *l, *le;
805 for (lang = languages; *lang != NULL; lang++) {
806 if (!strcmp(*lang, "all"))
807 /*@innerbreak@*/ break;
808 for (l = fi->flangs[i]; *l != '\0'; l = le) {
809 for (le = l; *le != '\0' && *le != '|'; le++)
811 if ((le-l) > 0 && !strncmp(*lang, l, (le-l)))
812 /*@innerbreak@*/ break;
813 if (*le == '|') le++; /* skip over | */
816 /*@innerbreak@*/ break;
819 drc[ix]--; dff[ix] = 1;
820 fi->actions[i] = FA_SKIPNSTATE;
826 * Skip documentation if requested.
828 if (noDocs && (fi->fflags[i] & RPMFILE_DOC)) {
829 drc[ix]--; dff[ix] = 1;
830 fi->actions[i] = FA_SKIPNSTATE;
835 /* Skip (now empty) directories that had skipped files. */
836 for (j = 0; j < fi->dc; j++) {
838 if (drc[j]) continue; /* dir still has files. */
839 if (!dff[j]) continue; /* dir was not emptied here. */
841 /* Find parent directory and basename. */
842 dn = fi->dnl[j]; dnlen = strlen(dn) - 1;
843 bn = dn + dnlen; bnlen = 0;
844 while (bn > dn && bn[-1] != '/') {
850 /* If explicitly included in the package, skip the directory. */
851 for (i = 0; i < fi->fc; i++) {
854 if (XFA_SKIPPING(fi->actions[i]))
855 /*@innercontinue@*/ continue;
856 if (whatis(fi->fmodes[i]) != XDIR)
857 /*@innercontinue@*/ continue;
858 dir = fi->dnl[fi->dil[i]];
859 if (strlen(dir) != dnlen)
860 /*@innercontinue@*/ continue;
861 if (strncmp(dir, dn, dnlen))
862 /*@innercontinue@*/ continue;
863 if (strlen(fi->bnl[i]) != bnlen)
864 /*@innercontinue@*/ continue;
865 if (strncmp(fi->bnl[i], bn, bnlen))
866 /*@innercontinue@*/ continue;
867 rpmMessage(RPMMESS_DEBUG, _("excluding directory %s\n"), dn);
868 fi->actions[i] = FA_SKIPNSTATE;
869 /*@innerbreak@*/ break;
873 if (netsharedPaths) freeSplitString(netsharedPaths);
874 #ifdef DYING /* XXX freeFi will deal with this later. */
875 fi->flangs = _free(fi->flangs);
877 if (languages) freeSplitString((char **)languages);
881 * Iterator across transaction elements, forward on install, backward on erase.
883 struct tsIterator_s {
884 /*@refcounted@*/ rpmTransactionSet ts; /*!< transaction set. */
885 int reverse; /*!< reversed traversal? */
886 int ocsave; /*!< last returned iterator index. */
887 int oc; /*!< iterator index. */
891 * Return transaction element order count.
892 * @param a transaction element iterator
893 * @return element order count
895 static int tsGetOc(void * a)
898 struct tsIterator_s * iter = a;
899 int oc = iter->ocsave;
904 * Return transaction element available package pointer.
905 * @param a transaction element iterator
906 * @return available package pointer
908 static /*@dependent@*/ availablePackage tsGetAlp(void * a)
911 struct tsIterator_s * iter = a;
912 availablePackage alp = NULL;
913 int oc = iter->ocsave;
917 rpmTransactionSet ts = iter->ts;
918 TFI_t fi = ts->flList + oc;
919 if (ts->addedPackages->list && fi->type == TR_ADDED)
920 alp = ts->addedPackages->list + ts->order[oc].u.addedIndex;
927 * Destroy transaction element iterator.
928 * @param a transaction element iterator
929 * @return NULL always
931 static /*@null@*/ void * tsFreeIterator(/*@only@*//*@null@*/ void * a)
934 struct tsIterator_s * iter = a;
936 iter->ts = rpmtsUnlink(iter->ts, "tsIterator");
941 * Create transaction element iterator.
942 * @param ts transaction set
943 * @return transaction element iterator
945 static void * tsInitIterator(rpmTransactionSet ts)
948 struct tsIterator_s * iter = NULL;
950 iter = xcalloc(1, sizeof(*iter));
951 iter->ts = rpmtsLink(ts, "tsIterator");
952 iter->reverse = ((ts->transFlags & RPMTRANS_FLAG_REVERSE) ? 1 : 0);
953 iter->oc = (iter->reverse ? (ts->orderCount - 1) : 0);
954 iter->ocsave = iter->oc;
959 * Return next transaction element's file info.
960 * @param a file info iterator
961 * @return next index, -1 on termination
963 static TFI_t tsNextIterator(void * a)
966 struct tsIterator_s * iter = a;
971 if (iter->oc >= 0) oc = iter->oc--;
973 if (iter->oc < iter->ts->orderCount) oc = iter->oc++;
977 fi = iter->ts->flList + oc;
981 #define NOTIFY(_ts, _al) if ((_ts)->notify) (void) (_ts)->notify _al
983 int rpmRunTransactions( rpmTransactionSet ts,
984 rpmCallbackFunction notify, rpmCallbackData notifyData,
985 rpmProblemSet okProbs, rpmProblemSet * newProbs,
986 rpmtransFlags transFlags, rpmprobFilterFlags ignoreSet)
990 availablePackage alp;
991 int totalFileCount = 0;
993 struct diskspaceInfo * dip;
994 struct sharedFileInfo * shared, * sharedList;
999 fingerPrintCache fpc;
1000 struct psm_s psmbuf;
1001 PSM_t psm = &psmbuf;
1004 int keep_header = 1; /* XXX rpmProblemSetAppend prevents dumping headers. */
1006 /* FIXME: what if the same package is included in ts twice? */
1008 ts->transFlags = transFlags;
1009 if (ts->transFlags & RPMTRANS_FLAG_NOSCRIPTS)
1010 ts->transFlags |= (_noTransScripts | _noTransTriggers);
1011 if (ts->transFlags & RPMTRANS_FLAG_NOTRIGGERS)
1012 ts->transFlags |= _noTransTriggers;
1014 /* XXX MULTILIB is broken, as packages can and do execute /sbin/ldconfig. */
1015 if (ts->transFlags & (RPMTRANS_FLAG_JUSTDB | RPMTRANS_FLAG_MULTILIB))
1016 ts->transFlags |= (_noTransScripts | _noTransTriggers);
1018 ts->notify = notify;
1019 ts->notifyData = notifyData;
1021 ts->probs = *newProbs = rpmProblemSetCreate();
1023 ts->ignoreSet = ignoreSet;
1024 ts->currDir = _free(ts->currDir);
1025 ts->currDir = currentDirectory();
1027 if (ts->rpmdb) ts->rpmdb->db_chrootDone = 0;
1028 ts->id = (int_32) time(NULL);
1030 memset(psm, 0, sizeof(*psm));
1032 psm->ts = rpmtsLink(ts, "tsRun");
1035 /* Get available space on mounted file systems. */
1036 if (!(ts->ignoreSet & RPMPROB_FILTER_DISKSPACE) &&
1037 !rpmGetFilesystemList(&ts->filesystems, &ts->filesystemCount)) {
1040 ts->di = _free(ts->di);
1041 dip = ts->di = xcalloc((ts->filesystemCount + 1), sizeof(*ts->di));
1043 for (i = 0; (i < ts->filesystemCount) && dip; i++) {
1044 #if STATFS_IN_SYS_STATVFS
1046 memset(&sfb, 0, sizeof(sfb));
1047 if (statvfs(ts->filesystems[i], &sfb))
1051 /* This platform has the 4-argument version of the statfs call. The last two
1052 * should be the size of struct statfs and 0, respectively. The 0 is the
1053 * filesystem type, and is always 0 when statfs is called on a mounted
1054 * filesystem, as we're doing.
1056 memset(&sfb, 0, sizeof(sfb));
1057 if (statfs(ts->filesystems[i], &sfb, sizeof(sfb), 0))
1059 memset(&sfb, 0, sizeof(sfb));
1060 if (statfs(ts->filesystems[i], &sfb))
1066 ts->di[i].bsize = sfb.f_bsize;
1067 ts->di[i].bneeded = 0;
1068 ts->di[i].ineeded = 0;
1069 #ifdef STATFS_HAS_F_BAVAIL
1070 ts->di[i].bavail = sfb.f_bavail;
1072 /* FIXME: the statfs struct doesn't have a member to tell how many blocks are
1073 * available for non-superusers. f_blocks - f_bfree is probably too big, but
1074 * it's about all we can do.
1076 ts->di[i].bavail = sfb.f_blocks - sfb.f_bfree;
1078 /* XXX Avoid FAT and other file systems that have not inodes. */
1079 ts->di[i].iavail = !(sfb.f_ffree == 0 && sfb.f_files == 0)
1082 xx = stat(ts->filesystems[i], &sb);
1083 ts->di[i].dev = sb.st_dev;
1087 if (dip) ts->di[i].bsize = 0;
1090 /* ===============================================
1091 * For packages being installed:
1092 * - verify package arch/os.
1093 * - verify package epoch:version-release is newer.
1095 * For packages being removed:
1098 /* The ordering doesn't matter here */
1099 if (ts->addedPackages->list != NULL)
1100 for (alp = ts->addedPackages->list;
1101 (alp - ts->addedPackages->list) < ts->addedPackages->size;
1104 if (!archOkay(alp->h) && !(ts->ignoreSet & RPMPROB_FILTER_IGNOREARCH))
1105 rpmProblemSetAppend(ts->probs, RPMPROB_BADARCH, alp,
1106 NULL, NULL, NULL, 0);
1108 if (!osOkay(alp->h) && !(ts->ignoreSet & RPMPROB_FILTER_IGNOREOS))
1109 rpmProblemSetAppend(ts->probs, RPMPROB_BADOS, alp,
1110 NULL, NULL, NULL, 0);
1112 if (!(ts->ignoreSet & RPMPROB_FILTER_OLDPACKAGE)) {
1113 rpmdbMatchIterator mi;
1115 mi = rpmtsInitIterator(ts, RPMTAG_NAME, alp->name, 0);
1116 while ((oldH = rpmdbNextIterator(mi)) != NULL)
1117 xx = ensureOlder(ts, alp, oldH);
1118 mi = rpmdbFreeIterator(mi);
1121 /* XXX multilib should not display "already installed" problems */
1122 if (!(ts->ignoreSet & RPMPROB_FILTER_REPLACEPKG) && !alp->multiLib) {
1123 rpmdbMatchIterator mi;
1124 mi = rpmtsInitIterator(ts, RPMTAG_NAME, alp->name, 0);
1125 xx = rpmdbSetIteratorRE(mi, RPMTAG_VERSION,
1126 RPMMIRE_DEFAULT, alp->version);
1127 xx = rpmdbSetIteratorRE(mi, RPMTAG_RELEASE,
1128 RPMMIRE_DEFAULT, alp->release);
1130 while (rpmdbNextIterator(mi) != NULL) {
1131 rpmProblemSetAppend(ts->probs, RPMPROB_PKG_INSTALLED, alp,
1132 NULL, NULL, NULL, 0);
1133 /*@innerbreak@*/ break;
1135 mi = rpmdbFreeIterator(mi);
1138 totalFileCount += alp->filesCount;
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 file list:
1161 ts->flEntries = ts->addedPackages->size + 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 /* XXX watchout: fi->type must be set for tsGetAlp() to "work" */
1175 fi->type = ts->order[oc].type;
1179 /* XXX watchout: fi->type must be set for tsGetAlp() to "work" */
1180 fi->ap = tsGetAlp(tsi);
1182 loadFi(ts, fi, fi->ap->h, keep_header);
1183 /* XXX free fi->ap->h here if/when possible */
1187 /* Skip netshared paths, not our i18n files, and excluded docs */
1189 /*@switchbreak@*/ break;
1192 fi->record = ts->order[oc].u.removed.dboffset;
1193 /* Retrieve erased package header from the database. */
1194 { rpmdbMatchIterator mi;
1196 mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES,
1197 &fi->record, sizeof(fi->record));
1198 if ((fi->h = rpmdbNextIterator(mi)) != NULL)
1199 fi->h = headerLink(fi->h);
1200 mi = rpmdbFreeIterator(mi);
1202 if (fi->h == NULL) {
1206 /* XXX header arg unused. */
1207 loadFi(ts, fi, fi->h, 0);
1208 /*@switchbreak@*/ break;
1213 fi->fps = xmalloc(fi->fc * sizeof(*fi->fps));
1215 tsi = tsFreeIterator(tsi);
1217 if (!ts->chrootDone) {
1219 /*@-superuser -noeffect @*/
1220 xx = chroot(ts->rootDir);
1221 /*@=superuser =noeffect @*/
1223 if (ts->rpmdb) ts->rpmdb->db_chrootDone = 1;
1226 chroot_prefix = ts->rootDir;
1231 ts->ht = htCreate(totalFileCount * 2, 0, 0, fpHashFunction, fpEqual);
1232 fpc = fpCacheCreate(totalFileCount);
1234 /* ===============================================
1235 * Add fingerprint for each file not skipped.
1237 tsi = tsInitIterator(ts);
1238 while ((fi = tsNextIterator(tsi)) != NULL) {
1239 fpLookupList(fpc, fi->dnl, fi->bnl, fi->dil, fi->fc, fi->fps);
1240 for (i = 0; i < fi->fc; i++) {
1241 if (XFA_SKIPPING(fi->actions[i]))
1242 /*@innercontinue@*/ continue;
1243 /*@-dependenttrans@*/
1244 htAddEntry(ts->ht, fi->fps + i, fi);
1245 /*@=dependenttrans@*/
1248 tsi = tsFreeIterator(tsi);
1250 /*@-noeffectuncon @*/ /* FIX: check rc */
1251 NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_START, 6, ts->flEntries,
1252 NULL, ts->notifyData));
1253 /*@=noeffectuncon@*/
1255 /* ===============================================
1256 * Compute file disposition for each package in transaction set.
1258 tsi = tsInitIterator(ts);
1259 while ((fi = tsNextIterator(tsi)) != NULL) {
1260 dbiIndexSet * matches;
1263 /*@-noeffectuncon @*/ /* FIX: check rc */
1264 NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_PROGRESS, (fi - ts->flList),
1265 ts->flEntries, NULL, ts->notifyData));
1266 /*@=noeffectuncon@*/
1268 if (fi->fc == 0) continue;
1270 /* Extract file info for all files in this package from the database. */
1271 matches = xcalloc(fi->fc, sizeof(*matches));
1272 if (rpmdbFindFpList(ts->rpmdb, fi->fps, matches, fi->fc)) {
1273 psm->ts = rpmtsUnlink(ts, "tsRun (rpmFindFpList fail)");
1274 return 1; /* XXX WTFO? */
1278 for (i = 0; i < fi->fc; i++)
1279 numShared += dbiIndexSetCount(matches[i]);
1281 /* Build sorted file info list for this package. */
1282 shared = sharedList = xcalloc((numShared + 1), sizeof(*sharedList));
1283 for (i = 0; i < fi->fc; i++) {
1285 * Take care not to mark files as replaced in packages that will
1286 * have been removed before we will get here.
1288 for (j = 0; j < dbiIndexSetCount(matches[i]); j++) {
1290 ro = dbiIndexRecordOffset(matches[i], j);
1292 for (k = 0; ro != knownBad && k < ts->orderCount; k++) {
1293 switch (ts->order[k].type) {
1295 if (ts->order[k].u.removed.dboffset == ro)
1297 /*@switchbreak@*/ break;
1299 /*@switchbreak@*/ break;
1303 shared->pkgFileNum = i;
1304 shared->otherPkg = dbiIndexRecordOffset(matches[i], j);
1305 shared->otherFileNum = dbiIndexRecordFileNumber(matches[i], j);
1306 shared->isRemoved = (knownBad == ro);
1309 matches[i] = dbiFreeIndexSet(matches[i]);
1311 numShared = shared - sharedList;
1312 shared->otherPkg = -1;
1313 matches = _free(matches);
1315 /* Sort file info by other package index (otherPkg) */
1316 qsort(sharedList, numShared, sizeof(*shared), sharedCmp);
1318 /* For all files from this package that are in the database ... */
1319 for (i = 0; i < numShared; i = nexti) {
1322 shared = sharedList + i;
1324 /* Find the end of the files in the other package. */
1325 for (nexti = i + 1; nexti < numShared; nexti++) {
1326 if (sharedList[nexti].otherPkg != shared->otherPkg)
1327 /*@innerbreak@*/ break;
1330 /* Is this file from a package being removed? */
1332 for (j = 0; j < ts->numRemovedPackages; j++) {
1333 if (ts->removedPackages[j] != shared->otherPkg)
1334 /*@innercontinue@*/ continue;
1336 /*@innerbreak@*/ break;
1339 /* Determine the fate of each file. */
1342 xx = handleInstInstalledFiles(ts, fi, shared, nexti - i,
1343 !(beingRemoved || (ts->ignoreSet & RPMPROB_FILTER_REPLACEOLDFILES)));
1344 /*@switchbreak@*/ break;
1347 xx = handleRmvdInstalledFiles(ts, fi, shared, nexti - i);
1348 /*@switchbreak@*/ break;
1354 /* Update disk space needs on each partition for this package. */
1355 handleOverlappedFiles(ts, fi);
1357 /* Check added package has sufficient space on each partition used. */
1360 if (!(ts->di && fi->fc))
1361 /*@switchbreak@*/ break;
1362 for (i = 0; i < ts->filesystemCount; i++) {
1366 /* XXX Avoid FAT and other file systems that have not inodes. */
1367 if (dip->iavail <= 0)
1368 /*@innercontinue@*/ continue;
1370 if (adj_fs_blocks(dip->bneeded) > dip->bavail)
1371 rpmProblemSetAppend(ts->probs, RPMPROB_DISKSPACE, fi->ap,
1372 ts->filesystems[i], NULL, NULL,
1373 (adj_fs_blocks(dip->bneeded) - dip->bavail) * dip->bsize);
1375 if (adj_fs_blocks(dip->ineeded) > dip->iavail)
1376 rpmProblemSetAppend(ts->probs, RPMPROB_DISKNODES, fi->ap,
1377 ts->filesystems[i], NULL, NULL,
1378 (adj_fs_blocks(dip->ineeded) - dip->iavail));
1380 /*@switchbreak@*/ break;
1382 /*@switchbreak@*/ break;
1385 tsi = tsFreeIterator(tsi);
1387 if (ts->chrootDone) {
1388 /*@-superuser -noeffect @*/
1390 /*@=superuser =noeffect @*/
1392 if (ts->rpmdb) ts->rpmdb->db_chrootDone = 0;
1394 chroot_prefix = NULL;
1396 xx = chdir(ts->currDir);
1399 /*@-noeffectuncon @*/ /* FIX: check rc */
1400 NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_STOP, 6, ts->flEntries,
1401 NULL, ts->notifyData));
1402 /*@=noeffectuncon @*/
1404 /* ===============================================
1405 * Free unused memory as soon as possible.
1408 tsi = tsInitIterator(ts);
1409 while ((fi = tsNextIterator(tsi)) != NULL) {
1412 fi->fps = _free(fi->fps);
1414 tsi = tsFreeIterator(tsi);
1420 /* ===============================================
1421 * If unfiltered problems exist, free memory and return.
1423 if ((ts->transFlags & RPMTRANS_FLAG_BUILD_PROBS)
1424 || (ts->probs->numProblems &&
1425 (okProbs != NULL || rpmProblemSetTrim(ts->probs, okProbs)))
1428 *newProbs = ts->probs;
1430 ts->flList = freeFl(ts, ts->flList);
1433 psm->ts = rpmtsUnlink(psm->ts, "tsRun (problems)");
1434 return ts->orderCount;
1438 /* ===============================================
1439 * Save removed files before erasing.
1441 if (ts->transFlags & (RPMTRANS_FLAG_DIRSTASH | RPMTRANS_FLAG_REPACKAGE)) {
1442 tsi = tsInitIterator(ts);
1443 while ((fi = tsNextIterator(tsi)) != NULL) {
1446 /*@switchbreak@*/ break;
1448 if (ts->transFlags & RPMTRANS_FLAG_REPACKAGE) {
1449 psm->fi = rpmfiLink(fi, "tsRepackage");
1450 xx = psmStage(psm, PSM_PKGSAVE);
1451 (void) rpmfiUnlink(fi, "tsRepackage");
1454 /*@switchbreak@*/ break;
1457 tsi = tsFreeIterator(tsi);
1460 /* ===============================================
1461 * Install and remove packages.
1464 lastFailed = -2; /* erased packages have -1 */
1465 tsi = tsInitIterator(ts);
1466 /*@-branchstate@*/ /* FIX: fi reload needs work */
1467 while ((fi = tsNextIterator(tsi)) != NULL) {
1472 psm->fi = rpmfiLink(fi, "tsInstall");
1475 alp = tsGetAlp(tsi);
1476 assert(alp == fi->ap);
1477 i = alp - ts->addedPackages->list;
1479 rpmMessage(RPMMESS_DEBUG, "========== +++ %s-%s-%s\n",
1480 fi->name, fi->version, fi->release);
1481 h = (fi->h ? headerLink(fi->h) : NULL);
1483 if (alp->fd == NULL) {
1484 alp->fd = ts->notify(fi->h, RPMCALLBACK_INST_OPEN_FILE, 0, 0,
1485 alp->key, ts->notifyData);
1491 /*@-mustmod@*/ /* LCL: segfault */
1492 rpmrc = rpmReadPackageFile(ts, alp->fd,
1493 "rpmRunTransactions", &h);
1496 if (!(rpmrc == RPMRC_OK || rpmrc == RPMRC_BADSIZE)) {
1497 /*@-noeffectuncon @*/ /* FIX: check rc */
1498 (void)ts->notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE,
1499 0, 0, alp->key, ts->notifyData);
1500 /*@=noeffectuncon @*/
1503 } else if (fi->h != NULL) {
1504 Header foo = relocateFileList(ts, fi, alp, h, NULL);
1506 h = headerLink(foo);
1507 foo = headerFree(foo);
1509 if (alp->fd) gotfd = 1;
1515 Header hsave = NULL;
1518 hsave = headerLink(fi->h);
1519 fi->h = headerFree(fi->h);
1520 fi->h = headerLink(h);
1522 char * fstates = fi->fstates;
1523 fileAction * actions = fi->actions;
1528 fi->magic = TFIMAGIC;
1529 fi->type = ts->order[oc].type;
1530 fi->ap = tsGetAlp(tsi);
1532 loadFi(ts, fi, h, 1);
1533 fi->fstates = _free(fi->fstates);
1534 fi->fstates = fstates;
1535 fi->actions = _free(fi->actions);
1536 fi->actions = actions;
1539 ts->transFlags |= RPMTRANS_FLAG_MULTILIB;
1541 assert(alp == fi->ap);
1542 if (psmStage(psm, PSM_PKGINSTALL)) {
1546 fi->h = headerFree(fi->h);
1548 fi->h = headerLink(hsave);
1549 hsave = headerFree(hsave);
1559 /*@-noeffectuncon @*/ /* FIX: check rc */
1560 (void)ts->notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE, 0, 0,
1561 alp->key, ts->notifyData);
1562 /*@=noeffectuncon @*/
1566 /*@switchbreak@*/ break;
1568 rpmMessage(RPMMESS_DEBUG, "========== --- %s-%s-%s\n",
1569 fi->name, fi->version, fi->release);
1571 /* If install failed, then we shouldn't erase. */
1572 if (ts->order[oc].u.removed.dependsOnIndex != lastFailed) {
1573 if (psmStage(psm, PSM_PKGERASE))
1577 /*@switchbreak@*/ break;
1579 xx = rpmdbSync(ts->rpmdb);
1580 (void) rpmfiUnlink(fi, "tsInstall");
1584 tsi = tsFreeIterator(tsi);
1586 ts->flList = freeFl(ts, ts->flList);
1589 psm->ts = rpmtsUnlink(psm->ts, "tsRun");