2 * \file lib/transaction.c
8 #include <rpmmacro.h> /* XXX for rpmExpand */
11 #include "legacy.h" /* XXX mdfile */
12 #include "misc.h" /* XXX stripTrailingChar, splitString, currentDirectory */
15 /*@-redecl -exportheadervar@*/
17 extern const char * chroot_prefix;
18 /*@=redecl =exportheadervar@*/
20 /* XXX FIXME: merge with existing (broken?) tests in system.h */
21 /* portability fiddles */
22 #if STATFS_IN_SYS_STATVFS
24 # include <sys/statvfs.h>
25 #if defined(__LCLINT__)
26 /*@-declundef -exportheader -protoparammatch @*/ /* LCL: missing annotation */
27 extern int statvfs (const char * file, /*@out@*/ struct statvfs * buf)
28 /*@globals fileSystem @*/
29 /*@modifies *buf, fileSystem @*/;
30 /*@=declundef =exportheader =protoparammatch @*/
34 # if STATFS_IN_SYS_VFS
37 # if STATFS_IN_SYS_MOUNT
38 # include <sys/mount.h>
40 # if STATFS_IN_SYS_STATFS
41 # include <sys/statfs.h>
49 /*@access FD_t@*/ /* XXX compared with NULL */
50 /*@access Header@*/ /* XXX compared with NULL */
51 /*@access dbiIndexSet@*/
53 /*@access rpmTransactionSet@*/
56 /*@access rpmProblemSet@*/
57 /*@access rpmProblem@*/
59 /*@access availablePackage@*/
63 struct diskspaceInfo {
64 dev_t dev; /*!< file system device number. */
65 signed long bneeded; /*!< no. of blocks needed. */
66 signed long ineeded; /*!< no. of inodes needed. */
67 int bsize; /*!< file system block size. */
68 signed long bavail; /*!< no. of blocks available. */
69 signed long iavail; /*!< no. of inodes available. */
73 * Adjust for root only reserved space. On linux e2fs, this is 5%.
75 #define adj_fs_blocks(_nb) (((_nb) * 21) / 20)
77 /* argon thought a shift optimization here was a waste of time... he's
79 #define BLOCK_ROUND(size, block) (((size) + (block) - 1) / (block))
81 #define XSTRCMP(a, b) ((!(a) && !(b)) || ((a) && (b) && !strcmp((a), (b))))
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)) {
201 void rpmProblemSetFree(rpmProblemSet probs)
205 for (i = 0; i < probs->numProblems; i++) {
206 rpmProblem p = probs->probs + i;
207 p->h = headerFree(p->h);
208 p->pkgNEVR = _free(p->pkgNEVR);
209 p->altNEVR = _free(p->altNEVR);
210 p->str1 = _free(p->str1);
216 * Filter a problem set.
217 * As the problem sets are generated in an order solely dependent
218 * on the ordering of the packages in the transaction, and that
219 * ordering can't be changed, the problem sets must be parallel to
220 * one another. Additionally, the filter set must be a subset of the
221 * target set, given the operations available on transaction set.
222 * This is good, as it lets us perform this trim in linear time, rather
223 * then logarithmic or quadratic.
225 * @param filter filter
226 * @param target problem set
227 * @return 0 no problems, 1 if problems remain
229 static int psTrim(rpmProblemSet filter, rpmProblemSet target)
230 /*@modifies target @*/
232 rpmProblem f = filter->probs;
233 rpmProblem t = target->probs;
237 while ((f - filter->probs) < filter->numProblems) {
238 if (!f->ignoreProblem) {
242 while ((t - target->probs) < target->numProblems) {
243 /*@-nullpass@*/ /* LCL: looks good to me */
244 if (f->h == t->h && f->type == t->type && t->key == f->key &&
245 XSTRCMP(f->str1, t->str1))
246 /*@innerbreak@*/ break;
252 if ((t - target->probs) == target->numProblems) {
253 /* this can't happen ;-) let's be sane if it doesn though */
257 t->ignoreProblem = f->ignoreProblem;
262 if ((t - target->probs) < target->numProblems)
270 static int sharedCmp(const void * one, const void * two)
273 const struct sharedFileInfo * a = one;
274 const struct sharedFileInfo * b = two;
276 if (a->otherPkg < b->otherPkg)
278 else if (a->otherPkg > b->otherPkg)
286 static fileAction decideFileFate(const char * dirName,
287 const char * baseName, short dbMode,
288 const char * dbMd5, const char * dbLink, short newMode,
289 const char * newMd5, const char * newLink, int newFlags,
290 rpmtransFlags transFlags)
291 /*@globals fileSystem @*/
292 /*@modifies fileSystem @*/
295 const char * dbAttr, * newAttr;
296 fileTypes dbWhat, newWhat, diskWhat;
299 int save = (newFlags & RPMFILE_NOREPLACE) ? FA_ALTNAME : FA_SAVE;
300 char * filespec = alloca(strlen(dirName) + strlen(baseName) + 1);
302 (void) stpcpy( stpcpy(filespec, dirName), baseName);
304 if (lstat(filespec, &sb)) {
306 * The file doesn't exist on the disk. Create it unless the new
307 * package has marked it as missingok, or allfiles is requested.
309 if (!(transFlags & RPMTRANS_FLAG_ALLFILES) &&
310 (newFlags & RPMFILE_MISSINGOK)) {
311 rpmMessage(RPMMESS_DEBUG, _("%s skipped due to missingok flag\n"),
319 diskWhat = whatis(sb.st_mode);
320 dbWhat = whatis(dbMode);
321 newWhat = whatis(newMode);
323 /* RPM >= 2.3.10 shouldn't create config directories -- we'll ignore
324 them in older packages as well */
325 if (newWhat == XDIR) {
329 if (diskWhat != newWhat) {
331 } else if (newWhat != dbWhat && diskWhat != dbWhat) {
333 } else if (dbWhat != newWhat) {
335 } else if (dbWhat != LINK && dbWhat != REG) {
340 rc = mdfile(filespec, buffer);
343 /* assume the file has been removed, don't freak */
348 } else /* dbWhat == LINK */ {
349 memset(buffer, 0, sizeof(buffer));
350 i = readlink(filespec, buffer, sizeof(buffer) - 1);
352 /* assume the file has been removed, don't freak */
359 /* this order matters - we'd prefer to CREATE the file if at all
360 possible in case something else (like the timestamp) has changed */
362 if (!strcmp(dbAttr, buffer)) {
363 /* this config file has never been modified, so just replace it */
367 if (!strcmp(dbAttr, newAttr)) {
368 /* this file is the same in all versions of this package */
373 * The config file on the disk has been modified, but
374 * the ones in the two packages are different. It would
375 * be nice if RPM was smart enough to at least try and
376 * merge the difference ala CVS, but...
383 static int filecmp(short mode1, const char * md51, const char * link1,
384 short mode2, const char * md52, const char * link2)
387 fileTypes what1 = whatis(mode1);
388 fileTypes what2 = whatis(mode2);
390 if (what1 != what2) return 1;
393 return strcmp(link1, link2);
394 else if (what1 == REG)
395 return strcmp(md51, md52);
402 /* XXX only ts->{probs,rpmdb} modified */
403 static int handleInstInstalledFiles(const rpmTransactionSet ts, TFI_t fi,
404 struct sharedFileInfo * shared,
405 int sharedCount, int reportConflicts)
406 /*@globals fileSystem @*/
407 /*@modifies ts, fi, fileSystem @*/
410 HFD_t hfd = (fi->hfd ? fi->hfd : headerFreeData);
411 rpmProblemSet probs = ts->probs;
412 rpmtransFlags transFlags = ts->transFlags;
413 rpmTagType oltype, omtype;
416 const char ** otherMd5s;
417 const char ** otherLinks;
418 const char * otherStates;
419 uint_32 * otherFlags;
420 uint_32 * otherSizes;
421 uint_16 * otherModes;
425 rpmdbMatchIterator mi;
427 mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES, &shared->otherPkg, sizeof(shared->otherPkg));
428 h = rpmdbNextIterator(mi);
430 mi = rpmdbFreeIterator(mi);
434 xx = hge(h, RPMTAG_FILEMD5S, &omtype, (void **) &otherMd5s, NULL);
435 xx = hge(h, RPMTAG_FILELINKTOS, &oltype, (void **) &otherLinks, NULL);
436 xx = hge(h, RPMTAG_FILESTATES, NULL, (void **) &otherStates, NULL);
437 xx = hge(h, RPMTAG_FILEMODES, NULL, (void **) &otherModes, NULL);
438 xx = hge(h, RPMTAG_FILEFLAGS, NULL, (void **) &otherFlags, NULL);
439 xx = hge(h, RPMTAG_FILESIZES, NULL, (void **) &otherSizes, NULL);
441 fi->replaced = xmalloc(sharedCount * sizeof(*fi->replaced));
443 for (i = 0; i < sharedCount; i++, shared++) {
444 int otherFileNum, fileNum;
445 otherFileNum = shared->otherFileNum;
446 fileNum = shared->pkgFileNum;
448 /* XXX another tedious segfault, assume file state normal. */
449 if (otherStates && otherStates[otherFileNum] != RPMFILE_STATE_NORMAL)
452 if (XFA_SKIPPING(fi->actions[fileNum]))
455 if (filecmp(otherModes[otherFileNum],
456 otherMd5s[otherFileNum],
457 otherLinks[otherFileNum],
460 fi->flinks[fileNum])) {
462 psAppend(probs, RPMPROB_FILE_CONFLICT, fi->ap,
463 fi->dnl[fi->dil[fileNum]], fi->bnl[fileNum], h, 0);
464 if (!(otherFlags[otherFileNum] | fi->fflags[fileNum])
467 if (!shared->isRemoved)
468 fi->replaced[numReplaced++] = *shared;
473 if ((otherFlags[otherFileNum] | fi->fflags[fileNum]) & RPMFILE_CONFIG) {
474 fi->actions[fileNum] = decideFileFate(
475 fi->dnl[fi->dil[fileNum]],
477 otherModes[otherFileNum],
478 otherMd5s[otherFileNum],
479 otherLinks[otherFileNum],
487 fi->replacedSizes[fileNum] = otherSizes[otherFileNum];
490 otherMd5s = hfd(otherMd5s, omtype);
491 otherLinks = hfd(otherLinks, oltype);
492 mi = rpmdbFreeIterator(mi);
494 fi->replaced = xrealloc(fi->replaced, /* XXX memory leak */
495 sizeof(*fi->replaced) * (numReplaced + 1));
496 fi->replaced[numReplaced].otherPkg = 0;
503 /* XXX only ts->rpmdb modified */
504 static int handleRmvdInstalledFiles(const rpmTransactionSet ts, TFI_t fi,
505 struct sharedFileInfo * shared, int sharedCount)
506 /*@globals fileSystem @*/
507 /*@modifies fi, fileSystem @*/
511 const char * otherStates;
514 rpmdbMatchIterator mi;
516 mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES,
517 &shared->otherPkg, sizeof(shared->otherPkg));
518 h = rpmdbNextIterator(mi);
520 mi = rpmdbFreeIterator(mi);
524 xx = hge(h, RPMTAG_FILESTATES, NULL, (void **) &otherStates, NULL);
526 for (i = 0; i < sharedCount; i++, shared++) {
527 int otherFileNum, fileNum;
528 otherFileNum = shared->otherFileNum;
529 fileNum = shared->pkgFileNum;
531 if (otherStates[otherFileNum] != RPMFILE_STATE_NORMAL)
534 fi->actions[fileNum] = FA_SKIP;
537 mi = rpmdbFreeIterator(mi);
543 * Update disk space needs on each partition for this package.
545 /* XXX only ts->{probs,di} modified */
546 static void handleOverlappedFiles(const rpmTransactionSet ts, TFI_t fi)
547 /*@globals fileSystem @*/
548 /*@modifies ts, fi, fileSystem @*/
550 struct diskspaceInfo * dsl = ts->di;
551 rpmProblemSet probs = (ts->ignoreSet & RPMPROB_FILTER_REPLACENEWFILES)
553 hashTable ht = ts->ht;
554 struct diskspaceInfo * ds = NULL;
555 uint_32 fixupSize = 0;
556 char * filespec = NULL;
557 int fileSpecAlloced = 0;
560 for (i = 0; i < fi->fc; i++) {
561 int otherPkgNum, otherFileNum;
565 if (XFA_SKIPPING(fi->actions[i]))
568 j = strlen(fi->dnl[fi->dil[i]]) + strlen(fi->bnl[i]) + 1;
570 if (j > fileSpecAlloced) {
571 fileSpecAlloced = j * 2;
572 filespec = xrealloc(filespec, fileSpecAlloced);
576 (void) stpcpy( stpcpy( filespec, fi->dnl[fi->dil[i]]), fi->bnl[i]);
580 while (ds->bsize && ds->dev != fi->fps[i].entry->dev) ds++;
581 if (!ds->bsize) ds = NULL;
586 * Retrieve all records that apply to this file. Note that the
587 * file info records were built in the same order as the packages
588 * will be installed and removed so the records for an overlapped
589 * files will be sorted in exactly the same order.
591 (void) htGetEntry(ht, &fi->fps[i], (const void ***) &recs, &numRecs, NULL);
594 * If this package is being added, look only at other packages
595 * being added -- removed packages dance to a different tune.
596 * If both this and the other package are being added, overlapped
597 * files must be identical (or marked as a conflict). The
598 * disposition of already installed config files leads to
599 * a small amount of extra complexity.
601 * If this package is being removed, then there are two cases that
602 * need to be worried about:
603 * If the other package is being added, then skip any overlapped files
604 * so that this package removal doesn't nuke the overlapped files
605 * that were just installed.
606 * If both this and the other package are being removed, then each
607 * file removal from preceding packages needs to be skipped so that
608 * the file removal occurs only on the last occurence of an overlapped
609 * file in the transaction set.
613 /* Locate this overlapped file in the set of added/removed packages. */
614 for (j = 0; j < numRecs && recs[j] != fi; j++)
617 /* Find what the previous disposition of this file was. */
618 otherFileNum = -1; /* keep gcc quiet */
619 for (otherPkgNum = j - 1; otherPkgNum >= 0; otherPkgNum--) {
620 /* Added packages need only look at other added packages. */
621 if (fi->type == TR_ADDED && recs[otherPkgNum]->type != TR_ADDED)
622 /*@innercontinue@*/ continue;
624 /* TESTME: there are more efficient searches in the world... */
625 for (otherFileNum = 0; otherFileNum < recs[otherPkgNum]->fc;
628 /* If the addresses are the same, so are the values. */
629 if ((fi->fps + i) == (recs[otherPkgNum]->fps + otherFileNum))
630 /*@innerbreak@*/ break;
632 /* Otherwise, compare fingerprints by value. */
633 /*@-nullpass@*/ /* LCL: looks good to me */
634 if (FP_EQUAL(fi->fps[i], recs[otherPkgNum]->fps[otherFileNum]))
635 /*@innerbreak@*/ break;
639 /* XXX is this test still necessary? */
640 if (recs[otherPkgNum]->actions[otherFileNum] != FA_UNKNOWN)
641 /*@innerbreak@*/ break;
647 if (otherPkgNum < 0) {
648 /* XXX is this test still necessary? */
649 if (fi->actions[i] != FA_UNKNOWN)
650 /*@switchbreak@*/ break;
651 if ((fi->fflags[i] & RPMFILE_CONFIG) &&
652 !lstat(filespec, &sb)) {
653 /* Here is a non-overlapped pre-existing config file. */
654 fi->actions[i] = (fi->fflags[i] & RPMFILE_NOREPLACE)
655 ? FA_ALTNAME : FA_BACKUP;
657 fi->actions[i] = FA_CREATE;
659 /*@switchbreak@*/ break;
662 /* Mark added overlapped non-identical files as a conflict. */
663 if (probs && filecmp(recs[otherPkgNum]->fmodes[otherFileNum],
664 recs[otherPkgNum]->fmd5s[otherFileNum],
665 recs[otherPkgNum]->flinks[otherFileNum],
669 psAppend(probs, RPMPROB_NEW_FILE_CONFLICT, fi->ap,
670 filespec, NULL, recs[otherPkgNum]->ap->h, 0);
673 /* Try to get the disk accounting correct even if a conflict. */
674 fixupSize = recs[otherPkgNum]->fsizes[otherFileNum];
676 if ((fi->fflags[i] & RPMFILE_CONFIG) && !lstat(filespec, &sb)) {
677 /* Here is an overlapped pre-existing config file. */
678 fi->actions[i] = (fi->fflags[i] & RPMFILE_NOREPLACE)
679 ? FA_ALTNAME : FA_SKIP;
681 fi->actions[i] = FA_CREATE;
683 } /*@switchbreak@*/ break;
685 if (otherPkgNum >= 0) {
686 /* Here is an overlapped added file we don't want to nuke. */
687 if (recs[otherPkgNum]->actions[otherFileNum] != FA_ERASE) {
688 /* On updates, don't remove files. */
689 fi->actions[i] = FA_SKIP;
690 /*@switchbreak@*/ break;
692 /* Here is an overlapped removed file: skip in previous. */
693 recs[otherPkgNum]->actions[otherFileNum] = FA_SKIP;
695 if (XFA_SKIPPING(fi->actions[i]))
696 /*@switchbreak@*/ break;
697 if (fi->fstates && fi->fstates[i] != RPMFILE_STATE_NORMAL)
698 /*@switchbreak@*/ break;
699 if (!(S_ISREG(fi->fmodes[i]) && (fi->fflags[i] & RPMFILE_CONFIG))) {
700 fi->actions[i] = FA_ERASE;
701 /*@switchbreak@*/ break;
704 /* Here is a pre-existing modified config file that needs saving. */
706 if (!mdfile(filespec, mdsum) && strcmp(fi->fmd5s[i], mdsum)) {
707 fi->actions[i] = FA_BACKUP;
708 /*@switchbreak@*/ break;
711 fi->actions[i] = FA_ERASE;
712 /*@switchbreak@*/ break;
716 uint_32 s = BLOCK_ROUND(fi->fsizes[i], ds->bsize);
718 switch (fi->actions[i]) {
724 /*@switchbreak@*/ break;
727 * FIXME: If two packages share a file (same md5sum), and
728 * that file is being replaced on disk, will ds->bneeded get
729 * decremented twice? Quite probably!
733 ds->bneeded -= BLOCK_ROUND(fi->replacedSizes[i], ds->bsize);
734 /*@switchbreak@*/ break;
739 /*@switchbreak@*/ break;
742 /*@switchbreak@*/ break;
745 ds->bneeded -= BLOCK_ROUND(fixupSize, ds->bsize);
748 if (filespec) free(filespec);
753 static int ensureOlder(availablePackage alp, Header old,
755 /*@modifies alp, probs @*/
759 if (old == NULL) return 1;
761 result = rpmVersionCompare(old, alp->h);
764 else if (result > 0) {
766 psAppend(probs, RPMPROB_OLDPACKAGE, alp, NULL, NULL, old, 0);
774 static void skipFiles(const rpmTransactionSet ts, TFI_t fi)
775 /*@globals rpmGlobalMacroContext @*/
776 /*@modifies fi, rpmGlobalMacroContext @*/
778 int noDocs = (ts->transFlags & RPMTRANS_FLAG_NODOCS);
779 char ** netsharedPaths = NULL;
780 const char ** languages;
781 const char * dn, * bn;
782 int dnlen, bnlen, ix;
789 noDocs = rpmExpandNumeric("%{_excludedocs}");
791 { const char *tmpPath = rpmExpand("%{_netsharedpath}", NULL);
793 if (tmpPath && *tmpPath != '%')
794 netsharedPaths = splitString(tmpPath, strlen(tmpPath), ':');
796 tmpPath = _free(tmpPath);
799 s = rpmExpand("%{_install_langs}", NULL);
801 if (!(s && *s != '%'))
804 languages = (const char **) splitString(s, strlen(s), ':');
810 /* Compute directory refcount, skip directory if now empty. */
811 drc = alloca(fi->dc * sizeof(*drc));
812 memset(drc, 0, fi->dc * sizeof(*drc));
813 dff = alloca(fi->dc * sizeof(*dff));
814 memset(dff, 0, fi->dc * sizeof(*dff));
816 for (i = 0; i < fi->fc; i++) {
827 /* Don't bother with skipped files */
828 if (XFA_SKIPPING(fi->actions[i])) {
834 * Skip net shared paths.
835 * Net shared paths are not relative to the current root (though
836 * they do need to take package relocations into account).
838 for (nsp = netsharedPaths; nsp && *nsp; nsp++) {
843 if (strncmp(dn, *nsp, len))
844 /*@innercontinue@*/ continue;
845 /* Only directories or complete file paths can be net shared */
846 if (!(dn[len] == '/' || dn[len] == '\0'))
847 /*@innercontinue@*/ continue;
849 if (len < (dnlen + bnlen))
850 /*@innercontinue@*/ continue;
851 if (strncmp(dn, *nsp, dnlen))
852 /*@innercontinue@*/ continue;
853 if (strncmp(bn, (*nsp) + dnlen, bnlen))
854 /*@innercontinue@*/ continue;
856 /* Only directories or complete file paths can be net shared */
857 if (!((*nsp)[len] == '/' || (*nsp)[len] == '\0'))
858 /*@innercontinue@*/ continue;
861 /*@innerbreak@*/ break;
865 drc[ix]--; dff[ix] = 1;
866 fi->actions[i] = FA_SKIPNETSHARED;
871 * Skip i18n language specific files.
873 if (fi->flangs && languages && *fi->flangs[i]) {
874 const char **lang, *l, *le;
875 for (lang = languages; *lang != NULL; lang++) {
876 if (!strcmp(*lang, "all"))
877 /*@innerbreak@*/ break;
878 for (l = fi->flangs[i]; *l != '\0'; l = le) {
879 for (le = l; *le != '\0' && *le != '|'; le++)
881 if ((le-l) > 0 && !strncmp(*lang, l, (le-l)))
882 /*@innerbreak@*/ break;
883 if (*le == '|') le++; /* skip over | */
886 /*@innerbreak@*/ break;
889 drc[ix]--; dff[ix] = 1;
890 fi->actions[i] = FA_SKIPNSTATE;
896 * Skip documentation if requested.
898 if (noDocs && (fi->fflags[i] & RPMFILE_DOC)) {
899 drc[ix]--; dff[ix] = 1;
900 fi->actions[i] = FA_SKIPNSTATE;
905 /* Skip (now empty) directories that had skipped files. */
906 for (j = 0; j < fi->dc; j++) {
908 if (drc[j]) continue; /* dir still has files. */
909 if (!dff[j]) continue; /* dir was not emptied here. */
911 /* Find parent directory and basename. */
912 dn = fi->dnl[j]; dnlen = strlen(dn) - 1;
913 bn = dn + dnlen; bnlen = 0;
914 while (bn > dn && bn[-1] != '/') {
920 /* If explicitly included in the package, skip the directory. */
921 for (i = 0; i < fi->fc; i++) {
924 if (XFA_SKIPPING(fi->actions[i]))
925 /*@innercontinue@*/ continue;
926 if (whatis(fi->fmodes[i]) != XDIR)
927 /*@innercontinue@*/ continue;
928 dir = fi->dnl[fi->dil[i]];
929 if (strlen(dir) != dnlen)
930 /*@innercontinue@*/ continue;
931 if (strncmp(dir, dn, dnlen))
932 /*@innercontinue@*/ continue;
933 if (strlen(fi->bnl[i]) != bnlen)
934 /*@innercontinue@*/ continue;
935 if (strncmp(fi->bnl[i], bn, bnlen))
936 /*@innercontinue@*/ continue;
937 rpmMessage(RPMMESS_DEBUG, _("excluding directory %s\n"), dn);
938 fi->actions[i] = FA_SKIPNSTATE;
939 /*@innerbreak@*/ break;
943 if (netsharedPaths) freeSplitString(netsharedPaths);
944 #ifdef DYING /* XXX freeFi will deal with this later. */
945 fi->flangs = _free(fi->flangs);
947 if (languages) freeSplitString((char **)languages);
951 * Iterator across transaction elements, forward on install, backward on erase.
953 struct tsIterator_s {
954 /*@refcounted@*/ rpmTransactionSet ts; /*!< transaction set. */
955 int reverse; /*!< reversed traversal? */
956 int ocsave; /*!< last returned iterator index. */
957 int oc; /*!< iterator index. */
961 * Return transaction element order count.
962 * @param a transaction element iterator
963 * @return element order count
965 static int tsGetOc(void * a)
968 struct tsIterator_s * iter = a;
969 int oc = iter->ocsave;
974 * Return transaction element available package pointer.
975 * @param a transaction element iterator
976 * @return available package pointer
978 static /*@dependent@*/ availablePackage tsGetAlp(void * a)
981 struct tsIterator_s * iter = a;
982 availablePackage alp = NULL;
983 int oc = iter->ocsave;
987 rpmTransactionSet ts = iter->ts;
988 TFI_t fi = ts->flList + oc;
989 if (ts->addedPackages.list && fi->type == TR_ADDED)
990 alp = ts->addedPackages.list + ts->order[oc].u.addedIndex;
997 * Destroy transaction element iterator.
998 * @param a transaction element iterator
999 * @return NULL always
1001 static /*@null@*/ void * tsFreeIterator(/*@only@*//*@null@*/ void * a)
1004 struct tsIterator_s * iter = a;
1006 iter->ts = rpmtsUnlink(iter->ts, "tsIterator");
1011 * Create transaction element iterator.
1012 * @param ts transaction set
1013 * @return transaction element iterator
1015 static void * tsInitIterator(rpmTransactionSet ts)
1018 struct tsIterator_s * iter = NULL;
1020 iter = xcalloc(1, sizeof(*iter));
1021 iter->ts = rpmtsLink(ts, "tsIterator");
1022 iter->reverse = ((ts->transFlags & RPMTRANS_FLAG_REVERSE) ? 1 : 0);
1023 iter->oc = (iter->reverse ? (ts->orderCount - 1) : 0);
1024 iter->ocsave = iter->oc;
1029 * Return next transaction element's file info.
1030 * @param a file info iterator
1031 * @return next index, -1 on termination
1033 static TFI_t tsNextIterator(void * a)
1036 struct tsIterator_s * iter = a;
1040 if (iter->reverse) {
1041 if (iter->oc >= 0) oc = iter->oc--;
1043 if (iter->oc < iter->ts->orderCount) oc = iter->oc++;
1047 fi = iter->ts->flList + oc;
1051 #define NOTIFY(_ts, _al) if ((_ts)->notify) (void) (_ts)->notify _al
1053 int rpmRunTransactions( rpmTransactionSet ts,
1054 rpmCallbackFunction notify, rpmCallbackData notifyData,
1055 rpmProblemSet okProbs, rpmProblemSet * newProbs,
1056 rpmtransFlags transFlags, rpmprobFilterFlags ignoreSet)
1060 availablePackage alp;
1061 int totalFileCount = 0;
1063 struct diskspaceInfo * dip;
1064 struct sharedFileInfo * shared, * sharedList;
1069 fingerPrintCache fpc;
1070 struct psm_s psmbuf;
1071 PSM_t psm = &psmbuf;
1076 /* FIXME: what if the same package is included in ts twice? */
1078 ts->transFlags = transFlags;
1079 if (ts->transFlags & RPMTRANS_FLAG_NOSCRIPTS)
1080 ts->transFlags |= (_noTransScripts | _noTransTriggers);
1081 if (ts->transFlags & RPMTRANS_FLAG_NOTRIGGERS)
1082 ts->transFlags |= _noTransTriggers;
1084 /* XXX MULTILIB is broken, as packages can and do execute /sbin/ldconfig. */
1085 if (ts->transFlags & (RPMTRANS_FLAG_JUSTDB | RPMTRANS_FLAG_MULTILIB))
1086 ts->transFlags |= (_noTransScripts | _noTransTriggers);
1088 ts->notify = notify;
1089 ts->notifyData = notifyData;
1091 ts->probs = *newProbs = psCreate();
1093 ts->ignoreSet = ignoreSet;
1094 ts->currDir = _free(ts->currDir);
1095 ts->currDir = currentDirectory();
1097 if (ts->rpmdb) ts->rpmdb->db_chrootDone = 0;
1098 ts->id = (int_32) time(NULL);
1100 memset(psm, 0, sizeof(*psm));
1102 psm->ts = rpmtsLink(ts, "tsRun");
1105 /* Get available space on mounted file systems. */
1106 if (!(ts->ignoreSet & RPMPROB_FILTER_DISKSPACE) &&
1107 !rpmGetFilesystemList(&ts->filesystems, &ts->filesystemCount)) {
1110 ts->di = _free(ts->di);
1111 dip = ts->di = xcalloc((ts->filesystemCount + 1), sizeof(*ts->di));
1113 for (i = 0; (i < ts->filesystemCount) && dip; i++) {
1114 #if STATFS_IN_SYS_STATVFS
1116 memset(&sfb, 0, sizeof(sfb));
1117 if (statvfs(ts->filesystems[i], &sfb))
1121 /* This platform has the 4-argument version of the statfs call. The last two
1122 * should be the size of struct statfs and 0, respectively. The 0 is the
1123 * filesystem type, and is always 0 when statfs is called on a mounted
1124 * filesystem, as we're doing.
1126 memset(&sfb, 0, sizeof(sfb));
1127 if (statfs(ts->filesystems[i], &sfb, sizeof(sfb), 0))
1129 memset(&sfb, 0, sizeof(sfb));
1130 if (statfs(ts->filesystems[i], &sfb))
1136 ts->di[i].bsize = sfb.f_bsize;
1137 ts->di[i].bneeded = 0;
1138 ts->di[i].ineeded = 0;
1139 #ifdef STATFS_HAS_F_BAVAIL
1140 ts->di[i].bavail = sfb.f_bavail;
1142 /* FIXME: the statfs struct doesn't have a member to tell how many blocks are
1143 * available for non-superusers. f_blocks - f_bfree is probably too big, but
1144 * it's about all we can do.
1146 ts->di[i].bavail = sfb.f_blocks - sfb.f_bfree;
1148 /* XXX Avoid FAT and other file systems that have not inodes. */
1149 ts->di[i].iavail = !(sfb.f_ffree == 0 && sfb.f_files == 0)
1152 xx = stat(ts->filesystems[i], &sb);
1153 ts->di[i].dev = sb.st_dev;
1157 if (dip) ts->di[i].bsize = 0;
1160 /* ===============================================
1161 * For packages being installed:
1162 * - verify package arch/os.
1163 * - verify package epoch:version-release is newer.
1165 * For packages being removed:
1168 /* The ordering doesn't matter here */
1169 if (ts->addedPackages.list != NULL)
1170 for (alp = ts->addedPackages.list;
1171 (alp - ts->addedPackages.list) < ts->addedPackages.size;
1174 if (!archOkay(alp->h) && !(ts->ignoreSet & RPMPROB_FILTER_IGNOREARCH))
1175 psAppend(ts->probs, RPMPROB_BADARCH, alp, NULL, NULL, NULL, 0);
1177 if (!osOkay(alp->h) && !(ts->ignoreSet & RPMPROB_FILTER_IGNOREOS))
1178 psAppend(ts->probs, RPMPROB_BADOS, alp, NULL, NULL, NULL, 0);
1180 if (!(ts->ignoreSet & RPMPROB_FILTER_OLDPACKAGE)) {
1181 rpmdbMatchIterator mi;
1183 mi = rpmtsInitIterator(ts, RPMTAG_NAME, alp->name, 0);
1184 while ((oldH = rpmdbNextIterator(mi)) != NULL)
1185 xx = ensureOlder(alp, oldH, ts->probs);
1186 mi = rpmdbFreeIterator(mi);
1189 /* XXX multilib should not display "already installed" problems */
1190 if (!(ts->ignoreSet & RPMPROB_FILTER_REPLACEPKG) && !alp->multiLib) {
1191 rpmdbMatchIterator mi;
1192 mi = rpmtsInitIterator(ts, RPMTAG_NAME, alp->name, 0);
1193 xx = rpmdbSetIteratorRE(mi, RPMTAG_VERSION,
1194 RPMMIRE_DEFAULT, alp->version);
1195 xx = rpmdbSetIteratorRE(mi, RPMTAG_RELEASE,
1196 RPMMIRE_DEFAULT, alp->release);
1198 while (rpmdbNextIterator(mi) != NULL) {
1199 psAppend(ts->probs, RPMPROB_PKG_INSTALLED, alp,
1200 NULL, NULL, NULL, 0);
1201 /*@innerbreak@*/ break;
1203 mi = rpmdbFreeIterator(mi);
1206 totalFileCount += alp->filesCount;
1210 /* FIXME: it seems a bit silly to read in all of these headers twice */
1211 /* The ordering doesn't matter here */
1212 if (ts->numRemovedPackages > 0) {
1213 rpmdbMatchIterator mi;
1217 mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES, NULL, 0);
1218 xx = rpmdbAppendIterator(mi, ts->removedPackages, ts->numRemovedPackages);
1219 while ((h = rpmdbNextIterator(mi)) != NULL) {
1220 if (headerGetEntry(h, RPMTAG_BASENAMES, NULL, NULL, &fileCount))
1221 totalFileCount += fileCount;
1223 mi = rpmdbFreeIterator(mi);
1226 /* ===============================================
1227 * Initialize file list:
1229 ts->flEntries = ts->addedPackages.size + ts->numRemovedPackages;
1230 ts->flList = xcalloc(ts->flEntries, sizeof(*ts->flList));
1233 * FIXME?: we'd be better off assembling one very large file list and
1234 * calling fpLookupList only once. I'm not sure that the speedup is
1235 * worth the trouble though.
1237 tsi = tsInitIterator(ts);
1238 while ((fi = tsNextIterator(tsi)) != NULL) {
1240 fi->magic = TFIMAGIC;
1242 /* XXX watchout: fi->type must be set for tsGetAlp() to "work" */
1243 fi->type = ts->order[oc].type;
1247 /* XXX watchout: fi->type must be set for tsGetAlp() to "work" */
1248 fi->ap = tsGetAlp(tsi);
1250 loadFi(ts, fi, fi->ap->h, scareMem);
1251 /* XXX free fi->ap->h here if/when possible */
1255 /* Skip netshared paths, not our i18n files, and excluded docs */
1257 /*@switchbreak@*/ break;
1260 fi->record = ts->order[oc].u.removed.dboffset;
1261 /* Retrieve erased package header from the database. */
1262 { rpmdbMatchIterator mi;
1264 mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES,
1265 &fi->record, sizeof(fi->record));
1266 if ((fi->h = rpmdbNextIterator(mi)) != NULL)
1267 fi->h = headerLink(fi->h);
1268 mi = rpmdbFreeIterator(mi);
1270 if (fi->h == NULL) {
1274 /* XXX header arg unused. */
1275 loadFi(ts, fi, fi->h, 0);
1276 /*@switchbreak@*/ break;
1281 fi->fps = xmalloc(fi->fc * sizeof(*fi->fps));
1283 tsi = tsFreeIterator(tsi);
1285 if (!ts->chrootDone) {
1287 /*@-superuser -noeffect @*/
1288 xx = chroot(ts->rootDir);
1289 /*@=superuser =noeffect @*/
1291 if (ts->rpmdb) ts->rpmdb->db_chrootDone = 1;
1294 chroot_prefix = ts->rootDir;
1299 ts->ht = htCreate(totalFileCount * 2, 0, 0, fpHashFunction, fpEqual);
1300 fpc = fpCacheCreate(totalFileCount);
1302 /* ===============================================
1303 * Add fingerprint for each file not skipped.
1305 tsi = tsInitIterator(ts);
1306 while ((fi = tsNextIterator(tsi)) != NULL) {
1307 fpLookupList(fpc, fi->dnl, fi->bnl, fi->dil, fi->fc, fi->fps);
1308 for (i = 0; i < fi->fc; i++) {
1309 if (XFA_SKIPPING(fi->actions[i]))
1310 /*@innercontinue@*/ continue;
1311 /*@-dependenttrans@*/
1312 htAddEntry(ts->ht, fi->fps + i, fi);
1313 /*@=dependenttrans@*/
1316 tsi = tsFreeIterator(tsi);
1318 /*@-noeffectuncon @*/ /* FIX: check rc */
1319 NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_START, 6, ts->flEntries,
1320 NULL, ts->notifyData));
1321 /*@=noeffectuncon@*/
1323 /* ===============================================
1324 * Compute file disposition for each package in transaction set.
1326 tsi = tsInitIterator(ts);
1327 while ((fi = tsNextIterator(tsi)) != NULL) {
1328 dbiIndexSet * matches;
1331 /*@-noeffectuncon @*/ /* FIX: check rc */
1332 NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_PROGRESS, (fi - ts->flList),
1333 ts->flEntries, NULL, ts->notifyData));
1334 /*@=noeffectuncon@*/
1336 if (fi->fc == 0) continue;
1338 /* Extract file info for all files in this package from the database. */
1339 matches = xcalloc(fi->fc, sizeof(*matches));
1340 if (rpmdbFindFpList(ts->rpmdb, fi->fps, matches, fi->fc)) {
1341 psm->ts = rpmtsUnlink(ts, "tsRun (rpmFindFpList fail)");
1342 return 1; /* XXX WTFO? */
1346 for (i = 0; i < fi->fc; i++)
1347 numShared += dbiIndexSetCount(matches[i]);
1349 /* Build sorted file info list for this package. */
1350 shared = sharedList = xcalloc((numShared + 1), sizeof(*sharedList));
1351 for (i = 0; i < fi->fc; i++) {
1353 * Take care not to mark files as replaced in packages that will
1354 * have been removed before we will get here.
1356 for (j = 0; j < dbiIndexSetCount(matches[i]); j++) {
1358 ro = dbiIndexRecordOffset(matches[i], j);
1360 for (k = 0; ro != knownBad && k < ts->orderCount; k++) {
1361 switch (ts->order[k].type) {
1363 if (ts->order[k].u.removed.dboffset == ro)
1365 /*@switchbreak@*/ break;
1367 /*@switchbreak@*/ break;
1371 shared->pkgFileNum = i;
1372 shared->otherPkg = dbiIndexRecordOffset(matches[i], j);
1373 shared->otherFileNum = dbiIndexRecordFileNumber(matches[i], j);
1374 shared->isRemoved = (knownBad == ro);
1377 matches[i] = dbiFreeIndexSet(matches[i]);
1379 numShared = shared - sharedList;
1380 shared->otherPkg = -1;
1381 matches = _free(matches);
1383 /* Sort file info by other package index (otherPkg) */
1384 qsort(sharedList, numShared, sizeof(*shared), sharedCmp);
1386 /* For all files from this package that are in the database ... */
1387 for (i = 0; i < numShared; i = nexti) {
1390 shared = sharedList + i;
1392 /* Find the end of the files in the other package. */
1393 for (nexti = i + 1; nexti < numShared; nexti++) {
1394 if (sharedList[nexti].otherPkg != shared->otherPkg)
1395 /*@innerbreak@*/ break;
1398 /* Is this file from a package being removed? */
1400 for (j = 0; j < ts->numRemovedPackages; j++) {
1401 if (ts->removedPackages[j] != shared->otherPkg)
1402 /*@innercontinue@*/ continue;
1404 /*@innerbreak@*/ break;
1407 /* Determine the fate of each file. */
1410 xx = handleInstInstalledFiles(ts, fi, shared, nexti - i,
1411 !(beingRemoved || (ts->ignoreSet & RPMPROB_FILTER_REPLACEOLDFILES)));
1412 /*@switchbreak@*/ break;
1415 xx = handleRmvdInstalledFiles(ts, fi, shared, nexti - i);
1416 /*@switchbreak@*/ break;
1422 /* Update disk space needs on each partition for this package. */
1423 handleOverlappedFiles(ts, fi);
1425 /* Check added package has sufficient space on each partition used. */
1428 if (!(ts->di && fi->fc))
1429 /*@switchbreak@*/ break;
1430 for (i = 0; i < ts->filesystemCount; i++) {
1434 /* XXX Avoid FAT and other file systems that have not inodes. */
1435 if (dip->iavail <= 0)
1436 /*@innercontinue@*/ continue;
1438 if (adj_fs_blocks(dip->bneeded) > dip->bavail)
1439 psAppend(ts->probs, RPMPROB_DISKSPACE, fi->ap,
1440 ts->filesystems[i], NULL, NULL,
1441 (adj_fs_blocks(dip->bneeded) - dip->bavail) * dip->bsize);
1443 if (adj_fs_blocks(dip->ineeded) > dip->iavail)
1444 psAppend(ts->probs, RPMPROB_DISKNODES, fi->ap,
1445 ts->filesystems[i], NULL, NULL,
1446 (adj_fs_blocks(dip->ineeded) - dip->iavail));
1448 /*@switchbreak@*/ break;
1450 /*@switchbreak@*/ break;
1453 tsi = tsFreeIterator(tsi);
1455 if (ts->chrootDone) {
1456 /*@-superuser -noeffect @*/
1458 /*@=superuser =noeffect @*/
1460 if (ts->rpmdb) ts->rpmdb->db_chrootDone = 0;
1462 chroot_prefix = NULL;
1464 xx = chdir(ts->currDir);
1467 /*@-noeffectuncon @*/ /* FIX: check rc */
1468 NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_STOP, 6, ts->flEntries,
1469 NULL, ts->notifyData));
1470 /*@=noeffectuncon @*/
1472 /* ===============================================
1473 * Free unused memory as soon as possible.
1476 tsi = tsInitIterator(ts);
1477 while ((fi = tsNextIterator(tsi)) != NULL) {
1480 fi->fps = _free(fi->fps);
1482 tsi = tsFreeIterator(tsi);
1488 /* ===============================================
1489 * If unfiltered problems exist, free memory and return.
1491 if ((ts->transFlags & RPMTRANS_FLAG_BUILD_PROBS) ||
1492 (ts->probs->numProblems && (!okProbs || psTrim(okProbs, ts->probs))))
1494 *newProbs = ts->probs;
1496 ts->flList = freeFl(ts, ts->flList);
1499 psm->ts = rpmtsUnlink(psm->ts, "tsRun (problems)");
1500 return ts->orderCount;
1504 /* ===============================================
1505 * Save removed files before erasing.
1507 if (ts->transFlags & (RPMTRANS_FLAG_DIRSTASH | RPMTRANS_FLAG_REPACKAGE)) {
1508 tsi = tsInitIterator(ts);
1509 while ((fi = tsNextIterator(tsi)) != NULL) {
1512 /*@switchbreak@*/ break;
1514 if (ts->transFlags & RPMTRANS_FLAG_REPACKAGE) {
1515 psm->fi = rpmfiLink(fi, "tsRepackage");
1516 xx = psmStage(psm, PSM_PKGSAVE);
1517 (void) rpmfiUnlink(fi, "tsRepackage");
1520 /*@switchbreak@*/ break;
1523 tsi = tsFreeIterator(tsi);
1526 /* ===============================================
1527 * Install and remove packages.
1530 lastFailed = -2; /* erased packages have -1 */
1531 tsi = tsInitIterator(ts);
1532 /*@-branchstate@*/ /* FIX: fi reload needs work */
1533 while ((fi = tsNextIterator(tsi)) != NULL) {
1538 psm->fi = rpmfiLink(fi, "tsInstall");
1541 alp = tsGetAlp(tsi);
1542 assert(alp == fi->ap);
1543 i = alp - ts->addedPackages.list;
1545 rpmMessage(RPMMESS_DEBUG, "========== +++ %s-%s-%s\n",
1546 fi->name, fi->version, fi->release);
1547 h = (fi->h ? headerLink(fi->h) : NULL);
1549 if (alp->fd == NULL) {
1550 alp->fd = ts->notify(fi->h, RPMCALLBACK_INST_OPEN_FILE, 0, 0,
1551 alp->key, ts->notifyData);
1557 /*@-mustmod@*/ /* LCL: segfault */
1558 rpmrc = rpmReadPackageFile(ts, alp->fd,
1559 "rpmRunTransactions", &h);
1562 if (!(rpmrc == RPMRC_OK || rpmrc == RPMRC_BADSIZE)) {
1563 /*@-noeffectuncon @*/ /* FIX: check rc */
1564 (void)ts->notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE,
1565 0, 0, alp->key, ts->notifyData);
1566 /*@=noeffectuncon @*/
1569 } else if (fi->h != NULL) {
1570 Header foo = relocateFileList(ts, fi, alp, h, NULL);
1572 h = headerLink(foo);
1573 foo = headerFree(foo);
1575 if (alp->fd) gotfd = 1;
1581 Header hsave = NULL;
1584 hsave = headerLink(fi->h);
1585 fi->h = headerFree(fi->h);
1586 fi->h = headerLink(h);
1588 char * fstates = fi->fstates;
1589 fileAction * actions = fi->actions;
1594 fi->magic = TFIMAGIC;
1595 fi->type = ts->order[oc].type;
1596 fi->ap = tsGetAlp(tsi);
1598 loadFi(ts, fi, h, 1);
1599 fi->fstates = _free(fi->fstates);
1600 fi->fstates = fstates;
1601 fi->actions = _free(fi->actions);
1602 fi->actions = actions;
1605 ts->transFlags |= RPMTRANS_FLAG_MULTILIB;
1607 assert(alp == fi->ap);
1608 if (psmStage(psm, PSM_PKGINSTALL)) {
1612 fi->h = headerFree(fi->h);
1614 fi->h = headerLink(hsave);
1615 hsave = headerFree(hsave);
1625 /*@-noeffectuncon @*/ /* FIX: check rc */
1626 (void)ts->notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE, 0, 0,
1627 alp->key, ts->notifyData);
1628 /*@=noeffectuncon @*/
1632 /*@switchbreak@*/ break;
1634 rpmMessage(RPMMESS_DEBUG, "========== --- %s-%s-%s\n",
1635 fi->name, fi->version, fi->release);
1637 /* If install failed, then we shouldn't erase. */
1638 if (ts->order[oc].u.removed.dependsOnIndex != lastFailed) {
1639 if (psmStage(psm, PSM_PKGERASE))
1643 /*@switchbreak@*/ break;
1645 xx = rpmdbSync(ts->rpmdb);
1646 (void) rpmfiUnlink(fi, "tsInstall");
1650 tsi = tsFreeIterator(tsi);
1652 ts->flList = freeFl(ts, ts->flList);
1655 psm->ts = rpmtsUnlink(psm->ts, "tsRun");