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 rpmTransactionSet @*/
70 struct diskspaceInfo {
71 dev_t dev; /*!< File system device number. */
72 signed long bneeded; /*!< No. of blocks needed. */
73 signed long ineeded; /*!< No. of inodes needed. */
74 int bsize; /*!< File system block size. */
75 signed long bavail; /*!< No. of blocks available. */
76 signed long iavail; /*!< No. of inodes available. */
80 * Adjust for root only reserved space. On linux e2fs, this is 5%.
82 #define adj_fs_blocks(_nb) (((_nb) * 21) / 20)
84 /* argon thought a shift optimization here was a waste of time... he's
86 #define BLOCK_ROUND(size, block) (((size) + (block) - 1) / (block))
88 void rpmtransSetScriptFd(rpmTransactionSet ts, FD_t fd)
90 ts->scriptFd = (fd ? fdLink(fd, "rpmtransSetScriptFd") : NULL);
93 int rpmtransGetKeys(const rpmTransactionSet ts, fnpyKey ** ep, int * nep)
97 if (nep) *nep = ts->orderCount;
99 teIterator pi; transactionElement p;
102 *ep = e = xmalloc(ts->orderCount * sizeof(*e));
103 pi = teInitIterator(ts);
104 while ((p = teNextIterator(pi)) != NULL) {
105 switch (teGetType(p)) {
107 /*@-dependenttrans@*/
109 /*@=dependenttrans@*/
110 /*@switchbreak@*/ break;
114 /*@switchbreak@*/ break;
118 pi = teFreeIterator(pi);
125 static int archOkay(/*@null@*/ const char * pkgArch)
128 if (pkgArch == NULL) return 0;
129 return (rpmMachineScore(RPM_MACHTABLE_INSTARCH, pkgArch) ? 1 : 0);
134 static int osOkay(/*@null@*/ const char * pkgOs)
137 if (pkgOs == NULL) return 0;
138 return (rpmMachineScore(RPM_MACHTABLE_INSTOS, pkgOs) ? 1 : 0);
143 static int sharedCmp(const void * one, const void * two)
146 sharedFileInfo a = (sharedFileInfo) one;
147 sharedFileInfo b = (sharedFileInfo) two;
149 if (a->otherPkg < b->otherPkg)
151 else if (a->otherPkg > b->otherPkg)
159 static fileAction decideFileFate(const char * fn,
161 const char * dbMd5, const char * dbLink, short newMode,
162 const char * newMd5, const char * newLink, int newFlags,
163 rpmtransFlags transFlags)
164 /*@globals fileSystem @*/
165 /*@modifies fileSystem @*/
168 const char * dbAttr, * newAttr;
169 fileTypes dbWhat, newWhat, diskWhat;
172 int save = (newFlags & RPMFILE_NOREPLACE) ? FA_ALTNAME : FA_SAVE;
174 if (lstat(fn, &sb)) {
176 * The file doesn't exist on the disk. Create it unless the new
177 * package has marked it as missingok, or allfiles is requested.
179 if (!(transFlags & RPMTRANS_FLAG_ALLFILES) &&
180 (newFlags & RPMFILE_MISSINGOK)) {
181 rpmMessage(RPMMESS_DEBUG, _("%s skipped due to missingok flag\n"),
189 diskWhat = whatis(sb.st_mode);
190 dbWhat = whatis(dbMode);
191 newWhat = whatis(newMode);
193 /* RPM >= 2.3.10 shouldn't create config directories -- we'll ignore
194 them in older packages as well */
195 if (newWhat == XDIR) {
199 if (diskWhat != newWhat) {
201 } else if (newWhat != dbWhat && diskWhat != dbWhat) {
203 } else if (dbWhat != newWhat) {
205 } else if (dbWhat != LINK && dbWhat != REG) {
210 rc = mdfile(fn, buffer);
213 /* assume the file has been removed, don't freak */
218 } else /* dbWhat == LINK */ {
219 memset(buffer, 0, sizeof(buffer));
220 i = readlink(fn, buffer, sizeof(buffer) - 1);
222 /* assume the file has been removed, don't freak */
229 /* this order matters - we'd prefer to CREATE the file if at all
230 possible in case something else (like the timestamp) has changed */
232 if (!strcmp(dbAttr, buffer)) {
233 /* this config file has never been modified, so just replace it */
237 if (!strcmp(dbAttr, newAttr)) {
238 /* this file is the same in all versions of this package */
243 * The config file on the disk has been modified, but
244 * the ones in the two packages are different. It would
245 * be nice if RPM was smart enough to at least try and
246 * merge the difference ala CVS, but...
253 static int filecmp(short mode1, const char * md51, const char * link1,
254 short mode2, const char * md52, const char * link2)
257 fileTypes what1 = whatis(mode1);
258 fileTypes what2 = whatis(mode2);
260 if (what1 != what2) return 1;
263 return strcmp(link1, link2);
264 else if (what1 == REG)
265 return strcmp(md51, md52);
272 /* XXX only ts->{probs,rpmdb} modified */
273 static int handleInstInstalledFiles(const rpmTransactionSet ts,
274 transactionElement p, TFI_t fi,
275 sharedFileInfo shared,
276 int sharedCount, int reportConflicts)
277 /*@globals fileSystem @*/
278 /*@modifies ts, fi, fileSystem @*/
281 HFD_t hfd = (fi->hfd ? fi->hfd : headerFreeData);
282 rpmtransFlags transFlags = ts->transFlags;
283 rpmTagType oltype, omtype;
286 const char ** otherMd5s;
287 const char ** otherLinks;
288 const char * otherStates;
289 uint_32 * otherFlags;
290 uint_32 * otherSizes;
291 uint_16 * otherModes;
295 rpmdbMatchIterator mi;
297 mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES,
298 &shared->otherPkg, sizeof(shared->otherPkg));
299 h = rpmdbNextIterator(mi);
301 mi = rpmdbFreeIterator(mi);
305 xx = hge(h, RPMTAG_FILEMD5S, &omtype, (void **) &otherMd5s, NULL);
306 xx = hge(h, RPMTAG_FILELINKTOS, &oltype, (void **) &otherLinks, NULL);
307 xx = hge(h, RPMTAG_FILESTATES, NULL, (void **) &otherStates, NULL);
308 xx = hge(h, RPMTAG_FILEMODES, NULL, (void **) &otherModes, NULL);
309 xx = hge(h, RPMTAG_FILEFLAGS, NULL, (void **) &otherFlags, NULL);
310 xx = hge(h, RPMTAG_FILESIZES, NULL, (void **) &otherSizes, NULL);
312 fi->replaced = xcalloc(sharedCount, sizeof(*fi->replaced));
314 for (i = 0; i < sharedCount; i++, shared++) {
315 int otherFileNum, fileNum;
318 otherFileNum = shared->otherFileNum;
319 fileNum = shared->pkgFileNum;
321 (void) tfiSetFX(fi, fileNum);
324 /* XXX another tedious segfault, assume file state normal. */
325 if (otherStates && otherStates[otherFileNum] != RPMFILE_STATE_NORMAL)
328 if (XFA_SKIPPING(fi->actions[fileNum]))
331 if (filecmp(otherModes[otherFileNum],
332 otherMd5s[otherFileNum],
333 otherLinks[otherFileNum],
336 fi->flinks[fileNum])) {
337 if (reportConflicts) {
338 const char * altNEVR = hGetNEVR(h, NULL);
339 rpmProblemSetAppend(ts->probs, RPMPROB_FILE_CONFLICT,
340 teGetNEVR(p), teGetKey(p),
341 tfiGetDN(fi), tfiGetBN(fi),
344 altNEVR = _free(altNEVR);
346 if (!(otherFlags[otherFileNum] | fi->fflags[fileNum])
348 /*@-assignexpose@*/ /* FIX: p->replaced, not fi */
349 if (!shared->isRemoved)
350 fi->replaced[numReplaced++] = *shared;
355 if ((otherFlags[otherFileNum] | fi->fflags[fileNum]) & RPMFILE_CONFIG) {
356 fi->actions[fileNum] = decideFileFate(fn,
357 otherModes[otherFileNum],
358 otherMd5s[otherFileNum],
359 otherLinks[otherFileNum],
367 fi->replacedSizes[fileNum] = otherSizes[otherFileNum];
370 otherMd5s = hfd(otherMd5s, omtype);
371 otherLinks = hfd(otherLinks, oltype);
372 mi = rpmdbFreeIterator(mi);
374 fi->replaced = xrealloc(fi->replaced, /* XXX memory leak */
375 sizeof(*fi->replaced) * (numReplaced + 1));
376 fi->replaced[numReplaced].otherPkg = 0;
383 /* XXX only ts->rpmdb modified */
384 static int handleRmvdInstalledFiles(const rpmTransactionSet ts, TFI_t fi,
385 sharedFileInfo shared, int sharedCount)
386 /*@globals fileSystem @*/
387 /*@modifies ts, fi, fileSystem @*/
391 const char * otherStates;
394 rpmdbMatchIterator mi;
396 mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES,
397 &shared->otherPkg, sizeof(shared->otherPkg));
398 h = rpmdbNextIterator(mi);
400 mi = rpmdbFreeIterator(mi);
404 xx = hge(h, RPMTAG_FILESTATES, NULL, (void **) &otherStates, NULL);
406 for (i = 0; i < sharedCount; i++, shared++) {
407 int otherFileNum, fileNum;
408 otherFileNum = shared->otherFileNum;
409 fileNum = shared->pkgFileNum;
411 if (otherStates[otherFileNum] != RPMFILE_STATE_NORMAL)
414 fi->actions[fileNum] = FA_SKIP;
417 mi = rpmdbFreeIterator(mi);
422 #define ISROOT(_d) (((_d)[0] == '/' && (_d)[1] == '\0') ? "" : (_d))
425 static int _fps_debug = 0;
427 static int fpsCompare (const void * one, const void * two)
429 const struct fingerPrint_s * a = (const struct fingerPrint_s *)one;
430 const struct fingerPrint_s * b = (const struct fingerPrint_s *)two;
431 int adnlen = strlen(a->entry->dirName);
432 int asnlen = (a->subDir ? strlen(a->subDir) : 0);
433 int abnlen = strlen(a->baseName);
434 int bdnlen = strlen(b->entry->dirName);
435 int bsnlen = (b->subDir ? strlen(b->subDir) : 0);
436 int bbnlen = strlen(b->baseName);
437 char * afn, * bfn, * t;
440 if (adnlen == 1 && asnlen != 0) adnlen = 0;
441 if (bdnlen == 1 && bsnlen != 0) bdnlen = 0;
443 afn = t = alloca(adnlen+asnlen+abnlen+2);
444 if (adnlen) t = stpcpy(t, a->entry->dirName);
446 if (a->subDir && asnlen) t = stpcpy(t, a->subDir);
447 if (abnlen) t = stpcpy(t, a->baseName);
448 if (afn[0] == '/' && afn[1] == '/') afn++;
450 bfn = t = alloca(bdnlen+bsnlen+bbnlen+2);
451 if (bdnlen) t = stpcpy(t, b->entry->dirName);
453 if (b->subDir && bsnlen) t = stpcpy(t, b->subDir);
454 if (bbnlen) t = stpcpy(t, b->baseName);
455 if (bfn[0] == '/' && bfn[1] == '/') bfn++;
457 rc = strcmp(afn, bfn);
460 fprintf(stderr, "\trc(%d) = strcmp(\"%s\", \"%s\")\n", rc, afn, bfn);
465 fprintf(stderr, "\t%s/%s%s\trc %d\n",
466 ISROOT(b->entry->dirName),
467 (b->subDir ? b->subDir : ""),
477 static int _linear_fps_search = 0;
479 static int findFps(const struct fingerPrint_s * fiFps,
480 const struct fingerPrint_s * otherFps,
488 fprintf(stderr, "==> %s/%s%s\n",
489 ISROOT(fiFps->entry->dirName),
490 (fiFps->subDir ? fiFps->subDir : ""),
494 if (_linear_fps_search) {
497 for (otherFileNum = 0; otherFileNum < otherFc; otherFileNum++, otherFps++) {
501 fprintf(stderr, "\t%4d %s/%s%s\n", otherFileNum,
502 ISROOT(otherFps->entry->dirName),
503 (otherFps->subDir ? otherFps->subDir : ""),
507 /* If the addresses are the same, so are the values. */
508 if (fiFps == otherFps)
511 /* Otherwise, compare fingerprints by value. */
512 /*@-nullpass@*/ /* LCL: looks good to me */
513 if (FP_EQUAL((*fiFps), (*otherFps)))
518 if (otherFileNum == otherFc) {
521 fprintf(stderr, "*** NULL %s/%s%s\n",
522 ISROOT(fiFps->entry->dirName),
523 (fiFps->subDir ? fiFps->subDir : ""),
532 const struct fingerPrint_s * bingoFps;
534 bingoFps = bsearch(fiFps, otherFps, otherFc, sizeof(*otherFps), fpsCompare);
535 if (bingoFps == NULL) {
537 fprintf(stderr, "*** NULL %s/%s%s\n",
538 ISROOT(fiFps->entry->dirName),
539 (fiFps->subDir ? fiFps->subDir : ""),
545 /* If the addresses are the same, so are the values. */
546 /*@-nullpass@*/ /* LCL: looks good to me */
547 if (!(fiFps == bingoFps || FP_EQUAL((*fiFps), (*bingoFps)))) {
549 fprintf(stderr, "*** BAD %s/%s%s\n",
550 ISROOT(bingoFps->entry->dirName),
551 (bingoFps->subDir ? bingoFps->subDir : ""),
557 otherFileNum = (bingoFps != NULL ? (bingoFps - otherFps) : 0);
565 * Update disk space needs on each partition for this package.
567 /* XXX only ts->{probs,di} modified */
568 static void handleOverlappedFiles(const rpmTransactionSet ts,
569 const transactionElement p, TFI_t fi)
570 /*@globals fileSystem @*/
571 /*@modifies ts, fi, fileSystem @*/
573 struct diskspaceInfo * ds = NULL;
574 uint_32 fixupSize = 0;
580 while ((i = tfiNext(fi)) >= 0) {
581 struct fingerPrint_s * fiFps;
582 int otherPkgNum, otherFileNum;
587 if (XFA_SKIPPING(fi->actions[i]))
595 while (ds->bsize && ds->dev != fi->fps[i].entry->dev) ds++;
596 if (!ds->bsize) ds = NULL;
601 * Retrieve all records that apply to this file. Note that the
602 * file info records were built in the same order as the packages
603 * will be installed and removed so the records for an overlapped
604 * files will be sorted in exactly the same order.
606 (void) htGetEntry(ts->ht, fiFps,
607 (const void ***) &recs, &numRecs, NULL);
610 * If this package is being added, look only at other packages
611 * being added -- removed packages dance to a different tune.
612 * If both this and the other package are being added, overlapped
613 * files must be identical (or marked as a conflict). The
614 * disposition of already installed config files leads to
615 * a small amount of extra complexity.
617 * If this package is being removed, then there are two cases that
618 * need to be worried about:
619 * If the other package is being added, then skip any overlapped files
620 * so that this package removal doesn't nuke the overlapped files
621 * that were just installed.
622 * If both this and the other package are being removed, then each
623 * file removal from preceding packages needs to be skipped so that
624 * the file removal occurs only on the last occurence of an overlapped
625 * file in the transaction set.
629 /* Locate this overlapped file in the set of added/removed packages. */
630 for (j = 0; j < numRecs && recs[j] != fi; j++)
633 /* Find what the previous disposition of this file was. */
634 otherFileNum = -1; /* keep gcc quiet */
636 for (otherPkgNum = j - 1; otherPkgNum >= 0; otherPkgNum--) {
637 struct fingerPrint_s * otherFps;
640 otherFi = recs[otherPkgNum];
642 /* Added packages need only look at other added packages. */
643 if (teGetType(p) == TR_ADDED && teGetType(otherFi->te) != TR_ADDED)
644 /*@innercontinue@*/ continue;
646 otherFps = otherFi->fps;
647 otherFc = tfiGetFC(otherFi);
649 otherFileNum = findFps(fiFps, otherFps, otherFc);
651 /* XXX is this test still necessary? */
652 if (otherFi->actions[otherFileNum] != FA_UNKNOWN)
653 /*@innerbreak@*/ break;
656 switch (teGetType(p)) {
659 if (otherPkgNum < 0) {
660 /* XXX is this test still necessary? */
661 if (fi->actions[i] != FA_UNKNOWN)
662 /*@switchbreak@*/ break;
663 if ((fi->fflags[i] & RPMFILE_CONFIG) &&
665 /* Here is a non-overlapped pre-existing config file. */
666 fi->actions[i] = (fi->fflags[i] & RPMFILE_NOREPLACE)
667 ? FA_ALTNAME : FA_BACKUP;
669 fi->actions[i] = FA_CREATE;
671 /*@switchbreak@*/ break;
674 assert(otherFi != NULL);
675 /* Mark added overlapped non-identical files as a conflict. */
676 if ((ts->ignoreSet & RPMPROB_FILTER_REPLACENEWFILES)
677 && filecmp(otherFi->fmodes[otherFileNum],
678 otherFi->fmd5s[otherFileNum],
679 otherFi->flinks[otherFileNum],
684 rpmProblemSetAppend(ts->probs, RPMPROB_NEW_FILE_CONFLICT,
685 teGetNEVR(p), teGetKey(p),
687 teGetNEVR(otherFi->te),
691 /* Try to get the disk accounting correct even if a conflict. */
692 fixupSize = otherFi->fsizes[otherFileNum];
694 if ((fi->fflags[i] & RPMFILE_CONFIG) && !lstat(fn, &sb)) {
695 /* Here is an overlapped pre-existing config file. */
696 fi->actions[i] = (fi->fflags[i] & RPMFILE_NOREPLACE)
697 ? FA_ALTNAME : FA_SKIP;
699 fi->actions[i] = FA_CREATE;
701 } /*@switchbreak@*/ break;
704 if (otherPkgNum >= 0) {
705 assert(otherFi != NULL);
706 /* Here is an overlapped added file we don't want to nuke. */
707 if (otherFi->actions[otherFileNum] != FA_ERASE) {
708 /* On updates, don't remove files. */
709 fi->actions[i] = FA_SKIP;
710 /*@switchbreak@*/ break;
712 /* Here is an overlapped removed file: skip in previous. */
713 otherFi->actions[otherFileNum] = FA_SKIP;
715 if (XFA_SKIPPING(fi->actions[i]))
716 /*@switchbreak@*/ break;
717 if (fi->fstates && fi->fstates[i] != RPMFILE_STATE_NORMAL)
718 /*@switchbreak@*/ break;
719 if (!(S_ISREG(fi->fmodes[i]) && (fi->fflags[i] & RPMFILE_CONFIG))) {
720 fi->actions[i] = FA_ERASE;
721 /*@switchbreak@*/ break;
724 /* Here is a pre-existing modified config file that needs saving. */
726 if (!mdfile(fn, mdsum) && strcmp(fi->fmd5s[i], mdsum)) {
727 fi->actions[i] = FA_BACKUP;
728 /*@switchbreak@*/ break;
731 fi->actions[i] = FA_ERASE;
732 /*@switchbreak@*/ break;
736 uint_32 s = BLOCK_ROUND(fi->fsizes[i], ds->bsize);
738 switch (fi->actions[i]) {
744 /*@switchbreak@*/ break;
747 * FIXME: If two packages share a file (same md5sum), and
748 * that file is being replaced on disk, will ds->bneeded get
749 * decremented twice? Quite probably!
753 ds->bneeded -= BLOCK_ROUND(fi->replacedSizes[i], ds->bsize);
754 /*@switchbreak@*/ break;
759 /*@switchbreak@*/ break;
762 /*@switchbreak@*/ break;
765 ds->bneeded -= BLOCK_ROUND(fixupSize, ds->bsize);
771 * Ensure that current package is newer than installed package.
772 * @param ts transaction set
773 * @param p current transaction element
774 * @param h installed header
775 * @return 0 if not newer, 1 if okay
777 static int ensureOlder(rpmTransactionSet ts,
778 const transactionElement p, const Header h)
781 int_32 reqFlags = (RPMSENSE_LESS | RPMSENSE_EQUAL);
788 if (p == NULL || h == NULL)
791 nb = strlen(teGetNEVR(p)) + (teGetE(p) != NULL ? strlen(teGetE(p)) : 0) + 1;
795 if (teGetE(p) != NULL) t = stpcpy( stpcpy(t, teGetE(p)), ":");
796 if (teGetV(p) != NULL) t = stpcpy(t, teGetV(p));
798 if (teGetR(p) != NULL) t = stpcpy(t, teGetR(p));
800 req = dsSingle(RPMTAG_REQUIRENAME, teGetN(p), reqEVR, reqFlags);
801 rc = headerMatchesDepFlags(h, req);
805 const char * altNEVR = hGetNEVR(h, NULL);
806 rpmProblemSetAppend(ts->probs, RPMPROB_OLDPACKAGE,
807 teGetNEVR(p), teGetKey(p),
811 altNEVR = _free(altNEVR);
821 /*@-mustmod@*/ /* FIX: fi->actions is modified. */
822 static void skipFiles(const rpmTransactionSet ts, TFI_t fi)
823 /*@globals rpmGlobalMacroContext @*/
824 /*@modifies fi, rpmGlobalMacroContext @*/
826 int noDocs = (ts->transFlags & RPMTRANS_FLAG_NODOCS);
827 char ** netsharedPaths = NULL;
828 const char ** languages;
829 const char * dn, * bn;
830 int dnlen, bnlen, ix;
838 noDocs = rpmExpandNumeric("%{_excludedocs}");
840 { const char *tmpPath = rpmExpand("%{_netsharedpath}", NULL);
842 if (tmpPath && *tmpPath != '%')
843 netsharedPaths = splitString(tmpPath, strlen(tmpPath), ':');
845 tmpPath = _free(tmpPath);
848 s = rpmExpand("%{_install_langs}", NULL);
850 if (!(s && *s != '%'))
853 languages = (const char **) splitString(s, strlen(s), ':');
859 /* Compute directory refcount, skip directory if now empty. */
861 drc = alloca(dc * sizeof(*drc));
862 memset(drc, 0, dc * sizeof(*drc));
863 dff = alloca(dc * sizeof(*dff));
864 memset(dff, 0, dc * sizeof(*dff));
866 if ((fi = tfiInit(fi, 0)) != NULL)
867 while ((i = tfiNext(fi)) >= 0)
877 continue; /* XXX can't happen */
881 /* Don't bother with skipped files */
882 if (XFA_SKIPPING(fi->actions[i])) {
888 * Skip net shared paths.
889 * Net shared paths are not relative to the current root (though
890 * they do need to take package relocations into account).
892 for (nsp = netsharedPaths; nsp && *nsp; nsp++) {
897 if (strncmp(dn, *nsp, len))
898 /*@innercontinue@*/ continue;
899 /* Only directories or complete file paths can be net shared */
900 if (!(dn[len] == '/' || dn[len] == '\0'))
901 /*@innercontinue@*/ continue;
903 if (len < (dnlen + bnlen))
904 /*@innercontinue@*/ continue;
905 if (strncmp(dn, *nsp, dnlen))
906 /*@innercontinue@*/ continue;
907 if (strncmp(bn, (*nsp) + dnlen, bnlen))
908 /*@innercontinue@*/ continue;
910 /* Only directories or complete file paths can be net shared */
911 if (!((*nsp)[len] == '/' || (*nsp)[len] == '\0'))
912 /*@innercontinue@*/ continue;
915 /*@innerbreak@*/ break;
919 drc[ix]--; dff[ix] = 1;
920 fi->actions[i] = FA_SKIPNETSHARED;
925 * Skip i18n language specific files.
927 if (fi->flangs && languages && *fi->flangs[i]) {
928 const char **lang, *l, *le;
929 for (lang = languages; *lang != NULL; lang++) {
930 if (!strcmp(*lang, "all"))
931 /*@innerbreak@*/ break;
932 for (l = fi->flangs[i]; *l != '\0'; l = le) {
933 for (le = l; *le != '\0' && *le != '|'; le++)
935 if ((le-l) > 0 && !strncmp(*lang, l, (le-l)))
936 /*@innerbreak@*/ break;
937 if (*le == '|') le++; /* skip over | */
940 /*@innerbreak@*/ break;
943 drc[ix]--; dff[ix] = 1;
944 fi->actions[i] = FA_SKIPNSTATE;
950 * Skip documentation if requested.
952 if (noDocs && (fi->fflags[i] & RPMFILE_DOC)) {
953 drc[ix]--; dff[ix] = 1;
954 fi->actions[i] = FA_SKIPNSTATE;
959 /* Skip (now empty) directories that had skipped files. */
961 if (fi != NULL) /* XXX can't happen */
962 for (j = 0; j < dc; j++)
964 if ((fi = tdiInit(fi)) != NULL)
965 while (j = tdiNext(fi) >= 0)
969 if (drc[j]) continue; /* dir still has files. */
970 if (!dff[j]) continue; /* dir was not emptied here. */
972 /* Find parent directory and basename. */
973 dn = fi->dnl[j]; dnlen = strlen(dn) - 1;
974 bn = dn + dnlen; bnlen = 0;
975 while (bn > dn && bn[-1] != '/') {
981 /* If explicitly included in the package, skip the directory. */
982 if ((fi = tfiInit(fi, 0)) != NULL)
983 while ((i = tfiNext(fi)) >= 0) {
986 if (XFA_SKIPPING(fi->actions[i]))
987 /*@innercontinue@*/ continue;
988 if (whatis(fi->fmodes[i]) != XDIR)
989 /*@innercontinue@*/ continue;
990 dir = fi->dnl[fi->dil[i]];
991 if (strlen(dir) != dnlen)
992 /*@innercontinue@*/ continue;
993 if (strncmp(dir, dn, dnlen))
994 /*@innercontinue@*/ continue;
995 if (strlen(fi->bnl[i]) != bnlen)
996 /*@innercontinue@*/ continue;
997 if (strncmp(fi->bnl[i], bn, bnlen))
998 /*@innercontinue@*/ continue;
999 rpmMessage(RPMMESS_DEBUG, _("excluding directory %s\n"), dn);
1000 fi->actions[i] = FA_SKIPNSTATE;
1001 /*@innerbreak@*/ break;
1005 if (netsharedPaths) freeSplitString(netsharedPaths);
1006 #ifdef DYING /* XXX freeFi will deal with this later. */
1007 fi->flangs = _free(fi->flangs);
1009 if (languages) freeSplitString((char **)languages);
1014 * Return transaction element's file info.
1015 * @todo Take a TFI_t refcount here.
1016 * @param tei transaction element iterator
1017 * @return transaction element file info
1020 TFI_t teiGetFi(const teIterator tei)
1025 if (tei != NULL && tei->ocsave != -1) {
1026 /*@-type -abstract@*/ /* FIX: transactionElement not opaque */
1027 transactionElement te = tei->ts->order + tei->ocsave;
1028 if ((fi = te->fi) != NULL)
1030 /*@=type =abstract@*/
1032 /*@-compdef -refcounttrans -usereleased @*/
1034 /*@=compdef =refcounttrans =usereleased @*/
1037 #define NOTIFY(_ts, _al) if ((_ts)->notify) (void) (_ts)->notify _al
1039 int rpmRunTransactions( rpmTransactionSet ts,
1040 rpmCallbackFunction notify, rpmCallbackData notifyData,
1041 rpmProblemSet okProbs, rpmProblemSet * newProbs,
1042 rpmtransFlags transFlags, rpmprobFilterFlags ignoreSet)
1046 int totalFileCount = 0;
1048 struct diskspaceInfo * dip;
1049 sharedFileInfo shared, sharedList;
1053 fingerPrintCache fpc;
1054 PSM_t psm = memset(alloca(sizeof(*psm)), 0, sizeof(*psm));
1055 teIterator pi; transactionElement p;
1056 teIterator qi; transactionElement q;
1059 /* FIXME: what if the same package is included in ts twice? */
1061 ts->transFlags = transFlags;
1062 if (ts->transFlags & RPMTRANS_FLAG_NOSCRIPTS)
1063 ts->transFlags |= (_noTransScripts | _noTransTriggers);
1064 if (ts->transFlags & RPMTRANS_FLAG_NOTRIGGERS)
1065 ts->transFlags |= _noTransTriggers;
1067 /* XXX MULTILIB is broken, as packages can and do execute /sbin/ldconfig. */
1068 if (ts->transFlags & (RPMTRANS_FLAG_JUSTDB | RPMTRANS_FLAG_MULTILIB))
1069 ts->transFlags |= (_noTransScripts | _noTransTriggers);
1071 ts->notify = notify;
1072 ts->notifyData = notifyData;
1073 ts->probs = rpmProblemSetFree(ts->probs);
1074 ts->probs = rpmProblemSetCreate();
1075 *newProbs = rpmpsLink(ts->probs, "RunTransactions");
1076 ts->ignoreSet = ignoreSet;
1077 ts->currDir = _free(ts->currDir);
1078 ts->currDir = currentDirectory();
1080 if (ts->rpmdb) ts->rpmdb->db_chrootDone = 0;
1081 ts->id = (int_32) time(NULL);
1083 memset(psm, 0, sizeof(*psm));
1084 psm->ts = rpmtsLink(ts, "tsRun");
1086 /* Get available space on mounted file systems. */
1087 if (!(ts->ignoreSet & RPMPROB_FILTER_DISKSPACE) &&
1088 !rpmGetFilesystemList(&ts->filesystems, &ts->filesystemCount))
1092 rpmMessage(RPMMESS_DEBUG, _("getting list of mounted filesystems\n"));
1094 ts->di = _free(ts->di);
1095 dip = ts->di = xcalloc((ts->filesystemCount + 1), sizeof(*ts->di));
1097 for (i = 0; (i < ts->filesystemCount) && dip; i++) {
1098 #if STATFS_IN_SYS_STATVFS
1100 memset(&sfb, 0, sizeof(sfb));
1101 if (statvfs(ts->filesystems[i], &sfb))
1105 /* This platform has the 4-argument version of the statfs call. The last two
1106 * should be the size of struct statfs and 0, respectively. The 0 is the
1107 * filesystem type, and is always 0 when statfs is called on a mounted
1108 * filesystem, as we're doing.
1110 memset(&sfb, 0, sizeof(sfb));
1111 if (statfs(ts->filesystems[i], &sfb, sizeof(sfb), 0))
1113 memset(&sfb, 0, sizeof(sfb));
1114 if (statfs(ts->filesystems[i], &sfb))
1120 ts->di[i].bsize = sfb.f_bsize;
1121 ts->di[i].bneeded = 0;
1122 ts->di[i].ineeded = 0;
1123 #ifdef STATFS_HAS_F_BAVAIL
1124 ts->di[i].bavail = sfb.f_bavail;
1126 /* FIXME: the statfs struct doesn't have a member to tell how many blocks are
1127 * available for non-superusers. f_blocks - f_bfree is probably too big, but
1128 * it's about all we can do.
1130 ts->di[i].bavail = sfb.f_blocks - sfb.f_bfree;
1132 /* XXX Avoid FAT and other file systems that have not inodes. */
1133 ts->di[i].iavail = !(sfb.f_ffree == 0 && sfb.f_files == 0)
1136 xx = stat(ts->filesystems[i], &sb);
1137 ts->di[i].dev = sb.st_dev;
1141 if (dip) ts->di[i].bsize = 0;
1144 /* ===============================================
1145 * For packages being installed:
1146 * - verify package arch/os.
1147 * - verify package epoch:version-release is newer.
1149 * For packages being removed:
1152 /* The ordering doesn't matter here */
1153 pi = teInitIterator(ts);
1154 while ((p = teNext(pi, TR_ADDED)) != NULL) {
1155 rpmdbMatchIterator mi;
1158 if ((fi = teiGetFi(pi)) == NULL)
1159 continue; /* XXX can't happen */
1162 if (!(ts->ignoreSet & RPMPROB_FILTER_IGNOREARCH))
1163 if (!archOkay(teGetA(p)))
1164 rpmProblemSetAppend(ts->probs, RPMPROB_BADARCH,
1165 teGetNEVR(p), teGetKey(p),
1169 if (!(ts->ignoreSet & RPMPROB_FILTER_IGNOREOS))
1170 if (!osOkay(teGetO(p)))
1171 rpmProblemSetAppend(ts->probs, RPMPROB_BADOS,
1172 teGetNEVR(p), teGetKey(p),
1176 if (!(ts->ignoreSet & RPMPROB_FILTER_OLDPACKAGE)) {
1178 mi = rpmtsInitIterator(ts, RPMTAG_NAME, teGetN(p), 0);
1179 while ((h = rpmdbNextIterator(mi)) != NULL)
1180 xx = ensureOlder(ts, p, h);
1181 mi = rpmdbFreeIterator(mi);
1184 /* XXX multilib should not display "already installed" problems */
1185 if (!(ts->ignoreSet & RPMPROB_FILTER_REPLACEPKG) && !teGetMultiLib(p)) {
1186 mi = rpmtsInitIterator(ts, RPMTAG_NAME, teGetN(p), 0);
1187 xx = rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_DEFAULT,
1189 xx = rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_DEFAULT,
1192 while (rpmdbNextIterator(mi) != NULL) {
1193 rpmProblemSetAppend(ts->probs, RPMPROB_PKG_INSTALLED,
1194 teGetNEVR(p), teGetKey(p),
1197 /*@innerbreak@*/ break;
1199 mi = rpmdbFreeIterator(mi);
1202 /* Count no. of files (if any). */
1203 totalFileCount += fc;
1206 pi = teFreeIterator(pi);
1208 /* The ordering doesn't matter here */
1209 pi = teInitIterator(ts);
1210 while ((p = teNext(pi, TR_REMOVED)) != NULL) {
1213 if ((fi = teiGetFi(pi)) == NULL)
1214 continue; /* XXX can't happen */
1217 totalFileCount += fc;
1219 pi = teFreeIterator(pi);
1221 /* ===============================================
1222 * Initialize transaction element file info for package:
1226 * FIXME?: we'd be better off assembling one very large file list and
1227 * calling fpLookupList only once. I'm not sure that the speedup is
1228 * worth the trouble though.
1230 pi = teInitIterator(ts);
1231 while ((p = teNextIterator(pi)) != NULL) {
1234 if ((fi = teiGetFi(pi)) == NULL)
1235 continue; /* XXX can't happen */
1238 #ifdef DYING /* XXX W2DO? this is now done teiGetFi, okay ??? */
1239 fi->magic = TFIMAGIC;
1244 switch (teGetType(p)) {
1247 /* Skip netshared paths, not our i18n files, and excluded docs */
1250 /*@switchbreak@*/ break;
1252 fi->record = teGetDBOffset(p);
1253 /*@switchbreak@*/ break;
1257 fi->fps = (fc > 0 ? xmalloc(fc * sizeof(*fi->fps)) : NULL);
1259 pi = teFreeIterator(pi);
1261 if (!ts->chrootDone) {
1263 /*@-superuser -noeffect @*/
1264 xx = chroot(ts->rootDir);
1265 /*@=superuser =noeffect @*/
1267 if (ts->rpmdb) ts->rpmdb->db_chrootDone = 1;
1270 chroot_prefix = ts->rootDir;
1275 ts->ht = htCreate(totalFileCount * 2, 0, 0, fpHashFunction, fpEqual);
1276 fpc = fpCacheCreate(totalFileCount);
1278 /* ===============================================
1279 * Add fingerprint for each file not skipped.
1281 pi = teInitIterator(ts);
1282 while ((p = teNextIterator(pi)) != NULL) {
1285 if ((fi = teiGetFi(pi)) == NULL)
1286 continue; /* XXX can't happen */
1289 fpLookupList(fpc, fi->dnl, fi->bnl, fi->dil, fc, fi->fps);
1291 if ((fi = tfiInit(fi, 0)) != NULL)
1292 while ((i = tfiNext(fi)) >= 0) {
1293 if (XFA_SKIPPING(fi->actions[i]))
1294 /*@innercontinue@*/ continue;
1295 /*@-dependenttrans@*/
1296 htAddEntry(ts->ht, fi->fps + i, (void *) fi);
1297 /*@=dependenttrans@*/
1301 pi = teFreeIterator(pi);
1303 /*@-noeffectuncon @*/ /* FIX: check rc */
1304 NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_START, 6, ts->orderCount,
1305 NULL, ts->notifyData));
1306 /*@=noeffectuncon@*/
1308 /* ===============================================
1309 * Compute file disposition for each package in transaction set.
1311 pi = teInitIterator(ts);
1312 while ((p = teNextIterator(pi)) != NULL) {
1313 dbiIndexSet * matches;
1317 if ((fi = teiGetFi(pi)) == NULL)
1318 continue; /* XXX can't happen */
1321 /*@-noeffectuncon @*/ /* FIX: check rc */
1322 NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_PROGRESS, teiGetOc(pi),
1323 ts->orderCount, NULL, ts->notifyData));
1324 /*@=noeffectuncon@*/
1326 if (fc == 0) continue;
1328 /* Extract file info for all files in this package from the database. */
1329 matches = xcalloc(fc, sizeof(*matches));
1330 if (rpmdbFindFpList(ts->rpmdb, fi->fps, matches, fc)) {
1331 psm->ts = rpmtsUnlink(ts, "tsRun (rpmFindFpList fail)");
1332 return 1; /* XXX WTFO? */
1336 if ((fi = tfiInit(fi, 0)) != NULL)
1337 while ((i = tfiNext(fi)) >= 0)
1338 numShared += dbiIndexSetCount(matches[i]);
1340 /* Build sorted file info list for this package. */
1341 shared = sharedList = xcalloc((numShared + 1), sizeof(*sharedList));
1343 if ((fi = tfiInit(fi, 0)) != NULL)
1344 while ((i = tfiNext(fi)) >= 0) {
1346 * Take care not to mark files as replaced in packages that will
1347 * have been removed before we will get here.
1349 for (j = 0; j < dbiIndexSetCount(matches[i]); j++) {
1351 ro = dbiIndexRecordOffset(matches[i], j);
1353 qi = teInitIterator(ts);
1354 while ((q = teNext(qi, TR_REMOVED)) != NULL) {
1356 /*@innerbreak@*/ break;
1357 if (teGetDBOffset(q) == ro)
1360 qi = teFreeIterator(qi);
1362 shared->pkgFileNum = i;
1363 shared->otherPkg = dbiIndexRecordOffset(matches[i], j);
1364 shared->otherFileNum = dbiIndexRecordFileNumber(matches[i], j);
1365 shared->isRemoved = (knownBad == ro);
1368 matches[i] = dbiFreeIndexSet(matches[i]);
1370 numShared = shared - sharedList;
1371 shared->otherPkg = -1;
1372 matches = _free(matches);
1374 /* Sort file info by other package index (otherPkg) */
1375 qsort(sharedList, numShared, sizeof(*shared), sharedCmp);
1377 /* For all files from this package that are in the database ... */
1378 for (i = 0; i < numShared; i = nexti) {
1381 shared = sharedList + i;
1383 /* Find the end of the files in the other package. */
1384 for (nexti = i + 1; nexti < numShared; nexti++) {
1385 if (sharedList[nexti].otherPkg != shared->otherPkg)
1386 /*@innerbreak@*/ break;
1389 /* Is this file from a package being removed? */
1391 if (ts->removedPackages != NULL)
1392 for (j = 0; j < ts->numRemovedPackages; j++) {
1393 if (ts->removedPackages[j] != shared->otherPkg)
1394 /*@innercontinue@*/ continue;
1396 /*@innerbreak@*/ break;
1399 /* Determine the fate of each file. */
1400 switch (teGetType(p)) {
1402 xx = handleInstInstalledFiles(ts, p, fi, shared, nexti - i,
1403 !(beingRemoved || (ts->ignoreSet & RPMPROB_FILTER_REPLACEOLDFILES)));
1404 /*@switchbreak@*/ break;
1407 xx = handleRmvdInstalledFiles(ts, fi, shared, nexti - i);
1408 /*@switchbreak@*/ break;
1414 /* Update disk space needs on each partition for this package. */
1415 handleOverlappedFiles(ts, p, fi);
1417 /* Check added package has sufficient space on each partition used. */
1418 switch (teGetType(p)) {
1420 if (!(ts->di && tfiGetFC(fi) > 0))
1421 /*@switchbreak@*/ break;
1422 for (i = 0; i < ts->filesystemCount; i++) {
1426 /* XXX Avoid FAT and other file systems that have not inodes. */
1427 if (dip->iavail <= 0)
1428 /*@innercontinue@*/ continue;
1430 if (adj_fs_blocks(dip->bneeded) > dip->bavail) {
1431 rpmProblemSetAppend(ts->probs, RPMPROB_DISKSPACE,
1432 teGetNEVR(p), teGetKey(p),
1433 ts->filesystems[i], NULL, NULL,
1434 (adj_fs_blocks(dip->bneeded) - dip->bavail) * dip->bsize);
1437 if (adj_fs_blocks(dip->ineeded) > dip->iavail) {
1438 rpmProblemSetAppend(ts->probs, RPMPROB_DISKNODES,
1439 teGetNEVR(p), teGetKey(p),
1440 ts->filesystems[i], NULL, NULL,
1441 (adj_fs_blocks(dip->ineeded) - dip->iavail));
1444 /*@switchbreak@*/ break;
1446 /*@switchbreak@*/ break;
1449 pi = teFreeIterator(pi);
1451 if (ts->chrootDone) {
1452 /*@-superuser -noeffect @*/
1454 /*@=superuser =noeffect @*/
1456 if (ts->rpmdb) ts->rpmdb->db_chrootDone = 0;
1458 chroot_prefix = NULL;
1460 xx = chdir(ts->currDir);
1463 /*@-noeffectuncon @*/ /* FIX: check rc */
1464 NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_STOP, 6, ts->orderCount,
1465 NULL, ts->notifyData));
1466 /*@=noeffectuncon @*/
1468 /* ===============================================
1469 * Free unused memory as soon as possible.
1471 pi = teInitIterator(ts);
1472 while ((p = teNextIterator(pi)) != NULL) {
1473 if ((fi = teiGetFi(pi)) == NULL)
1474 continue; /* XXX can't happen */
1475 if (tfiGetFC(fi) == 0)
1477 fi->fps = _free(fi->fps);
1479 pi = teFreeIterator(pi);
1485 /* ===============================================
1486 * If unfiltered problems exist, free memory and return.
1488 if ((ts->transFlags & RPMTRANS_FLAG_BUILD_PROBS)
1489 || (ts->probs->numProblems &&
1490 (okProbs != NULL || rpmProblemSetTrim(ts->probs, okProbs)))
1493 if (psm->ts != NULL)
1494 psm->ts = rpmtsUnlink(psm->ts, "tsRun (problems)");
1495 return ts->orderCount;
1498 /* ===============================================
1499 * Save removed files before erasing.
1501 if (ts->transFlags & (RPMTRANS_FLAG_DIRSTASH | RPMTRANS_FLAG_REPACKAGE)) {
1502 pi = teInitIterator(ts);
1503 while ((p = teNextIterator(pi)) != NULL) {
1505 switch (teGetType(p)) {
1507 /*@switchbreak@*/ break;
1509 if (!(ts->transFlags & RPMTRANS_FLAG_REPACKAGE))
1510 /*@switchbreak@*/ break;
1512 psm->fi = rpmfiLink(fi, "tsRepackage");
1513 xx = psmStage(psm, PSM_PKGSAVE);
1514 (void) rpmfiUnlink(fi, "tsRepackage");
1517 /*@switchbreak@*/ break;
1520 pi = teFreeIterator(pi);
1523 /* ===============================================
1524 * Install and remove packages.
1526 lastKey = (alKey)-2; /* erased packages have -1 */
1527 pi = teInitIterator(ts);
1528 /*@-branchstate@*/ /* FIX: fi reload needs work */
1529 while ((p = teNextIterator(pi)) != NULL) {
1535 if ((fi = teiGetFi(pi)) == NULL)
1536 continue; /* XXX can't happen */
1539 psm->fi = rpmfiLink(fi, "tsInstall");
1540 switch (teGetType(p)) {
1543 pkgKey = teGetAddedKey(p);
1545 rpmMessage(RPMMESS_DEBUG, "========== +++ %s\n", teGetNEVR(p));
1547 /*@-type@*/ /* FIX: transactionElement not opaque */
1549 /*@-noeffectuncon@*/ /* FIX: notify annotations */
1550 p->fd = ts->notify(fi->h, RPMCALLBACK_INST_OPEN_FILE, 0, 0,
1551 teGetKey(p), ts->notifyData);
1552 /*@=noeffectuncon@*/
1553 if (teGetFd(p) != NULL) {
1556 rpmrc = rpmReadPackageFile(ts, teGetFd(p),
1557 "rpmRunTransactions", &h);
1559 if (!(rpmrc == RPMRC_OK || rpmrc == RPMRC_BADSIZE)) {
1560 /*@-noeffectuncon@*/ /* FIX: notify annotations */
1561 p->fd = ts->notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE,
1563 teGetKey(p), ts->notifyData);
1564 /*@=noeffectuncon@*/
1568 if (teGetFd(p) != NULL) gotfd = 1;
1573 if (teGetFd(p) != NULL) {
1575 char * fstates = fi->fstates;
1576 fileAction * actions = fi->actions;
1580 (void) fiFree(fi, 0);
1582 fi->magic = TFIMAGIC;
1585 (void) fiNew(ts, fi, h, RPMTAG_BASENAMES, 1);
1586 fi->fstates = _free(fi->fstates);
1587 fi->fstates = fstates;
1588 fi->actions = _free(fi->actions);
1589 fi->actions = actions;
1593 if (teGetMultiLib(p))
1594 ts->transFlags |= RPMTRANS_FLAG_MULTILIB;
1596 if (psmStage(psm, PSM_PKGINSTALL)) {
1600 fi->h = headerFree(fi->h, "TR_ADDED fi->h free");
1606 h = headerFree(h, "TR_ADDED h free");
1609 /*@-noeffectuncon @*/ /* FIX: check rc */
1610 (void) ts->notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE, 0, 0,
1611 teGetKey(p), ts->notifyData);
1612 /*@=noeffectuncon @*/
1617 (void) fiFree(fi, 0);
1618 /*@switchbreak@*/ break;
1620 rpmMessage(RPMMESS_DEBUG, "========== --- %s\n", teGetNEVR(p));
1621 /* If install failed, then we shouldn't erase. */
1622 if (teGetDependsOnKey(p) != lastKey) {
1623 if (psmStage(psm, PSM_PKGERASE))
1626 (void) fiFree(fi, 0);
1627 /*@switchbreak@*/ break;
1629 xx = rpmdbSync(ts->rpmdb);
1630 (void) rpmfiUnlink(psm->fi, "tsInstall");
1635 pi = teFreeIterator(pi);
1637 psm->ts = rpmtsUnlink(psm->ts, "tsRun");
1639 /*@-nullstate@*/ /* FIX: ts->flList may be NULL */