2 * \file lib/transaction.c
7 #include <rpmmacro.h> /* XXX for rpmExpand */
10 #define _NEED_TEITERATOR 1
14 #include "legacy.h" /* XXX mdfile */
15 #include "misc.h" /* XXX stripTrailingChar, splitString, currentDirectory */
18 /*@-redecl -exportheadervar@*/
20 extern const char * chroot_prefix;
21 /*@=redecl =exportheadervar@*/
23 /* XXX FIXME: merge with existing (broken?) tests in system.h */
24 /* portability fiddles */
25 #if STATFS_IN_SYS_STATVFS
27 # include <sys/statvfs.h>
28 #if defined(__LCLINT__)
29 /*@-declundef -exportheader -protoparammatch @*/ /* LCL: missing annotation */
30 extern int statvfs (const char * file, /*@out@*/ struct statvfs * buf)
31 /*@globals fileSystem @*/
32 /*@modifies *buf, fileSystem @*/;
33 /*@=declundef =exportheader =protoparammatch @*/
37 # if STATFS_IN_SYS_VFS
40 # if STATFS_IN_SYS_MOUNT
41 # include <sys/mount.h>
43 # if STATFS_IN_SYS_STATFS
44 # include <sys/statfs.h>
52 /*@access FD_t @*/ /* XXX compared with NULL */
53 /*@access Header @*/ /* XXX compared with NULL */
54 /*@access rpmProblemSet @*/ /* XXX need rpmProblemSetOK() */
55 /*@access dbiIndexSet @*/
65 /*@access teIterator @*/
66 /*@access transactionElement @*/
67 /*@access rpmTransactionSet @*/
71 struct diskspaceInfo {
72 dev_t dev; /*!< File system device number. */
73 signed long bneeded; /*!< No. of blocks needed. */
74 signed long ineeded; /*!< No. of inodes needed. */
75 int bsize; /*!< File system block size. */
76 signed long bavail; /*!< No. of blocks available. */
77 signed long iavail; /*!< No. of inodes available. */
81 * Adjust for root only reserved space. On linux e2fs, this is 5%.
83 #define adj_fs_blocks(_nb) (((_nb) * 21) / 20)
85 /* argon thought a shift optimization here was a waste of time... he's
87 #define BLOCK_ROUND(size, block) (((size) + (block) - 1) / (block))
92 static /*@null@*/ void * freeFl(rpmTransactionSet ts,
93 /*@only@*/ /*@null@*/ TFI_t flList)
100 /*@-usereleased -onlytrans @*/ /* FIX: fi needs to be only */
102 for (oc = 0, fi = flList; oc < ts->orderCount; oc++, fi++)
103 (void) fiFree(fi, 0);
105 flList = _free(flList);
106 /*@=usereleased =onlytrans @*/
112 void rpmtransSetScriptFd(rpmTransactionSet ts, FD_t fd)
114 /*@-type@*/ /* FIX: cast? */
115 ts->scriptFd = (fd ? fdLink(fd, "rpmtransSetScriptFd") : NULL);
119 int rpmtransGetKeys(const rpmTransactionSet ts, fnpyKey ** ep, int * nep)
123 if (nep) *nep = ts->orderCount;
125 teIterator pi; transactionElement p;
128 *ep = e = xmalloc(ts->orderCount * sizeof(*e));
129 pi = teInitIterator(ts);
130 while ((p = teNextIterator(pi)) != NULL) {
136 /*@switchbreak@*/ break;
139 /*@-mods@*/ /* FIX: double indirection. */
142 /*@switchbreak@*/ break;
146 pi = teFreeIterator(pi);
153 static int archOkay(/*@null@*/ const char * pkgArch)
156 if (pkgArch == NULL) return 0;
157 return (rpmMachineScore(RPM_MACHTABLE_INSTARCH, pkgArch) ? 1 : 0);
162 static int osOkay(/*@null@*/ const char * pkgOs)
165 if (pkgOs == NULL) return 0;
166 return (rpmMachineScore(RPM_MACHTABLE_INSTOS, pkgOs) ? 1 : 0);
171 static int sharedCmp(const void * one, const void * two)
174 sharedFileInfo a = (sharedFileInfo) one;
175 sharedFileInfo b = (sharedFileInfo) two;
177 if (a->otherPkg < b->otherPkg)
179 else if (a->otherPkg > b->otherPkg)
187 static fileAction decideFileFate(const char * dirName,
188 const char * baseName, short dbMode,
189 const char * dbMd5, const char * dbLink, short newMode,
190 const char * newMd5, const char * newLink, int newFlags,
191 rpmtransFlags transFlags)
192 /*@globals fileSystem @*/
193 /*@modifies fileSystem @*/
196 const char * dbAttr, * newAttr;
197 fileTypes dbWhat, newWhat, diskWhat;
200 int save = (newFlags & RPMFILE_NOREPLACE) ? FA_ALTNAME : FA_SAVE;
201 char * filespec = alloca(strlen(dirName) + strlen(baseName) + 1);
203 (void) stpcpy( stpcpy(filespec, dirName), baseName);
205 if (lstat(filespec, &sb)) {
207 * The file doesn't exist on the disk. Create it unless the new
208 * package has marked it as missingok, or allfiles is requested.
210 if (!(transFlags & RPMTRANS_FLAG_ALLFILES) &&
211 (newFlags & RPMFILE_MISSINGOK)) {
212 rpmMessage(RPMMESS_DEBUG, _("%s skipped due to missingok flag\n"),
220 diskWhat = whatis(sb.st_mode);
221 dbWhat = whatis(dbMode);
222 newWhat = whatis(newMode);
224 /* RPM >= 2.3.10 shouldn't create config directories -- we'll ignore
225 them in older packages as well */
226 if (newWhat == XDIR) {
230 if (diskWhat != newWhat) {
232 } else if (newWhat != dbWhat && diskWhat != dbWhat) {
234 } else if (dbWhat != newWhat) {
236 } else if (dbWhat != LINK && dbWhat != REG) {
241 rc = mdfile(filespec, buffer);
244 /* assume the file has been removed, don't freak */
249 } else /* dbWhat == LINK */ {
250 memset(buffer, 0, sizeof(buffer));
251 i = readlink(filespec, buffer, sizeof(buffer) - 1);
253 /* assume the file has been removed, don't freak */
260 /* this order matters - we'd prefer to CREATE the file if at all
261 possible in case something else (like the timestamp) has changed */
263 if (!strcmp(dbAttr, buffer)) {
264 /* this config file has never been modified, so just replace it */
268 if (!strcmp(dbAttr, newAttr)) {
269 /* this file is the same in all versions of this package */
274 * The config file on the disk has been modified, but
275 * the ones in the two packages are different. It would
276 * be nice if RPM was smart enough to at least try and
277 * merge the difference ala CVS, but...
284 static int filecmp(short mode1, const char * md51, const char * link1,
285 short mode2, const char * md52, const char * link2)
288 fileTypes what1 = whatis(mode1);
289 fileTypes what2 = whatis(mode2);
291 if (what1 != what2) return 1;
294 return strcmp(link1, link2);
295 else if (what1 == REG)
296 return strcmp(md51, md52);
303 /* XXX only ts->{probs,rpmdb} modified */
304 static int handleInstInstalledFiles(const rpmTransactionSet ts,
305 transactionElement p, TFI_t fi,
306 sharedFileInfo shared,
307 int sharedCount, int reportConflicts)
308 /*@globals fileSystem @*/
309 /*@modifies ts, fi, fileSystem @*/
312 HFD_t hfd = (fi->hfd ? fi->hfd : headerFreeData);
313 rpmtransFlags transFlags = ts->transFlags;
314 rpmTagType oltype, omtype;
317 const char ** otherMd5s;
318 const char ** otherLinks;
319 const char * otherStates;
320 uint_32 * otherFlags;
321 uint_32 * otherSizes;
322 uint_16 * otherModes;
326 rpmdbMatchIterator mi;
328 mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES,
329 &shared->otherPkg, sizeof(shared->otherPkg));
330 h = rpmdbNextIterator(mi);
332 mi = rpmdbFreeIterator(mi);
336 xx = hge(h, RPMTAG_FILEMD5S, &omtype, (void **) &otherMd5s, NULL);
337 xx = hge(h, RPMTAG_FILELINKTOS, &oltype, (void **) &otherLinks, NULL);
338 xx = hge(h, RPMTAG_FILESTATES, NULL, (void **) &otherStates, NULL);
339 xx = hge(h, RPMTAG_FILEMODES, NULL, (void **) &otherModes, NULL);
340 xx = hge(h, RPMTAG_FILEFLAGS, NULL, (void **) &otherFlags, NULL);
341 xx = hge(h, RPMTAG_FILESIZES, NULL, (void **) &otherSizes, NULL);
343 fi->replaced = xmalloc(sharedCount * sizeof(*fi->replaced));
345 for (i = 0; i < sharedCount; i++, shared++) {
346 int otherFileNum, fileNum;
347 otherFileNum = shared->otherFileNum;
348 fileNum = shared->pkgFileNum;
350 /* XXX another tedious segfault, assume file state normal. */
351 if (otherStates && otherStates[otherFileNum] != RPMFILE_STATE_NORMAL)
354 if (XFA_SKIPPING(fi->actions[fileNum]))
357 if (filecmp(otherModes[otherFileNum],
358 otherMd5s[otherFileNum],
359 otherLinks[otherFileNum],
362 fi->flinks[fileNum])) {
363 /*@-compdef@*/ /* FIX: *fi->replaced undefined */
364 if (reportConflicts) {
365 const char * altNEVR = hGetNEVR(h, NULL);
366 rpmProblemSetAppend(ts->probs, RPMPROB_FILE_CONFLICT,
368 fi->dnl[fi->dil[fileNum]], fi->bnl[fileNum],
371 altNEVR = _free(altNEVR);
374 if (!(otherFlags[otherFileNum] | fi->fflags[fileNum])
377 if (!shared->isRemoved)
378 fi->replaced[numReplaced++] = *shared;
383 if ((otherFlags[otherFileNum] | fi->fflags[fileNum]) & RPMFILE_CONFIG) {
384 fi->actions[fileNum] = decideFileFate(
385 fi->dnl[fi->dil[fileNum]],
387 otherModes[otherFileNum],
388 otherMd5s[otherFileNum],
389 otherLinks[otherFileNum],
397 fi->replacedSizes[fileNum] = otherSizes[otherFileNum];
400 otherMd5s = hfd(otherMd5s, omtype);
401 otherLinks = hfd(otherLinks, oltype);
402 mi = rpmdbFreeIterator(mi);
404 fi->replaced = xrealloc(fi->replaced, /* XXX memory leak */
405 sizeof(*fi->replaced) * (numReplaced + 1));
406 fi->replaced[numReplaced].otherPkg = 0;
413 /* XXX only ts->rpmdb modified */
414 static int handleRmvdInstalledFiles(const rpmTransactionSet ts, TFI_t fi,
415 sharedFileInfo shared, int sharedCount)
416 /*@globals fileSystem @*/
417 /*@modifies fi, fileSystem @*/
421 const char * otherStates;
424 rpmdbMatchIterator mi;
426 mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES,
427 &shared->otherPkg, sizeof(shared->otherPkg));
428 h = rpmdbNextIterator(mi);
430 mi = rpmdbFreeIterator(mi);
434 xx = hge(h, RPMTAG_FILESTATES, NULL, (void **) &otherStates, NULL);
436 for (i = 0; i < sharedCount; i++, shared++) {
437 int otherFileNum, fileNum;
438 otherFileNum = shared->otherFileNum;
439 fileNum = shared->pkgFileNum;
441 if (otherStates[otherFileNum] != RPMFILE_STATE_NORMAL)
444 fi->actions[fileNum] = FA_SKIP;
447 mi = rpmdbFreeIterator(mi);
453 * Update disk space needs on each partition for this package.
455 /* XXX only ts->{probs,di} modified */
456 static void handleOverlappedFiles(const rpmTransactionSet ts,
457 const transactionElement p, TFI_t fi)
458 /*@globals fileSystem @*/
459 /*@modifies ts, fi, fileSystem @*/
461 struct diskspaceInfo * ds = NULL;
462 uint_32 fixupSize = 0;
463 char * filespec = NULL;
464 int fileSpecAlloced = 0;
467 for (i = 0; i < fi->fc; i++) {
468 int otherPkgNum, otherFileNum;
472 if (XFA_SKIPPING(fi->actions[i]))
475 j = strlen(fi->dnl[fi->dil[i]]) + strlen(fi->bnl[i]) + 1;
477 if (j > fileSpecAlloced) {
478 fileSpecAlloced = j * 2;
479 filespec = xrealloc(filespec, fileSpecAlloced);
483 (void) stpcpy( stpcpy( filespec, fi->dnl[fi->dil[i]]), fi->bnl[i]);
487 while (ds->bsize && ds->dev != fi->fps[i].entry->dev) ds++;
488 if (!ds->bsize) ds = NULL;
493 * Retrieve all records that apply to this file. Note that the
494 * file info records were built in the same order as the packages
495 * will be installed and removed so the records for an overlapped
496 * files will be sorted in exactly the same order.
498 (void) htGetEntry(ts->ht, &fi->fps[i],
499 (const void ***) &recs, &numRecs, NULL);
502 * If this package is being added, look only at other packages
503 * being added -- removed packages dance to a different tune.
504 * If both this and the other package are being added, overlapped
505 * files must be identical (or marked as a conflict). The
506 * disposition of already installed config files leads to
507 * a small amount of extra complexity.
509 * If this package is being removed, then there are two cases that
510 * need to be worried about:
511 * If the other package is being added, then skip any overlapped files
512 * so that this package removal doesn't nuke the overlapped files
513 * that were just installed.
514 * If both this and the other package are being removed, then each
515 * file removal from preceding packages needs to be skipped so that
516 * the file removal occurs only on the last occurence of an overlapped
517 * file in the transaction set.
521 /* Locate this overlapped file in the set of added/removed packages. */
522 for (j = 0; j < numRecs && recs[j] != fi; j++)
525 /* Find what the previous disposition of this file was. */
526 otherFileNum = -1; /* keep gcc quiet */
527 for (otherPkgNum = j - 1; otherPkgNum >= 0; otherPkgNum--) {
528 /* Added packages need only look at other added packages. */
529 if (p->type == TR_ADDED && recs[otherPkgNum]->te->type != TR_ADDED)
530 /*@innercontinue@*/ continue;
532 /* TESTME: there are more efficient searches in the world... */
533 for (otherFileNum = 0;
534 otherFileNum < recs[otherPkgNum]->fc;
538 /* If the addresses are the same, so are the values. */
539 if ((fi->fps + i) == (recs[otherPkgNum]->fps + otherFileNum))
540 /*@innerbreak@*/ break;
542 /* Otherwise, compare fingerprints by value. */
543 /*@-nullpass@*/ /* LCL: looks good to me */
544 if (FP_EQUAL(fi->fps[i], recs[otherPkgNum]->fps[otherFileNum]))
545 /*@innerbreak@*/ break;
549 /* XXX is this test still necessary? */
550 if (recs[otherPkgNum]->actions[otherFileNum] != FA_UNKNOWN)
551 /*@innerbreak@*/ break;
557 if (otherPkgNum < 0) {
558 /* XXX is this test still necessary? */
559 if (fi->actions[i] != FA_UNKNOWN)
560 /*@switchbreak@*/ break;
561 if ((fi->fflags[i] & RPMFILE_CONFIG) &&
562 !lstat(filespec, &sb)) {
563 /* Here is a non-overlapped pre-existing config file. */
564 fi->actions[i] = (fi->fflags[i] & RPMFILE_NOREPLACE)
565 ? FA_ALTNAME : FA_BACKUP;
567 fi->actions[i] = FA_CREATE;
569 /*@switchbreak@*/ break;
572 /* Mark added overlapped non-identical files as a conflict. */
573 /*@-branchstate@*/ /* FIX: p->key ??? */
574 if ((ts->ignoreSet & RPMPROB_FILTER_REPLACENEWFILES)
575 && filecmp(recs[otherPkgNum]->fmodes[otherFileNum],
576 recs[otherPkgNum]->fmd5s[otherFileNum],
577 recs[otherPkgNum]->flinks[otherFileNum],
582 const char * altNEVR = recs[otherPkgNum]->te->NEVR;
583 rpmProblemSetAppend(ts->probs, RPMPROB_NEW_FILE_CONFLICT,
591 /* Try to get the disk accounting correct even if a conflict. */
592 fixupSize = recs[otherPkgNum]->fsizes[otherFileNum];
594 if ((fi->fflags[i] & RPMFILE_CONFIG) && !lstat(filespec, &sb)) {
595 /* Here is an overlapped pre-existing config file. */
596 fi->actions[i] = (fi->fflags[i] & RPMFILE_NOREPLACE)
597 ? FA_ALTNAME : FA_SKIP;
599 fi->actions[i] = FA_CREATE;
601 } /*@switchbreak@*/ break;
603 if (otherPkgNum >= 0) {
604 /* Here is an overlapped added file we don't want to nuke. */
605 if (recs[otherPkgNum]->actions[otherFileNum] != FA_ERASE) {
606 /* On updates, don't remove files. */
607 fi->actions[i] = FA_SKIP;
608 /*@switchbreak@*/ break;
610 /* Here is an overlapped removed file: skip in previous. */
611 recs[otherPkgNum]->actions[otherFileNum] = FA_SKIP;
613 if (XFA_SKIPPING(fi->actions[i]))
614 /*@switchbreak@*/ break;
615 if (fi->fstates && fi->fstates[i] != RPMFILE_STATE_NORMAL)
616 /*@switchbreak@*/ break;
617 if (!(S_ISREG(fi->fmodes[i]) && (fi->fflags[i] & RPMFILE_CONFIG))) {
618 fi->actions[i] = FA_ERASE;
619 /*@switchbreak@*/ break;
622 /* Here is a pre-existing modified config file that needs saving. */
624 if (!mdfile(filespec, mdsum) && strcmp(fi->fmd5s[i], mdsum)) {
625 fi->actions[i] = FA_BACKUP;
626 /*@switchbreak@*/ break;
629 fi->actions[i] = FA_ERASE;
630 /*@switchbreak@*/ break;
634 uint_32 s = BLOCK_ROUND(fi->fsizes[i], ds->bsize);
636 switch (fi->actions[i]) {
642 /*@switchbreak@*/ break;
645 * FIXME: If two packages share a file (same md5sum), and
646 * that file is being replaced on disk, will ds->bneeded get
647 * decremented twice? Quite probably!
651 ds->bneeded -= BLOCK_ROUND(fi->replacedSizes[i], ds->bsize);
652 /*@switchbreak@*/ break;
657 /*@switchbreak@*/ break;
660 /*@switchbreak@*/ break;
663 ds->bneeded -= BLOCK_ROUND(fixupSize, ds->bsize);
666 filespec = _free(filespec);
670 * Ensure that current package is newer than installed package.
671 * @param ts transaction set
672 * @param p current transaction element
673 * @param h installed header
674 * @return 0 if not newer, 1 if okay
676 static int ensureOlder(rpmTransactionSet ts,
677 const transactionElement p, const Header h)
680 int_32 reqFlags = (RPMSENSE_LESS | RPMSENSE_EQUAL);
686 if (p == NULL || h == NULL)
689 t = alloca(strlen(p->NEVR) + (p->epoch != NULL ? strlen(p->epoch) : 0) + 1);
692 if (p->epoch != NULL) t = stpcpy( stpcpy(t, p->epoch), ":");
693 if (p->version != NULL) t = stpcpy(t, p->version);
695 if (p->release != NULL) t = stpcpy(t, p->release);
697 req = dsSingle(RPMTAG_REQUIRENAME, p->name, reqEVR, reqFlags);
698 rc = headerMatchesDepFlags(h, req);
701 /*@-branchstate@*/ /* FIX: p->key ??? */
703 const char * altNEVR = hGetNEVR(h, NULL);
704 rpmProblemSetAppend(ts->probs, RPMPROB_OLDPACKAGE,
709 altNEVR = _free(altNEVR);
720 static void skipFiles(const rpmTransactionSet ts, TFI_t fi)
721 /*@globals rpmGlobalMacroContext @*/
722 /*@modifies fi, rpmGlobalMacroContext @*/
724 int noDocs = (ts->transFlags & RPMTRANS_FLAG_NODOCS);
725 char ** netsharedPaths = NULL;
726 const char ** languages;
727 const char * dn, * bn;
728 int dnlen, bnlen, ix;
735 noDocs = rpmExpandNumeric("%{_excludedocs}");
737 { const char *tmpPath = rpmExpand("%{_netsharedpath}", NULL);
739 if (tmpPath && *tmpPath != '%')
740 netsharedPaths = splitString(tmpPath, strlen(tmpPath), ':');
742 tmpPath = _free(tmpPath);
745 s = rpmExpand("%{_install_langs}", NULL);
747 if (!(s && *s != '%'))
750 languages = (const char **) splitString(s, strlen(s), ':');
756 /* Compute directory refcount, skip directory if now empty. */
757 drc = alloca(fi->dc * sizeof(*drc));
758 memset(drc, 0, fi->dc * sizeof(*drc));
759 dff = alloca(fi->dc * sizeof(*dff));
760 memset(dff, 0, fi->dc * sizeof(*dff));
762 for (i = 0; i < fi->fc; i++) {
773 /* Don't bother with skipped files */
774 if (XFA_SKIPPING(fi->actions[i])) {
780 * Skip net shared paths.
781 * Net shared paths are not relative to the current root (though
782 * they do need to take package relocations into account).
784 for (nsp = netsharedPaths; nsp && *nsp; nsp++) {
789 if (strncmp(dn, *nsp, len))
790 /*@innercontinue@*/ continue;
791 /* Only directories or complete file paths can be net shared */
792 if (!(dn[len] == '/' || dn[len] == '\0'))
793 /*@innercontinue@*/ continue;
795 if (len < (dnlen + bnlen))
796 /*@innercontinue@*/ continue;
797 if (strncmp(dn, *nsp, dnlen))
798 /*@innercontinue@*/ continue;
799 if (strncmp(bn, (*nsp) + dnlen, bnlen))
800 /*@innercontinue@*/ continue;
802 /* Only directories or complete file paths can be net shared */
803 if (!((*nsp)[len] == '/' || (*nsp)[len] == '\0'))
804 /*@innercontinue@*/ continue;
807 /*@innerbreak@*/ break;
811 drc[ix]--; dff[ix] = 1;
812 fi->actions[i] = FA_SKIPNETSHARED;
817 * Skip i18n language specific files.
819 if (fi->flangs && languages && *fi->flangs[i]) {
820 const char **lang, *l, *le;
821 for (lang = languages; *lang != NULL; lang++) {
822 if (!strcmp(*lang, "all"))
823 /*@innerbreak@*/ break;
824 for (l = fi->flangs[i]; *l != '\0'; l = le) {
825 for (le = l; *le != '\0' && *le != '|'; le++)
827 if ((le-l) > 0 && !strncmp(*lang, l, (le-l)))
828 /*@innerbreak@*/ break;
829 if (*le == '|') le++; /* skip over | */
832 /*@innerbreak@*/ break;
835 drc[ix]--; dff[ix] = 1;
836 fi->actions[i] = FA_SKIPNSTATE;
842 * Skip documentation if requested.
844 if (noDocs && (fi->fflags[i] & RPMFILE_DOC)) {
845 drc[ix]--; dff[ix] = 1;
846 fi->actions[i] = FA_SKIPNSTATE;
851 /* Skip (now empty) directories that had skipped files. */
852 for (j = 0; j < fi->dc; j++) {
854 if (drc[j]) continue; /* dir still has files. */
855 if (!dff[j]) continue; /* dir was not emptied here. */
857 /* Find parent directory and basename. */
858 dn = fi->dnl[j]; dnlen = strlen(dn) - 1;
859 bn = dn + dnlen; bnlen = 0;
860 while (bn > dn && bn[-1] != '/') {
866 /* If explicitly included in the package, skip the directory. */
867 for (i = 0; i < fi->fc; i++) {
870 if (XFA_SKIPPING(fi->actions[i]))
871 /*@innercontinue@*/ continue;
872 if (whatis(fi->fmodes[i]) != XDIR)
873 /*@innercontinue@*/ continue;
874 dir = fi->dnl[fi->dil[i]];
875 if (strlen(dir) != dnlen)
876 /*@innercontinue@*/ continue;
877 if (strncmp(dir, dn, dnlen))
878 /*@innercontinue@*/ continue;
879 if (strlen(fi->bnl[i]) != bnlen)
880 /*@innercontinue@*/ continue;
881 if (strncmp(fi->bnl[i], bn, bnlen))
882 /*@innercontinue@*/ continue;
883 rpmMessage(RPMMESS_DEBUG, _("excluding directory %s\n"), dn);
884 fi->actions[i] = FA_SKIPNSTATE;
885 /*@innerbreak@*/ break;
889 if (netsharedPaths) freeSplitString(netsharedPaths);
890 #ifdef DYING /* XXX freeFi will deal with this later. */
891 fi->flangs = _free(fi->flangs);
893 if (languages) freeSplitString((char **)languages);
896 #define NOTIFY(_ts, _al) if ((_ts)->notify) (void) (_ts)->notify _al
898 int rpmRunTransactions( rpmTransactionSet ts,
899 rpmCallbackFunction notify, rpmCallbackData notifyData,
900 rpmProblemSet okProbs, rpmProblemSet * newProbs,
901 rpmtransFlags transFlags, rpmprobFilterFlags ignoreSet)
905 int totalFileCount = 0;
907 struct diskspaceInfo * dip;
908 sharedFileInfo shared, sharedList;
912 fingerPrintCache fpc;
913 PSM_t psm = memset(alloca(sizeof(*psm)), 0, sizeof(*psm));
914 teIterator pi; transactionElement p;
915 teIterator qi; transactionElement q;
922 /* FIXME: what if the same package is included in ts twice? */
924 ts->transFlags = transFlags;
925 if (ts->transFlags & RPMTRANS_FLAG_NOSCRIPTS)
926 ts->transFlags |= (_noTransScripts | _noTransTriggers);
927 if (ts->transFlags & RPMTRANS_FLAG_NOTRIGGERS)
928 ts->transFlags |= _noTransTriggers;
930 /* XXX MULTILIB is broken, as packages can and do execute /sbin/ldconfig. */
931 if (ts->transFlags & (RPMTRANS_FLAG_JUSTDB | RPMTRANS_FLAG_MULTILIB))
932 ts->transFlags |= (_noTransScripts | _noTransTriggers);
935 ts->notifyData = notifyData;
937 ts->probs = *newProbs = rpmProblemSetCreate();
938 *newProbs = rpmpsLink(ts->probs, "RunTransactions");
940 ts->ignoreSet = ignoreSet;
941 ts->currDir = _free(ts->currDir);
942 ts->currDir = currentDirectory();
944 if (ts->rpmdb) ts->rpmdb->db_chrootDone = 0;
945 ts->id = (int_32) time(NULL);
947 memset(psm, 0, sizeof(*psm));
949 psm->ts = rpmtsLink(ts, "tsRun");
952 /* Get available space on mounted file systems. */
953 if (!(ts->ignoreSet & RPMPROB_FILTER_DISKSPACE) &&
954 !rpmGetFilesystemList(&ts->filesystems, &ts->filesystemCount)) {
957 ts->di = _free(ts->di);
958 dip = ts->di = xcalloc((ts->filesystemCount + 1), sizeof(*ts->di));
960 for (i = 0; (i < ts->filesystemCount) && dip; i++) {
961 #if STATFS_IN_SYS_STATVFS
963 memset(&sfb, 0, sizeof(sfb));
964 if (statvfs(ts->filesystems[i], &sfb))
968 /* This platform has the 4-argument version of the statfs call. The last two
969 * should be the size of struct statfs and 0, respectively. The 0 is the
970 * filesystem type, and is always 0 when statfs is called on a mounted
971 * filesystem, as we're doing.
973 memset(&sfb, 0, sizeof(sfb));
974 if (statfs(ts->filesystems[i], &sfb, sizeof(sfb), 0))
976 memset(&sfb, 0, sizeof(sfb));
977 if (statfs(ts->filesystems[i], &sfb))
983 ts->di[i].bsize = sfb.f_bsize;
984 ts->di[i].bneeded = 0;
985 ts->di[i].ineeded = 0;
986 #ifdef STATFS_HAS_F_BAVAIL
987 ts->di[i].bavail = sfb.f_bavail;
989 /* FIXME: the statfs struct doesn't have a member to tell how many blocks are
990 * available for non-superusers. f_blocks - f_bfree is probably too big, but
991 * it's about all we can do.
993 ts->di[i].bavail = sfb.f_blocks - sfb.f_bfree;
995 /* XXX Avoid FAT and other file systems that have not inodes. */
996 ts->di[i].iavail = !(sfb.f_ffree == 0 && sfb.f_files == 0)
999 xx = stat(ts->filesystems[i], &sb);
1000 ts->di[i].dev = sb.st_dev;
1004 if (dip) ts->di[i].bsize = 0;
1007 /* ===============================================
1008 * For packages being installed:
1009 * - verify package arch/os.
1010 * - verify package epoch:version-release is newer.
1012 * For packages being removed:
1015 /* The ordering doesn't matter here */
1016 pi = teInitIterator(ts);
1017 while ((p = teNext(pi, TR_ADDED)) != NULL) {
1018 rpmdbMatchIterator mi;
1020 /*@-branchstate@*/ /* FIX: p->key ??? */
1021 if (!archOkay(p->arch) && !(ts->ignoreSet & RPMPROB_FILTER_IGNOREARCH))
1022 rpmProblemSetAppend(ts->probs, RPMPROB_BADARCH,
1027 if (!osOkay(p->os) && !(ts->ignoreSet & RPMPROB_FILTER_IGNOREOS))
1028 rpmProblemSetAppend(ts->probs, RPMPROB_BADOS,
1034 if (!(ts->ignoreSet & RPMPROB_FILTER_OLDPACKAGE)) {
1036 mi = rpmtsInitIterator(ts, RPMTAG_NAME, p->name, 0);
1037 while ((h = rpmdbNextIterator(mi)) != NULL)
1038 xx = ensureOlder(ts, p, h);
1039 mi = rpmdbFreeIterator(mi);
1042 /* XXX multilib should not display "already installed" problems */
1043 if (!(ts->ignoreSet & RPMPROB_FILTER_REPLACEPKG) && !p->multiLib) {
1044 mi = rpmtsInitIterator(ts, RPMTAG_NAME, p->name, 0);
1045 xx = rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_DEFAULT,
1047 xx = rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_DEFAULT,
1050 while (rpmdbNextIterator(mi) != NULL) {
1051 rpmProblemSetAppend(ts->probs, RPMPROB_PKG_INSTALLED,
1055 /*@innerbreak@*/ break;
1057 mi = rpmdbFreeIterator(mi);
1060 /* Count no. of files (if any). */
1062 totalFileCount += p->fi->fc;
1065 pi = teFreeIterator(pi);
1067 /* The ordering doesn't matter here */
1068 pi = teInitIterator(ts);
1069 while ((p = teNext(pi, TR_REMOVED)) != NULL) {
1073 if (fi->bnl == NULL)
1074 continue; /* XXX can't happen */
1075 if (fi->dnl == NULL)
1076 continue; /* XXX can't happen */
1077 if (fi->dil == NULL)
1078 continue; /* XXX can't happen */
1079 totalFileCount += fi->fc;
1081 pi = teFreeIterator(pi);
1083 /* ===============================================
1084 * Initialize transaction element file info for package:
1087 ts->flEntries = ts->numAddedPackages + ts->numRemovedPackages;
1088 ts->flList = xcalloc(ts->flEntries, sizeof(*ts->flList));
1092 * FIXME?: we'd be better off assembling one very large file list and
1093 * calling fpLookupList only once. I'm not sure that the speedup is
1094 * worth the trouble though.
1096 pi = teInitIterator(ts);
1097 while ((p = teNextIterator(pi)) != NULL) {
1100 if ((fi = teGetFi(pi)) == NULL)
1101 continue; /* XXX can't happen */
1104 fi->magic = TFIMAGIC;
1112 /* Skip netshared paths, not our i18n files, and excluded docs */
1115 /*@switchbreak@*/ break;
1117 fi->record = p->u.removed.dboffset;
1118 /*@switchbreak@*/ break;
1122 fi->fps = (fi->fc > 0 ? xmalloc(fi->fc * sizeof(*fi->fps)) : NULL);
1124 pi = teFreeIterator(pi);
1126 if (!ts->chrootDone) {
1128 /*@-superuser -noeffect @*/
1129 xx = chroot(ts->rootDir);
1130 /*@=superuser =noeffect @*/
1132 if (ts->rpmdb) ts->rpmdb->db_chrootDone = 1;
1135 chroot_prefix = ts->rootDir;
1140 ts->ht = htCreate(totalFileCount * 2, 0, 0, fpHashFunction, fpEqual);
1141 fpc = fpCacheCreate(totalFileCount);
1143 /* ===============================================
1144 * Add fingerprint for each file not skipped.
1146 pi = teInitIterator(ts);
1147 while ((p = teNextIterator(pi)) != NULL) {
1149 if ((fi = teGetFi(pi)) == NULL)
1150 continue; /* XXX can't happen */
1152 fpLookupList(fpc, fi->dnl, fi->bnl, fi->dil, fi->fc, fi->fps);
1154 for (i = 0; i < fi->fc; i++) {
1155 if (XFA_SKIPPING(fi->actions[i]))
1156 /*@innercontinue@*/ continue;
1157 /*@-dependenttrans@*/
1158 htAddEntry(ts->ht, fi->fps + i, fi);
1159 /*@=dependenttrans@*/
1163 pi = teFreeIterator(pi);
1165 /*@-noeffectuncon @*/ /* FIX: check rc */
1166 NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_START, 6, ts->orderCount,
1167 NULL, ts->notifyData));
1168 /*@=noeffectuncon@*/
1170 /* ===============================================
1171 * Compute file disposition for each package in transaction set.
1173 pi = teInitIterator(ts);
1174 while ((p = teNextIterator(pi)) != NULL) {
1175 dbiIndexSet * matches;
1178 if ((fi = teGetFi(pi)) == NULL)
1179 continue; /* XXX can't happen */
1181 /*@-noeffectuncon @*/ /* FIX: check rc */
1182 NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_PROGRESS, teGetOc(pi),
1183 ts->orderCount, NULL, ts->notifyData));
1184 /*@=noeffectuncon@*/
1186 if (fi->fc == 0) continue;
1188 /* Extract file info for all files in this package from the database. */
1189 matches = xcalloc(fi->fc, sizeof(*matches));
1190 if (rpmdbFindFpList(ts->rpmdb, fi->fps, matches, fi->fc)) {
1191 psm->ts = rpmtsUnlink(ts, "tsRun (rpmFindFpList fail)");
1192 return 1; /* XXX WTFO? */
1196 for (i = 0; i < fi->fc; i++)
1197 numShared += dbiIndexSetCount(matches[i]);
1199 /* Build sorted file info list for this package. */
1200 shared = sharedList = xcalloc((numShared + 1), sizeof(*sharedList));
1201 for (i = 0; i < fi->fc; i++) {
1203 * Take care not to mark files as replaced in packages that will
1204 * have been removed before we will get here.
1206 for (j = 0; j < dbiIndexSetCount(matches[i]); j++) {
1208 ro = dbiIndexRecordOffset(matches[i], j);
1210 qi = teInitIterator(ts);
1211 while ((q = teNext(qi, TR_REMOVED)) != NULL) {
1213 /*@innerbreak@*/ break;
1214 if (q->u.removed.dboffset == ro)
1217 qi = teFreeIterator(qi);
1219 shared->pkgFileNum = i;
1220 shared->otherPkg = dbiIndexRecordOffset(matches[i], j);
1221 shared->otherFileNum = dbiIndexRecordFileNumber(matches[i], j);
1222 shared->isRemoved = (knownBad == ro);
1225 matches[i] = dbiFreeIndexSet(matches[i]);
1227 numShared = shared - sharedList;
1228 shared->otherPkg = -1;
1229 matches = _free(matches);
1231 /* Sort file info by other package index (otherPkg) */
1232 qsort(sharedList, numShared, sizeof(*shared), sharedCmp);
1234 /* For all files from this package that are in the database ... */
1235 for (i = 0; i < numShared; i = nexti) {
1238 shared = sharedList + i;
1240 /* Find the end of the files in the other package. */
1241 for (nexti = i + 1; nexti < numShared; nexti++) {
1242 if (sharedList[nexti].otherPkg != shared->otherPkg)
1243 /*@innerbreak@*/ break;
1246 /* Is this file from a package being removed? */
1248 if (ts->removedPackages != NULL)
1249 for (j = 0; j < ts->numRemovedPackages; j++) {
1250 if (ts->removedPackages[j] != shared->otherPkg)
1251 /*@innercontinue@*/ continue;
1253 /*@innerbreak@*/ break;
1256 /* Determine the fate of each file. */
1259 xx = handleInstInstalledFiles(ts, p, fi, shared, nexti - i,
1260 !(beingRemoved || (ts->ignoreSet & RPMPROB_FILTER_REPLACEOLDFILES)));
1261 /*@switchbreak@*/ break;
1264 xx = handleRmvdInstalledFiles(ts, fi, shared, nexti - i);
1265 /*@switchbreak@*/ break;
1271 /* Update disk space needs on each partition for this package. */
1272 handleOverlappedFiles(ts, p, fi);
1274 /* Check added package has sufficient space on each partition used. */
1277 if (!(ts->di && fi->fc))
1278 /*@switchbreak@*/ break;
1279 for (i = 0; i < ts->filesystemCount; i++) {
1283 /* XXX Avoid FAT and other file systems that have not inodes. */
1284 if (dip->iavail <= 0)
1285 /*@innercontinue@*/ continue;
1287 if (adj_fs_blocks(dip->bneeded) > dip->bavail) {
1288 rpmProblemSetAppend(ts->probs, RPMPROB_DISKSPACE,
1290 ts->filesystems[i], NULL, NULL,
1291 (adj_fs_blocks(dip->bneeded) - dip->bavail) * dip->bsize);
1294 if (adj_fs_blocks(dip->ineeded) > dip->iavail) {
1295 rpmProblemSetAppend(ts->probs, RPMPROB_DISKNODES,
1297 ts->filesystems[i], NULL, NULL,
1298 (adj_fs_blocks(dip->ineeded) - dip->iavail));
1301 /*@switchbreak@*/ break;
1303 /*@switchbreak@*/ break;
1306 pi = teFreeIterator(pi);
1308 if (ts->chrootDone) {
1309 /*@-superuser -noeffect @*/
1311 /*@=superuser =noeffect @*/
1313 if (ts->rpmdb) ts->rpmdb->db_chrootDone = 0;
1315 chroot_prefix = NULL;
1317 xx = chdir(ts->currDir);
1320 /*@-noeffectuncon @*/ /* FIX: check rc */
1321 NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_STOP, 6, ts->orderCount,
1322 NULL, ts->notifyData));
1323 /*@=noeffectuncon @*/
1325 /* ===============================================
1326 * Free unused memory as soon as possible.
1329 pi = teInitIterator(ts);
1330 while ((p = teNextIterator(pi)) != NULL) {
1331 if ((fi = teGetFi(pi)) == NULL)
1332 continue; /* XXX can't happen */
1335 fi->fps = _free(fi->fps);
1337 pi = teFreeIterator(pi);
1343 /* ===============================================
1344 * If unfiltered problems exist, free memory and return.
1346 if ((ts->transFlags & RPMTRANS_FLAG_BUILD_PROBS)
1347 || (ts->probs->numProblems &&
1348 (okProbs != NULL || rpmProblemSetTrim(ts->probs, okProbs)))
1352 ts->flList = freeFl(ts, ts->flList);
1355 if (psm->ts != NULL)
1356 psm->ts = rpmtsUnlink(psm->ts, "tsRun (problems)");
1357 /*@-nullstate@*/ /* FIX: ts->flList may be NULL */
1358 return ts->orderCount;
1362 /* ===============================================
1363 * Save removed files before erasing.
1365 if (ts->transFlags & (RPMTRANS_FLAG_DIRSTASH | RPMTRANS_FLAG_REPACKAGE)) {
1366 pi = teInitIterator(ts);
1367 while ((p = teNextIterator(pi)) != NULL) {
1371 /*@switchbreak@*/ break;
1373 if (!(ts->transFlags & RPMTRANS_FLAG_REPACKAGE))
1374 /*@switchbreak@*/ break;
1376 psm->fi = rpmfiLink(fi, "tsRepackage");
1377 xx = psmStage(psm, PSM_PKGSAVE);
1378 (void) rpmfiUnlink(fi, "tsRepackage");
1381 /*@switchbreak@*/ break;
1384 pi = teFreeIterator(pi);
1387 /* ===============================================
1388 * Install and remove packages.
1391 lastKey = (alKey)-2; /* erased packages have -1 */
1392 pi = teInitIterator(ts);
1393 /*@-branchstate@*/ /* FIX: fi reload needs work */
1394 while ((p = teNextIterator(pi)) != NULL) {
1400 if ((fi = teGetFi(pi)) == NULL)
1401 continue; /* XXX can't happen */
1404 psm->fi = rpmfiLink(fi, "tsInstall");
1408 pkgKey = p->u.addedKey;
1410 rpmMessage(RPMMESS_DEBUG, "========== +++ %s\n", p->NEVR);
1412 h = (fi->h ? headerLink(fi->h, "TR_ADDED install") : NULL);
1419 /*@-noeffectuncon @*/ /* FIX: ??? */
1420 p->fd = ts->notify(fi->h, RPMCALLBACK_INST_OPEN_FILE, 0, 0,
1421 p->key, ts->notifyData);
1422 /*@=noeffectuncon @*/
1423 if (p->fd != NULL) {
1426 /*@-mustmod@*/ /* LCL: segfault */
1427 rpmrc = rpmReadPackageFile(ts, p->fd,
1428 "rpmRunTransactions", &h);
1431 if (!(rpmrc == RPMRC_OK || rpmrc == RPMRC_BADSIZE)) {
1432 /*@-noeffectuncon @*/ /* FIX: check rc */
1433 (void) ts->notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE,
1435 p->key, ts->notifyData);
1436 /*@=noeffectuncon @*/
1442 Header foo = relocateFileList(ts, fi, h, NULL);
1443 h = headerFree(h, "TR_ADDED read free");
1444 h = headerLink(foo, "TR_ADDED relocate xfer");
1445 foo = headerFree(foo, "TR_ADDED relocate");
1448 if (p->fd != NULL) gotfd = 1;
1454 if (p->fd != NULL) {
1456 fi->h = headerLink(h, "TR_ADDED fi->h link");
1459 char * fstates = fi->fstates;
1460 fileAction * actions = fi->actions;
1464 (void) fiFree(fi, 0);
1466 fi->magic = TFIMAGIC;
1469 (void) fiNew(ts, fi, h, RPMTAG_BASENAMES, 1);
1470 fi->fstates = _free(fi->fstates);
1471 fi->fstates = fstates;
1472 fi->actions = _free(fi->actions);
1473 fi->actions = actions;
1478 ts->transFlags |= RPMTRANS_FLAG_MULTILIB;
1480 if (psmStage(psm, PSM_PKGINSTALL)) {
1484 fi->h = headerFree(fi->h, "TR_ADDED fi->h free");
1491 h = headerFree(h, "TR_ADDED h free");
1494 /*@-noeffectuncon @*/ /* FIX: check rc */
1495 (void)ts->notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE, 0, 0,
1496 p->key, ts->notifyData);
1497 /*@=noeffectuncon @*/
1500 (void) fiFree(fi, 0);
1501 /*@switchbreak@*/ break;
1503 rpmMessage(RPMMESS_DEBUG, "========== --- %s\n", p->NEVR);
1504 /* If install failed, then we shouldn't erase. */
1505 if (p->u.removed.dependsOnKey != lastKey) {
1506 if (psmStage(psm, PSM_PKGERASE))
1509 (void) fiFree(fi, 0);
1510 /*@switchbreak@*/ break;
1512 xx = rpmdbSync(ts->rpmdb);
1513 (void) rpmfiUnlink(psm->fi, "tsInstall");
1518 pi = teFreeIterator(pi);
1521 ts->flList = freeFl(ts, ts->flList);
1525 psm->ts = rpmtsUnlink(psm->ts, "tsRun");
1527 /*@-nullstate@*/ /* FIX: ts->flList may be NULL */