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 @*/
63 /*@access rpmFNSet @*/
66 /*@access teIterator @*/
67 /*@access transactionElement @*/
68 /*@access rpmTransactionSet @*/
72 struct diskspaceInfo {
73 dev_t dev; /*!< File system device number. */
74 signed long bneeded; /*!< No. of blocks needed. */
75 signed long ineeded; /*!< No. of inodes needed. */
76 int bsize; /*!< File system block size. */
77 signed long bavail; /*!< No. of blocks available. */
78 signed long iavail; /*!< No. of inodes available. */
82 * Adjust for root only reserved space. On linux e2fs, this is 5%.
84 #define adj_fs_blocks(_nb) (((_nb) * 21) / 20)
86 /* argon thought a shift optimization here was a waste of time... he's
88 #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 */
101 for (oc = 0, fi = flList; oc < ts->orderCount; oc++, fi++)
103 flList = _free(flList);
104 /*@=usereleased =onlytrans @*/
109 void rpmtransSetScriptFd(rpmTransactionSet ts, FD_t fd)
111 /*@-type@*/ /* FIX: cast? */
112 ts->scriptFd = (fd ? fdLink(fd, "rpmtransSetScriptFd") : NULL);
116 int rpmtransGetKeys(const rpmTransactionSet ts, fnpyKey ** ep, int * nep)
120 if (nep) *nep = ts->orderCount;
122 teIterator pi; transactionElement p;
125 *ep = e = xmalloc(ts->orderCount * sizeof(*e));
126 pi = teInitIterator(ts);
127 while ((p = teNextIterator(pi)) != NULL) {
133 /*@switchbreak@*/ break;
136 /*@-mods@*/ /* FIX: double indirection. */
139 /*@switchbreak@*/ break;
143 pi = teFreeIterator(pi);
150 static int archOkay(/*@null@*/ const char * pkgArch)
153 if (pkgArch == NULL) return 0;
154 return (rpmMachineScore(RPM_MACHTABLE_INSTARCH, pkgArch) ? 1 : 0);
159 static int osOkay(/*@null@*/ const char * pkgOs)
162 if (pkgOs == NULL) return 0;
163 return (rpmMachineScore(RPM_MACHTABLE_INSTOS, pkgOs) ? 1 : 0);
168 static int sharedCmp(const void * one, const void * two)
171 sharedFileInfo a = (sharedFileInfo) one;
172 sharedFileInfo b = (sharedFileInfo) two;
174 if (a->otherPkg < b->otherPkg)
176 else if (a->otherPkg > b->otherPkg)
184 static fileAction decideFileFate(const char * dirName,
185 const char * baseName, short dbMode,
186 const char * dbMd5, const char * dbLink, short newMode,
187 const char * newMd5, const char * newLink, int newFlags,
188 rpmtransFlags transFlags)
189 /*@globals fileSystem @*/
190 /*@modifies fileSystem @*/
193 const char * dbAttr, * newAttr;
194 fileTypes dbWhat, newWhat, diskWhat;
197 int save = (newFlags & RPMFILE_NOREPLACE) ? FA_ALTNAME : FA_SAVE;
198 char * filespec = alloca(strlen(dirName) + strlen(baseName) + 1);
200 (void) stpcpy( stpcpy(filespec, dirName), baseName);
202 if (lstat(filespec, &sb)) {
204 * The file doesn't exist on the disk. Create it unless the new
205 * package has marked it as missingok, or allfiles is requested.
207 if (!(transFlags & RPMTRANS_FLAG_ALLFILES) &&
208 (newFlags & RPMFILE_MISSINGOK)) {
209 rpmMessage(RPMMESS_DEBUG, _("%s skipped due to missingok flag\n"),
217 diskWhat = whatis(sb.st_mode);
218 dbWhat = whatis(dbMode);
219 newWhat = whatis(newMode);
221 /* RPM >= 2.3.10 shouldn't create config directories -- we'll ignore
222 them in older packages as well */
223 if (newWhat == XDIR) {
227 if (diskWhat != newWhat) {
229 } else if (newWhat != dbWhat && diskWhat != dbWhat) {
231 } else if (dbWhat != newWhat) {
233 } else if (dbWhat != LINK && dbWhat != REG) {
238 rc = mdfile(filespec, buffer);
241 /* assume the file has been removed, don't freak */
246 } else /* dbWhat == LINK */ {
247 memset(buffer, 0, sizeof(buffer));
248 i = readlink(filespec, buffer, sizeof(buffer) - 1);
250 /* assume the file has been removed, don't freak */
257 /* this order matters - we'd prefer to CREATE the file if at all
258 possible in case something else (like the timestamp) has changed */
260 if (!strcmp(dbAttr, buffer)) {
261 /* this config file has never been modified, so just replace it */
265 if (!strcmp(dbAttr, newAttr)) {
266 /* this file is the same in all versions of this package */
271 * The config file on the disk has been modified, but
272 * the ones in the two packages are different. It would
273 * be nice if RPM was smart enough to at least try and
274 * merge the difference ala CVS, but...
281 static int filecmp(short mode1, const char * md51, const char * link1,
282 short mode2, const char * md52, const char * link2)
285 fileTypes what1 = whatis(mode1);
286 fileTypes what2 = whatis(mode2);
288 if (what1 != what2) return 1;
291 return strcmp(link1, link2);
292 else if (what1 == REG)
293 return strcmp(md51, md52);
300 /* XXX only ts->{probs,rpmdb} modified */
301 static int handleInstInstalledFiles(const rpmTransactionSet ts,
302 transactionElement p, TFI_t fi,
303 sharedFileInfo shared,
304 int sharedCount, int reportConflicts)
305 /*@globals fileSystem @*/
306 /*@modifies ts, fi, fileSystem @*/
309 HFD_t hfd = (fi->hfd ? fi->hfd : headerFreeData);
310 rpmtransFlags transFlags = ts->transFlags;
311 rpmTagType oltype, omtype;
314 const char ** otherMd5s;
315 const char ** otherLinks;
316 const char * otherStates;
317 uint_32 * otherFlags;
318 uint_32 * otherSizes;
319 uint_16 * otherModes;
323 rpmdbMatchIterator mi;
325 mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES,
326 &shared->otherPkg, sizeof(shared->otherPkg));
327 h = rpmdbNextIterator(mi);
329 mi = rpmdbFreeIterator(mi);
333 xx = hge(h, RPMTAG_FILEMD5S, &omtype, (void **) &otherMd5s, NULL);
334 xx = hge(h, RPMTAG_FILELINKTOS, &oltype, (void **) &otherLinks, NULL);
335 xx = hge(h, RPMTAG_FILESTATES, NULL, (void **) &otherStates, NULL);
336 xx = hge(h, RPMTAG_FILEMODES, NULL, (void **) &otherModes, NULL);
337 xx = hge(h, RPMTAG_FILEFLAGS, NULL, (void **) &otherFlags, NULL);
338 xx = hge(h, RPMTAG_FILESIZES, NULL, (void **) &otherSizes, NULL);
340 fi->replaced = xmalloc(sharedCount * sizeof(*fi->replaced));
342 for (i = 0; i < sharedCount; i++, shared++) {
343 int otherFileNum, fileNum;
344 otherFileNum = shared->otherFileNum;
345 fileNum = shared->pkgFileNum;
347 /* XXX another tedious segfault, assume file state normal. */
348 if (otherStates && otherStates[otherFileNum] != RPMFILE_STATE_NORMAL)
351 if (XFA_SKIPPING(fi->actions[fileNum]))
354 if (filecmp(otherModes[otherFileNum],
355 otherMd5s[otherFileNum],
356 otherLinks[otherFileNum],
359 fi->flinks[fileNum])) {
360 /*@-compdef@*/ /* FIX: *fi->replaced undefined */
361 if (reportConflicts) {
362 const char * altNEVR = hGetNEVR(h, NULL);
363 rpmProblemSetAppend(ts->probs, RPMPROB_FILE_CONFLICT,
365 fi->dnl[fi->dil[fileNum]], fi->bnl[fileNum],
368 altNEVR = _free(altNEVR);
371 if (!(otherFlags[otherFileNum] | fi->fflags[fileNum])
374 if (!shared->isRemoved)
375 fi->replaced[numReplaced++] = *shared;
380 if ((otherFlags[otherFileNum] | fi->fflags[fileNum]) & RPMFILE_CONFIG) {
381 fi->actions[fileNum] = decideFileFate(
382 fi->dnl[fi->dil[fileNum]],
384 otherModes[otherFileNum],
385 otherMd5s[otherFileNum],
386 otherLinks[otherFileNum],
394 fi->replacedSizes[fileNum] = otherSizes[otherFileNum];
397 otherMd5s = hfd(otherMd5s, omtype);
398 otherLinks = hfd(otherLinks, oltype);
399 mi = rpmdbFreeIterator(mi);
401 fi->replaced = xrealloc(fi->replaced, /* XXX memory leak */
402 sizeof(*fi->replaced) * (numReplaced + 1));
403 fi->replaced[numReplaced].otherPkg = 0;
410 /* XXX only ts->rpmdb modified */
411 static int handleRmvdInstalledFiles(const rpmTransactionSet ts, TFI_t fi,
412 sharedFileInfo shared, int sharedCount)
413 /*@globals fileSystem @*/
414 /*@modifies fi, fileSystem @*/
418 const char * otherStates;
421 rpmdbMatchIterator mi;
423 mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES,
424 &shared->otherPkg, sizeof(shared->otherPkg));
425 h = rpmdbNextIterator(mi);
427 mi = rpmdbFreeIterator(mi);
431 xx = hge(h, RPMTAG_FILESTATES, NULL, (void **) &otherStates, NULL);
433 for (i = 0; i < sharedCount; i++, shared++) {
434 int otherFileNum, fileNum;
435 otherFileNum = shared->otherFileNum;
436 fileNum = shared->pkgFileNum;
438 if (otherStates[otherFileNum] != RPMFILE_STATE_NORMAL)
441 fi->actions[fileNum] = FA_SKIP;
444 mi = rpmdbFreeIterator(mi);
450 * Update disk space needs on each partition for this package.
452 /* XXX only ts->{probs,di} modified */
453 static void handleOverlappedFiles(const rpmTransactionSet ts,
454 const transactionElement p, TFI_t fi)
455 /*@globals fileSystem @*/
456 /*@modifies ts, fi, fileSystem @*/
458 struct diskspaceInfo * ds = NULL;
459 uint_32 fixupSize = 0;
460 char * filespec = NULL;
461 int fileSpecAlloced = 0;
464 for (i = 0; i < fi->fc; i++) {
465 int otherPkgNum, otherFileNum;
469 if (XFA_SKIPPING(fi->actions[i]))
472 j = strlen(fi->dnl[fi->dil[i]]) + strlen(fi->bnl[i]) + 1;
474 if (j > fileSpecAlloced) {
475 fileSpecAlloced = j * 2;
476 filespec = xrealloc(filespec, fileSpecAlloced);
480 (void) stpcpy( stpcpy( filespec, fi->dnl[fi->dil[i]]), fi->bnl[i]);
484 while (ds->bsize && ds->dev != fi->fps[i].entry->dev) ds++;
485 if (!ds->bsize) ds = NULL;
490 * Retrieve all records that apply to this file. Note that the
491 * file info records were built in the same order as the packages
492 * will be installed and removed so the records for an overlapped
493 * files will be sorted in exactly the same order.
495 (void) htGetEntry(ts->ht, &fi->fps[i],
496 (const void ***) &recs, &numRecs, NULL);
499 * If this package is being added, look only at other packages
500 * being added -- removed packages dance to a different tune.
501 * If both this and the other package are being added, overlapped
502 * files must be identical (or marked as a conflict). The
503 * disposition of already installed config files leads to
504 * a small amount of extra complexity.
506 * If this package is being removed, then there are two cases that
507 * need to be worried about:
508 * If the other package is being added, then skip any overlapped files
509 * so that this package removal doesn't nuke the overlapped files
510 * that were just installed.
511 * If both this and the other package are being removed, then each
512 * file removal from preceding packages needs to be skipped so that
513 * the file removal occurs only on the last occurence of an overlapped
514 * file in the transaction set.
518 /* Locate this overlapped file in the set of added/removed packages. */
519 for (j = 0; j < numRecs && recs[j] != fi; j++)
522 /* Find what the previous disposition of this file was. */
523 otherFileNum = -1; /* keep gcc quiet */
524 for (otherPkgNum = j - 1; otherPkgNum >= 0; otherPkgNum--) {
525 /* Added packages need only look at other added packages. */
526 if (p->type == TR_ADDED && recs[otherPkgNum]->te->type != TR_ADDED)
527 /*@innercontinue@*/ continue;
529 /* TESTME: there are more efficient searches in the world... */
530 for (otherFileNum = 0;
531 otherFileNum < recs[otherPkgNum]->fc;
535 /* If the addresses are the same, so are the values. */
536 if ((fi->fps + i) == (recs[otherPkgNum]->fps + otherFileNum))
537 /*@innerbreak@*/ break;
539 /* Otherwise, compare fingerprints by value. */
540 /*@-nullpass@*/ /* LCL: looks good to me */
541 if (FP_EQUAL(fi->fps[i], recs[otherPkgNum]->fps[otherFileNum]))
542 /*@innerbreak@*/ break;
546 /* XXX is this test still necessary? */
547 if (recs[otherPkgNum]->actions[otherFileNum] != FA_UNKNOWN)
548 /*@innerbreak@*/ break;
554 if (otherPkgNum < 0) {
555 /* XXX is this test still necessary? */
556 if (fi->actions[i] != FA_UNKNOWN)
557 /*@switchbreak@*/ break;
558 if ((fi->fflags[i] & RPMFILE_CONFIG) &&
559 !lstat(filespec, &sb)) {
560 /* Here is a non-overlapped pre-existing config file. */
561 fi->actions[i] = (fi->fflags[i] & RPMFILE_NOREPLACE)
562 ? FA_ALTNAME : FA_BACKUP;
564 fi->actions[i] = FA_CREATE;
566 /*@switchbreak@*/ break;
569 /* Mark added overlapped non-identical files as a conflict. */
570 /*@-branchstate@*/ /* FIX: p->key ??? */
571 if ((ts->ignoreSet & RPMPROB_FILTER_REPLACENEWFILES)
572 && filecmp(recs[otherPkgNum]->fmodes[otherFileNum],
573 recs[otherPkgNum]->fmd5s[otherFileNum],
574 recs[otherPkgNum]->flinks[otherFileNum],
579 const char * altNEVR = recs[otherPkgNum]->te->NEVR;
580 rpmProblemSetAppend(ts->probs, RPMPROB_NEW_FILE_CONFLICT,
588 /* Try to get the disk accounting correct even if a conflict. */
589 fixupSize = recs[otherPkgNum]->fsizes[otherFileNum];
591 if ((fi->fflags[i] & RPMFILE_CONFIG) && !lstat(filespec, &sb)) {
592 /* Here is an overlapped pre-existing config file. */
593 fi->actions[i] = (fi->fflags[i] & RPMFILE_NOREPLACE)
594 ? FA_ALTNAME : FA_SKIP;
596 fi->actions[i] = FA_CREATE;
598 } /*@switchbreak@*/ break;
600 if (otherPkgNum >= 0) {
601 /* Here is an overlapped added file we don't want to nuke. */
602 if (recs[otherPkgNum]->actions[otherFileNum] != FA_ERASE) {
603 /* On updates, don't remove files. */
604 fi->actions[i] = FA_SKIP;
605 /*@switchbreak@*/ break;
607 /* Here is an overlapped removed file: skip in previous. */
608 recs[otherPkgNum]->actions[otherFileNum] = FA_SKIP;
610 if (XFA_SKIPPING(fi->actions[i]))
611 /*@switchbreak@*/ break;
612 if (fi->fstates && fi->fstates[i] != RPMFILE_STATE_NORMAL)
613 /*@switchbreak@*/ break;
614 if (!(S_ISREG(fi->fmodes[i]) && (fi->fflags[i] & RPMFILE_CONFIG))) {
615 fi->actions[i] = FA_ERASE;
616 /*@switchbreak@*/ break;
619 /* Here is a pre-existing modified config file that needs saving. */
621 if (!mdfile(filespec, mdsum) && strcmp(fi->fmd5s[i], mdsum)) {
622 fi->actions[i] = FA_BACKUP;
623 /*@switchbreak@*/ break;
626 fi->actions[i] = FA_ERASE;
627 /*@switchbreak@*/ break;
631 uint_32 s = BLOCK_ROUND(fi->fsizes[i], ds->bsize);
633 switch (fi->actions[i]) {
639 /*@switchbreak@*/ break;
642 * FIXME: If two packages share a file (same md5sum), and
643 * that file is being replaced on disk, will ds->bneeded get
644 * decremented twice? Quite probably!
648 ds->bneeded -= BLOCK_ROUND(fi->replacedSizes[i], ds->bsize);
649 /*@switchbreak@*/ break;
654 /*@switchbreak@*/ break;
657 /*@switchbreak@*/ break;
660 ds->bneeded -= BLOCK_ROUND(fixupSize, ds->bsize);
663 filespec = _free(filespec);
667 * Ensure that current package is newer than installed package.
668 * @param ts transaction set
669 * @param p current transaction element
670 * @param h installed header
671 * @return 0 if not newer, 1 if okay
673 static int ensureOlder(rpmTransactionSet ts,
674 const transactionElement p, const Header h)
677 int_32 reqFlags = (RPMSENSE_LESS | RPMSENSE_EQUAL);
683 if (p == NULL || h == NULL)
686 t = alloca(strlen(p->NEVR) + (p->epoch != NULL ? strlen(p->epoch) : 0) + 1);
689 if (p->epoch != NULL) t = stpcpy( stpcpy(t, p->epoch), ":");
690 if (p->version != NULL) t = stpcpy(t, p->version);
692 if (p->release != NULL) t = stpcpy(t, p->release);
694 req = dsSingle(RPMTAG_REQUIRENAME, p->name, reqEVR, reqFlags);
695 rc = headerMatchesDepFlags(h, req);
698 /*@-branchstate@*/ /* FIX: p->key ??? */
700 const char * altNEVR = hGetNEVR(h, NULL);
701 rpmProblemSetAppend(ts->probs, RPMPROB_OLDPACKAGE,
706 altNEVR = _free(altNEVR);
717 static void skipFiles(const rpmTransactionSet ts, TFI_t fi)
718 /*@globals rpmGlobalMacroContext @*/
719 /*@modifies fi, rpmGlobalMacroContext @*/
721 int noDocs = (ts->transFlags & RPMTRANS_FLAG_NODOCS);
722 char ** netsharedPaths = NULL;
723 const char ** languages;
724 const char * dn, * bn;
725 int dnlen, bnlen, ix;
732 noDocs = rpmExpandNumeric("%{_excludedocs}");
734 { const char *tmpPath = rpmExpand("%{_netsharedpath}", NULL);
736 if (tmpPath && *tmpPath != '%')
737 netsharedPaths = splitString(tmpPath, strlen(tmpPath), ':');
739 tmpPath = _free(tmpPath);
742 s = rpmExpand("%{_install_langs}", NULL);
744 if (!(s && *s != '%'))
747 languages = (const char **) splitString(s, strlen(s), ':');
753 /* Compute directory refcount, skip directory if now empty. */
754 drc = alloca(fi->dc * sizeof(*drc));
755 memset(drc, 0, fi->dc * sizeof(*drc));
756 dff = alloca(fi->dc * sizeof(*dff));
757 memset(dff, 0, fi->dc * sizeof(*dff));
759 for (i = 0; i < fi->fc; i++) {
770 /* Don't bother with skipped files */
771 if (XFA_SKIPPING(fi->actions[i])) {
777 * Skip net shared paths.
778 * Net shared paths are not relative to the current root (though
779 * they do need to take package relocations into account).
781 for (nsp = netsharedPaths; nsp && *nsp; nsp++) {
786 if (strncmp(dn, *nsp, len))
787 /*@innercontinue@*/ continue;
788 /* Only directories or complete file paths can be net shared */
789 if (!(dn[len] == '/' || dn[len] == '\0'))
790 /*@innercontinue@*/ continue;
792 if (len < (dnlen + bnlen))
793 /*@innercontinue@*/ continue;
794 if (strncmp(dn, *nsp, dnlen))
795 /*@innercontinue@*/ continue;
796 if (strncmp(bn, (*nsp) + dnlen, bnlen))
797 /*@innercontinue@*/ continue;
799 /* Only directories or complete file paths can be net shared */
800 if (!((*nsp)[len] == '/' || (*nsp)[len] == '\0'))
801 /*@innercontinue@*/ continue;
804 /*@innerbreak@*/ break;
808 drc[ix]--; dff[ix] = 1;
809 fi->actions[i] = FA_SKIPNETSHARED;
814 * Skip i18n language specific files.
816 if (fi->flangs && languages && *fi->flangs[i]) {
817 const char **lang, *l, *le;
818 for (lang = languages; *lang != NULL; lang++) {
819 if (!strcmp(*lang, "all"))
820 /*@innerbreak@*/ break;
821 for (l = fi->flangs[i]; *l != '\0'; l = le) {
822 for (le = l; *le != '\0' && *le != '|'; le++)
824 if ((le-l) > 0 && !strncmp(*lang, l, (le-l)))
825 /*@innerbreak@*/ break;
826 if (*le == '|') le++; /* skip over | */
829 /*@innerbreak@*/ break;
832 drc[ix]--; dff[ix] = 1;
833 fi->actions[i] = FA_SKIPNSTATE;
839 * Skip documentation if requested.
841 if (noDocs && (fi->fflags[i] & RPMFILE_DOC)) {
842 drc[ix]--; dff[ix] = 1;
843 fi->actions[i] = FA_SKIPNSTATE;
848 /* Skip (now empty) directories that had skipped files. */
849 for (j = 0; j < fi->dc; j++) {
851 if (drc[j]) continue; /* dir still has files. */
852 if (!dff[j]) continue; /* dir was not emptied here. */
854 /* Find parent directory and basename. */
855 dn = fi->dnl[j]; dnlen = strlen(dn) - 1;
856 bn = dn + dnlen; bnlen = 0;
857 while (bn > dn && bn[-1] != '/') {
863 /* If explicitly included in the package, skip the directory. */
864 for (i = 0; i < fi->fc; i++) {
867 if (XFA_SKIPPING(fi->actions[i]))
868 /*@innercontinue@*/ continue;
869 if (whatis(fi->fmodes[i]) != XDIR)
870 /*@innercontinue@*/ continue;
871 dir = fi->dnl[fi->dil[i]];
872 if (strlen(dir) != dnlen)
873 /*@innercontinue@*/ continue;
874 if (strncmp(dir, dn, dnlen))
875 /*@innercontinue@*/ continue;
876 if (strlen(fi->bnl[i]) != bnlen)
877 /*@innercontinue@*/ continue;
878 if (strncmp(fi->bnl[i], bn, bnlen))
879 /*@innercontinue@*/ continue;
880 rpmMessage(RPMMESS_DEBUG, _("excluding directory %s\n"), dn);
881 fi->actions[i] = FA_SKIPNSTATE;
882 /*@innerbreak@*/ break;
886 if (netsharedPaths) freeSplitString(netsharedPaths);
887 #ifdef DYING /* XXX freeFi will deal with this later. */
888 fi->flangs = _free(fi->flangs);
890 if (languages) freeSplitString((char **)languages);
893 #define NOTIFY(_ts, _al) if ((_ts)->notify) (void) (_ts)->notify _al
895 int rpmRunTransactions( rpmTransactionSet ts,
896 rpmCallbackFunction notify, rpmCallbackData notifyData,
897 rpmProblemSet okProbs, rpmProblemSet * newProbs,
898 rpmtransFlags transFlags, rpmprobFilterFlags ignoreSet)
902 int totalFileCount = 0;
904 struct diskspaceInfo * dip;
905 sharedFileInfo shared, sharedList;
909 fingerPrintCache fpc;
910 PSM_t psm = memset(alloca(sizeof(*psm)), 0, sizeof(*psm));
911 teIterator pi; transactionElement p;
912 teIterator qi; transactionElement q;
917 /* FIXME: what if the same package is included in ts twice? */
919 ts->transFlags = transFlags;
920 if (ts->transFlags & RPMTRANS_FLAG_NOSCRIPTS)
921 ts->transFlags |= (_noTransScripts | _noTransTriggers);
922 if (ts->transFlags & RPMTRANS_FLAG_NOTRIGGERS)
923 ts->transFlags |= _noTransTriggers;
925 /* XXX MULTILIB is broken, as packages can and do execute /sbin/ldconfig. */
926 if (ts->transFlags & (RPMTRANS_FLAG_JUSTDB | RPMTRANS_FLAG_MULTILIB))
927 ts->transFlags |= (_noTransScripts | _noTransTriggers);
930 ts->notifyData = notifyData;
932 ts->probs = *newProbs = rpmProblemSetCreate();
933 *newProbs = rpmpsLink(ts->probs, "RunTransactions");
935 ts->ignoreSet = ignoreSet;
936 ts->currDir = _free(ts->currDir);
937 ts->currDir = currentDirectory();
939 if (ts->rpmdb) ts->rpmdb->db_chrootDone = 0;
940 ts->id = (int_32) time(NULL);
942 memset(psm, 0, sizeof(*psm));
944 psm->ts = rpmtsLink(ts, "tsRun");
947 /* Get available space on mounted file systems. */
948 if (!(ts->ignoreSet & RPMPROB_FILTER_DISKSPACE) &&
949 !rpmGetFilesystemList(&ts->filesystems, &ts->filesystemCount)) {
952 ts->di = _free(ts->di);
953 dip = ts->di = xcalloc((ts->filesystemCount + 1), sizeof(*ts->di));
955 for (i = 0; (i < ts->filesystemCount) && dip; i++) {
956 #if STATFS_IN_SYS_STATVFS
958 memset(&sfb, 0, sizeof(sfb));
959 if (statvfs(ts->filesystems[i], &sfb))
963 /* This platform has the 4-argument version of the statfs call. The last two
964 * should be the size of struct statfs and 0, respectively. The 0 is the
965 * filesystem type, and is always 0 when statfs is called on a mounted
966 * filesystem, as we're doing.
968 memset(&sfb, 0, sizeof(sfb));
969 if (statfs(ts->filesystems[i], &sfb, sizeof(sfb), 0))
971 memset(&sfb, 0, sizeof(sfb));
972 if (statfs(ts->filesystems[i], &sfb))
978 ts->di[i].bsize = sfb.f_bsize;
979 ts->di[i].bneeded = 0;
980 ts->di[i].ineeded = 0;
981 #ifdef STATFS_HAS_F_BAVAIL
982 ts->di[i].bavail = sfb.f_bavail;
984 /* FIXME: the statfs struct doesn't have a member to tell how many blocks are
985 * available for non-superusers. f_blocks - f_bfree is probably too big, but
986 * it's about all we can do.
988 ts->di[i].bavail = sfb.f_blocks - sfb.f_bfree;
990 /* XXX Avoid FAT and other file systems that have not inodes. */
991 ts->di[i].iavail = !(sfb.f_ffree == 0 && sfb.f_files == 0)
994 xx = stat(ts->filesystems[i], &sb);
995 ts->di[i].dev = sb.st_dev;
999 if (dip) ts->di[i].bsize = 0;
1002 /* ===============================================
1003 * For packages being installed:
1004 * - verify package arch/os.
1005 * - verify package epoch:version-release is newer.
1007 * For packages being removed:
1010 /* The ordering doesn't matter here */
1011 pi = teInitIterator(ts);
1012 while ((p = teNext(pi, TR_ADDED)) != NULL) {
1013 rpmdbMatchIterator mi;
1015 /*@-branchstate@*/ /* FIX: p->key ??? */
1016 if (!archOkay(p->arch) && !(ts->ignoreSet & RPMPROB_FILTER_IGNOREARCH))
1017 rpmProblemSetAppend(ts->probs, RPMPROB_BADARCH,
1022 if (!osOkay(p->os) && !(ts->ignoreSet & RPMPROB_FILTER_IGNOREOS))
1023 rpmProblemSetAppend(ts->probs, RPMPROB_BADOS,
1029 if (!(ts->ignoreSet & RPMPROB_FILTER_OLDPACKAGE)) {
1031 mi = rpmtsInitIterator(ts, RPMTAG_NAME, p->name, 0);
1032 while ((h = rpmdbNextIterator(mi)) != NULL)
1033 xx = ensureOlder(ts, p, h);
1034 mi = rpmdbFreeIterator(mi);
1037 /* XXX multilib should not display "already installed" problems */
1038 if (!(ts->ignoreSet & RPMPROB_FILTER_REPLACEPKG) && !p->multiLib) {
1039 mi = rpmtsInitIterator(ts, RPMTAG_NAME, p->name, 0);
1040 xx = rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_DEFAULT,
1042 xx = rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_DEFAULT,
1045 while (rpmdbNextIterator(mi) != NULL) {
1046 rpmProblemSetAppend(ts->probs, RPMPROB_PKG_INSTALLED,
1050 /*@innerbreak@*/ break;
1052 mi = rpmdbFreeIterator(mi);
1055 /* Count no. of files (if any). */
1057 totalFileCount += p->fns->fc;
1060 pi = teFreeIterator(pi);
1062 /* The ordering doesn't matter here */
1063 pi = teInitIterator(ts);
1064 while ((p = teNext(pi, TR_REMOVED)) != NULL) {
1070 if (fns->bnl == NULL)
1071 continue; /* XXX can't happen */
1072 if (fns->dnl == NULL)
1073 continue; /* XXX can't happen */
1074 if (fns->dil == NULL)
1075 continue; /* XXX can't happen */
1076 totalFileCount += fns->fc;
1078 pi = teFreeIterator(pi);
1080 /* ===============================================
1081 * Initialize transaction element file info for package:
1083 ts->flEntries = ts->numAddedPackages + ts->numRemovedPackages;
1084 ts->flList = xcalloc(ts->flEntries, sizeof(*ts->flList));
1087 * FIXME?: we'd be better off assembling one very large file list and
1088 * calling fpLookupList only once. I'm not sure that the speedup is
1089 * worth the trouble though.
1091 pi = teInitIterator(ts);
1092 while ((p = teNextIterator(pi)) != NULL) {
1095 fi->magic = TFIMAGIC;
1103 /*@i@*/ fi->h = headerLink(p->h, "xfer to fi->h");
1104 p->h = headerFree(p->h, "xfer to fi->h");
1106 /* XXX header arg unused. */
1107 loadFi(ts, fi, fi->h, keep_header);
1112 /* Skip netshared paths, not our i18n files, and excluded docs */
1114 /*@switchbreak@*/ break;
1116 fi->record = p->u.removed.dboffset;
1117 /* Retrieve erased package header from the database. */
1118 { rpmdbMatchIterator mi;
1120 mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES,
1121 &fi->record, sizeof(fi->record));
1122 if ((fi->h = rpmdbNextIterator(mi)) != NULL)
1123 fi->h = headerLink(fi->h, "TR_REMOVED loadFi");
1124 mi = rpmdbFreeIterator(mi);
1126 if (fi->h == NULL) {
1130 /* XXX header arg unused. */
1131 loadFi(ts, fi, fi->h, 0);
1132 /*@switchbreak@*/ break;
1137 fi->fps = xmalloc(fi->fc * sizeof(*fi->fps));
1139 pi = teFreeIterator(pi);
1141 if (!ts->chrootDone) {
1143 /*@-superuser -noeffect @*/
1144 xx = chroot(ts->rootDir);
1145 /*@=superuser =noeffect @*/
1147 if (ts->rpmdb) ts->rpmdb->db_chrootDone = 1;
1150 chroot_prefix = ts->rootDir;
1155 ts->ht = htCreate(totalFileCount * 2, 0, 0, fpHashFunction, fpEqual);
1156 fpc = fpCacheCreate(totalFileCount);
1158 /* ===============================================
1159 * Add fingerprint for each file not skipped.
1161 pi = teInitIterator(ts);
1162 while ((p = teNextIterator(pi)) != NULL) {
1166 fpLookupList(fpc, fi->dnl, fi->bnl, fi->dil, fi->fc, fi->fps);
1168 for (i = 0; i < fi->fc; i++) {
1169 if (XFA_SKIPPING(fi->actions[i]))
1170 /*@innercontinue@*/ continue;
1171 /*@-dependenttrans@*/
1172 htAddEntry(ts->ht, fi->fps + i, fi);
1173 /*@=dependenttrans@*/
1177 pi = teFreeIterator(pi);
1179 /*@-noeffectuncon @*/ /* FIX: check rc */
1180 NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_START, 6, ts->flEntries,
1181 NULL, ts->notifyData));
1182 /*@=noeffectuncon@*/
1184 /* ===============================================
1185 * Compute file disposition for each package in transaction set.
1187 pi = teInitIterator(ts);
1188 while ((p = teNextIterator(pi)) != NULL) {
1189 dbiIndexSet * matches;
1194 /*@-noeffectuncon @*/ /* FIX: check rc */
1195 NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_PROGRESS, (fi - ts->flList),
1196 ts->flEntries, NULL, ts->notifyData));
1197 /*@=noeffectuncon@*/
1199 if (fi->fc == 0) continue;
1201 /* Extract file info for all files in this package from the database. */
1202 matches = xcalloc(fi->fc, sizeof(*matches));
1203 if (rpmdbFindFpList(ts->rpmdb, fi->fps, matches, fi->fc)) {
1204 psm->ts = rpmtsUnlink(ts, "tsRun (rpmFindFpList fail)");
1205 return 1; /* XXX WTFO? */
1209 for (i = 0; i < fi->fc; i++)
1210 numShared += dbiIndexSetCount(matches[i]);
1212 /* Build sorted file info list for this package. */
1213 shared = sharedList = xcalloc((numShared + 1), sizeof(*sharedList));
1214 for (i = 0; i < fi->fc; i++) {
1216 * Take care not to mark files as replaced in packages that will
1217 * have been removed before we will get here.
1219 for (j = 0; j < dbiIndexSetCount(matches[i]); j++) {
1221 ro = dbiIndexRecordOffset(matches[i], j);
1223 qi = teInitIterator(ts);
1224 while ((q = teNext(qi, TR_REMOVED)) != NULL) {
1226 /*@innerbreak@*/ break;
1227 if (q->u.removed.dboffset == ro)
1230 qi = teFreeIterator(qi);
1232 shared->pkgFileNum = i;
1233 shared->otherPkg = dbiIndexRecordOffset(matches[i], j);
1234 shared->otherFileNum = dbiIndexRecordFileNumber(matches[i], j);
1235 shared->isRemoved = (knownBad == ro);
1238 matches[i] = dbiFreeIndexSet(matches[i]);
1240 numShared = shared - sharedList;
1241 shared->otherPkg = -1;
1242 matches = _free(matches);
1244 /* Sort file info by other package index (otherPkg) */
1245 qsort(sharedList, numShared, sizeof(*shared), sharedCmp);
1247 /* For all files from this package that are in the database ... */
1248 for (i = 0; i < numShared; i = nexti) {
1251 shared = sharedList + i;
1253 /* Find the end of the files in the other package. */
1254 for (nexti = i + 1; nexti < numShared; nexti++) {
1255 if (sharedList[nexti].otherPkg != shared->otherPkg)
1256 /*@innerbreak@*/ break;
1259 /* Is this file from a package being removed? */
1261 for (j = 0; j < ts->numRemovedPackages; j++) {
1262 if (ts->removedPackages[j] != shared->otherPkg)
1263 /*@innercontinue@*/ continue;
1265 /*@innerbreak@*/ break;
1268 /* Determine the fate of each file. */
1271 xx = handleInstInstalledFiles(ts, p, fi, shared, nexti - i,
1272 !(beingRemoved || (ts->ignoreSet & RPMPROB_FILTER_REPLACEOLDFILES)));
1273 /*@switchbreak@*/ break;
1276 xx = handleRmvdInstalledFiles(ts, fi, shared, nexti - i);
1277 /*@switchbreak@*/ break;
1283 /* Update disk space needs on each partition for this package. */
1284 handleOverlappedFiles(ts, p, fi);
1286 /* Check added package has sufficient space on each partition used. */
1289 if (!(ts->di && fi->fc))
1290 /*@switchbreak@*/ break;
1291 for (i = 0; i < ts->filesystemCount; i++) {
1295 /* XXX Avoid FAT and other file systems that have not inodes. */
1296 if (dip->iavail <= 0)
1297 /*@innercontinue@*/ continue;
1299 if (adj_fs_blocks(dip->bneeded) > dip->bavail) {
1300 rpmProblemSetAppend(ts->probs, RPMPROB_DISKSPACE,
1302 ts->filesystems[i], NULL, NULL,
1303 (adj_fs_blocks(dip->bneeded) - dip->bavail) * dip->bsize);
1306 if (adj_fs_blocks(dip->ineeded) > dip->iavail) {
1307 rpmProblemSetAppend(ts->probs, RPMPROB_DISKNODES,
1309 ts->filesystems[i], NULL, NULL,
1310 (adj_fs_blocks(dip->ineeded) - dip->iavail));
1313 /*@switchbreak@*/ break;
1315 /*@switchbreak@*/ break;
1318 pi = teFreeIterator(pi);
1320 if (ts->chrootDone) {
1321 /*@-superuser -noeffect @*/
1323 /*@=superuser =noeffect @*/
1325 if (ts->rpmdb) ts->rpmdb->db_chrootDone = 0;
1327 chroot_prefix = NULL;
1329 xx = chdir(ts->currDir);
1332 /*@-noeffectuncon @*/ /* FIX: check rc */
1333 NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_STOP, 6, ts->flEntries,
1334 NULL, ts->notifyData));
1335 /*@=noeffectuncon @*/
1337 /* ===============================================
1338 * Free unused memory as soon as possible.
1341 pi = teInitIterator(ts);
1342 while ((p = teNextIterator(pi)) != NULL) {
1346 fi->fps = _free(fi->fps);
1348 pi = teFreeIterator(pi);
1354 /* ===============================================
1355 * If unfiltered problems exist, free memory and return.
1357 if ((ts->transFlags & RPMTRANS_FLAG_BUILD_PROBS)
1358 || (ts->probs->numProblems &&
1359 (okProbs != NULL || rpmProblemSetTrim(ts->probs, okProbs)))
1362 ts->flList = freeFl(ts, ts->flList);
1364 if (psm->ts != NULL)
1365 psm->ts = rpmtsUnlink(psm->ts, "tsRun (problems)");
1366 /*@-nullstate@*/ /* FIX: ts->flList may be NULL */
1367 return ts->orderCount;
1371 /* ===============================================
1372 * Save removed files before erasing.
1374 if (ts->transFlags & (RPMTRANS_FLAG_DIRSTASH | RPMTRANS_FLAG_REPACKAGE)) {
1375 pi = teInitIterator(ts);
1376 while ((p = teNextIterator(pi)) != NULL) {
1380 /*@switchbreak@*/ break;
1382 if (!(ts->transFlags & RPMTRANS_FLAG_REPACKAGE))
1383 /*@switchbreak@*/ break;
1385 psm->fi = rpmfiLink(fi, "tsRepackage");
1386 xx = psmStage(psm, PSM_PKGSAVE);
1387 (void) rpmfiUnlink(fi, "tsRepackage");
1390 /*@switchbreak@*/ break;
1393 pi = teFreeIterator(pi);
1396 /* ===============================================
1397 * Install and remove packages.
1400 lastKey = (alKey)-2; /* erased packages have -1 */
1401 pi = teInitIterator(ts);
1402 /*@-branchstate@*/ /* FIX: fi reload needs work */
1403 while ((p = teNextIterator(pi)) != NULL) {
1412 psm->fi = rpmfiLink(fi, "tsInstall");
1416 pkgKey = p->u.addedKey;
1418 rpmMessage(RPMMESS_DEBUG, "========== +++ %s\n", p->NEVR);
1419 h = (fi->h ? headerLink(fi->h, "TR_ADDED install") : NULL);
1421 if (p->fd == NULL) {
1422 /*@-noeffectuncon @*/ /* FIX: ??? */
1423 p->fd = ts->notify(fi->h, RPMCALLBACK_INST_OPEN_FILE, 0, 0,
1424 p->key, ts->notifyData);
1425 /*@=noeffectuncon @*/
1426 if (p->fd != NULL) {
1429 h = headerFree(h, "TR_ADDED install");
1431 /*@-mustmod@*/ /* LCL: segfault */
1432 rpmrc = rpmReadPackageFile(ts, p->fd,
1433 "rpmRunTransactions", &h);
1436 if (!(rpmrc == RPMRC_OK || rpmrc == RPMRC_BADSIZE)) {
1437 /*@-noeffectuncon @*/ /* FIX: check rc */
1438 (void) ts->notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE,
1440 p->key, ts->notifyData);
1441 /*@=noeffectuncon @*/
1444 } else if (fi->h != NULL) {
1445 Header foo = relocateFileList(ts, fi, h, NULL);
1446 h = headerFree(h, "TR_ADDED read free");
1447 h = headerLink(foo, "TR_ADDED relocate xfer");
1448 foo = headerFree(foo, "TR_ADDED relocate");
1450 if (p->fd != NULL) gotfd = 1;
1455 if (p->fd != NULL) {
1456 Header hsave = NULL;
1459 hsave = headerLink(fi->h, "TR_ADDED fi->h hsave");
1460 fi->h = headerFree(fi->h, "TR_ADDED fi->h free");
1461 fi->h = headerLink(h, "TR_ADDED fi->h link");
1463 char * fstates = fi->fstates;
1464 fileAction * actions = fi->actions;
1469 fi->magic = TFIMAGIC;
1472 loadFi(ts, fi, h, 1);
1473 fi->fstates = _free(fi->fstates);
1474 fi->fstates = fstates;
1475 fi->actions = _free(fi->actions);
1476 fi->actions = actions;
1480 ts->transFlags |= RPMTRANS_FLAG_MULTILIB;
1482 if (psmStage(psm, PSM_PKGINSTALL)) {
1486 fi->h = headerFree(fi->h, "TR_ADDED fi->h free");
1488 fi->h = headerLink(hsave, "TR_ADDED fi->h restore");
1489 hsave = headerFree(hsave, "TR_ADDED hsave free");
1496 h = headerFree(h, "TR_ADDED h free");
1499 /*@-noeffectuncon @*/ /* FIX: check rc */
1500 (void)ts->notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE, 0, 0,
1501 p->key, ts->notifyData);
1502 /*@=noeffectuncon @*/
1505 fi->h = headerFree(fi->h, "TR_ADDED fini");
1507 /*@switchbreak@*/ break;
1509 rpmMessage(RPMMESS_DEBUG, "========== --- %s\n", p->NEVR);
1510 /* If install failed, then we shouldn't erase. */
1511 if (p->u.removed.dependsOnKey != lastKey) {
1512 if (psmStage(psm, PSM_PKGERASE))
1515 fi->h = headerFree(fi->h, "TR_REMOVED fini");
1517 /*@switchbreak@*/ break;
1519 xx = rpmdbSync(ts->rpmdb);
1520 (void) rpmfiUnlink(fi, "tsInstall");
1525 pi = teFreeIterator(pi);
1527 ts->flList = freeFl(ts, ts->flList);
1530 psm->ts = rpmtsUnlink(psm->ts, "tsRun");
1532 /*@-nullstate@*/ /* FIX: ts->flList may be NULL */