2 * \file lib/transaction.c
7 #define _NEED_TEITERATOR 1
12 #include <rpmmacro.h> /* XXX for rpmExpand */
15 #include "legacy.h" /* XXX mdfile */
16 #include "misc.h" /* XXX stripTrailingChar, splitString, currentDirectory */
19 /*@-redecl -exportheadervar@*/
21 extern const char * chroot_prefix;
22 /*@=redecl =exportheadervar@*/
24 /* XXX FIXME: merge with existing (broken?) tests in system.h */
25 /* portability fiddles */
26 #if STATFS_IN_SYS_STATVFS
28 # include <sys/statvfs.h>
29 #if defined(__LCLINT__)
30 /*@-declundef -exportheader -protoparammatch @*/ /* LCL: missing annotation */
31 extern int statvfs (const char * file, /*@out@*/ struct statvfs * buf)
32 /*@globals fileSystem @*/
33 /*@modifies *buf, fileSystem @*/;
34 /*@=declundef =exportheader =protoparammatch @*/
38 # if STATFS_IN_SYS_VFS
41 # if STATFS_IN_SYS_MOUNT
42 # include <sys/mount.h>
44 # if STATFS_IN_SYS_STATFS
45 # include <sys/statfs.h>
53 /*@access FD_t @*/ /* XXX compared with NULL */
54 /*@access Header @*/ /* XXX compared with NULL */
55 /*@access rpmProblemSet @*/ /* XXX need rpmProblemSetOK() */
56 /*@access dbiIndexSet @*/
64 /*@access rpmDepSet @*/
65 /*@access rpmFNSet @*/
68 /*@access teIterator @*/
69 /*@access transactionElement @*/
70 /*@access rpmTransactionSet @*/
74 struct diskspaceInfo {
75 dev_t dev; /*!< File system device number. */
76 signed long bneeded; /*!< No. of blocks needed. */
77 signed long ineeded; /*!< No. of inodes needed. */
78 int bsize; /*!< File system block size. */
79 signed long bavail; /*!< No. of blocks available. */
80 signed long iavail; /*!< No. of inodes available. */
84 * Adjust for root only reserved space. On linux e2fs, this is 5%.
86 #define adj_fs_blocks(_nb) (((_nb) * 21) / 20)
88 /* argon thought a shift optimization here was a waste of time... he's
90 #define BLOCK_ROUND(size, block) (((size) + (block) - 1) / (block))
94 static /*@null@*/ void * freeFl(rpmTransactionSet ts,
95 /*@only@*/ /*@null@*/ TFI_t flList)
102 /*@-usereleased -onlytrans @*/ /* FIX: fi needs to be only */
103 for (oc = 0, fi = flList; oc < ts->orderCount; oc++, fi++)
105 flList = _free(flList);
106 /*@=usereleased =onlytrans @*/
111 void rpmtransSetScriptFd(rpmTransactionSet ts, FD_t fd)
113 /*@-type@*/ /* FIX: cast? */
114 ts->scriptFd = (fd ? fdLink(fd, "rpmtransSetScriptFd") : NULL);
118 int rpmtransGetKeys(const rpmTransactionSet ts, fnpyKey ** ep, int * nep)
122 if (nep) *nep = ts->orderCount;
127 *ep = e = xmalloc(ts->orderCount * sizeof(*e));
128 for (oc = 0; oc < ts->orderCount; oc++, e++) {
129 switch (ts->order[oc].type) {
131 *e = ts->order[oc].key;
132 /*@switchbreak@*/ break;
135 /*@-mods@*/ /* FIX: double indirection. */
138 /*@switchbreak@*/ break;
147 static int archOkay(/*@null@*/ const char * pkgArch)
150 if (pkgArch == NULL) return 0;
151 return (rpmMachineScore(RPM_MACHTABLE_INSTARCH, pkgArch) ? 1 : 0);
156 static int osOkay(/*@null@*/ const char * pkgOs)
159 if (pkgOs == NULL) return 0;
160 return (rpmMachineScore(RPM_MACHTABLE_INSTOS, pkgOs) ? 1 : 0);
165 static int sharedCmp(const void * one, const void * two)
168 const struct sharedFileInfo * a = one;
169 const struct sharedFileInfo * b = two;
171 if (a->otherPkg < b->otherPkg)
173 else if (a->otherPkg > b->otherPkg)
181 static fileAction decideFileFate(const char * dirName,
182 const char * baseName, short dbMode,
183 const char * dbMd5, const char * dbLink, short newMode,
184 const char * newMd5, const char * newLink, int newFlags,
185 rpmtransFlags transFlags)
186 /*@globals fileSystem @*/
187 /*@modifies fileSystem @*/
190 const char * dbAttr, * newAttr;
191 fileTypes dbWhat, newWhat, diskWhat;
194 int save = (newFlags & RPMFILE_NOREPLACE) ? FA_ALTNAME : FA_SAVE;
195 char * filespec = alloca(strlen(dirName) + strlen(baseName) + 1);
197 (void) stpcpy( stpcpy(filespec, dirName), baseName);
199 if (lstat(filespec, &sb)) {
201 * The file doesn't exist on the disk. Create it unless the new
202 * package has marked it as missingok, or allfiles is requested.
204 if (!(transFlags & RPMTRANS_FLAG_ALLFILES) &&
205 (newFlags & RPMFILE_MISSINGOK)) {
206 rpmMessage(RPMMESS_DEBUG, _("%s skipped due to missingok flag\n"),
214 diskWhat = whatis(sb.st_mode);
215 dbWhat = whatis(dbMode);
216 newWhat = whatis(newMode);
218 /* RPM >= 2.3.10 shouldn't create config directories -- we'll ignore
219 them in older packages as well */
220 if (newWhat == XDIR) {
224 if (diskWhat != newWhat) {
226 } else if (newWhat != dbWhat && diskWhat != dbWhat) {
228 } else if (dbWhat != newWhat) {
230 } else if (dbWhat != LINK && dbWhat != REG) {
235 rc = mdfile(filespec, buffer);
238 /* assume the file has been removed, don't freak */
243 } else /* dbWhat == LINK */ {
244 memset(buffer, 0, sizeof(buffer));
245 i = readlink(filespec, buffer, sizeof(buffer) - 1);
247 /* assume the file has been removed, don't freak */
254 /* this order matters - we'd prefer to CREATE the file if at all
255 possible in case something else (like the timestamp) has changed */
257 if (!strcmp(dbAttr, buffer)) {
258 /* this config file has never been modified, so just replace it */
262 if (!strcmp(dbAttr, newAttr)) {
263 /* this file is the same in all versions of this package */
268 * The config file on the disk has been modified, but
269 * the ones in the two packages are different. It would
270 * be nice if RPM was smart enough to at least try and
271 * merge the difference ala CVS, but...
278 static int filecmp(short mode1, const char * md51, const char * link1,
279 short mode2, const char * md52, const char * link2)
282 fileTypes what1 = whatis(mode1);
283 fileTypes what2 = whatis(mode2);
285 if (what1 != what2) return 1;
288 return strcmp(link1, link2);
289 else if (what1 == REG)
290 return strcmp(md51, md52);
297 /* XXX only ts->{probs,rpmdb} modified */
298 static int handleInstInstalledFiles(const rpmTransactionSet ts, TFI_t fi,
299 struct sharedFileInfo * shared,
300 int sharedCount, int reportConflicts)
301 /*@globals fileSystem @*/
302 /*@modifies ts, fi, fileSystem @*/
305 HFD_t hfd = (fi->hfd ? fi->hfd : headerFreeData);
306 rpmtransFlags transFlags = ts->transFlags;
307 rpmTagType oltype, omtype;
310 const char ** otherMd5s;
311 const char ** otherLinks;
312 const char * otherStates;
313 uint_32 * otherFlags;
314 uint_32 * otherSizes;
315 uint_16 * otherModes;
319 rpmdbMatchIterator mi;
321 mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES,
322 &shared->otherPkg, sizeof(shared->otherPkg));
323 h = rpmdbNextIterator(mi);
325 mi = rpmdbFreeIterator(mi);
329 xx = hge(h, RPMTAG_FILEMD5S, &omtype, (void **) &otherMd5s, NULL);
330 xx = hge(h, RPMTAG_FILELINKTOS, &oltype, (void **) &otherLinks, NULL);
331 xx = hge(h, RPMTAG_FILESTATES, NULL, (void **) &otherStates, NULL);
332 xx = hge(h, RPMTAG_FILEMODES, NULL, (void **) &otherModes, NULL);
333 xx = hge(h, RPMTAG_FILEFLAGS, NULL, (void **) &otherFlags, NULL);
334 xx = hge(h, RPMTAG_FILESIZES, NULL, (void **) &otherSizes, NULL);
336 fi->replaced = xmalloc(sharedCount * sizeof(*fi->replaced));
338 for (i = 0; i < sharedCount; i++, shared++) {
339 int otherFileNum, fileNum;
340 otherFileNum = shared->otherFileNum;
341 fileNum = shared->pkgFileNum;
343 /* XXX another tedious segfault, assume file state normal. */
344 if (otherStates && otherStates[otherFileNum] != RPMFILE_STATE_NORMAL)
347 if (XFA_SKIPPING(fi->actions[fileNum]))
350 if (filecmp(otherModes[otherFileNum],
351 otherMd5s[otherFileNum],
352 otherLinks[otherFileNum],
355 fi->flinks[fileNum])) {
356 /*@-compdef@*/ /* FIX: *fi->replaced undefined */
357 if (reportConflicts) {
358 const char * pkgNEVR = fiGetNEVR(fi);
359 const char * altNEVR = hGetNEVR(h, NULL);
360 rpmProblemSetAppend(ts->probs, RPMPROB_FILE_CONFLICT,
362 fi->dnl[fi->dil[fileNum]], fi->bnl[fileNum],
365 pkgNEVR = _free(pkgNEVR);
366 altNEVR = _free(altNEVR);
369 if (!(otherFlags[otherFileNum] | fi->fflags[fileNum])
372 if (!shared->isRemoved)
373 fi->replaced[numReplaced++] = *shared;
378 if ((otherFlags[otherFileNum] | fi->fflags[fileNum]) & RPMFILE_CONFIG) {
379 fi->actions[fileNum] = decideFileFate(
380 fi->dnl[fi->dil[fileNum]],
382 otherModes[otherFileNum],
383 otherMd5s[otherFileNum],
384 otherLinks[otherFileNum],
392 fi->replacedSizes[fileNum] = otherSizes[otherFileNum];
395 otherMd5s = hfd(otherMd5s, omtype);
396 otherLinks = hfd(otherLinks, oltype);
397 mi = rpmdbFreeIterator(mi);
399 fi->replaced = xrealloc(fi->replaced, /* XXX memory leak */
400 sizeof(*fi->replaced) * (numReplaced + 1));
401 fi->replaced[numReplaced].otherPkg = 0;
408 /* XXX only ts->rpmdb modified */
409 static int handleRmvdInstalledFiles(const rpmTransactionSet ts, TFI_t fi,
410 struct sharedFileInfo * shared, int sharedCount)
411 /*@globals fileSystem @*/
412 /*@modifies fi, fileSystem @*/
416 const char * otherStates;
419 rpmdbMatchIterator mi;
421 mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES,
422 &shared->otherPkg, sizeof(shared->otherPkg));
423 h = rpmdbNextIterator(mi);
425 mi = rpmdbFreeIterator(mi);
429 xx = hge(h, RPMTAG_FILESTATES, NULL, (void **) &otherStates, NULL);
431 for (i = 0; i < sharedCount; i++, shared++) {
432 int otherFileNum, fileNum;
433 otherFileNum = shared->otherFileNum;
434 fileNum = shared->pkgFileNum;
436 if (otherStates[otherFileNum] != RPMFILE_STATE_NORMAL)
439 fi->actions[fileNum] = FA_SKIP;
442 mi = rpmdbFreeIterator(mi);
448 * Update disk space needs on each partition for this package.
450 /* XXX only ts->{probs,di} modified */
451 static void handleOverlappedFiles(const rpmTransactionSet ts, TFI_t fi)
452 /*@globals fileSystem @*/
453 /*@modifies ts, fi, fileSystem @*/
455 struct diskspaceInfo * ds = NULL;
456 uint_32 fixupSize = 0;
457 char * filespec = NULL;
458 int fileSpecAlloced = 0;
461 for (i = 0; i < fi->fc; i++) {
462 int otherPkgNum, otherFileNum;
466 if (XFA_SKIPPING(fi->actions[i]))
469 j = strlen(fi->dnl[fi->dil[i]]) + strlen(fi->bnl[i]) + 1;
471 if (j > fileSpecAlloced) {
472 fileSpecAlloced = j * 2;
473 filespec = xrealloc(filespec, fileSpecAlloced);
477 (void) stpcpy( stpcpy( filespec, fi->dnl[fi->dil[i]]), fi->bnl[i]);
481 while (ds->bsize && ds->dev != fi->fps[i].entry->dev) ds++;
482 if (!ds->bsize) ds = NULL;
487 * Retrieve all records that apply to this file. Note that the
488 * file info records were built in the same order as the packages
489 * will be installed and removed so the records for an overlapped
490 * files will be sorted in exactly the same order.
492 (void) htGetEntry(ts->ht, &fi->fps[i],
493 (const void ***) &recs, &numRecs, NULL);
496 * If this package is being added, look only at other packages
497 * being added -- removed packages dance to a different tune.
498 * If both this and the other package are being added, overlapped
499 * files must be identical (or marked as a conflict). The
500 * disposition of already installed config files leads to
501 * a small amount of extra complexity.
503 * If this package is being removed, then there are two cases that
504 * need to be worried about:
505 * If the other package is being added, then skip any overlapped files
506 * so that this package removal doesn't nuke the overlapped files
507 * that were just installed.
508 * If both this and the other package are being removed, then each
509 * file removal from preceding packages needs to be skipped so that
510 * the file removal occurs only on the last occurence of an overlapped
511 * file in the transaction set.
515 /* Locate this overlapped file in the set of added/removed packages. */
516 for (j = 0; j < numRecs && recs[j] != fi; j++)
519 /* Find what the previous disposition of this file was. */
520 otherFileNum = -1; /* keep gcc quiet */
521 for (otherPkgNum = j - 1; otherPkgNum >= 0; otherPkgNum--) {
522 /* Added packages need only look at other added packages. */
523 if (fi->type == TR_ADDED && recs[otherPkgNum]->type != TR_ADDED)
524 /*@innercontinue@*/ continue;
526 /* TESTME: there are more efficient searches in the world... */
527 for (otherFileNum = 0; otherFileNum < recs[otherPkgNum]->fc;
530 /* If the addresses are the same, so are the values. */
531 if ((fi->fps + i) == (recs[otherPkgNum]->fps + otherFileNum))
532 /*@innerbreak@*/ break;
534 /* Otherwise, compare fingerprints by value. */
535 /*@-nullpass@*/ /* LCL: looks good to me */
536 if (FP_EQUAL(fi->fps[i], recs[otherPkgNum]->fps[otherFileNum]))
537 /*@innerbreak@*/ break;
541 /* XXX is this test still necessary? */
542 if (recs[otherPkgNum]->actions[otherFileNum] != FA_UNKNOWN)
543 /*@innerbreak@*/ break;
549 if (otherPkgNum < 0) {
550 /* XXX is this test still necessary? */
551 if (fi->actions[i] != FA_UNKNOWN)
552 /*@switchbreak@*/ break;
553 if ((fi->fflags[i] & RPMFILE_CONFIG) &&
554 !lstat(filespec, &sb)) {
555 /* Here is a non-overlapped pre-existing config file. */
556 fi->actions[i] = (fi->fflags[i] & RPMFILE_NOREPLACE)
557 ? FA_ALTNAME : FA_BACKUP;
559 fi->actions[i] = FA_CREATE;
561 /*@switchbreak@*/ break;
564 /* Mark added overlapped non-identical files as a conflict. */
565 if ((ts->ignoreSet & RPMPROB_FILTER_REPLACENEWFILES)
566 && filecmp(recs[otherPkgNum]->fmodes[otherFileNum],
567 recs[otherPkgNum]->fmd5s[otherFileNum],
568 recs[otherPkgNum]->flinks[otherFileNum],
573 const char * pkgNEVR = fiGetNEVR(fi);
574 const char * altNEVR = fiGetNEVR(recs[otherPkgNum]);
575 rpmProblemSetAppend(ts->probs, RPMPROB_NEW_FILE_CONFLICT,
580 pkgNEVR = _free(pkgNEVR);
581 altNEVR = _free(altNEVR);
584 /* Try to get the disk accounting correct even if a conflict. */
585 fixupSize = recs[otherPkgNum]->fsizes[otherFileNum];
587 if ((fi->fflags[i] & RPMFILE_CONFIG) && !lstat(filespec, &sb)) {
588 /* Here is an overlapped pre-existing config file. */
589 fi->actions[i] = (fi->fflags[i] & RPMFILE_NOREPLACE)
590 ? FA_ALTNAME : FA_SKIP;
592 fi->actions[i] = FA_CREATE;
594 } /*@switchbreak@*/ break;
596 if (otherPkgNum >= 0) {
597 /* Here is an overlapped added file we don't want to nuke. */
598 if (recs[otherPkgNum]->actions[otherFileNum] != FA_ERASE) {
599 /* On updates, don't remove files. */
600 fi->actions[i] = FA_SKIP;
601 /*@switchbreak@*/ break;
603 /* Here is an overlapped removed file: skip in previous. */
604 recs[otherPkgNum]->actions[otherFileNum] = FA_SKIP;
606 if (XFA_SKIPPING(fi->actions[i]))
607 /*@switchbreak@*/ break;
608 if (fi->fstates && fi->fstates[i] != RPMFILE_STATE_NORMAL)
609 /*@switchbreak@*/ break;
610 if (!(S_ISREG(fi->fmodes[i]) && (fi->fflags[i] & RPMFILE_CONFIG))) {
611 fi->actions[i] = FA_ERASE;
612 /*@switchbreak@*/ break;
615 /* Here is a pre-existing modified config file that needs saving. */
617 if (!mdfile(filespec, mdsum) && strcmp(fi->fmd5s[i], mdsum)) {
618 fi->actions[i] = FA_BACKUP;
619 /*@switchbreak@*/ break;
622 fi->actions[i] = FA_ERASE;
623 /*@switchbreak@*/ break;
627 uint_32 s = BLOCK_ROUND(fi->fsizes[i], ds->bsize);
629 switch (fi->actions[i]) {
635 /*@switchbreak@*/ break;
638 * FIXME: If two packages share a file (same md5sum), and
639 * that file is being replaced on disk, will ds->bneeded get
640 * decremented twice? Quite probably!
644 ds->bneeded -= BLOCK_ROUND(fi->replacedSizes[i], ds->bsize);
645 /*@switchbreak@*/ break;
650 /*@switchbreak@*/ break;
653 /*@switchbreak@*/ break;
656 ds->bneeded -= BLOCK_ROUND(fixupSize, ds->bsize);
659 filespec = _free(filespec);
663 * Ensure that current package is newer than installed package.
664 * @param ts transaction set
665 * @param p current transaction element
666 * @param h installed header
667 * @return 0 if not newer, 1 if okay
670 static int ensureOlder(rpmTransactionSet ts,
671 const Header h, /*@null@*/ const Header old,
672 /*@dependent@*/ /*@null@*/ const void * key)
677 if (old == NULL) return 1;
679 result = rpmVersionCompare(old, h);
682 else if (result > 0) {
683 const char * pkgNEVR = hGetNEVR(h, NULL);
684 const char * altNEVR = hGetNEVR(old, NULL);
685 /*@-evalorder@*/ /* LCL: is confused */
686 rpmProblemSetAppend(ts->probs, RPMPROB_OLDPACKAGE,
692 pkgNEVR = _free(pkgNEVR);
693 altNEVR = _free(altNEVR);
700 static int ensureOlder(rpmTransactionSet ts, transactionElement p, Header h)
703 rpmDepSet req = memset(alloca(sizeof(*req)), 0, sizeof(*req));
705 int_32 reqFlags = (RPMSENSE_LESS | RPMSENSE_EQUAL);
709 if (p == NULL || h == NULL)
712 t = alloca(strlen(p->NEVR) + (p->epoch != NULL ? strlen(p->epoch) : 0) + 1);
715 if (p->epoch != NULL) t = stpcpy( stpcpy(t, p->epoch), ":");
716 if (p->version != NULL) t = stpcpy(t, p->version);
718 if (p->release != NULL) t = stpcpy(t, p->release);
722 req->Type = "Requires";
723 req->tagN = RPMTAG_REQUIRENAME;
725 /*@-immediatetrans@*/
727 req->N = (const char **) &p->name;
730 req->Flags = &reqFlags;
731 /*@=immediatetrans@*/
733 (void) dsiNext(dsiInit(req));
735 rc = headerMatchesDepFlags(h, req);
737 req->DNEVR = _free(req->DNEVR);
740 /*@-branchstate@*/ /* FIX: p->key ??? */
742 const char * altNEVR = hGetNEVR(h, NULL);
743 rpmProblemSetAppend(ts->probs, RPMPROB_OLDPACKAGE,
748 altNEVR = _free(altNEVR);
760 static void skipFiles(const rpmTransactionSet ts, TFI_t fi)
761 /*@globals rpmGlobalMacroContext @*/
762 /*@modifies fi, rpmGlobalMacroContext @*/
764 int noDocs = (ts->transFlags & RPMTRANS_FLAG_NODOCS);
765 char ** netsharedPaths = NULL;
766 const char ** languages;
767 const char * dn, * bn;
768 int dnlen, bnlen, ix;
775 noDocs = rpmExpandNumeric("%{_excludedocs}");
777 { const char *tmpPath = rpmExpand("%{_netsharedpath}", NULL);
779 if (tmpPath && *tmpPath != '%')
780 netsharedPaths = splitString(tmpPath, strlen(tmpPath), ':');
782 tmpPath = _free(tmpPath);
785 s = rpmExpand("%{_install_langs}", NULL);
787 if (!(s && *s != '%'))
790 languages = (const char **) splitString(s, strlen(s), ':');
796 /* Compute directory refcount, skip directory if now empty. */
797 drc = alloca(fi->dc * sizeof(*drc));
798 memset(drc, 0, fi->dc * sizeof(*drc));
799 dff = alloca(fi->dc * sizeof(*dff));
800 memset(dff, 0, fi->dc * sizeof(*dff));
802 for (i = 0; i < fi->fc; i++) {
813 /* Don't bother with skipped files */
814 if (XFA_SKIPPING(fi->actions[i])) {
820 * Skip net shared paths.
821 * Net shared paths are not relative to the current root (though
822 * they do need to take package relocations into account).
824 for (nsp = netsharedPaths; nsp && *nsp; nsp++) {
829 if (strncmp(dn, *nsp, len))
830 /*@innercontinue@*/ continue;
831 /* Only directories or complete file paths can be net shared */
832 if (!(dn[len] == '/' || dn[len] == '\0'))
833 /*@innercontinue@*/ continue;
835 if (len < (dnlen + bnlen))
836 /*@innercontinue@*/ continue;
837 if (strncmp(dn, *nsp, dnlen))
838 /*@innercontinue@*/ continue;
839 if (strncmp(bn, (*nsp) + dnlen, bnlen))
840 /*@innercontinue@*/ continue;
842 /* Only directories or complete file paths can be net shared */
843 if (!((*nsp)[len] == '/' || (*nsp)[len] == '\0'))
844 /*@innercontinue@*/ continue;
847 /*@innerbreak@*/ break;
851 drc[ix]--; dff[ix] = 1;
852 fi->actions[i] = FA_SKIPNETSHARED;
857 * Skip i18n language specific files.
859 if (fi->flangs && languages && *fi->flangs[i]) {
860 const char **lang, *l, *le;
861 for (lang = languages; *lang != NULL; lang++) {
862 if (!strcmp(*lang, "all"))
863 /*@innerbreak@*/ break;
864 for (l = fi->flangs[i]; *l != '\0'; l = le) {
865 for (le = l; *le != '\0' && *le != '|'; le++)
867 if ((le-l) > 0 && !strncmp(*lang, l, (le-l)))
868 /*@innerbreak@*/ break;
869 if (*le == '|') le++; /* skip over | */
872 /*@innerbreak@*/ break;
875 drc[ix]--; dff[ix] = 1;
876 fi->actions[i] = FA_SKIPNSTATE;
882 * Skip documentation if requested.
884 if (noDocs && (fi->fflags[i] & RPMFILE_DOC)) {
885 drc[ix]--; dff[ix] = 1;
886 fi->actions[i] = FA_SKIPNSTATE;
891 /* Skip (now empty) directories that had skipped files. */
892 for (j = 0; j < fi->dc; j++) {
894 if (drc[j]) continue; /* dir still has files. */
895 if (!dff[j]) continue; /* dir was not emptied here. */
897 /* Find parent directory and basename. */
898 dn = fi->dnl[j]; dnlen = strlen(dn) - 1;
899 bn = dn + dnlen; bnlen = 0;
900 while (bn > dn && bn[-1] != '/') {
906 /* If explicitly included in the package, skip the directory. */
907 for (i = 0; i < fi->fc; i++) {
910 if (XFA_SKIPPING(fi->actions[i]))
911 /*@innercontinue@*/ continue;
912 if (whatis(fi->fmodes[i]) != XDIR)
913 /*@innercontinue@*/ continue;
914 dir = fi->dnl[fi->dil[i]];
915 if (strlen(dir) != dnlen)
916 /*@innercontinue@*/ continue;
917 if (strncmp(dir, dn, dnlen))
918 /*@innercontinue@*/ continue;
919 if (strlen(fi->bnl[i]) != bnlen)
920 /*@innercontinue@*/ continue;
921 if (strncmp(fi->bnl[i], bn, bnlen))
922 /*@innercontinue@*/ continue;
923 rpmMessage(RPMMESS_DEBUG, _("excluding directory %s\n"), dn);
924 fi->actions[i] = FA_SKIPNSTATE;
925 /*@innerbreak@*/ break;
929 if (netsharedPaths) freeSplitString(netsharedPaths);
930 #ifdef DYING /* XXX freeFi will deal with this later. */
931 fi->flangs = _free(fi->flangs);
933 if (languages) freeSplitString((char **)languages);
937 * Return next transaction element's file info.
938 * @param tei transaction element iterator
939 * @return nest transaction element file info, NULL on termination
941 /*@unused@*/ static inline
942 TFI_t teNextFi(teIterator tei)
947 if (teNextIterator(tei) != NULL && tei->ocsave != -1)
948 fi = tei->ts->flList + tei->ocsave;
949 /*@-compdef -onlytrans -usereleased@*/ /* FIX: ts->flList may be released */
951 /*@=compdef =onlytrans =usereleased@*/
954 #define NOTIFY(_ts, _al) if ((_ts)->notify) (void) (_ts)->notify _al
956 int rpmRunTransactions( rpmTransactionSet ts,
957 rpmCallbackFunction notify, rpmCallbackData notifyData,
958 rpmProblemSet okProbs, rpmProblemSet * newProbs,
959 rpmtransFlags transFlags, rpmprobFilterFlags ignoreSet)
963 int totalFileCount = 0;
965 struct diskspaceInfo * dip;
966 struct sharedFileInfo * shared, * sharedList;
969 alKey pkgKey, lastKey;
971 transactionElement p;
972 fingerPrintCache fpc;
978 int keep_header = 1; /* XXX rpmProblemSetAppend prevents dumping headers. */
980 /* FIXME: what if the same package is included in ts twice? */
982 ts->transFlags = transFlags;
983 if (ts->transFlags & RPMTRANS_FLAG_NOSCRIPTS)
984 ts->transFlags |= (_noTransScripts | _noTransTriggers);
985 if (ts->transFlags & RPMTRANS_FLAG_NOTRIGGERS)
986 ts->transFlags |= _noTransTriggers;
988 /* XXX MULTILIB is broken, as packages can and do execute /sbin/ldconfig. */
989 if (ts->transFlags & (RPMTRANS_FLAG_JUSTDB | RPMTRANS_FLAG_MULTILIB))
990 ts->transFlags |= (_noTransScripts | _noTransTriggers);
993 ts->notifyData = notifyData;
995 ts->probs = *newProbs = rpmProblemSetCreate();
996 *newProbs = rpmpsLink(ts->probs, "RunTransactions");
998 ts->ignoreSet = ignoreSet;
999 ts->currDir = _free(ts->currDir);
1000 ts->currDir = currentDirectory();
1002 if (ts->rpmdb) ts->rpmdb->db_chrootDone = 0;
1003 ts->id = (int_32) time(NULL);
1005 memset(psm, 0, sizeof(*psm));
1007 psm->ts = rpmtsLink(ts, "tsRun");
1010 /* Get available space on mounted file systems. */
1011 if (!(ts->ignoreSet & RPMPROB_FILTER_DISKSPACE) &&
1012 !rpmGetFilesystemList(&ts->filesystems, &ts->filesystemCount)) {
1015 ts->di = _free(ts->di);
1016 dip = ts->di = xcalloc((ts->filesystemCount + 1), sizeof(*ts->di));
1018 for (i = 0; (i < ts->filesystemCount) && dip; i++) {
1019 #if STATFS_IN_SYS_STATVFS
1021 memset(&sfb, 0, sizeof(sfb));
1022 if (statvfs(ts->filesystems[i], &sfb))
1026 /* This platform has the 4-argument version of the statfs call. The last two
1027 * should be the size of struct statfs and 0, respectively. The 0 is the
1028 * filesystem type, and is always 0 when statfs is called on a mounted
1029 * filesystem, as we're doing.
1031 memset(&sfb, 0, sizeof(sfb));
1032 if (statfs(ts->filesystems[i], &sfb, sizeof(sfb), 0))
1034 memset(&sfb, 0, sizeof(sfb));
1035 if (statfs(ts->filesystems[i], &sfb))
1041 ts->di[i].bsize = sfb.f_bsize;
1042 ts->di[i].bneeded = 0;
1043 ts->di[i].ineeded = 0;
1044 #ifdef STATFS_HAS_F_BAVAIL
1045 ts->di[i].bavail = sfb.f_bavail;
1047 /* FIXME: the statfs struct doesn't have a member to tell how many blocks are
1048 * available for non-superusers. f_blocks - f_bfree is probably too big, but
1049 * it's about all we can do.
1051 ts->di[i].bavail = sfb.f_blocks - sfb.f_bfree;
1053 /* XXX Avoid FAT and other file systems that have not inodes. */
1054 ts->di[i].iavail = !(sfb.f_ffree == 0 && sfb.f_files == 0)
1057 xx = stat(ts->filesystems[i], &sb);
1058 ts->di[i].dev = sb.st_dev;
1062 if (dip) ts->di[i].bsize = 0;
1065 /* ===============================================
1066 * For packages being installed:
1067 * - verify package arch/os.
1068 * - verify package epoch:version-release is newer.
1070 * For packages being removed:
1073 /* The ordering doesn't matter here */
1074 tei = teInitIterator(ts);
1075 while ((p = teNext(tei, TR_ADDED)) != NULL) {
1076 rpmdbMatchIterator mi;
1078 pkgKey = p->u.addedKey;
1080 /*@-branchstate@*/ /* FIX: p->key ??? */
1081 if (!archOkay(p->arch) && !(ts->ignoreSet & RPMPROB_FILTER_IGNOREARCH))
1082 rpmProblemSetAppend(ts->probs, RPMPROB_BADARCH,
1087 if (!osOkay(p->os) && !(ts->ignoreSet & RPMPROB_FILTER_IGNOREOS))
1088 rpmProblemSetAppend(ts->probs, RPMPROB_BADOS,
1094 if (!(ts->ignoreSet & RPMPROB_FILTER_OLDPACKAGE)) {
1096 mi = rpmtsInitIterator(ts, RPMTAG_NAME, p->name, 0);
1097 while ((h = rpmdbNextIterator(mi)) != NULL)
1098 xx = ensureOlder(ts, p, h);
1099 mi = rpmdbFreeIterator(mi);
1102 /* XXX multilib should not display "already installed" problems */
1103 if (!(ts->ignoreSet & RPMPROB_FILTER_REPLACEPKG)
1104 #ifdef DYING /* XXX MULTILIB multiLib from transactionElement */
1105 && !alGetMultiLib(ts->addedPackages, i)
1108 mi = rpmtsInitIterator(ts, RPMTAG_NAME, p->name, 0);
1109 xx = rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_DEFAULT,
1111 xx = rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_DEFAULT,
1114 while (rpmdbNextIterator(mi) != NULL) {
1115 rpmProblemSetAppend(ts->probs, RPMPROB_PKG_INSTALLED,
1119 /*@innerbreak@*/ break;
1121 mi = rpmdbFreeIterator(mi);
1124 /* Count no. of files (if any). */
1126 totalFileCount += p->fns->fc;
1129 tei = teFreeIterator(tei);
1131 /* FIXME: it seems a bit silly to read in all of these headers twice */
1132 /* The ordering doesn't matter here */
1133 if (ts->numRemovedPackages > 0) {
1134 rpmdbMatchIterator mi;
1138 mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES, NULL, 0);
1139 xx = rpmdbAppendIterator(mi, ts->removedPackages, ts->numRemovedPackages);
1140 while ((h = rpmdbNextIterator(mi)) != NULL) {
1141 if (headerGetEntry(h, RPMTAG_BASENAMES, NULL, NULL, &fileCount))
1142 totalFileCount += fileCount;
1144 mi = rpmdbFreeIterator(mi);
1147 /* ===============================================
1148 * Initialize transaction element file info for package:
1150 ts->flEntries = ts->numAddedPackages + ts->numRemovedPackages;
1151 ts->flList = xcalloc(ts->flEntries, sizeof(*ts->flList));
1154 * FIXME?: we'd be better off assembling one very large file list and
1155 * calling fpLookupList only once. I'm not sure that the speedup is
1156 * worth the trouble though.
1158 tei = teInitIterator(ts);
1159 while ((fi = teNextFi(tei)) != NULL) {
1162 fi->magic = TFIMAGIC;
1164 fi->type = ts->order[oc].type;
1171 pkgKey = ts->order[oc].u.addedKey;
1173 fi->h = alGetHeader(ts->addedPackages, pkgKey, 1);
1174 #ifdef DYING /* XXX MULTILIB multiLib from transactionElement */
1175 fi->multiLib = alGetMultiLib(ts->addedPackages, i);
1177 fi->multiLib = ts->order[oc].multiLib;
1180 /*@i@*/ fi->key = ts->order[oc].key;
1181 fi->relocs = ts->order[oc].relocs;
1182 /*@i@*/ fi->fd = ts->order[oc].fd;
1184 /* XXX availablePackage can be dumped here XXX */
1186 /* XXX header arg unused. */
1187 loadFi(ts, fi, fi->h, keep_header);
1192 /* Skip netshared paths, not our i18n files, and excluded docs */
1194 /*@switchbreak@*/ break;
1196 fi->record = ts->order[oc].u.removed.dboffset;
1197 /* Retrieve erased package header from the database. */
1198 { rpmdbMatchIterator mi;
1200 mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES,
1201 &fi->record, sizeof(fi->record));
1202 if ((fi->h = rpmdbNextIterator(mi)) != NULL)
1203 fi->h = headerLink(fi->h, "TR_REMOVED loadFi");
1204 mi = rpmdbFreeIterator(mi);
1206 if (fi->h == NULL) {
1210 /* XXX header arg unused. */
1211 loadFi(ts, fi, fi->h, 0);
1212 /*@switchbreak@*/ break;
1217 fi->fps = xmalloc(fi->fc * sizeof(*fi->fps));
1219 tei = teFreeIterator(tei);
1221 if (!ts->chrootDone) {
1223 /*@-superuser -noeffect @*/
1224 xx = chroot(ts->rootDir);
1225 /*@=superuser =noeffect @*/
1227 if (ts->rpmdb) ts->rpmdb->db_chrootDone = 1;
1230 chroot_prefix = ts->rootDir;
1235 ts->ht = htCreate(totalFileCount * 2, 0, 0, fpHashFunction, fpEqual);
1236 fpc = fpCacheCreate(totalFileCount);
1238 /* ===============================================
1239 * Add fingerprint for each file not skipped.
1241 tei = teInitIterator(ts);
1242 while ((fi = teNextFi(tei)) != NULL) {
1243 fpLookupList(fpc, fi->dnl, fi->bnl, fi->dil, fi->fc, fi->fps);
1244 for (i = 0; i < fi->fc; i++) {
1245 if (XFA_SKIPPING(fi->actions[i]))
1246 /*@innercontinue@*/ continue;
1247 /*@-dependenttrans@*/
1248 htAddEntry(ts->ht, fi->fps + i, fi);
1249 /*@=dependenttrans@*/
1252 tei = teFreeIterator(tei);
1254 /*@-noeffectuncon @*/ /* FIX: check rc */
1255 NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_START, 6, ts->flEntries,
1256 NULL, ts->notifyData));
1257 /*@=noeffectuncon@*/
1259 /* ===============================================
1260 * Compute file disposition for each package in transaction set.
1262 tei = teInitIterator(ts);
1263 while ((fi = teNextFi(tei)) != NULL) {
1264 dbiIndexSet * matches;
1267 /*@-noeffectuncon @*/ /* FIX: check rc */
1268 NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_PROGRESS, (fi - ts->flList),
1269 ts->flEntries, NULL, ts->notifyData));
1270 /*@=noeffectuncon@*/
1272 if (fi->fc == 0) continue;
1274 /* Extract file info for all files in this package from the database. */
1275 matches = xcalloc(fi->fc, sizeof(*matches));
1276 if (rpmdbFindFpList(ts->rpmdb, fi->fps, matches, fi->fc)) {
1277 psm->ts = rpmtsUnlink(ts, "tsRun (rpmFindFpList fail)");
1278 return 1; /* XXX WTFO? */
1282 for (i = 0; i < fi->fc; i++)
1283 numShared += dbiIndexSetCount(matches[i]);
1285 /* Build sorted file info list for this package. */
1286 shared = sharedList = xcalloc((numShared + 1), sizeof(*sharedList));
1287 for (i = 0; i < fi->fc; i++) {
1289 * Take care not to mark files as replaced in packages that will
1290 * have been removed before we will get here.
1292 for (j = 0; j < dbiIndexSetCount(matches[i]); j++) {
1294 ro = dbiIndexRecordOffset(matches[i], j);
1296 for (k = 0; ro != knownBad && k < ts->orderCount; k++) {
1297 switch (ts->order[k].type) {
1299 if (ts->order[k].u.removed.dboffset == ro)
1301 /*@switchbreak@*/ break;
1303 /*@switchbreak@*/ break;
1307 shared->pkgFileNum = i;
1308 shared->otherPkg = dbiIndexRecordOffset(matches[i], j);
1309 shared->otherFileNum = dbiIndexRecordFileNumber(matches[i], j);
1310 shared->isRemoved = (knownBad == ro);
1313 matches[i] = dbiFreeIndexSet(matches[i]);
1315 numShared = shared - sharedList;
1316 shared->otherPkg = -1;
1317 matches = _free(matches);
1319 /* Sort file info by other package index (otherPkg) */
1320 qsort(sharedList, numShared, sizeof(*shared), sharedCmp);
1322 /* For all files from this package that are in the database ... */
1323 for (i = 0; i < numShared; i = nexti) {
1326 shared = sharedList + i;
1328 /* Find the end of the files in the other package. */
1329 for (nexti = i + 1; nexti < numShared; nexti++) {
1330 if (sharedList[nexti].otherPkg != shared->otherPkg)
1331 /*@innerbreak@*/ break;
1334 /* Is this file from a package being removed? */
1336 for (j = 0; j < ts->numRemovedPackages; j++) {
1337 if (ts->removedPackages[j] != shared->otherPkg)
1338 /*@innercontinue@*/ continue;
1340 /*@innerbreak@*/ break;
1343 /* Determine the fate of each file. */
1346 xx = handleInstInstalledFiles(ts, fi, shared, nexti - i,
1347 !(beingRemoved || (ts->ignoreSet & RPMPROB_FILTER_REPLACEOLDFILES)));
1348 /*@switchbreak@*/ break;
1351 xx = handleRmvdInstalledFiles(ts, fi, shared, nexti - i);
1352 /*@switchbreak@*/ break;
1358 /* Update disk space needs on each partition for this package. */
1359 handleOverlappedFiles(ts, fi);
1361 /* Check added package has sufficient space on each partition used. */
1364 if (!(ts->di && fi->fc))
1365 /*@switchbreak@*/ break;
1366 for (i = 0; i < ts->filesystemCount; i++) {
1370 /* XXX Avoid FAT and other file systems that have not inodes. */
1371 if (dip->iavail <= 0)
1372 /*@innercontinue@*/ continue;
1374 if (adj_fs_blocks(dip->bneeded) > dip->bavail) {
1375 const char * pkgNEVR = fiGetNEVR(fi);
1376 rpmProblemSetAppend(ts->probs, RPMPROB_DISKSPACE,
1378 ts->filesystems[i], NULL, NULL,
1379 (adj_fs_blocks(dip->bneeded) - dip->bavail) * dip->bsize);
1380 pkgNEVR = _free(pkgNEVR);
1383 if (adj_fs_blocks(dip->ineeded) > dip->iavail) {
1384 const char * pkgNEVR = fiGetNEVR(fi);
1385 rpmProblemSetAppend(ts->probs, RPMPROB_DISKNODES,
1387 ts->filesystems[i], NULL, NULL,
1388 (adj_fs_blocks(dip->ineeded) - dip->iavail));
1389 pkgNEVR = _free(pkgNEVR);
1392 /*@switchbreak@*/ break;
1394 /*@switchbreak@*/ break;
1397 tei = teFreeIterator(tei);
1399 if (ts->chrootDone) {
1400 /*@-superuser -noeffect @*/
1402 /*@=superuser =noeffect @*/
1404 if (ts->rpmdb) ts->rpmdb->db_chrootDone = 0;
1406 chroot_prefix = NULL;
1408 xx = chdir(ts->currDir);
1411 /*@-noeffectuncon @*/ /* FIX: check rc */
1412 NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_STOP, 6, ts->flEntries,
1413 NULL, ts->notifyData));
1414 /*@=noeffectuncon @*/
1416 /* ===============================================
1417 * Free unused memory as soon as possible.
1420 tei = teInitIterator(ts);
1421 while ((fi = teNextFi(tei)) != NULL) {
1424 fi->fps = _free(fi->fps);
1426 tei = teFreeIterator(tei);
1432 /* ===============================================
1433 * If unfiltered problems exist, free memory and return.
1435 if ((ts->transFlags & RPMTRANS_FLAG_BUILD_PROBS)
1436 || (ts->probs->numProblems &&
1437 (okProbs != NULL || rpmProblemSetTrim(ts->probs, okProbs)))
1440 ts->flList = freeFl(ts, ts->flList);
1442 if (psm->ts != NULL)
1443 psm->ts = rpmtsUnlink(psm->ts, "tsRun (problems)");
1444 /*@-nullstate@*/ /* FIX: ts->flList may be NULL */
1445 return ts->orderCount;
1449 /* ===============================================
1450 * Save removed files before erasing.
1452 if (ts->transFlags & (RPMTRANS_FLAG_DIRSTASH | RPMTRANS_FLAG_REPACKAGE)) {
1453 tei = teInitIterator(ts);
1454 while ((fi = teNextFi(tei)) != NULL) {
1457 /*@switchbreak@*/ break;
1459 if (ts->transFlags & RPMTRANS_FLAG_REPACKAGE) {
1460 psm->fi = rpmfiLink(fi, "tsRepackage");
1461 xx = psmStage(psm, PSM_PKGSAVE);
1462 (void) rpmfiUnlink(fi, "tsRepackage");
1465 /*@switchbreak@*/ break;
1468 tei = teFreeIterator(tei);
1471 /* ===============================================
1472 * Install and remove packages.
1475 lastKey = (alKey)-2; /* erased packages have -1 */
1476 tei = teInitIterator(ts);
1477 /*@-branchstate@*/ /* FIX: fi reload needs work */
1478 while ((fi = teNextFi(tei)) != NULL) {
1484 psm->fi = rpmfiLink(fi, "tsInstall");
1488 pkgKey = ts->order[oc].u.addedKey;
1490 rpmMessage(RPMMESS_DEBUG, "========== +++ %s-%s-%s\n",
1491 fi->name, fi->version, fi->release);
1492 h = (fi->h ? headerLink(fi->h, "TR_ADDED install") : NULL);
1494 if (fi->fd == NULL) {
1495 /*@-noeffectuncon @*/ /* FIX: ??? */
1496 fi->fd = ts->notify(fi->h, RPMCALLBACK_INST_OPEN_FILE, 0, 0,
1497 fi->key, ts->notifyData);
1498 /*@=noeffectuncon @*/
1499 if (fi->fd != NULL) {
1502 h = headerFree(h, "TR_ADDED install");
1504 /*@-mustmod@*/ /* LCL: segfault */
1505 rpmrc = rpmReadPackageFile(ts, fi->fd,
1506 "rpmRunTransactions", &h);
1509 if (!(rpmrc == RPMRC_OK || rpmrc == RPMRC_BADSIZE)) {
1510 /*@-noeffectuncon @*/ /* FIX: check rc */
1511 (void) ts->notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE,
1513 fi->key, ts->notifyData);
1514 /*@=noeffectuncon @*/
1517 } else if (fi->h != NULL) {
1518 Header foo = relocateFileList(ts, fi, h, NULL);
1519 h = headerFree(h, "TR_ADDED read free");
1520 h = headerLink(foo, "TR_ADDED relocate xfer");
1521 foo = headerFree(foo, "TR_ADDED relocate");
1523 if (fi->fd != NULL) gotfd = 1;
1528 if (fi->fd != NULL) {
1529 Header hsave = NULL;
1532 hsave = headerLink(fi->h, "TR_ADDED fi->h hsave");
1533 fi->h = headerFree(fi->h, "TR_ADDED fi->h free");
1534 fi->h = headerLink(h, "TR_ADDED fi->h link");
1536 char * fstates = fi->fstates;
1537 fileAction * actions = fi->actions;
1538 uint_32 multiLib = fi->multiLib;
1539 const void * key = fi->key;
1540 rpmRelocation * relocs = fi->relocs;
1550 fi->magic = TFIMAGIC;
1551 fi->type = ts->order[oc].type;
1553 loadFi(ts, fi, h, 1);
1554 fi->fstates = _free(fi->fstates);
1555 fi->fstates = fstates;
1556 fi->actions = _free(fi->actions);
1557 fi->actions = actions;
1558 fi->multiLib = multiLib;
1560 fi->relocs = relocs;
1562 /*@i@*/ fi->fd = fd;
1567 ts->transFlags |= RPMTRANS_FLAG_MULTILIB;
1569 if (psmStage(psm, PSM_PKGINSTALL)) {
1573 fi->h = headerFree(fi->h, "TR_ADDED fi->h free");
1575 fi->h = headerLink(hsave, "TR_ADDED fi->h restore");
1576 hsave = headerFree(hsave, "TR_ADDED hsave free");
1583 h = headerFree(h, "TR_ADDED h free");
1586 /*@-noeffectuncon @*/ /* FIX: check rc */
1587 (void)ts->notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE, 0, 0,
1588 fi->key, ts->notifyData);
1589 /*@=noeffectuncon @*/
1593 /*@switchbreak@*/ break;
1595 rpmMessage(RPMMESS_DEBUG, "========== --- %s-%s-%s\n",
1596 fi->name, fi->version, fi->release);
1598 /* If install failed, then we shouldn't erase. */
1599 if (ts->order[oc].u.removed.dependsOnKey != lastKey) {
1600 if (psmStage(psm, PSM_PKGERASE))
1604 /*@switchbreak@*/ break;
1606 xx = rpmdbSync(ts->rpmdb);
1607 (void) rpmfiUnlink(fi, "tsInstall");
1611 tei = teFreeIterator(tei);
1613 ts->flList = freeFl(ts, ts->flList);
1616 psm->ts = rpmtsUnlink(psm->ts, "tsRun");
1618 /*@-nullstate@*/ /* FIX: ts->flList may be NULL */