2 * \file lib/transaction.c
8 #include <rpmmacro.h> /* XXX for rpmExpand */
11 #include "misc.h" /* XXX stripTrailingChar, splitString, currentDirectory */
14 /*@-redecl -exportheadervar@*/
16 extern const char * chroot_prefix;
17 /*@=redecl =exportheadervar@*/
19 /* XXX FIXME: merge with existing (broken?) tests in system.h */
20 /* portability fiddles */
21 #if STATFS_IN_SYS_STATVFS
23 # include <sys/statvfs.h>
24 #if defined(__LCLINT__)
25 /*@-declundef -exportheader -protoparammatch @*/ /* LCL: missing annotation */
26 extern int statvfs (const char * file, /*@out@*/ struct statvfs * buf)
27 /*@globals fileSystem @*/
28 /*@modifies *buf, fileSystem @*/;
29 /*@=declundef =exportheader =protoparammatch @*/
33 # if STATFS_IN_SYS_VFS
36 # if STATFS_IN_SYS_MOUNT
37 # include <sys/mount.h>
39 # if STATFS_IN_SYS_STATFS
40 # include <sys/statfs.h>
48 /*@access FD_t@*/ /* XXX compared with NULL */
49 /*@access Header@*/ /* XXX compared with NULL */
50 /*@access dbiIndexSet@*/
52 /*@access rpmTransactionSet@*/
55 /*@access rpmProblemSet@*/
56 /*@access rpmProblem@*/
60 struct diskspaceInfo {
61 dev_t dev; /*!< file system device number. */
62 signed long bneeded; /*!< no. of blocks needed. */
63 signed long ineeded; /*!< no. of inodes needed. */
64 int bsize; /*!< file system block size. */
65 signed long bavail; /*!< no. of blocks available. */
66 signed long iavail; /*!< no. of inodes available. */
70 * Adjust for root only reserved space. On linux e2fs, this is 5%.
72 #define adj_fs_blocks(_nb) (((_nb) * 21) / 20)
74 /* argon thought a shift optimization here was a waste of time... he's
76 #define BLOCK_ROUND(size, block) (((size) + (block) - 1) / (block))
78 #define XSTRCMP(a, b) ((!(a) && !(b)) || ((a) && (b) && !strcmp((a), (b))))
82 static /*@null@*/ void * freeFl(rpmTransactionSet ts,
83 /*@only@*/ /*@null@*/ TFI_t flList)
91 for (oc = 0, fi = flList; oc < ts->orderCount; oc++, fi++)
93 flList = _free(flList);
99 void rpmtransSetScriptFd(rpmTransactionSet ts, FD_t fd)
101 /*@-type@*/ /* FIX: cast? */
102 ts->scriptFd = (fd ? fdLink(fd, "rpmtransSetScriptFd") : NULL);
106 int rpmtransGetKeys(const rpmTransactionSet ts, const void *** ep, int * nep)
110 if (nep) *nep = ts->orderCount;
115 *ep = e = xmalloc(ts->orderCount * sizeof(*e));
116 for (oc = 0; oc < ts->orderCount; oc++, e++) {
117 switch (ts->order[oc].type) {
119 if (ts->addedPackages.list) {
120 struct availablePackage * alp;
121 alp = ts->addedPackages.list + ts->order[oc].u.addedIndex;
123 /*@switchbreak@*/ break;
128 /*@-mods@*/ /* FIX: double indirection. */
131 /*@switchbreak@*/ break;
141 static rpmProblemSet psCreate(void)
146 probs = xcalloc(1, sizeof(*probs)); /* XXX memory leak */
147 probs->numProblems = probs->numProblemsAlloced = 0;
155 static void psAppend(rpmProblemSet probs, rpmProblemType type,
156 const struct availablePackage * alp,
157 const char * dn, const char *bn,
158 Header altH, unsigned long ulong1)
159 /*@modifies *probs, alp @*/
164 if (probs->numProblems == probs->numProblemsAlloced) {
165 if (probs->numProblemsAlloced)
166 probs->numProblemsAlloced *= 2;
168 probs->numProblemsAlloced = 2;
169 probs->probs = xrealloc(probs->probs,
170 probs->numProblemsAlloced * sizeof(*probs->probs));
173 p = probs->probs + probs->numProblems;
174 probs->numProblems++;
175 memset(p, 0, sizeof(*p));
181 p->ignoreProblem = 0;
189 t = xcalloc(1, (dn ? strlen(dn) : 0) + (bn ? strlen(bn) : 0) + 1);
190 if (dn) t = stpcpy(t, dn);
191 if (bn) t = stpcpy(t, bn);
195 p->h = headerLink(alp->h);
197 t = xcalloc(1, strlen(alp->name) +
198 strlen(alp->version) +
199 strlen(alp->release) + sizeof("--"));
200 t = stpcpy(t, alp->name);
202 t = stpcpy(t, alp->version);
204 t = stpcpy(t, alp->release);
208 const char * n, * v, * r;
209 (void) headerNVR(altH, &n, &v, &r);
211 t = xcalloc(1, strlen(n) + strlen(v) + strlen(r) + sizeof("--"));
223 static int archOkay(Header h)
229 /* make sure we're trying to install this on the proper architecture */
230 (void) headerGetEntry(h, RPMTAG_ARCH, &type, (void **) &pkgArch, &count);
232 if (type == RPM_INT8_TYPE) {
236 /* old arch handling */
237 rpmGetArchInfo(NULL, &archNum);
238 pkgArchNum = pkgArch;
239 if (archNum != *pkgArchNum) {
245 /* new arch handling */
246 if (!rpmMachineScore(RPM_MACHTABLE_INSTARCH, pkgArch)) {
256 static int osOkay(Header h)
262 /* make sure we're trying to install this on the proper os */
263 (void) headerGetEntry(h, RPMTAG_OS, &type, (void **) &pkgOs, &count);
265 if (type == RPM_INT8_TYPE) {
266 /* v1 packages and v2 packages both used improper OS numbers, so just
267 deal with it hope things work */
272 /* new os handling */
273 if (!rpmMachineScore(RPM_MACHTABLE_INSTOS, pkgOs)) {
281 void rpmProblemSetFree(rpmProblemSet probs)
285 for (i = 0; i < probs->numProblems; i++) {
286 rpmProblem p = probs->probs + i;
287 p->h = headerFree(p->h);
288 p->pkgNEVR = _free(p->pkgNEVR);
289 p->altNEVR = _free(p->altNEVR);
290 p->str1 = _free(p->str1);
298 static /*@observer@*/ const char *const ftstring (fileTypes ft)
302 case XDIR: return "directory";
303 case CDEV: return "char dev";
304 case BDEV: return "block dev";
305 case LINK: return "link";
306 case SOCK: return "sock";
307 case PIPE: return "fifo/pipe";
308 case REG: return "file";
309 default: return "unknown file type";
316 static fileTypes whatis(uint_16 mode)
319 if (S_ISDIR(mode)) return XDIR;
320 if (S_ISCHR(mode)) return CDEV;
321 if (S_ISBLK(mode)) return BDEV;
322 if (S_ISLNK(mode)) return LINK;
323 if (S_ISSOCK(mode)) return SOCK;
324 if (S_ISFIFO(mode)) return PIPE;
328 #define alloca_strdup(_s) strcpy(alloca(strlen(_s)+1), (_s))
331 * Relocate files in header.
332 * @todo multilib file dispositions need to be checked.
333 * @param ts transaction set
334 * @param fi transaction element file info
335 * @param alp available package
336 * @param origH package header
337 * @param actions file dispositions
338 * @return header with relocated files
340 static Header relocateFileList(const rpmTransactionSet ts, TFI_t fi,
341 struct availablePackage * alp,
342 Header origH, fileAction * actions)
343 /*@modifies ts, fi, alp, origH, actions @*/
348 HFD_t hfd = (fi->hfd ? fi->hfd : headerFreeData);
349 static int _printed = 0;
350 int allowBadRelocate = (ts->ignoreSet & RPMPROB_FILTER_FORCERELOCATE);
351 rpmRelocation * rawRelocations = alp->relocs;
352 rpmRelocation * relocations = NULL;
354 const char ** validRelocations;
355 rpmTagType validType;
357 const char ** baseNames;
358 const char ** dirNames;
360 int_32 * newDirIndexes;
363 uint_32 * fFlags = NULL;
364 uint_16 * fModes = NULL;
370 int haveRelocatedFile = 0;
375 if (!hge(origH, RPMTAG_PREFIXES, &validType,
376 (void **) &validRelocations, &numValid))
381 while (rawRelocations[numRelocations].newPath ||
382 rawRelocations[numRelocations].oldPath)
386 * If no relocations are specified (usually the case), then return the
387 * original header. If there are prefixes, however, then INSTPREFIXES
388 * should be added, but, since relocateFileList() can be called more
389 * than once for the same header, don't bother if already present.
391 if (rawRelocations == NULL || numRelocations == 0) {
393 if (!headerIsEntry(origH, RPMTAG_INSTPREFIXES))
394 xx = hae(origH, RPMTAG_INSTPREFIXES,
395 validType, validRelocations, numValid);
396 validRelocations = hfd(validRelocations, validType);
398 /* XXX FIXME multilib file actions need to be checked. */
399 return headerLink(origH);
402 h = headerLink(origH);
404 relocations = alloca(sizeof(*relocations) * numRelocations);
406 /* Build sorted relocation list from raw relocations. */
407 for (i = 0; i < numRelocations; i++) {
411 * Default relocations (oldPath == NULL) are handled in the UI,
414 if (rawRelocations[i].oldPath == NULL) continue; /* XXX can't happen */
416 /* FIXME: Trailing /'s will confuse us greatly. Internal ones will
417 too, but those are more trouble to fix up. :-( */
418 t = alloca_strdup(rawRelocations[i].oldPath);
420 relocations[i].oldPath = (t[0] == '/' && t[1] == '\0')
422 : stripTrailingChar(t, '/');
425 /* An old path w/o a new path is valid, and indicates exclusion */
426 if (rawRelocations[i].newPath) {
429 t = alloca_strdup(rawRelocations[i].newPath);
431 relocations[i].newPath = (t[0] == '/' && t[1] == '\0')
433 : stripTrailingChar(t, '/');
436 /*@-nullpass@*/ /* FIX: relocations[i].oldPath == NULL */
437 /* Verify that the relocation's old path is in the header. */
438 for (j = 0; j < numValid; j++)
439 if (!strcmp(validRelocations[j], relocations[i].oldPath))
440 /*@innerbreak@*/ break;
441 /* XXX actions check prevents problem from being appended twice. */
442 if (j == numValid && !allowBadRelocate && actions)
443 psAppend(ts->probs, RPMPROB_BADRELOCATE, alp,
444 relocations[i].oldPath, NULL, NULL, 0);
446 strlen(relocations[i].newPath) - strlen(relocations[i].oldPath);
452 relocations[i].newPath = NULL;
456 /* stupid bubble sort, but it's probably faster here */
457 for (i = 0; i < numRelocations; i++) {
460 for (j = 1; j < numRelocations; j++) {
461 rpmRelocation tmpReloc;
462 if (relocations[j - 1].oldPath == NULL || /* XXX can't happen */
463 relocations[j ].oldPath == NULL || /* XXX can't happen */
464 strcmp(relocations[j - 1].oldPath, relocations[j].oldPath) <= 0)
465 /*@innercontinue@*/ continue;
466 /*@-usereleased@*/ /* LCL: ??? */
467 tmpReloc = relocations[j - 1];
468 relocations[j - 1] = relocations[j];
469 relocations[j] = tmpReloc;
473 if (!madeSwap) break;
478 rpmMessage(RPMMESS_DEBUG, _("========== relocations\n"));
479 for (i = 0; i < numRelocations; i++) {
480 if (relocations[i].oldPath == NULL) continue; /* XXX can't happen */
481 if (relocations[i].newPath == NULL)
482 rpmMessage(RPMMESS_DEBUG, _("%5d exclude %s\n"),
483 i, relocations[i].oldPath);
485 rpmMessage(RPMMESS_DEBUG, _("%5d relocate %s -> %s\n"),
486 i, relocations[i].oldPath, relocations[i].newPath);
490 /* Add relocation values to the header */
492 const char ** actualRelocations;
495 actualRelocations = xmalloc(numValid * sizeof(*actualRelocations));
497 for (i = 0; i < numValid; i++) {
498 for (j = 0; j < numRelocations; j++) {
499 if (relocations[j].oldPath == NULL || /* XXX can't happen */
500 strcmp(validRelocations[i], relocations[j].oldPath))
501 /*@innercontinue@*/ continue;
502 /* On install, a relocate to NULL means skip the path. */
503 if (relocations[j].newPath) {
504 actualRelocations[numActual] = relocations[j].newPath;
507 /*@innerbreak@*/ break;
509 if (j == numRelocations) {
510 actualRelocations[numActual] = validRelocations[i];
516 xx = hae(h, RPMTAG_INSTPREFIXES, RPM_STRING_ARRAY_TYPE,
517 (void **) actualRelocations, numActual);
519 actualRelocations = _free(actualRelocations);
520 validRelocations = hfd(validRelocations, validType);
523 xx = hge(h, RPMTAG_BASENAMES, NULL, (void **) &baseNames, &fileCount);
524 xx = hge(h, RPMTAG_DIRINDEXES, NULL, (void **) &dirIndexes, NULL);
525 xx = hge(h, RPMTAG_DIRNAMES, NULL, (void **) &dirNames, &dirCount);
526 xx = hge(h, RPMTAG_FILEFLAGS, NULL, (void **) &fFlags, NULL);
527 xx = hge(h, RPMTAG_FILEMODES, NULL, (void **) &fModes, NULL);
529 skipDirList = alloca(dirCount * sizeof(*skipDirList));
530 memset(skipDirList, 0, dirCount * sizeof(*skipDirList));
532 newDirIndexes = alloca(sizeof(*newDirIndexes) * fileCount);
533 memcpy(newDirIndexes, dirIndexes, sizeof(*newDirIndexes) * fileCount);
534 dirIndexes = newDirIndexes;
537 * For all relocations, we go through sorted file/relocation lists
538 * backwards so that /usr/local relocations take precedence over /usr
542 /* Relocate individual paths. */
544 for (i = fileCount - 1; i >= 0; i--) {
549 * If only adding libraries of different arch into an already
550 * installed package, skip all other files.
552 if (alp->multiLib && !isFileMULTILIB((fFlags[i]))) {
554 actions[i] = FA_SKIPMULTILIB;
555 rpmMessage(RPMMESS_DEBUG, _("excluding multilib path %s%s\n"),
556 dirNames[dirIndexes[i]], baseNames[i]);
562 strlen(dirNames[dirIndexes[i]]) + strlen(baseNames[i]) + 1;
564 if (len >= fileAlloced) {
565 fileAlloced = len * 2;
566 fn = xrealloc(fn, fileAlloced);
570 fnlen = stpcpy( stpcpy(fn, dirNames[dirIndexes[i]]), baseNames[i]) - fn;
573 * See if this file path needs relocating.
576 * XXX FIXME: Would a bsearch of the (already sorted)
577 * relocation list be a good idea?
579 for (j = numRelocations - 1; j >= 0; j--) {
580 if (relocations[j].oldPath == NULL) /* XXX can't happen */
581 /*@innercontinue@*/ continue;
582 len = strcmp(relocations[j].oldPath, "/")
583 ? strlen(relocations[j].oldPath)
587 /*@innercontinue@*/ continue;
589 * Only subdirectories or complete file paths may be relocated. We
590 * don't check for '\0' as our directory names all end in '/'.
592 if (!(fn[len] == '/' || fnlen == len))
593 /*@innercontinue@*/ continue;
595 if (strncmp(relocations[j].oldPath, fn, len))
596 /*@innercontinue@*/ continue;
597 /*@innerbreak@*/ break;
601 ft = whatis(fModes[i]);
603 /* On install, a relocate to NULL means skip the path. */
604 if (relocations[j].newPath == NULL) {
606 /* Start with the parent, looking for directory to exclude. */
607 for (j = dirIndexes[i]; j < dirCount; j++) {
608 len = strlen(dirNames[j]) - 1;
609 while (len > 0 && dirNames[j][len-1] == '/') len--;
611 /*@innercontinue@*/ continue;
612 if (strncmp(fn, dirNames[j], fnlen))
613 /*@innercontinue@*/ continue;
614 /*@innerbreak@*/ break;
620 actions[i] = FA_SKIPNSTATE;
621 rpmMessage(RPMMESS_DEBUG, _("excluding %s %s\n"),
627 /* Relocation on full paths only, please. */
628 if (fnlen != len) continue;
631 rpmMessage(RPMMESS_DEBUG, _("relocating %s to %s\n"),
632 fn, relocations[j].newPath);
635 strcpy(fn, relocations[j].newPath);
636 { char * te = strrchr(fn, '/');
638 if (te > fn) te++; /* root is special */
641 te = fn + strlen(fn);
642 /*@-nullpass -nullderef@*/ /* LCL: te != NULL here. */
643 if (strcmp(baseNames[i], te)) /* basename changed too? */
644 baseNames[i] = alloca_strdup(te);
645 *te = '\0'; /* terminate new directory name */
646 /*@=nullpass =nullderef@*/
649 /* Does this directory already exist in the directory list? */
650 for (j = 0; j < dirCount; j++) {
651 if (fnlen != strlen(dirNames[j]))
652 /*@innercontinue@*/ continue;
653 if (strncmp(fn, dirNames[j], fnlen))
654 /*@innercontinue@*/ continue;
655 /*@innerbreak@*/ break;
663 /* Creating new paths is a pita */
664 if (!haveRelocatedFile) {
665 const char ** newDirList;
667 haveRelocatedFile = 1;
668 newDirList = xmalloc((dirCount + 1) * sizeof(*newDirList));
669 for (j = 0; j < dirCount; j++)
670 newDirList[j] = alloca_strdup(dirNames[j]);
671 dirNames = hfd(dirNames, RPM_STRING_ARRAY_TYPE);
672 dirNames = newDirList;
674 dirNames = xrealloc(dirNames,
675 sizeof(*dirNames) * (dirCount + 1));
678 dirNames[dirCount] = alloca_strdup(fn);
679 dirIndexes[i] = dirCount;
683 /* Finish off by relocating directories. */
684 for (i = dirCount - 1; i >= 0; i--) {
685 for (j = numRelocations - 1; j >= 0; j--) {
687 if (relocations[j].oldPath == NULL) /* XXX can't happen */
688 /*@innercontinue@*/ continue;
689 len = strcmp(relocations[j].oldPath, "/")
690 ? strlen(relocations[j].oldPath)
693 if (len && strncmp(relocations[j].oldPath, dirNames[i], len))
694 /*@innercontinue@*/ continue;
697 * Only subdirectories or complete file paths may be relocated. We
698 * don't check for '\0' as our directory names all end in '/'.
700 if (dirNames[i][len] != '/')
701 /*@innercontinue@*/ continue;
703 if (relocations[j].newPath) { /* Relocate the path */
704 const char * s = relocations[j].newPath;
705 char * t = alloca(strlen(s) + strlen(dirNames[i]) - len + 1);
707 (void) stpcpy( stpcpy(t, s) , dirNames[i] + len);
709 rpmMessage(RPMMESS_DEBUG,
710 _("relocating directory %s to %s\n"), dirNames[i], t);
717 /* Save original filenames in header and replace (relocated) filenames. */
724 xx = hge(h, RPMTAG_BASENAMES, &t, &p, &c);
725 xx = hae(h, RPMTAG_ORIGBASENAMES, t, p, c);
729 xx = hge(h, RPMTAG_DIRNAMES, &t, &p, &c);
730 xx = hae(h, RPMTAG_ORIGDIRNAMES, t, p, c);
734 xx = hge(h, RPMTAG_DIRINDEXES, &t, &p, &c);
735 xx = hae(h, RPMTAG_ORIGDIRINDEXES, t, p, c);
738 xx = hme(h, RPMTAG_BASENAMES, RPM_STRING_ARRAY_TYPE,
739 baseNames, fileCount);
740 fi->bnl = hfd(fi->bnl, RPM_STRING_ARRAY_TYPE);
741 xx = hge(h, RPMTAG_BASENAMES, NULL, (void **) &fi->bnl, &fi->fc);
743 xx = hme(h, RPMTAG_DIRNAMES, RPM_STRING_ARRAY_TYPE,
745 fi->dnl = hfd(fi->dnl, RPM_STRING_ARRAY_TYPE);
746 xx = hge(h, RPMTAG_DIRNAMES, NULL, (void **) &fi->dnl, &fi->dc);
748 xx = hme(h, RPMTAG_DIRINDEXES, RPM_INT32_TYPE,
749 dirIndexes, fileCount);
750 xx = hge(h, RPMTAG_DIRINDEXES, NULL, (void **) &fi->dil, NULL);
753 baseNames = hfd(baseNames, RPM_STRING_ARRAY_TYPE);
754 dirNames = hfd(dirNames, RPM_STRING_ARRAY_TYPE);
762 * Filter a problem set.
763 * As the problem sets are generated in an order solely dependent
764 * on the ordering of the packages in the transaction, and that
765 * ordering can't be changed, the problem sets must be parallel to
766 * one another. Additionally, the filter set must be a subset of the
767 * target set, given the operations available on transaction set.
768 * This is good, as it lets us perform this trim in linear time, rather
769 * then logarithmic or quadratic.
771 * @param filter filter
772 * @param target problem set
773 * @return 0 no problems, 1 if problems remain
775 static int psTrim(rpmProblemSet filter, rpmProblemSet target)
776 /*@modifies target @*/
778 rpmProblem f = filter->probs;
779 rpmProblem t = target->probs;
783 while ((f - filter->probs) < filter->numProblems) {
784 if (!f->ignoreProblem) {
788 while ((t - target->probs) < target->numProblems) {
789 /*@-nullpass@*/ /* LCL: looks good to me */
790 if (f->h == t->h && f->type == t->type && t->key == f->key &&
791 XSTRCMP(f->str1, t->str1))
792 /*@innerbreak@*/ break;
798 if ((t - target->probs) == target->numProblems) {
799 /* this can't happen ;-) let's be sane if it doesn though */
803 t->ignoreProblem = f->ignoreProblem;
808 if ((t - target->probs) < target->numProblems)
816 static int sharedCmp(const void * one, const void * two)
819 const struct sharedFileInfo * a = one;
820 const struct sharedFileInfo * b = two;
822 if (a->otherPkg < b->otherPkg)
824 else if (a->otherPkg > b->otherPkg)
832 static fileAction decideFileFate(const char * dirName,
833 const char * baseName, short dbMode,
834 const char * dbMd5, const char * dbLink, short newMode,
835 const char * newMd5, const char * newLink, int newFlags,
836 rpmtransFlags transFlags)
837 /*@globals fileSystem @*/
838 /*@modifies fileSystem @*/
841 const char * dbAttr, * newAttr;
842 fileTypes dbWhat, newWhat, diskWhat;
845 int save = (newFlags & RPMFILE_NOREPLACE) ? FA_ALTNAME : FA_SAVE;
846 char * filespec = alloca(strlen(dirName) + strlen(baseName) + 1);
848 (void) stpcpy( stpcpy(filespec, dirName), baseName);
850 if (lstat(filespec, &sb)) {
852 * The file doesn't exist on the disk. Create it unless the new
853 * package has marked it as missingok, or allfiles is requested.
855 if (!(transFlags & RPMTRANS_FLAG_ALLFILES) &&
856 (newFlags & RPMFILE_MISSINGOK)) {
857 rpmMessage(RPMMESS_DEBUG, _("%s skipped due to missingok flag\n"),
865 diskWhat = whatis(sb.st_mode);
866 dbWhat = whatis(dbMode);
867 newWhat = whatis(newMode);
869 /* RPM >= 2.3.10 shouldn't create config directories -- we'll ignore
870 them in older packages as well */
871 if (newWhat == XDIR) {
875 if (diskWhat != newWhat) {
877 } else if (newWhat != dbWhat && diskWhat != dbWhat) {
879 } else if (dbWhat != newWhat) {
881 } else if (dbWhat != LINK && dbWhat != REG) {
886 rc = mdfile(filespec, buffer);
889 /* assume the file has been removed, don't freak */
894 } else /* dbWhat == LINK */ {
895 memset(buffer, 0, sizeof(buffer));
896 i = readlink(filespec, buffer, sizeof(buffer) - 1);
898 /* assume the file has been removed, don't freak */
905 /* this order matters - we'd prefer to CREATE the file if at all
906 possible in case something else (like the timestamp) has changed */
908 if (!strcmp(dbAttr, buffer)) {
909 /* this config file has never been modified, so just replace it */
913 if (!strcmp(dbAttr, newAttr)) {
914 /* this file is the same in all versions of this package */
919 * The config file on the disk has been modified, but
920 * the ones in the two packages are different. It would
921 * be nice if RPM was smart enough to at least try and
922 * merge the difference ala CVS, but...
929 static int filecmp(short mode1, const char * md51, const char * link1,
930 short mode2, const char * md52, const char * link2)
933 fileTypes what1 = whatis(mode1);
934 fileTypes what2 = whatis(mode2);
936 if (what1 != what2) return 1;
939 return strcmp(link1, link2);
940 else if (what1 == REG)
941 return strcmp(md51, md52);
948 /* XXX ts->{probs,rpmdb} modified, could be const ... ts */
949 static int handleInstInstalledFiles(const rpmTransactionSet ts, TFI_t fi,
950 struct sharedFileInfo * shared,
951 int sharedCount, int reportConflicts)
952 /*@globals fileSystem @*/
953 /*@modifies ts, fi, fileSystem @*/
956 HFD_t hfd = (fi->hfd ? fi->hfd : headerFreeData);
957 rpmdb db = ts->rpmdb;
958 rpmProblemSet probs = ts->probs;
959 rpmtransFlags transFlags = ts->transFlags;
960 rpmTagType oltype, omtype;
963 const char ** otherMd5s;
964 const char ** otherLinks;
965 const char * otherStates;
966 uint_32 * otherFlags;
967 uint_32 * otherSizes;
968 uint_16 * otherModes;
972 rpmdbMatchIterator mi;
974 mi = rpmdbInitIterator(db, RPMDBI_PACKAGES, &shared->otherPkg, sizeof(shared->otherPkg));
975 h = rpmdbNextIterator(mi);
977 mi = rpmdbFreeIterator(mi);
981 xx = hge(h, RPMTAG_FILEMD5S, &omtype, (void **) &otherMd5s, NULL);
982 xx = hge(h, RPMTAG_FILELINKTOS, &oltype, (void **) &otherLinks, NULL);
983 xx = hge(h, RPMTAG_FILESTATES, NULL, (void **) &otherStates, NULL);
984 xx = hge(h, RPMTAG_FILEMODES, NULL, (void **) &otherModes, NULL);
985 xx = hge(h, RPMTAG_FILEFLAGS, NULL, (void **) &otherFlags, NULL);
986 xx = hge(h, RPMTAG_FILESIZES, NULL, (void **) &otherSizes, NULL);
988 fi->replaced = xmalloc(sharedCount * sizeof(*fi->replaced));
990 for (i = 0; i < sharedCount; i++, shared++) {
991 int otherFileNum, fileNum;
992 otherFileNum = shared->otherFileNum;
993 fileNum = shared->pkgFileNum;
995 /* XXX another tedious segfault, assume file state normal. */
996 if (otherStates && otherStates[otherFileNum] != RPMFILE_STATE_NORMAL)
999 if (XFA_SKIPPING(fi->actions[fileNum]))
1002 if (filecmp(otherModes[otherFileNum],
1003 otherMd5s[otherFileNum],
1004 otherLinks[otherFileNum],
1005 fi->fmodes[fileNum],
1007 fi->flinks[fileNum])) {
1008 if (reportConflicts)
1009 psAppend(probs, RPMPROB_FILE_CONFLICT, fi->ap,
1010 fi->dnl[fi->dil[fileNum]], fi->bnl[fileNum], h, 0);
1011 if (!(otherFlags[otherFileNum] | fi->fflags[fileNum])
1014 if (!shared->isRemoved)
1015 fi->replaced[numReplaced++] = *shared;
1020 if ((otherFlags[otherFileNum] | fi->fflags[fileNum]) & RPMFILE_CONFIG) {
1021 fi->actions[fileNum] = decideFileFate(
1022 fi->dnl[fi->dil[fileNum]],
1024 otherModes[otherFileNum],
1025 otherMd5s[otherFileNum],
1026 otherLinks[otherFileNum],
1027 fi->fmodes[fileNum],
1029 fi->flinks[fileNum],
1030 fi->fflags[fileNum],
1034 fi->replacedSizes[fileNum] = otherSizes[otherFileNum];
1037 otherMd5s = hfd(otherMd5s, omtype);
1038 otherLinks = hfd(otherLinks, oltype);
1039 mi = rpmdbFreeIterator(mi);
1041 fi->replaced = xrealloc(fi->replaced, /* XXX memory leak */
1042 sizeof(*fi->replaced) * (numReplaced + 1));
1043 fi->replaced[numReplaced].otherPkg = 0;
1050 /* XXX ts->rpmdb modified, could be const ... ts */
1051 static int handleRmvdInstalledFiles(const rpmTransactionSet ts, TFI_t fi,
1052 struct sharedFileInfo * shared, int sharedCount)
1053 /*@globals fileSystem @*/
1054 /*@modifies ts, fi, fileSystem @*/
1056 HGE_t hge = fi->hge;
1057 rpmdb db = ts->rpmdb;
1059 const char * otherStates;
1062 rpmdbMatchIterator mi;
1064 mi = rpmdbInitIterator(db, RPMDBI_PACKAGES,
1065 &shared->otherPkg, sizeof(shared->otherPkg));
1066 h = rpmdbNextIterator(mi);
1068 mi = rpmdbFreeIterator(mi);
1072 xx = hge(h, RPMTAG_FILESTATES, NULL, (void **) &otherStates, NULL);
1074 for (i = 0; i < sharedCount; i++, shared++) {
1075 int otherFileNum, fileNum;
1076 otherFileNum = shared->otherFileNum;
1077 fileNum = shared->pkgFileNum;
1079 if (otherStates[otherFileNum] != RPMFILE_STATE_NORMAL)
1082 fi->actions[fileNum] = FA_SKIP;
1085 mi = rpmdbFreeIterator(mi);
1091 * Update disk space needs on each partition for this package.
1093 /* XXX ts->{probs,di} modified, could be const ... ts */
1094 static void handleOverlappedFiles(const rpmTransactionSet ts, TFI_t fi)
1095 /*@globals fileSystem @*/
1096 /*@modifies ts, fi, fileSystem @*/
1098 struct diskspaceInfo * dsl = ts->di;
1099 rpmProblemSet probs = (ts->ignoreSet & RPMPROB_FILTER_REPLACENEWFILES)
1101 hashTable ht = ts->ht;
1102 struct diskspaceInfo * ds = NULL;
1103 uint_32 fixupSize = 0;
1104 char * filespec = NULL;
1105 int fileSpecAlloced = 0;
1108 for (i = 0; i < fi->fc; i++) {
1109 int otherPkgNum, otherFileNum;
1113 if (XFA_SKIPPING(fi->actions[i]))
1116 j = strlen(fi->dnl[fi->dil[i]]) + strlen(fi->bnl[i]) + 1;
1118 if (j > fileSpecAlloced) {
1119 fileSpecAlloced = j * 2;
1120 filespec = xrealloc(filespec, fileSpecAlloced);
1124 (void) stpcpy( stpcpy( filespec, fi->dnl[fi->dil[i]]), fi->bnl[i]);
1128 while (ds->bsize && ds->dev != fi->fps[i].entry->dev) ds++;
1129 if (!ds->bsize) ds = NULL;
1134 * Retrieve all records that apply to this file. Note that the
1135 * file info records were built in the same order as the packages
1136 * will be installed and removed so the records for an overlapped
1137 * files will be sorted in exactly the same order.
1139 (void) htGetEntry(ht, &fi->fps[i], (const void ***) &recs, &numRecs, NULL);
1142 * If this package is being added, look only at other packages
1143 * being added -- removed packages dance to a different tune.
1144 * If both this and the other package are being added, overlapped
1145 * files must be identical (or marked as a conflict). The
1146 * disposition of already installed config files leads to
1147 * a small amount of extra complexity.
1149 * If this package is being removed, then there are two cases that
1150 * need to be worried about:
1151 * If the other package is being added, then skip any overlapped files
1152 * so that this package removal doesn't nuke the overlapped files
1153 * that were just installed.
1154 * If both this and the other package are being removed, then each
1155 * file removal from preceding packages needs to be skipped so that
1156 * the file removal occurs only on the last occurence of an overlapped
1157 * file in the transaction set.
1161 /* Locate this overlapped file in the set of added/removed packages. */
1162 for (j = 0; j < numRecs && recs[j] != fi; j++)
1165 /* Find what the previous disposition of this file was. */
1166 otherFileNum = -1; /* keep gcc quiet */
1167 for (otherPkgNum = j - 1; otherPkgNum >= 0; otherPkgNum--) {
1168 /* Added packages need only look at other added packages. */
1169 if (fi->type == TR_ADDED && recs[otherPkgNum]->type != TR_ADDED)
1170 /*@innercontinue@*/ continue;
1172 /* TESTME: there are more efficient searches in the world... */
1173 for (otherFileNum = 0; otherFileNum < recs[otherPkgNum]->fc;
1176 /* If the addresses are the same, so are the values. */
1177 if ((fi->fps + i) == (recs[otherPkgNum]->fps + otherFileNum))
1178 /*@innerbreak@*/ break;
1180 /* Otherwise, compare fingerprints by value. */
1181 /*@-nullpass@*/ /* LCL: looks good to me */
1182 if (FP_EQUAL(fi->fps[i], recs[otherPkgNum]->fps[otherFileNum]))
1183 /*@innerbreak@*/ break;
1187 /* XXX is this test still necessary? */
1188 if (recs[otherPkgNum]->actions[otherFileNum] != FA_UNKNOWN)
1189 /*@innerbreak@*/ break;
1195 if (otherPkgNum < 0) {
1196 /* XXX is this test still necessary? */
1197 if (fi->actions[i] != FA_UNKNOWN)
1198 /*@switchbreak@*/ break;
1199 if ((fi->fflags[i] & RPMFILE_CONFIG) &&
1200 !lstat(filespec, &sb)) {
1201 /* Here is a non-overlapped pre-existing config file. */
1202 fi->actions[i] = (fi->fflags[i] & RPMFILE_NOREPLACE)
1203 ? FA_ALTNAME : FA_BACKUP;
1205 fi->actions[i] = FA_CREATE;
1207 /*@switchbreak@*/ break;
1210 /* Mark added overlapped non-identical files as a conflict. */
1211 if (probs && filecmp(recs[otherPkgNum]->fmodes[otherFileNum],
1212 recs[otherPkgNum]->fmd5s[otherFileNum],
1213 recs[otherPkgNum]->flinks[otherFileNum],
1217 psAppend(probs, RPMPROB_NEW_FILE_CONFLICT, fi->ap,
1218 filespec, NULL, recs[otherPkgNum]->ap->h, 0);
1221 /* Try to get the disk accounting correct even if a conflict. */
1222 fixupSize = recs[otherPkgNum]->fsizes[otherFileNum];
1224 if ((fi->fflags[i] & RPMFILE_CONFIG) && !lstat(filespec, &sb)) {
1225 /* Here is an overlapped pre-existing config file. */
1226 fi->actions[i] = (fi->fflags[i] & RPMFILE_NOREPLACE)
1227 ? FA_ALTNAME : FA_SKIP;
1229 fi->actions[i] = FA_CREATE;
1231 } /*@switchbreak@*/ break;
1233 if (otherPkgNum >= 0) {
1234 /* Here is an overlapped added file we don't want to nuke. */
1235 if (recs[otherPkgNum]->actions[otherFileNum] != FA_ERASE) {
1236 /* On updates, don't remove files. */
1237 fi->actions[i] = FA_SKIP;
1238 /*@switchbreak@*/ break;
1240 /* Here is an overlapped removed file: skip in previous. */
1241 recs[otherPkgNum]->actions[otherFileNum] = FA_SKIP;
1243 if (XFA_SKIPPING(fi->actions[i]))
1244 /*@switchbreak@*/ break;
1245 if (fi->fstates && fi->fstates[i] != RPMFILE_STATE_NORMAL)
1246 /*@switchbreak@*/ break;
1247 if (!(S_ISREG(fi->fmodes[i]) && (fi->fflags[i] & RPMFILE_CONFIG))) {
1248 fi->actions[i] = FA_ERASE;
1249 /*@switchbreak@*/ break;
1252 /* Here is a pre-existing modified config file that needs saving. */
1254 if (!mdfile(filespec, mdsum) && strcmp(fi->fmd5s[i], mdsum)) {
1255 fi->actions[i] = FA_BACKUP;
1256 /*@switchbreak@*/ break;
1259 fi->actions[i] = FA_ERASE;
1260 /*@switchbreak@*/ break;
1264 uint_32 s = BLOCK_ROUND(fi->fsizes[i], ds->bsize);
1266 switch (fi->actions[i]) {
1272 /*@switchbreak@*/ break;
1275 * FIXME: If two packages share a file (same md5sum), and
1276 * that file is being replaced on disk, will ds->bneeded get
1277 * decremented twice? Quite probably!
1281 ds->bneeded -= BLOCK_ROUND(fi->replacedSizes[i], ds->bsize);
1282 /*@switchbreak@*/ break;
1287 /*@switchbreak@*/ break;
1290 /*@switchbreak@*/ break;
1293 ds->bneeded -= BLOCK_ROUND(fixupSize, ds->bsize);
1296 if (filespec) free(filespec);
1301 static int ensureOlder(struct availablePackage * alp, Header old,
1302 rpmProblemSet probs)
1303 /*@modifies alp, probs @*/
1307 if (old == NULL) return 1;
1309 result = rpmVersionCompare(old, alp->h);
1312 else if (result > 0) {
1314 psAppend(probs, RPMPROB_OLDPACKAGE, alp, NULL, NULL, old, 0);
1322 static void skipFiles(const rpmTransactionSet ts, TFI_t fi)
1323 /*@globals rpmGlobalMacroContext @*/
1324 /*@modifies fi, rpmGlobalMacroContext @*/
1326 int noDocs = (ts->transFlags & RPMTRANS_FLAG_NODOCS);
1327 char ** netsharedPaths = NULL;
1328 const char ** languages;
1329 const char * dn, * bn;
1330 int dnlen, bnlen, ix;
1337 noDocs = rpmExpandNumeric("%{_excludedocs}");
1339 { const char *tmpPath = rpmExpand("%{_netsharedpath}", NULL);
1341 if (tmpPath && *tmpPath != '%')
1342 netsharedPaths = splitString(tmpPath, strlen(tmpPath), ':');
1344 tmpPath = _free(tmpPath);
1347 s = rpmExpand("%{_install_langs}", NULL);
1349 if (!(s && *s != '%'))
1352 languages = (const char **) splitString(s, strlen(s), ':');
1358 /* Compute directory refcount, skip directory if now empty. */
1359 drc = alloca(fi->dc * sizeof(*drc));
1360 memset(drc, 0, fi->dc * sizeof(*drc));
1361 dff = alloca(fi->dc * sizeof(*dff));
1362 memset(dff, 0, fi->dc * sizeof(*dff));
1364 for (i = 0; i < fi->fc; i++) {
1375 /* Don't bother with skipped files */
1376 if (XFA_SKIPPING(fi->actions[i])) {
1382 * Skip net shared paths.
1383 * Net shared paths are not relative to the current root (though
1384 * they do need to take package relocations into account).
1386 for (nsp = netsharedPaths; nsp && *nsp; nsp++) {
1391 if (strncmp(dn, *nsp, len))
1392 /*@innercontinue@*/ continue;
1393 /* Only directories or complete file paths can be net shared */
1394 if (!(dn[len] == '/' || dn[len] == '\0'))
1395 /*@innercontinue@*/ continue;
1397 if (len < (dnlen + bnlen))
1398 /*@innercontinue@*/ continue;
1399 if (strncmp(dn, *nsp, dnlen))
1400 /*@innercontinue@*/ continue;
1401 if (strncmp(bn, (*nsp) + dnlen, bnlen))
1402 /*@innercontinue@*/ continue;
1403 len = dnlen + bnlen;
1404 /* Only directories or complete file paths can be net shared */
1405 if (!((*nsp)[len] == '/' || (*nsp)[len] == '\0'))
1406 /*@innercontinue@*/ continue;
1409 /*@innerbreak@*/ break;
1413 drc[ix]--; dff[ix] = 1;
1414 fi->actions[i] = FA_SKIPNETSHARED;
1419 * Skip i18n language specific files.
1421 if (fi->flangs && languages && *fi->flangs[i]) {
1422 const char **lang, *l, *le;
1423 for (lang = languages; *lang != NULL; lang++) {
1424 if (!strcmp(*lang, "all"))
1425 /*@innerbreak@*/ break;
1426 for (l = fi->flangs[i]; *l != '\0'; l = le) {
1427 for (le = l; *le != '\0' && *le != '|'; le++)
1429 if ((le-l) > 0 && !strncmp(*lang, l, (le-l)))
1430 /*@innerbreak@*/ break;
1431 if (*le == '|') le++; /* skip over | */
1434 /*@innerbreak@*/ break;
1436 if (*lang == NULL) {
1437 drc[ix]--; dff[ix] = 1;
1438 fi->actions[i] = FA_SKIPNSTATE;
1444 * Skip documentation if requested.
1446 if (noDocs && (fi->fflags[i] & RPMFILE_DOC)) {
1447 drc[ix]--; dff[ix] = 1;
1448 fi->actions[i] = FA_SKIPNSTATE;
1453 /* Skip (now empty) directories that had skipped files. */
1454 for (j = 0; j < fi->dc; j++) {
1456 if (drc[j]) continue; /* dir still has files. */
1457 if (!dff[j]) continue; /* dir was not emptied here. */
1459 /* Find parent directory and basename. */
1460 dn = fi->dnl[j]; dnlen = strlen(dn) - 1;
1461 bn = dn + dnlen; bnlen = 0;
1462 while (bn > dn && bn[-1] != '/') {
1468 /* If explicitly included in the package, skip the directory. */
1469 for (i = 0; i < fi->fc; i++) {
1472 if (XFA_SKIPPING(fi->actions[i]))
1473 /*@innercontinue@*/ continue;
1474 if (whatis(fi->fmodes[i]) != XDIR)
1475 /*@innercontinue@*/ continue;
1476 dir = fi->dnl[fi->dil[i]];
1477 if (strlen(dir) != dnlen)
1478 /*@innercontinue@*/ continue;
1479 if (strncmp(dir, dn, dnlen))
1480 /*@innercontinue@*/ continue;
1481 if (strlen(fi->bnl[i]) != bnlen)
1482 /*@innercontinue@*/ continue;
1483 if (strncmp(fi->bnl[i], bn, bnlen))
1484 /*@innercontinue@*/ continue;
1485 rpmMessage(RPMMESS_DEBUG, _("excluding directory %s\n"), dn);
1486 fi->actions[i] = FA_SKIPNSTATE;
1487 /*@innerbreak@*/ break;
1491 if (netsharedPaths) freeSplitString(netsharedPaths);
1492 #ifdef DYING /* XXX freeFi will deal with this later. */
1493 fi->flangs = _free(fi->flangs);
1495 if (languages) freeSplitString((char **)languages);
1499 * Iterator across transaction elements, forward on install, backward on erase.
1501 struct tsIterator_s {
1502 /*@kept@*/ rpmTransactionSet ts; /*!< transaction set. */
1503 int reverse; /*!< reversed traversal? */
1504 int ocsave; /*!< last returned iterator index. */
1505 int oc; /*!< iterator index. */
1509 * Return transaction element order count.
1510 * @param a transaction element iterator
1511 * @return element order count
1513 static int tsGetOc(void * a)
1516 struct tsIterator_s * iter = a;
1517 int oc = iter->ocsave;
1522 * Return transaction element available package pointer.
1523 * @param a transaction element iterator
1524 * @return available package pointer
1526 static /*@dependent@*/ struct availablePackage * tsGetAlp(void * a)
1529 struct tsIterator_s * iter = a;
1530 struct availablePackage * alp = NULL;
1531 int oc = iter->ocsave;
1535 rpmTransactionSet ts = iter->ts;
1536 TFI_t fi = ts->flList + oc;
1537 if (ts->addedPackages.list && fi->type == TR_ADDED)
1538 alp = ts->addedPackages.list + ts->order[oc].u.addedIndex;
1545 * Destroy transaction element iterator.
1546 * @param a transaction element iterator
1547 * @return NULL always
1549 static /*@null@*/ void * tsFreeIterator(/*@only@*//*@null@*/ const void * a)
1556 * Create transaction element iterator.
1557 * @param a transaction set
1558 * @return transaction element iterator
1560 static void * tsInitIterator(/*@kept@*/ const void * a)
1563 rpmTransactionSet ts = (void *)a;
1564 struct tsIterator_s * iter = NULL;
1566 iter = xcalloc(1, sizeof(*iter));
1568 iter->reverse = ((ts->transFlags & RPMTRANS_FLAG_REVERSE) ? 1 : 0);
1569 iter->oc = (iter->reverse ? (ts->orderCount - 1) : 0);
1570 iter->ocsave = iter->oc;
1575 * Return next transaction element's file info.
1576 * @param a file info iterator
1577 * @return next index, -1 on termination
1579 static /*@dependent@*/ TFI_t tsNextIterator(void * a)
1582 struct tsIterator_s * iter = a;
1583 rpmTransactionSet ts = iter->ts;
1587 if (iter->reverse) {
1588 if (iter->oc >= 0) oc = iter->oc--;
1590 if (iter->oc < ts->orderCount) oc = iter->oc++;
1594 fi = ts->flList + oc;
1598 #define NOTIFY(_ts, _al) if ((_ts)->notify) (void) (_ts)->notify _al
1600 int rpmRunTransactions( rpmTransactionSet ts,
1601 rpmCallbackFunction notify, rpmCallbackData notifyData,
1602 rpmProblemSet okProbs, rpmProblemSet * newProbs,
1603 rpmtransFlags transFlags, rpmprobFilterFlags ignoreSet)
1607 struct availablePackage * alp;
1608 int totalFileCount = 0;
1610 struct diskspaceInfo * dip;
1611 struct sharedFileInfo * shared, * sharedList;
1616 fingerPrintCache fpc;
1617 struct psm_s psmbuf;
1618 PSM_t psm = &psmbuf;
1622 /* FIXME: what if the same package is included in ts twice? */
1624 ts->transFlags = transFlags;
1625 if (ts->transFlags & RPMTRANS_FLAG_NOSCRIPTS)
1626 ts->transFlags |= (_noTransScripts | _noTransTriggers);
1627 if (ts->transFlags & RPMTRANS_FLAG_NOTRIGGERS)
1628 ts->transFlags |= _noTransTriggers;
1630 /* XXX MULTILIB is broken, as packages can and do execute /sbin/ldconfig. */
1631 if (ts->transFlags & (RPMTRANS_FLAG_JUSTDB | RPMTRANS_FLAG_MULTILIB))
1632 ts->transFlags |= (_noTransScripts | _noTransTriggers);
1634 ts->notify = notify;
1635 ts->notifyData = notifyData;
1637 ts->probs = *newProbs = psCreate();
1639 ts->ignoreSet = ignoreSet;
1640 ts->currDir = _free(ts->currDir);
1641 ts->currDir = currentDirectory();
1643 if (ts->rpmdb) ts->rpmdb->db_chrootDone = 0;
1644 ts->id = (int_32) time(NULL);
1646 memset(psm, 0, sizeof(*psm));
1651 /* Get available space on mounted file systems. */
1652 if (!(ts->ignoreSet & RPMPROB_FILTER_DISKSPACE) &&
1653 !rpmGetFilesystemList(&ts->filesystems, &ts->filesystemCount)) {
1656 ts->di = _free(ts->di);
1657 dip = ts->di = xcalloc((ts->filesystemCount + 1), sizeof(*ts->di));
1659 for (i = 0; (i < ts->filesystemCount) && dip; i++) {
1660 #if STATFS_IN_SYS_STATVFS
1662 memset(&sfb, 0, sizeof(sfb));
1663 if (statvfs(ts->filesystems[i], &sfb))
1667 /* This platform has the 4-argument version of the statfs call. The last two
1668 * should be the size of struct statfs and 0, respectively. The 0 is the
1669 * filesystem type, and is always 0 when statfs is called on a mounted
1670 * filesystem, as we're doing.
1672 memset(&sfb, 0, sizeof(sfb));
1673 if (statfs(ts->filesystems[i], &sfb, sizeof(sfb), 0))
1675 memset(&sfb, 0, sizeof(sfb));
1676 if (statfs(ts->filesystems[i], &sfb))
1682 ts->di[i].bsize = sfb.f_bsize;
1683 ts->di[i].bneeded = 0;
1684 ts->di[i].ineeded = 0;
1685 #ifdef STATFS_HAS_F_BAVAIL
1686 ts->di[i].bavail = sfb.f_bavail;
1688 /* FIXME: the statfs struct doesn't have a member to tell how many blocks are
1689 * available for non-superusers. f_blocks - f_bfree is probably too big, but
1690 * it's about all we can do.
1692 ts->di[i].bavail = sfb.f_blocks - sfb.f_bfree;
1694 /* XXX Avoid FAT and other file systems that have not inodes. */
1695 ts->di[i].iavail = !(sfb.f_ffree == 0 && sfb.f_files == 0)
1698 xx = stat(ts->filesystems[i], &sb);
1699 ts->di[i].dev = sb.st_dev;
1703 if (dip) ts->di[i].bsize = 0;
1706 /* ===============================================
1707 * For packages being installed:
1708 * - verify package arch/os.
1709 * - verify package epoch:version-release is newer.
1711 * For packages being removed:
1714 /* The ordering doesn't matter here */
1715 if (ts->addedPackages.list != NULL)
1716 for (alp = ts->addedPackages.list;
1717 (alp - ts->addedPackages.list) < ts->addedPackages.size;
1720 if (!archOkay(alp->h) && !(ts->ignoreSet & RPMPROB_FILTER_IGNOREARCH))
1721 psAppend(ts->probs, RPMPROB_BADARCH, alp, NULL, NULL, NULL, 0);
1723 if (!osOkay(alp->h) && !(ts->ignoreSet & RPMPROB_FILTER_IGNOREOS))
1724 psAppend(ts->probs, RPMPROB_BADOS, alp, NULL, NULL, NULL, 0);
1726 if (!(ts->ignoreSet & RPMPROB_FILTER_OLDPACKAGE)) {
1727 rpmdbMatchIterator mi;
1729 mi = rpmdbInitIterator(ts->rpmdb, RPMTAG_NAME, alp->name, 0);
1730 while ((oldH = rpmdbNextIterator(mi)) != NULL)
1731 xx = ensureOlder(alp, oldH, ts->probs);
1732 mi = rpmdbFreeIterator(mi);
1735 /* XXX multilib should not display "already installed" problems */
1736 if (!(ts->ignoreSet & RPMPROB_FILTER_REPLACEPKG) && !alp->multiLib) {
1737 rpmdbMatchIterator mi;
1738 mi = rpmdbInitIterator(ts->rpmdb, RPMTAG_NAME, alp->name, 0);
1739 xx = rpmdbSetIteratorRE(mi, RPMTAG_VERSION,
1740 RPMMIRE_DEFAULT, alp->version);
1741 xx = rpmdbSetIteratorRE(mi, RPMTAG_RELEASE,
1742 RPMMIRE_DEFAULT, alp->release);
1744 while (rpmdbNextIterator(mi) != NULL) {
1745 psAppend(ts->probs, RPMPROB_PKG_INSTALLED, alp,
1746 NULL, NULL, NULL, 0);
1747 /*@innerbreak@*/ break;
1749 mi = rpmdbFreeIterator(mi);
1752 totalFileCount += alp->filesCount;
1756 /* FIXME: it seems a bit silly to read in all of these headers twice */
1757 /* The ordering doesn't matter here */
1758 if (ts->numRemovedPackages > 0) {
1759 rpmdbMatchIterator mi;
1763 mi = rpmdbInitIterator(ts->rpmdb, RPMDBI_PACKAGES, NULL, 0);
1764 xx = rpmdbAppendIterator(mi, ts->removedPackages, ts->numRemovedPackages);
1765 while ((h = rpmdbNextIterator(mi)) != NULL) {
1766 if (headerGetEntry(h, RPMTAG_BASENAMES, NULL, NULL, &fileCount))
1767 totalFileCount += fileCount;
1769 mi = rpmdbFreeIterator(mi);
1772 /* ===============================================
1773 * Initialize file list:
1775 ts->flEntries = ts->addedPackages.size + ts->numRemovedPackages;
1776 ts->flList = xcalloc(ts->flEntries, sizeof(*ts->flList));
1779 * FIXME?: we'd be better off assembling one very large file list and
1780 * calling fpLookupList only once. I'm not sure that the speedup is
1781 * worth the trouble though.
1783 tsi = tsInitIterator(ts);
1784 while ((fi = tsNextIterator(tsi)) != NULL) {
1786 fi->magic = TFIMAGIC;
1788 /* XXX watchout: fi->type must be set for tsGetAlp() to "work" */
1789 fi->type = ts->order[oc].type;
1794 i = ts->order[oc].u.addedIndex;
1796 /* XXX watchout: fi->type must be set for tsGetAlp() to "work" */
1797 fi->ap = tsGetAlp(tsi);
1799 loadFi(ts, fi, fi->ap->h, 1);
1800 /* XXX free fi->ap->h here */
1805 { Header foo = relocateFileList(ts, fi, fi->ap, fi->h, fi->actions);
1806 foo = headerFree(foo);
1810 /* Skip netshared paths, not our i18n files, and excluded docs */
1812 /*@switchbreak@*/ break;
1815 fi->record = ts->order[oc].u.removed.dboffset;
1816 /* Retrieve erased package header from the database. */
1817 { rpmdbMatchIterator mi;
1819 mi = rpmdbInitIterator(ts->rpmdb, RPMDBI_PACKAGES,
1820 &fi->record, sizeof(fi->record));
1821 if ((fi->h = rpmdbNextIterator(mi)) != NULL)
1822 fi->h = headerLink(fi->h);
1823 mi = rpmdbFreeIterator(mi);
1825 if (fi->h == NULL) {
1829 /* XXX header arg unused. */
1830 loadFi(ts, fi, fi->h, 0);
1831 /*@switchbreak@*/ break;
1836 fi->fps = xmalloc(fi->fc * sizeof(*fi->fps));
1838 tsi = tsFreeIterator(tsi);
1840 if (!ts->chrootDone) {
1842 /*@-superuser -noeffect @*/
1843 xx = chroot(ts->rootDir);
1844 /*@=superuser =noeffect @*/
1846 if (ts->rpmdb) ts->rpmdb->db_chrootDone = 1;
1849 chroot_prefix = ts->rootDir;
1854 ts->ht = htCreate(totalFileCount * 2, 0, 0, fpHashFunction, fpEqual);
1855 fpc = fpCacheCreate(totalFileCount);
1857 /* ===============================================
1858 * Add fingerprint for each file not skipped.
1860 tsi = tsInitIterator(ts);
1861 while ((fi = tsNextIterator(tsi)) != NULL) {
1862 fpLookupList(fpc, fi->dnl, fi->bnl, fi->dil, fi->fc, fi->fps);
1863 for (i = 0; i < fi->fc; i++) {
1864 if (XFA_SKIPPING(fi->actions[i]))
1865 /*@innercontinue@*/ continue;
1866 /*@-dependenttrans@*/
1867 htAddEntry(ts->ht, fi->fps + i, fi);
1868 /*@=dependenttrans@*/
1871 tsi = tsFreeIterator(tsi);
1873 /*@-noeffectuncon @*/ /* FIX: check rc */
1874 NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_START, 6, ts->flEntries,
1875 NULL, ts->notifyData));
1876 /*@=noeffectuncon@*/
1878 /* ===============================================
1879 * Compute file disposition for each package in transaction set.
1881 tsi = tsInitIterator(ts);
1882 while ((fi = tsNextIterator(tsi)) != NULL) {
1883 dbiIndexSet * matches;
1886 /*@-noeffectuncon @*/ /* FIX: check rc */
1887 NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_PROGRESS, (fi - ts->flList),
1888 ts->flEntries, NULL, ts->notifyData));
1889 /*@=noeffectuncon@*/
1891 if (fi->fc == 0) continue;
1893 /* Extract file info for all files in this package from the database. */
1894 matches = xcalloc(fi->fc, sizeof(*matches));
1895 if (rpmdbFindFpList(ts->rpmdb, fi->fps, matches, fi->fc))
1896 return 1; /* XXX WTFO? */
1899 for (i = 0; i < fi->fc; i++)
1900 numShared += dbiIndexSetCount(matches[i]);
1902 /* Build sorted file info list for this package. */
1903 shared = sharedList = xcalloc((numShared + 1), sizeof(*sharedList));
1904 for (i = 0; i < fi->fc; i++) {
1906 * Take care not to mark files as replaced in packages that will
1907 * have been removed before we will get here.
1909 for (j = 0; j < dbiIndexSetCount(matches[i]); j++) {
1911 ro = dbiIndexRecordOffset(matches[i], j);
1913 for (k = 0; ro != knownBad && k < ts->orderCount; k++) {
1914 switch (ts->order[k].type) {
1916 if (ts->order[k].u.removed.dboffset == ro)
1918 /*@switchbreak@*/ break;
1920 /*@switchbreak@*/ break;
1924 shared->pkgFileNum = i;
1925 shared->otherPkg = dbiIndexRecordOffset(matches[i], j);
1926 shared->otherFileNum = dbiIndexRecordFileNumber(matches[i], j);
1927 shared->isRemoved = (knownBad == ro);
1930 matches[i] = dbiFreeIndexSet(matches[i]);
1932 numShared = shared - sharedList;
1933 shared->otherPkg = -1;
1934 matches = _free(matches);
1936 /* Sort file info by other package index (otherPkg) */
1937 qsort(sharedList, numShared, sizeof(*shared), sharedCmp);
1939 /* For all files from this package that are in the database ... */
1940 for (i = 0; i < numShared; i = nexti) {
1943 shared = sharedList + i;
1945 /* Find the end of the files in the other package. */
1946 for (nexti = i + 1; nexti < numShared; nexti++) {
1947 if (sharedList[nexti].otherPkg != shared->otherPkg)
1948 /*@innerbreak@*/ break;
1951 /* Is this file from a package being removed? */
1953 for (j = 0; j < ts->numRemovedPackages; j++) {
1954 if (ts->removedPackages[j] != shared->otherPkg)
1955 /*@innercontinue@*/ continue;
1957 /*@innerbreak@*/ break;
1960 /* Determine the fate of each file. */
1963 xx = handleInstInstalledFiles(ts, fi, shared, nexti - i,
1964 !(beingRemoved || (ts->ignoreSet & RPMPROB_FILTER_REPLACEOLDFILES)));
1965 /*@switchbreak@*/ break;
1968 xx = handleRmvdInstalledFiles(ts, fi, shared, nexti - i);
1969 /*@switchbreak@*/ break;
1975 /* Update disk space needs on each partition for this package. */
1976 handleOverlappedFiles(ts, fi);
1978 /* Check added package has sufficient space on each partition used. */
1981 if (!(ts->di && fi->fc))
1982 /*@switchbreak@*/ break;
1983 for (i = 0; i < ts->filesystemCount; i++) {
1987 /* XXX Avoid FAT and other file systems that have not inodes. */
1988 if (dip->iavail <= 0)
1989 /*@innercontinue@*/ continue;
1991 if (adj_fs_blocks(dip->bneeded) > dip->bavail)
1992 psAppend(ts->probs, RPMPROB_DISKSPACE, fi->ap,
1993 ts->filesystems[i], NULL, NULL,
1994 (adj_fs_blocks(dip->bneeded) - dip->bavail) * dip->bsize);
1996 if (adj_fs_blocks(dip->ineeded) > dip->iavail)
1997 psAppend(ts->probs, RPMPROB_DISKNODES, fi->ap,
1998 ts->filesystems[i], NULL, NULL,
1999 (adj_fs_blocks(dip->ineeded) - dip->iavail));
2001 /*@switchbreak@*/ break;
2003 /*@switchbreak@*/ break;
2006 tsi = tsFreeIterator(tsi);
2008 if (ts->chrootDone) {
2009 /*@-superuser -noeffect @*/
2011 /*@=superuser =noeffect @*/
2013 if (ts->rpmdb) ts->rpmdb->db_chrootDone = 0;
2015 chroot_prefix = NULL;
2017 xx = chdir(ts->currDir);
2020 /*@-noeffectuncon @*/ /* FIX: check rc */
2021 NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_STOP, 6, ts->flEntries,
2022 NULL, ts->notifyData));
2023 /*@=noeffectuncon @*/
2025 /* ===============================================
2026 * Free unused memory as soon as possible.
2029 tsi = tsInitIterator(ts);
2030 while ((fi = tsNextIterator(tsi)) != NULL) {
2034 fi->fps = _free(fi->fps);
2036 tsi = tsFreeIterator(tsi);
2042 /* ===============================================
2043 * If unfiltered problems exist, free memory and return.
2045 if ((ts->transFlags & RPMTRANS_FLAG_BUILD_PROBS) ||
2046 (ts->probs->numProblems && (!okProbs || psTrim(okProbs, ts->probs))))
2048 *newProbs = ts->probs;
2050 ts->flList = freeFl(ts, ts->flList);
2053 return ts->orderCount;
2057 /* ===============================================
2058 * Save removed files before erasing.
2060 if (ts->transFlags & (RPMTRANS_FLAG_DIRSTASH | RPMTRANS_FLAG_REPACKAGE)) {
2061 tsi = tsInitIterator(ts);
2062 while ((fi = tsNextIterator(tsi)) != NULL) {
2066 /*@switchbreak@*/ break;
2068 if (ts->transFlags & RPMTRANS_FLAG_REPACKAGE)
2069 xx = psmStage(psm, PSM_PKGSAVE);
2070 /*@switchbreak@*/ break;
2073 tsi = tsFreeIterator(tsi);
2076 /* ===============================================
2077 * Install and remove packages.
2080 lastFailed = -2; /* erased packages have -1 */
2081 tsi = tsInitIterator(ts);
2082 while ((fi = tsNextIterator(tsi)) != NULL) {
2090 alp = tsGetAlp(tsi);
2091 assert(alp == fi->ap);
2092 i = alp - ts->addedPackages.list;
2094 rpmMessage(RPMMESS_DEBUG, "========== +++ %s-%s-%s\n",
2095 fi->name, fi->version, fi->release);
2096 h = (fi->h ? headerLink(fi->h) : NULL);
2098 if (alp->fd == NULL) {
2099 alp->fd = ts->notify(fi->h, RPMCALLBACK_INST_OPEN_FILE, 0, 0,
2100 alp->key, ts->notifyData);
2106 /*@-mustmod@*/ /* LCL: segfault */
2107 rpmrc = rpmReadPackageHeader(alp->fd, &h, NULL, NULL, NULL);
2109 if (!(rpmrc == RPMRC_OK || rpmrc == RPMRC_BADSIZE)) {
2110 /*@-noeffectuncon @*/ /* FIX: check rc */
2111 (void)ts->notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE,
2112 0, 0, alp->key, ts->notifyData);
2113 /*@=noeffectuncon @*/
2116 } else if (fi->h != NULL) {
2117 Header foo = relocateFileList(ts, fi, alp, h, NULL);
2119 h = headerLink(foo);
2120 foo = headerFree(foo);
2122 if (alp->fd) gotfd = 1;
2128 Header hsave = NULL;
2131 hsave = headerLink(fi->h);
2132 fi->h = headerFree(fi->h);
2133 fi->h = headerLink(h);
2135 char * fstates = fi->fstates;
2136 fileAction * actions = fi->actions;
2141 fi->magic = TFIMAGIC;
2142 fi->type = ts->order[oc].type;
2143 fi->ap = tsGetAlp(tsi);
2145 loadFi(ts, fi, h, 1);
2146 fi->fstates = _free(fi->fstates);
2147 fi->fstates = fstates;
2148 fi->actions = _free(fi->actions);
2149 fi->actions = actions;
2152 ts->transFlags |= RPMTRANS_FLAG_MULTILIB;
2154 assert(alp == fi->ap);
2155 if (psmStage(psm, PSM_PKGINSTALL)) {
2159 fi->h = headerFree(fi->h);
2161 fi->h = headerLink(hsave);
2162 hsave = headerFree(hsave);
2172 /*@-noeffectuncon @*/ /* FIX: check rc */
2173 (void)ts->notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE, 0, 0,
2174 alp->key, ts->notifyData);
2175 /*@=noeffectuncon @*/
2179 /*@switchbreak@*/ break;
2181 rpmMessage(RPMMESS_DEBUG, "========== --- %s-%s-%s\n",
2182 fi->name, fi->version, fi->release);
2184 /* If install failed, then we shouldn't erase. */
2185 if (ts->order[oc].u.removed.dependsOnIndex != lastFailed) {
2186 if (psmStage(psm, PSM_PKGERASE))
2190 /*@switchbreak@*/ break;
2192 xx = rpmdbSync(ts->rpmdb);
2194 tsi = tsFreeIterator(tsi);
2196 ts->flList = freeFl(ts, ts->flList);