transaction.c: lclint cleanups.
[platform/upstream/rpm.git] / lib / transaction.c
1 /** \ingroup rpmtrans
2  * \file lib/transaction.c
3  */
4
5 #include "system.h"
6
7 #include <rpmmacro.h>   /* XXX for rpmExpand */
8
9
10 #define _NEED_TEITERATOR        1
11 #include "psm.h"
12
13 #include "fprint.h"
14 #include "legacy.h"     /* XXX mdfile */
15 #include "misc.h" /* XXX stripTrailingChar, splitString, currentDirectory */
16 #include "rpmdb.h"
17
18 /*@-redecl -exportheadervar@*/
19 /*@unchecked@*/
20 extern const char * chroot_prefix;
21 /*@=redecl =exportheadervar@*/
22
23 /* XXX FIXME: merge with existing (broken?) tests in system.h */
24 /* portability fiddles */
25 #if STATFS_IN_SYS_STATVFS
26 /*@-incondefs@*/
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 @*/
34 /*@=incondefs@*/
35 #endif
36 #else
37 # if STATFS_IN_SYS_VFS
38 #  include <sys/vfs.h>
39 # else
40 #  if STATFS_IN_SYS_MOUNT
41 #   include <sys/mount.h>
42 #  else
43 #   if STATFS_IN_SYS_STATFS
44 #    include <sys/statfs.h>
45 #   endif
46 #  endif
47 # endif
48 #endif
49
50 #include "debug.h"
51
52 /*@access FD_t @*/              /* XXX compared with NULL */
53 /*@access Header @*/            /* XXX compared with NULL */
54 /*@access rpmProblemSet @*/     /* XXX need rpmProblemSetOK() */
55 /*@access dbiIndexSet @*/
56 /*@access rpmdb @*/
57
58 /*@access PSM_t @*/
59
60 /*@access alKey @*/
61 /*@access fnpyKey @*/
62
63 /*@access TFI_t @*/
64
65 /*@access teIterator @*/
66 /*@access transactionElement @*/
67 /*@access rpmTransactionSet @*/
68
69 /**
70  */
71 struct diskspaceInfo {
72     dev_t dev;                  /*!< File system device number. */
73     signed long bneeded;        /*!< No. of blocks needed. */
74     signed long ineeded;        /*!< No. of inodes needed. */
75     int bsize;                  /*!< File system block size. */
76     signed long bavail;         /*!< No. of blocks available. */
77     signed long iavail;         /*!< No. of inodes available. */
78 };
79
80 /**
81  * Adjust for root only reserved space. On linux e2fs, this is 5%.
82  */
83 #define adj_fs_blocks(_nb)      (((_nb) * 21) / 20)
84
85 /* argon thought a shift optimization here was a waste of time...  he's
86    probably right :-( */
87 #define BLOCK_ROUND(size, block) (((size) + (block) - 1) / (block))
88
89 void rpmtransSetScriptFd(rpmTransactionSet ts, FD_t fd)
90 {
91     ts->scriptFd = (fd ? fdLink(fd, "rpmtransSetScriptFd") : NULL);
92 }
93
94 int rpmtransGetKeys(const rpmTransactionSet ts, fnpyKey ** ep, int * nep)
95 {
96     int rc = 0;
97
98     if (nep) *nep = ts->orderCount;
99     if (ep) {
100         teIterator pi;  transactionElement p;
101         fnpyKey * e;
102
103         *ep = e = xmalloc(ts->orderCount * sizeof(*e));
104         pi = teInitIterator(ts);
105         while ((p = teNextIterator(pi)) != NULL) {
106             switch (p->type) {
107             case TR_ADDED:
108                 /*@-dependenttrans@*/
109                 *e = p->key;
110                 /*@=dependenttrans@*/
111                 /*@switchbreak@*/ break;
112             case TR_REMOVED:
113             default:
114                 *e = NULL;
115                 /*@switchbreak@*/ break;
116             }
117             e++;
118         }
119         pi = teFreeIterator(pi);
120     }
121     return rc;
122 }
123
124 /**
125  */
126 static int archOkay(/*@null@*/ const char * pkgArch)
127         /*@*/
128 {
129     if (pkgArch == NULL) return 0;
130     return (rpmMachineScore(RPM_MACHTABLE_INSTARCH, pkgArch) ? 1 : 0);
131 }
132
133 /**
134  */
135 static int osOkay(/*@null@*/ const char * pkgOs)
136         /*@*/
137 {
138     if (pkgOs == NULL) return 0;
139     return (rpmMachineScore(RPM_MACHTABLE_INSTOS, pkgOs) ? 1 : 0);
140 }
141
142 /**
143  */
144 static int sharedCmp(const void * one, const void * two)
145         /*@*/
146 {
147     sharedFileInfo a = (sharedFileInfo) one;
148     sharedFileInfo b = (sharedFileInfo) two;
149
150     if (a->otherPkg < b->otherPkg)
151         return -1;
152     else if (a->otherPkg > b->otherPkg)
153         return 1;
154
155     return 0;
156 }
157
158 /**
159  */
160 static fileAction decideFileFate(const char * dirName,
161                         const char * baseName, short dbMode,
162                         const char * dbMd5, const char * dbLink, short newMode,
163                         const char * newMd5, const char * newLink, int newFlags,
164                         rpmtransFlags transFlags)
165         /*@globals fileSystem @*/
166         /*@modifies fileSystem @*/
167 {
168     char buffer[1024];
169     const char * dbAttr, * newAttr;
170     fileTypes dbWhat, newWhat, diskWhat;
171     struct stat sb;
172     int i, rc;
173     int save = (newFlags & RPMFILE_NOREPLACE) ? FA_ALTNAME : FA_SAVE;
174     char * filespec = alloca(strlen(dirName) + strlen(baseName) + 1);
175
176     (void) stpcpy( stpcpy(filespec, dirName), baseName);
177
178     if (lstat(filespec, &sb)) {
179         /*
180          * The file doesn't exist on the disk. Create it unless the new
181          * package has marked it as missingok, or allfiles is requested.
182          */
183         if (!(transFlags & RPMTRANS_FLAG_ALLFILES) &&
184            (newFlags & RPMFILE_MISSINGOK)) {
185             rpmMessage(RPMMESS_DEBUG, _("%s skipped due to missingok flag\n"),
186                         filespec);
187             return FA_SKIP;
188         } else {
189             return FA_CREATE;
190         }
191     }
192
193     diskWhat = whatis(sb.st_mode);
194     dbWhat = whatis(dbMode);
195     newWhat = whatis(newMode);
196
197     /* RPM >= 2.3.10 shouldn't create config directories -- we'll ignore
198        them in older packages as well */
199     if (newWhat == XDIR) {
200         return FA_CREATE;
201     }
202
203     if (diskWhat != newWhat) {
204         return save;
205     } else if (newWhat != dbWhat && diskWhat != dbWhat) {
206         return save;
207     } else if (dbWhat != newWhat) {
208         return FA_CREATE;
209     } else if (dbWhat != LINK && dbWhat != REG) {
210         return FA_CREATE;
211     }
212
213     if (dbWhat == REG) {
214         rc = mdfile(filespec, buffer);
215
216         if (rc) {
217             /* assume the file has been removed, don't freak */
218             return FA_CREATE;
219         }
220         dbAttr = dbMd5;
221         newAttr = newMd5;
222     } else /* dbWhat == LINK */ {
223         memset(buffer, 0, sizeof(buffer));
224         i = readlink(filespec, buffer, sizeof(buffer) - 1);
225         if (i == -1) {
226             /* assume the file has been removed, don't freak */
227             return FA_CREATE;
228         }
229         dbAttr = dbLink;
230         newAttr = newLink;
231      }
232
233     /* this order matters - we'd prefer to CREATE the file if at all
234        possible in case something else (like the timestamp) has changed */
235
236     if (!strcmp(dbAttr, buffer)) {
237         /* this config file has never been modified, so just replace it */
238         return FA_CREATE;
239     }
240
241     if (!strcmp(dbAttr, newAttr)) {
242         /* this file is the same in all versions of this package */
243         return FA_SKIP;
244     }
245
246     /*
247      * The config file on the disk has been modified, but
248      * the ones in the two packages are different. It would
249      * be nice if RPM was smart enough to at least try and
250      * merge the difference ala CVS, but...
251      */
252     return save;
253 }
254
255 /**
256  */
257 static int filecmp(short mode1, const char * md51, const char * link1,
258                    short mode2, const char * md52, const char * link2)
259         /*@*/
260 {
261     fileTypes what1 = whatis(mode1);
262     fileTypes what2 = whatis(mode2);
263
264     if (what1 != what2) return 1;
265
266     if (what1 == LINK)
267         return strcmp(link1, link2);
268     else if (what1 == REG)
269         return strcmp(md51, md52);
270
271     return 0;
272 }
273
274 /**
275  */
276 /* XXX only ts->{probs,rpmdb} modified */
277 static int handleInstInstalledFiles(const rpmTransactionSet ts,
278                 transactionElement p, TFI_t fi,
279                 sharedFileInfo shared,
280                 int sharedCount, int reportConflicts)
281         /*@globals fileSystem @*/
282         /*@modifies ts, fi, fileSystem @*/
283 {
284     HGE_t hge = fi->hge;
285     HFD_t hfd = (fi->hfd ? fi->hfd : headerFreeData);
286     rpmtransFlags transFlags = ts->transFlags;
287     rpmTagType oltype, omtype;
288     Header h;
289     int i;
290     const char ** otherMd5s;
291     const char ** otherLinks;
292     const char * otherStates;
293     uint_32 * otherFlags;
294     uint_32 * otherSizes;
295     uint_16 * otherModes;
296     int numReplaced = 0;
297     int xx;
298
299     rpmdbMatchIterator mi;
300
301     mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES,
302                         &shared->otherPkg, sizeof(shared->otherPkg));
303     h = rpmdbNextIterator(mi);
304     if (h == NULL) {
305         mi = rpmdbFreeIterator(mi);
306         return 1;
307     }
308
309     xx = hge(h, RPMTAG_FILEMD5S, &omtype, (void **) &otherMd5s, NULL);
310     xx = hge(h, RPMTAG_FILELINKTOS, &oltype, (void **) &otherLinks, NULL);
311     xx = hge(h, RPMTAG_FILESTATES, NULL, (void **) &otherStates, NULL);
312     xx = hge(h, RPMTAG_FILEMODES, NULL, (void **) &otherModes, NULL);
313     xx = hge(h, RPMTAG_FILEFLAGS, NULL, (void **) &otherFlags, NULL);
314     xx = hge(h, RPMTAG_FILESIZES, NULL, (void **) &otherSizes, NULL);
315
316     fi->replaced = xmalloc(sharedCount * sizeof(*fi->replaced));
317
318     for (i = 0; i < sharedCount; i++, shared++) {
319         int otherFileNum, fileNum;
320         otherFileNum = shared->otherFileNum;
321         fileNum = shared->pkgFileNum;
322
323         /* XXX another tedious segfault, assume file state normal. */
324         if (otherStates && otherStates[otherFileNum] != RPMFILE_STATE_NORMAL)
325             continue;
326
327         if (XFA_SKIPPING(fi->actions[fileNum]))
328             continue;
329
330         if (filecmp(otherModes[otherFileNum],
331                         otherMd5s[otherFileNum],
332                         otherLinks[otherFileNum],
333                         fi->fmodes[fileNum],
334                         fi->fmd5s[fileNum],
335                         fi->flinks[fileNum])) {
336             if (reportConflicts) {
337                 const char * altNEVR = hGetNEVR(h, NULL);
338                 rpmProblemSetAppend(ts->probs, RPMPROB_FILE_CONFLICT,
339                         p->NEVR, p->key,
340                         fi->dnl[fi->dil[fileNum]], fi->bnl[fileNum],
341                         altNEVR,
342                         0);
343                 altNEVR = _free(altNEVR);
344             }
345             if (!(otherFlags[otherFileNum] | fi->fflags[fileNum])
346                         & RPMFILE_CONFIG) {
347                 /*@-assignexpose@*/ /* FIX: p->replaced, not fi */
348                 if (!shared->isRemoved)
349                     fi->replaced[numReplaced++] = *shared;
350                 /*@=assignexpose@*/
351             }
352         }
353
354         if ((otherFlags[otherFileNum] | fi->fflags[fileNum]) & RPMFILE_CONFIG) {
355             fi->actions[fileNum] = decideFileFate(
356                         fi->dnl[fi->dil[fileNum]],
357                         fi->bnl[fileNum],
358                         otherModes[otherFileNum],
359                         otherMd5s[otherFileNum],
360                         otherLinks[otherFileNum],
361                         fi->fmodes[fileNum],
362                         fi->fmd5s[fileNum],
363                         fi->flinks[fileNum],
364                         fi->fflags[fileNum],
365                         transFlags);
366         }
367
368         fi->replacedSizes[fileNum] = otherSizes[otherFileNum];
369     }
370
371     otherMd5s = hfd(otherMd5s, omtype);
372     otherLinks = hfd(otherLinks, oltype);
373     mi = rpmdbFreeIterator(mi);
374
375     fi->replaced = xrealloc(fi->replaced,       /* XXX memory leak */
376                            sizeof(*fi->replaced) * (numReplaced + 1));
377     fi->replaced[numReplaced].otherPkg = 0;
378
379     return 0;
380 }
381
382 /**
383  */
384 /* XXX only ts->rpmdb modified */
385 static int handleRmvdInstalledFiles(const rpmTransactionSet ts, TFI_t fi,
386                 sharedFileInfo shared, int sharedCount)
387         /*@globals fileSystem @*/
388         /*@modifies ts, fi, fileSystem @*/
389 {
390     HGE_t hge = fi->hge;
391     Header h;
392     const char * otherStates;
393     int i, xx;
394    
395     rpmdbMatchIterator mi;
396
397     mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES,
398                         &shared->otherPkg, sizeof(shared->otherPkg));
399     h = rpmdbNextIterator(mi);
400     if (h == NULL) {
401         mi = rpmdbFreeIterator(mi);
402         return 1;
403     }
404
405     xx = hge(h, RPMTAG_FILESTATES, NULL, (void **) &otherStates, NULL);
406
407     for (i = 0; i < sharedCount; i++, shared++) {
408         int otherFileNum, fileNum;
409         otherFileNum = shared->otherFileNum;
410         fileNum = shared->pkgFileNum;
411
412         if (otherStates[otherFileNum] != RPMFILE_STATE_NORMAL)
413             continue;
414
415         fi->actions[fileNum] = FA_SKIP;
416     }
417
418     mi = rpmdbFreeIterator(mi);
419
420     return 0;
421 }
422
423 /**
424  * Update disk space needs on each partition for this package.
425  */
426 /* XXX only ts->{probs,di} modified */
427 static void handleOverlappedFiles(const rpmTransactionSet ts,
428                 const transactionElement p, TFI_t fi)
429         /*@globals fileSystem @*/
430         /*@modifies ts, fi, fileSystem @*/
431 {
432     struct diskspaceInfo * ds = NULL;
433     uint_32 fixupSize = 0;
434     char * filespec = NULL;
435     int fileSpecAlloced = 0;
436     int i, j;
437   
438     for (i = 0; i < fi->fc; i++) {
439         int otherPkgNum, otherFileNum;
440         const TFI_t * recs;
441         int numRecs;
442
443         if (XFA_SKIPPING(fi->actions[i]))
444             continue;
445
446         j = strlen(fi->dnl[fi->dil[i]]) + strlen(fi->bnl[i]) + 1;
447         /*@-branchstate@*/
448         if (j > fileSpecAlloced) {
449             fileSpecAlloced = j * 2;
450             filespec = xrealloc(filespec, fileSpecAlloced);
451         }
452         /*@=branchstate@*/
453
454         (void) stpcpy( stpcpy( filespec, fi->dnl[fi->dil[i]]), fi->bnl[i]);
455
456         if (ts->di) {
457             ds = ts->di;
458             while (ds->bsize && ds->dev != fi->fps[i].entry->dev) ds++;
459             if (!ds->bsize) ds = NULL;
460             fixupSize = 0;
461         }
462
463         /*
464          * Retrieve all records that apply to this file. Note that the
465          * file info records were built in the same order as the packages
466          * will be installed and removed so the records for an overlapped
467          * files will be sorted in exactly the same order.
468          */
469         (void) htGetEntry(ts->ht, &fi->fps[i],
470                         (const void ***) &recs, &numRecs, NULL);
471
472         /*
473          * If this package is being added, look only at other packages
474          * being added -- removed packages dance to a different tune.
475          * If both this and the other package are being added, overlapped
476          * files must be identical (or marked as a conflict). The
477          * disposition of already installed config files leads to
478          * a small amount of extra complexity.
479          *
480          * If this package is being removed, then there are two cases that
481          * need to be worried about:
482          * If the other package is being added, then skip any overlapped files
483          * so that this package removal doesn't nuke the overlapped files
484          * that were just installed.
485          * If both this and the other package are being removed, then each
486          * file removal from preceding packages needs to be skipped so that
487          * the file removal occurs only on the last occurence of an overlapped
488          * file in the transaction set.
489          *
490          */
491
492         /* Locate this overlapped file in the set of added/removed packages. */
493         for (j = 0; j < numRecs && recs[j] != fi; j++)
494             {};
495
496         /* Find what the previous disposition of this file was. */
497         otherFileNum = -1;                      /* keep gcc quiet */
498         for (otherPkgNum = j - 1; otherPkgNum >= 0; otherPkgNum--) {
499             /* Added packages need only look at other added packages. */
500             if (p->type == TR_ADDED && recs[otherPkgNum]->te->type != TR_ADDED)
501                 /*@innercontinue@*/ continue;
502
503             /* TESTME: there are more efficient searches in the world... */
504             for (otherFileNum = 0;
505                  otherFileNum < recs[otherPkgNum]->fc;
506                  otherFileNum++)
507             {
508
509                 /* If the addresses are the same, so are the values. */
510                 if ((fi->fps + i) == (recs[otherPkgNum]->fps + otherFileNum))
511                     /*@innerbreak@*/ break;
512
513                 /* Otherwise, compare fingerprints by value. */
514                 /*@-nullpass@*/ /* LCL: looks good to me */
515                 if (FP_EQUAL(fi->fps[i], recs[otherPkgNum]->fps[otherFileNum]))
516                     /*@innerbreak@*/ break;
517                 /*@=nullpass@*/
518
519             }
520             /* XXX is this test still necessary? */
521             if (recs[otherPkgNum]->actions[otherFileNum] != FA_UNKNOWN)
522                 /*@innerbreak@*/ break;
523         }
524
525         switch (p->type) {
526         case TR_ADDED:
527           { struct stat sb;
528             if (otherPkgNum < 0) {
529                 /* XXX is this test still necessary? */
530                 if (fi->actions[i] != FA_UNKNOWN)
531                     /*@switchbreak@*/ break;
532                 if ((fi->fflags[i] & RPMFILE_CONFIG) && 
533                         !lstat(filespec, &sb)) {
534                     /* Here is a non-overlapped pre-existing config file. */
535                     fi->actions[i] = (fi->fflags[i] & RPMFILE_NOREPLACE)
536                         ? FA_ALTNAME : FA_BACKUP;
537                 } else {
538                     fi->actions[i] = FA_CREATE;
539                 }
540                 /*@switchbreak@*/ break;
541             }
542
543             /* Mark added overlapped non-identical files as a conflict. */
544             if ((ts->ignoreSet & RPMPROB_FILTER_REPLACENEWFILES)
545              && filecmp(recs[otherPkgNum]->fmodes[otherFileNum],
546                         recs[otherPkgNum]->fmd5s[otherFileNum],
547                         recs[otherPkgNum]->flinks[otherFileNum],
548                         fi->fmodes[i],
549                         fi->fmd5s[i],
550                         fi->flinks[i]))
551             {
552                 const char * altNEVR = recs[otherPkgNum]->te->NEVR;
553                 rpmProblemSetAppend(ts->probs, RPMPROB_NEW_FILE_CONFLICT,
554                         p->NEVR, p->key,
555                         filespec, NULL,
556                         altNEVR,
557                         0);
558             }
559
560             /* Try to get the disk accounting correct even if a conflict. */
561             fixupSize = recs[otherPkgNum]->fsizes[otherFileNum];
562
563             if ((fi->fflags[i] & RPMFILE_CONFIG) && !lstat(filespec, &sb)) {
564                 /* Here is an overlapped  pre-existing config file. */
565                 fi->actions[i] = (fi->fflags[i] & RPMFILE_NOREPLACE)
566                         ? FA_ALTNAME : FA_SKIP;
567             } else {
568                 fi->actions[i] = FA_CREATE;
569             }
570           } /*@switchbreak@*/ break;
571         case TR_REMOVED:
572             if (otherPkgNum >= 0) {
573                 /* Here is an overlapped added file we don't want to nuke. */
574                 if (recs[otherPkgNum]->actions[otherFileNum] != FA_ERASE) {
575                     /* On updates, don't remove files. */
576                     fi->actions[i] = FA_SKIP;
577                     /*@switchbreak@*/ break;
578                 }
579                 /* Here is an overlapped removed file: skip in previous. */
580                 recs[otherPkgNum]->actions[otherFileNum] = FA_SKIP;
581             }
582             if (XFA_SKIPPING(fi->actions[i]))
583                 /*@switchbreak@*/ break;
584             if (fi->fstates && fi->fstates[i] != RPMFILE_STATE_NORMAL)
585                 /*@switchbreak@*/ break;
586             if (!(S_ISREG(fi->fmodes[i]) && (fi->fflags[i] & RPMFILE_CONFIG))) {
587                 fi->actions[i] = FA_ERASE;
588                 /*@switchbreak@*/ break;
589             }
590                 
591             /* Here is a pre-existing modified config file that needs saving. */
592             {   char mdsum[50];
593                 if (!mdfile(filespec, mdsum) && strcmp(fi->fmd5s[i], mdsum)) {
594                     fi->actions[i] = FA_BACKUP;
595                     /*@switchbreak@*/ break;
596                 }
597             }
598             fi->actions[i] = FA_ERASE;
599             /*@switchbreak@*/ break;
600         }
601
602         if (ds) {
603             uint_32 s = BLOCK_ROUND(fi->fsizes[i], ds->bsize);
604
605             switch (fi->actions[i]) {
606               case FA_BACKUP:
607               case FA_SAVE:
608               case FA_ALTNAME:
609                 ds->ineeded++;
610                 ds->bneeded += s;
611                 /*@switchbreak@*/ break;
612
613             /*
614              * FIXME: If two packages share a file (same md5sum), and
615              * that file is being replaced on disk, will ds->bneeded get
616              * decremented twice? Quite probably!
617              */
618               case FA_CREATE:
619                 ds->bneeded += s;
620                 ds->bneeded -= BLOCK_ROUND(fi->replacedSizes[i], ds->bsize);
621                 /*@switchbreak@*/ break;
622
623               case FA_ERASE:
624                 ds->ineeded--;
625                 ds->bneeded -= s;
626                 /*@switchbreak@*/ break;
627
628               default:
629                 /*@switchbreak@*/ break;
630             }
631
632             ds->bneeded -= BLOCK_ROUND(fixupSize, ds->bsize);
633         }
634     }
635     filespec = _free(filespec);
636 }
637
638 /**
639  * Ensure that current package is newer than installed package.
640  * @param ts            transaction set
641  * @param p             current transaction element
642  * @param h             installed header
643  * @return              0 if not newer, 1 if okay
644  */
645 static int ensureOlder(rpmTransactionSet ts,
646                 const transactionElement p, const Header h)
647         /*@modifies ts @*/
648 {
649     int_32 reqFlags = (RPMSENSE_LESS | RPMSENSE_EQUAL);
650     const char * reqEVR;
651     rpmDepSet req;
652     char * t;
653     int rc;
654
655     if (p == NULL || h == NULL)
656         return 1;
657
658     t = alloca(strlen(p->NEVR) + (p->epoch != NULL ? strlen(p->epoch) : 0) + 1);
659     *t = '\0';
660     reqEVR = t;
661     if (p->epoch != NULL)       t = stpcpy( stpcpy(t, p->epoch), ":");
662     if (p->version != NULL)     t = stpcpy(t, p->version);
663     *t++ = '-';
664     if (p->release != NULL)     t = stpcpy(t, p->release);
665     
666     req = dsSingle(RPMTAG_REQUIRENAME, p->name, reqEVR, reqFlags);
667     rc = headerMatchesDepFlags(h, req);
668     req = dsFree(req);
669
670     if (rc == 0) {
671         const char * altNEVR = hGetNEVR(h, NULL);
672         rpmProblemSetAppend(ts->probs, RPMPROB_OLDPACKAGE,
673                 p->NEVR, p->key,
674                 NULL, NULL,
675                 altNEVR,
676                 0);
677         altNEVR = _free(altNEVR);
678         rc = 1;
679     } else
680         rc = 0;
681
682     return rc;
683 }
684
685 /**
686  */
687 static void skipFiles(const rpmTransactionSet ts, TFI_t fi)
688         /*@globals rpmGlobalMacroContext @*/
689         /*@modifies fi, rpmGlobalMacroContext @*/
690 {
691     int noDocs = (ts->transFlags & RPMTRANS_FLAG_NODOCS);
692     char ** netsharedPaths = NULL;
693     const char ** languages;
694     const char * dn, * bn;
695     int dnlen, bnlen, ix;
696     const char * s;
697     int * drc;
698     char * dff;
699     int i, j;
700
701     if (!noDocs)
702         noDocs = rpmExpandNumeric("%{_excludedocs}");
703
704     {   const char *tmpPath = rpmExpand("%{_netsharedpath}", NULL);
705         /*@-branchstate@*/
706         if (tmpPath && *tmpPath != '%')
707             netsharedPaths = splitString(tmpPath, strlen(tmpPath), ':');
708         /*@=branchstate@*/
709         tmpPath = _free(tmpPath);
710     }
711
712     s = rpmExpand("%{_install_langs}", NULL);
713     /*@-branchstate@*/
714     if (!(s && *s != '%'))
715         s = _free(s);
716     if (s) {
717         languages = (const char **) splitString(s, strlen(s), ':');
718         s = _free(s);
719     } else
720         languages = NULL;
721     /*@=branchstate@*/
722
723     /* Compute directory refcount, skip directory if now empty. */
724     drc = alloca(fi->dc * sizeof(*drc));
725     memset(drc, 0, fi->dc * sizeof(*drc));
726     dff = alloca(fi->dc * sizeof(*dff));
727     memset(dff, 0, fi->dc * sizeof(*dff));
728
729     for (i = 0; i < fi->fc; i++) {
730         char **nsp;
731
732         bn = fi->bnl[i];
733         bnlen = strlen(bn);
734         ix = fi->dil[i];
735         dn = fi->dnl[ix];
736         dnlen = strlen(dn);
737
738         drc[ix]++;
739
740         /* Don't bother with skipped files */
741         if (XFA_SKIPPING(fi->actions[i])) {
742             drc[ix]--;
743             continue;
744         }
745
746         /*
747          * Skip net shared paths.
748          * Net shared paths are not relative to the current root (though
749          * they do need to take package relocations into account).
750          */
751         for (nsp = netsharedPaths; nsp && *nsp; nsp++) {
752             int len;
753
754             len = strlen(*nsp);
755             if (dnlen >= len) {
756                 if (strncmp(dn, *nsp, len))
757                     /*@innercontinue@*/ continue;
758                 /* Only directories or complete file paths can be net shared */
759                 if (!(dn[len] == '/' || dn[len] == '\0'))
760                     /*@innercontinue@*/ continue;
761             } else {
762                 if (len < (dnlen + bnlen))
763                     /*@innercontinue@*/ continue;
764                 if (strncmp(dn, *nsp, dnlen))
765                     /*@innercontinue@*/ continue;
766                 if (strncmp(bn, (*nsp) + dnlen, bnlen))
767                     /*@innercontinue@*/ continue;
768                 len = dnlen + bnlen;
769                 /* Only directories or complete file paths can be net shared */
770                 if (!((*nsp)[len] == '/' || (*nsp)[len] == '\0'))
771                     /*@innercontinue@*/ continue;
772             }
773
774             /*@innerbreak@*/ break;
775         }
776
777         if (nsp && *nsp) {
778             drc[ix]--;  dff[ix] = 1;
779             fi->actions[i] = FA_SKIPNETSHARED;
780             continue;
781         }
782
783         /*
784          * Skip i18n language specific files.
785          */
786         if (fi->flangs && languages && *fi->flangs[i]) {
787             const char **lang, *l, *le;
788             for (lang = languages; *lang != NULL; lang++) {
789                 if (!strcmp(*lang, "all"))
790                     /*@innerbreak@*/ break;
791                 for (l = fi->flangs[i]; *l != '\0'; l = le) {
792                     for (le = l; *le != '\0' && *le != '|'; le++)
793                         {};
794                     if ((le-l) > 0 && !strncmp(*lang, l, (le-l)))
795                         /*@innerbreak@*/ break;
796                     if (*le == '|') le++;       /* skip over | */
797                 }
798                 if (*l != '\0')
799                     /*@innerbreak@*/ break;
800             }
801             if (*lang == NULL) {
802                 drc[ix]--;      dff[ix] = 1;
803                 fi->actions[i] = FA_SKIPNSTATE;
804                 continue;
805             }
806         }
807
808         /*
809          * Skip documentation if requested.
810          */
811         if (noDocs && (fi->fflags[i] & RPMFILE_DOC)) {
812             drc[ix]--;  dff[ix] = 1;
813             fi->actions[i] = FA_SKIPNSTATE;
814             continue;
815         }
816     }
817
818     /* Skip (now empty) directories that had skipped files. */
819     for (j = 0; j < fi->dc; j++) {
820
821         if (drc[j]) continue;   /* dir still has files. */
822         if (!dff[j]) continue;  /* dir was not emptied here. */
823         
824         /* Find parent directory and basename. */
825         dn = fi->dnl[j];        dnlen = strlen(dn) - 1;
826         bn = dn + dnlen;        bnlen = 0;
827         while (bn > dn && bn[-1] != '/') {
828                 bnlen++;
829                 dnlen--;
830                 bn--;
831         }
832
833         /* If explicitly included in the package, skip the directory. */
834         for (i = 0; i < fi->fc; i++) {
835             const char * dir;
836
837             if (XFA_SKIPPING(fi->actions[i]))
838                 /*@innercontinue@*/ continue;
839             if (whatis(fi->fmodes[i]) != XDIR)
840                 /*@innercontinue@*/ continue;
841             dir = fi->dnl[fi->dil[i]];
842             if (strlen(dir) != dnlen)
843                 /*@innercontinue@*/ continue;
844             if (strncmp(dir, dn, dnlen))
845                 /*@innercontinue@*/ continue;
846             if (strlen(fi->bnl[i]) != bnlen)
847                 /*@innercontinue@*/ continue;
848             if (strncmp(fi->bnl[i], bn, bnlen))
849                 /*@innercontinue@*/ continue;
850             rpmMessage(RPMMESS_DEBUG, _("excluding directory %s\n"), dn);
851             fi->actions[i] = FA_SKIPNSTATE;
852             /*@innerbreak@*/ break;
853         }
854     }
855
856     if (netsharedPaths) freeSplitString(netsharedPaths);
857 #ifdef  DYING   /* XXX freeFi will deal with this later. */
858     fi->flangs = _free(fi->flangs);
859 #endif
860     if (languages) freeSplitString((char **)languages);
861 }
862
863 #define NOTIFY(_ts, _al)        if ((_ts)->notify) (void) (_ts)->notify _al
864
865 int rpmRunTransactions( rpmTransactionSet ts,
866                         rpmCallbackFunction notify, rpmCallbackData notifyData,
867                         rpmProblemSet okProbs, rpmProblemSet * newProbs,
868                         rpmtransFlags transFlags, rpmprobFilterFlags ignoreSet)
869 {
870     int i, j;
871     int ourrc = 0;
872     int totalFileCount = 0;
873     TFI_t fi;
874     struct diskspaceInfo * dip;
875     sharedFileInfo shared, sharedList;
876     int numShared;
877     int nexti;
878     alKey lastKey;
879     fingerPrintCache fpc;
880     PSM_t psm = memset(alloca(sizeof(*psm)), 0, sizeof(*psm));
881     teIterator pi;      transactionElement p;
882     teIterator qi;      transactionElement q;
883     int xx;
884
885     /* FIXME: what if the same package is included in ts twice? */
886
887     ts->transFlags = transFlags;
888     if (ts->transFlags & RPMTRANS_FLAG_NOSCRIPTS)
889         ts->transFlags |= (_noTransScripts | _noTransTriggers);
890     if (ts->transFlags & RPMTRANS_FLAG_NOTRIGGERS)
891         ts->transFlags |= _noTransTriggers;
892
893     /* XXX MULTILIB is broken, as packages can and do execute /sbin/ldconfig. */
894     if (ts->transFlags & (RPMTRANS_FLAG_JUSTDB | RPMTRANS_FLAG_MULTILIB))
895         ts->transFlags |= (_noTransScripts | _noTransTriggers);
896
897     ts->notify = notify;
898     ts->notifyData = notifyData;
899     ts->probs = rpmProblemSetFree(ts->probs);
900     ts->probs = rpmProblemSetCreate();
901     *newProbs = rpmpsLink(ts->probs, "RunTransactions");
902     ts->ignoreSet = ignoreSet;
903     ts->currDir = _free(ts->currDir);
904     ts->currDir = currentDirectory();
905     ts->chrootDone = 0;
906     if (ts->rpmdb) ts->rpmdb->db_chrootDone = 0;
907     ts->id = (int_32) time(NULL);
908
909     memset(psm, 0, sizeof(*psm));
910     psm->ts = rpmtsLink(ts, "tsRun");
911
912     /* Get available space on mounted file systems. */
913     if (!(ts->ignoreSet & RPMPROB_FILTER_DISKSPACE) &&
914                 !rpmGetFilesystemList(&ts->filesystems, &ts->filesystemCount))
915     {
916         struct stat sb;
917
918         rpmMessage(RPMMESS_DEBUG, _("getting list of mounted filesystems\n"));
919
920         ts->di = _free(ts->di);
921         dip = ts->di = xcalloc((ts->filesystemCount + 1), sizeof(*ts->di));
922
923         for (i = 0; (i < ts->filesystemCount) && dip; i++) {
924 #if STATFS_IN_SYS_STATVFS
925             struct statvfs sfb;
926             memset(&sfb, 0, sizeof(sfb));
927             if (statvfs(ts->filesystems[i], &sfb))
928 #else
929             struct statfs sfb;
930 #  if STAT_STATFS4
931 /* This platform has the 4-argument version of the statfs call.  The last two
932  * should be the size of struct statfs and 0, respectively.  The 0 is the
933  * filesystem type, and is always 0 when statfs is called on a mounted
934  * filesystem, as we're doing.
935  */
936             memset(&sfb, 0, sizeof(sfb));
937             if (statfs(ts->filesystems[i], &sfb, sizeof(sfb), 0))
938 #  else
939             memset(&sfb, 0, sizeof(sfb));
940             if (statfs(ts->filesystems[i], &sfb))
941 #  endif
942 #endif
943             {
944                 dip = NULL;
945             } else {
946                 ts->di[i].bsize = sfb.f_bsize;
947                 ts->di[i].bneeded = 0;
948                 ts->di[i].ineeded = 0;
949 #ifdef STATFS_HAS_F_BAVAIL
950                 ts->di[i].bavail = sfb.f_bavail;
951 #else
952 /* FIXME: the statfs struct doesn't have a member to tell how many blocks are
953  * available for non-superusers.  f_blocks - f_bfree is probably too big, but
954  * it's about all we can do.
955  */
956                 ts->di[i].bavail = sfb.f_blocks - sfb.f_bfree;
957 #endif
958                 /* XXX Avoid FAT and other file systems that have not inodes. */
959                 ts->di[i].iavail = !(sfb.f_ffree == 0 && sfb.f_files == 0)
960                                 ? sfb.f_ffree : -1;
961
962                 xx = stat(ts->filesystems[i], &sb);
963                 ts->di[i].dev = sb.st_dev;
964             }
965         }
966
967         if (dip) ts->di[i].bsize = 0;
968     }
969
970     /* ===============================================
971      * For packages being installed:
972      * - verify package arch/os.
973      * - verify package epoch:version-release is newer.
974      * - count files.
975      * For packages being removed:
976      * - count files.
977      */
978     /* The ordering doesn't matter here */
979     pi = teInitIterator(ts);
980     while ((p = teNext(pi, TR_ADDED)) != NULL) {
981         rpmdbMatchIterator mi;
982
983         if (!(ts->ignoreSet & RPMPROB_FILTER_IGNOREARCH))
984             if (!archOkay(p->arch))
985                 rpmProblemSetAppend(ts->probs, RPMPROB_BADARCH,
986                         p->NEVR, p->key,
987                         p->arch, NULL,
988                         NULL, 0);
989
990         if (!(ts->ignoreSet & RPMPROB_FILTER_IGNOREOS))
991             if (!osOkay(p->os))
992                 rpmProblemSetAppend(ts->probs, RPMPROB_BADOS,
993                         p->NEVR, p->key,
994                         p->os, NULL,
995                         NULL, 0);
996
997         if (!(ts->ignoreSet & RPMPROB_FILTER_OLDPACKAGE)) {
998             Header h;
999             mi = rpmtsInitIterator(ts, RPMTAG_NAME, p->name, 0);
1000             while ((h = rpmdbNextIterator(mi)) != NULL)
1001                 xx = ensureOlder(ts, p, h);
1002             mi = rpmdbFreeIterator(mi);
1003         }
1004
1005         /* XXX multilib should not display "already installed" problems */
1006         if (!(ts->ignoreSet & RPMPROB_FILTER_REPLACEPKG) && !p->multiLib) {
1007             mi = rpmtsInitIterator(ts, RPMTAG_NAME, p->name, 0);
1008             xx = rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_DEFAULT,
1009                                 p->version);
1010             xx = rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_DEFAULT,
1011                                 p->release);
1012
1013             while (rpmdbNextIterator(mi) != NULL) {
1014                 rpmProblemSetAppend(ts->probs, RPMPROB_PKG_INSTALLED,
1015                         p->NEVR, p->key,
1016                         NULL, NULL,
1017                         NULL, 0);
1018                 /*@innerbreak@*/ break;
1019             }
1020             mi = rpmdbFreeIterator(mi);
1021         }
1022
1023         /* Count no. of files (if any). */
1024         if (p->fi != NULL)
1025             totalFileCount += p->fi->fc;
1026
1027     }
1028     pi = teFreeIterator(pi);
1029
1030     /* The ordering doesn't matter here */
1031     pi = teInitIterator(ts);
1032     while ((p = teNext(pi, TR_REMOVED)) != NULL) {
1033         fi = p->fi;
1034         if (fi == NULL)
1035             continue;
1036         if (fi->bnl == NULL)
1037             continue;   /* XXX can't happen */
1038         if (fi->dnl == NULL)
1039             continue;   /* XXX can't happen */
1040         if (fi->dil == NULL)
1041             continue;   /* XXX can't happen */
1042         totalFileCount += fi->fc;
1043     }
1044     pi = teFreeIterator(pi);
1045
1046     /* ===============================================
1047      * Initialize transaction element file info for package:
1048      */
1049
1050     /*
1051      * FIXME?: we'd be better off assembling one very large file list and
1052      * calling fpLookupList only once. I'm not sure that the speedup is
1053      * worth the trouble though.
1054      */
1055     pi = teInitIterator(ts);
1056     while ((p = teNextIterator(pi)) != NULL) {
1057
1058         fi = teGetFi(pi);
1059         if ((fi = teGetFi(pi)) == NULL)
1060             continue;   /* XXX can't happen */
1061
1062 #ifdef  DYING   /* XXX W2DO? this is now done teGetFi, okay ??? */
1063         fi->magic = TFIMAGIC;
1064         fi->te = p;
1065 #endif
1066
1067         /*@-branchstate@*/
1068         switch (p->type) {
1069         case TR_ADDED:
1070             fi->record = 0;
1071             /* Skip netshared paths, not our i18n files, and excluded docs */
1072             if (fi->fc > 0)
1073                 skipFiles(ts, fi);
1074             /*@switchbreak@*/ break;
1075         case TR_REMOVED:
1076             fi->record = p->u.removed.dboffset;
1077             /*@switchbreak@*/ break;
1078         }
1079         /*@=branchstate@*/
1080
1081         fi->fps = (fi->fc > 0 ? xmalloc(fi->fc * sizeof(*fi->fps)) : NULL);
1082     }
1083     pi = teFreeIterator(pi);
1084
1085     if (!ts->chrootDone) {
1086         xx = chdir("/");
1087         /*@-superuser -noeffect @*/
1088         xx = chroot(ts->rootDir);
1089         /*@=superuser =noeffect @*/
1090         ts->chrootDone = 1;
1091         if (ts->rpmdb) ts->rpmdb->db_chrootDone = 1;
1092         /*@-onlytrans@*/
1093         /*@-mods@*/
1094         chroot_prefix = ts->rootDir;
1095         /*@=mods@*/
1096         /*@=onlytrans@*/
1097     }
1098
1099     ts->ht = htCreate(totalFileCount * 2, 0, 0, fpHashFunction, fpEqual);
1100     fpc = fpCacheCreate(totalFileCount);
1101
1102     /* ===============================================
1103      * Add fingerprint for each file not skipped.
1104      */
1105     pi = teInitIterator(ts);
1106     while ((p = teNextIterator(pi)) != NULL) {
1107
1108         if ((fi = teGetFi(pi)) == NULL)
1109             continue;   /* XXX can't happen */
1110
1111         fpLookupList(fpc, fi->dnl, fi->bnl, fi->dil, fi->fc, fi->fps);
1112         /*@-branchstate@*/
1113         for (i = 0; i < fi->fc; i++) {
1114             if (XFA_SKIPPING(fi->actions[i]))
1115                 /*@innercontinue@*/ continue;
1116             /*@-dependenttrans@*/
1117             htAddEntry(ts->ht, fi->fps + i, fi);
1118             /*@=dependenttrans@*/
1119         }
1120         /*@=branchstate@*/
1121     }
1122     pi = teFreeIterator(pi);
1123
1124     /*@-noeffectuncon @*/ /* FIX: check rc */
1125     NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_START, 6, ts->orderCount,
1126         NULL, ts->notifyData));
1127     /*@=noeffectuncon@*/
1128
1129     /* ===============================================
1130      * Compute file disposition for each package in transaction set.
1131      */
1132     pi = teInitIterator(ts);
1133     while ((p = teNextIterator(pi)) != NULL) {
1134         dbiIndexSet * matches;
1135         int knownBad;
1136
1137         if ((fi = teGetFi(pi)) == NULL)
1138             continue;   /* XXX can't happen */
1139
1140         /*@-noeffectuncon @*/ /* FIX: check rc */
1141         NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_PROGRESS, teGetOc(pi),
1142                         ts->orderCount, NULL, ts->notifyData));
1143         /*@=noeffectuncon@*/
1144
1145         if (fi->fc == 0) continue;
1146
1147         /* Extract file info for all files in this package from the database. */
1148         matches = xcalloc(fi->fc, sizeof(*matches));
1149         if (rpmdbFindFpList(ts->rpmdb, fi->fps, matches, fi->fc)) {
1150             psm->ts = rpmtsUnlink(ts, "tsRun (rpmFindFpList fail)");
1151             return 1;   /* XXX WTFO? */
1152         }
1153
1154         numShared = 0;
1155         for (i = 0; i < fi->fc; i++)
1156             numShared += dbiIndexSetCount(matches[i]);
1157
1158         /* Build sorted file info list for this package. */
1159         shared = sharedList = xcalloc((numShared + 1), sizeof(*sharedList));
1160         for (i = 0; i < fi->fc; i++) {
1161             /*
1162              * Take care not to mark files as replaced in packages that will
1163              * have been removed before we will get here.
1164              */
1165             for (j = 0; j < dbiIndexSetCount(matches[i]); j++) {
1166                 int ro;
1167                 ro = dbiIndexRecordOffset(matches[i], j);
1168                 knownBad = 0;
1169                 qi = teInitIterator(ts);
1170                 while ((q = teNext(qi, TR_REMOVED)) != NULL) {
1171                     if (ro == knownBad)
1172                         /*@innerbreak@*/ break;
1173                     if (q->u.removed.dboffset == ro)
1174                         knownBad = ro;
1175                 }
1176                 qi = teFreeIterator(qi);
1177
1178                 shared->pkgFileNum = i;
1179                 shared->otherPkg = dbiIndexRecordOffset(matches[i], j);
1180                 shared->otherFileNum = dbiIndexRecordFileNumber(matches[i], j);
1181                 shared->isRemoved = (knownBad == ro);
1182                 shared++;
1183             }
1184             matches[i] = dbiFreeIndexSet(matches[i]);
1185         }
1186         numShared = shared - sharedList;
1187         shared->otherPkg = -1;
1188         matches = _free(matches);
1189
1190         /* Sort file info by other package index (otherPkg) */
1191         qsort(sharedList, numShared, sizeof(*shared), sharedCmp);
1192
1193         /* For all files from this package that are in the database ... */
1194         for (i = 0; i < numShared; i = nexti) {
1195             int beingRemoved;
1196
1197             shared = sharedList + i;
1198
1199             /* Find the end of the files in the other package. */
1200             for (nexti = i + 1; nexti < numShared; nexti++) {
1201                 if (sharedList[nexti].otherPkg != shared->otherPkg)
1202                     /*@innerbreak@*/ break;
1203             }
1204
1205             /* Is this file from a package being removed? */
1206             beingRemoved = 0;
1207             if (ts->removedPackages != NULL)
1208             for (j = 0; j < ts->numRemovedPackages; j++) {
1209                 if (ts->removedPackages[j] != shared->otherPkg)
1210                     /*@innercontinue@*/ continue;
1211                 beingRemoved = 1;
1212                 /*@innerbreak@*/ break;
1213             }
1214
1215             /* Determine the fate of each file. */
1216             switch (p->type) {
1217             case TR_ADDED:
1218                 xx = handleInstInstalledFiles(ts, p, fi, shared, nexti - i,
1219         !(beingRemoved || (ts->ignoreSet & RPMPROB_FILTER_REPLACEOLDFILES)));
1220                 /*@switchbreak@*/ break;
1221             case TR_REMOVED:
1222                 if (!beingRemoved)
1223                     xx = handleRmvdInstalledFiles(ts, fi, shared, nexti - i);
1224                 /*@switchbreak@*/ break;
1225             }
1226         }
1227
1228         free(sharedList);
1229
1230         /* Update disk space needs on each partition for this package. */
1231         handleOverlappedFiles(ts, p, fi);
1232
1233         /* Check added package has sufficient space on each partition used. */
1234         switch (p->type) {
1235         case TR_ADDED:
1236             if (!(ts->di && fi->fc))
1237                 /*@switchbreak@*/ break;
1238             for (i = 0; i < ts->filesystemCount; i++) {
1239
1240                 dip = ts->di + i;
1241
1242                 /* XXX Avoid FAT and other file systems that have not inodes. */
1243                 if (dip->iavail <= 0)
1244                     /*@innercontinue@*/ continue;
1245
1246                 if (adj_fs_blocks(dip->bneeded) > dip->bavail) {
1247                     rpmProblemSetAppend(ts->probs, RPMPROB_DISKSPACE,
1248                                 p->NEVR, p->key,
1249                                 ts->filesystems[i], NULL, NULL,
1250                    (adj_fs_blocks(dip->bneeded) - dip->bavail) * dip->bsize);
1251                 }
1252
1253                 if (adj_fs_blocks(dip->ineeded) > dip->iavail) {
1254                     rpmProblemSetAppend(ts->probs, RPMPROB_DISKNODES,
1255                                 p->NEVR, p->key,
1256                                 ts->filesystems[i], NULL, NULL,
1257                     (adj_fs_blocks(dip->ineeded) - dip->iavail));
1258                 }
1259             }
1260             /*@switchbreak@*/ break;
1261         case TR_REMOVED:
1262             /*@switchbreak@*/ break;
1263         }
1264     }
1265     pi = teFreeIterator(pi);
1266
1267     if (ts->chrootDone) {
1268         /*@-superuser -noeffect @*/
1269         xx = chroot(".");
1270         /*@=superuser =noeffect @*/
1271         ts->chrootDone = 0;
1272         if (ts->rpmdb) ts->rpmdb->db_chrootDone = 0;
1273         /*@-mods@*/
1274         chroot_prefix = NULL;
1275         /*@=mods@*/
1276         xx = chdir(ts->currDir);
1277     }
1278
1279     /*@-noeffectuncon @*/ /* FIX: check rc */
1280     NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_STOP, 6, ts->orderCount,
1281         NULL, ts->notifyData));
1282     /*@=noeffectuncon @*/
1283
1284     /* ===============================================
1285      * Free unused memory as soon as possible.
1286      */
1287     pi = teInitIterator(ts);
1288     while ((p = teNextIterator(pi)) != NULL) {
1289         if ((fi = teGetFi(pi)) == NULL)
1290             continue;   /* XXX can't happen */
1291         if (fi->fc == 0)
1292             continue;
1293         fi->fps = _free(fi->fps);
1294     }
1295     pi = teFreeIterator(pi);
1296
1297     fpCacheFree(fpc);
1298     htFree(ts->ht);
1299     ts->ht = NULL;
1300
1301     /* ===============================================
1302      * If unfiltered problems exist, free memory and return.
1303      */
1304     if ((ts->transFlags & RPMTRANS_FLAG_BUILD_PROBS)
1305      || (ts->probs->numProblems &&
1306                 (okProbs != NULL || rpmProblemSetTrim(ts->probs, okProbs)))
1307        )
1308     {
1309         if (psm->ts != NULL)
1310             psm->ts = rpmtsUnlink(psm->ts, "tsRun (problems)");
1311         return ts->orderCount;
1312     }
1313
1314     /* ===============================================
1315      * Save removed files before erasing.
1316      */
1317     if (ts->transFlags & (RPMTRANS_FLAG_DIRSTASH | RPMTRANS_FLAG_REPACKAGE)) {
1318         pi = teInitIterator(ts);
1319         while ((p = teNextIterator(pi)) != NULL) {
1320             fi = teGetFi(pi);
1321             switch (p->type) {
1322             case TR_ADDED:
1323                 /*@switchbreak@*/ break;
1324             case TR_REMOVED:
1325                 if (!(ts->transFlags & RPMTRANS_FLAG_REPACKAGE))
1326                     /*@switchbreak@*/ break;
1327                 psm->te = p;
1328                 psm->fi = rpmfiLink(fi, "tsRepackage");
1329                 xx = psmStage(psm, PSM_PKGSAVE);
1330                 (void) rpmfiUnlink(fi, "tsRepackage");
1331                 psm->fi = NULL;
1332                 psm->te = NULL;
1333                 /*@switchbreak@*/ break;
1334             }
1335         }
1336         pi = teFreeIterator(pi);
1337     }
1338
1339     /* ===============================================
1340      * Install and remove packages.
1341      */
1342     lastKey = (alKey)-2;        /* erased packages have -1 */
1343     pi = teInitIterator(ts);
1344     /*@-branchstate@*/ /* FIX: fi reload needs work */
1345     while ((p = teNextIterator(pi)) != NULL) {
1346         alKey pkgKey;
1347         Header h;
1348         int gotfd;
1349
1350         gotfd = 0;
1351         if ((fi = teGetFi(pi)) == NULL)
1352             continue;   /* XXX can't happen */
1353         
1354         psm->te = p;
1355         psm->fi = rpmfiLink(fi, "tsInstall");
1356         switch (p->type) {
1357         case TR_ADDED:
1358
1359             pkgKey = p->u.addedKey;
1360
1361             rpmMessage(RPMMESS_DEBUG, "========== +++ %s\n", p->NEVR);
1362             h = NULL;
1363             {
1364                 p->fd = ts->notify(fi->h, RPMCALLBACK_INST_OPEN_FILE, 0, 0,
1365                                 p->key, ts->notifyData);
1366                 if (p->fd != NULL) {
1367                     rpmRC rpmrc;
1368
1369                     /*@=mustmod@*/      /* LCL: segfault */
1370                     rpmrc = rpmReadPackageFile(ts, p->fd,
1371                                 "rpmRunTransactions", &h);
1372                     /*@=mustmod@*/
1373
1374                     if (!(rpmrc == RPMRC_OK || rpmrc == RPMRC_BADSIZE)) {
1375                         p->fd = ts->notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE,
1376                                         0, 0,
1377                                         p->key, ts->notifyData);
1378                         p->fd = NULL;
1379                         ourrc++;
1380                     }
1381                     if (p->fd != NULL) gotfd = 1;
1382                 }
1383             }
1384             /*@=branchstate@*/
1385
1386             /*@-branchstate@*/
1387             if (p->fd != NULL) {
1388                 {
1389 char * fstates = fi->fstates;
1390 fileAction * actions = fi->actions;
1391
1392 fi->fstates = NULL;
1393 fi->actions = NULL;
1394                     (void) fiFree(fi, 0);
1395 /*@-usereleased@*/
1396 fi->magic = TFIMAGIC;
1397 fi->te = p;
1398 fi->record = 0;
1399                     (void) fiNew(ts, fi, h, RPMTAG_BASENAMES, 1);
1400 fi->fstates = _free(fi->fstates);
1401 fi->fstates = fstates;
1402 fi->actions = _free(fi->actions);
1403 fi->actions = actions;
1404 /*@=usereleased@*/
1405
1406                 }
1407                 if (p->multiLib)
1408                     ts->transFlags |= RPMTRANS_FLAG_MULTILIB;
1409
1410                 if (psmStage(psm, PSM_PKGINSTALL)) {
1411                     ourrc++;
1412                     lastKey = pkgKey;
1413                 }
1414                 fi->h = headerFree(fi->h, "TR_ADDED fi->h free");
1415             } else {
1416                 ourrc++;
1417                 lastKey = pkgKey;
1418             }
1419             /*@=branchstate@*/
1420
1421             h = headerFree(h, "TR_ADDED h free");
1422
1423             if (gotfd) {
1424                 /*@-noeffectuncon @*/ /* FIX: check rc */
1425                 (void)ts->notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE, 0, 0,
1426                         p->key, ts->notifyData);
1427                 /*@=noeffectuncon @*/
1428                 p->fd = NULL;
1429             }
1430             (void) fiFree(fi, 0);
1431             /*@switchbreak@*/ break;
1432         case TR_REMOVED:
1433             rpmMessage(RPMMESS_DEBUG, "========== --- %s\n", p->NEVR);
1434             /* If install failed, then we shouldn't erase. */
1435             if (p->u.removed.dependsOnKey != lastKey) {
1436                 if (psmStage(psm, PSM_PKGERASE))
1437                     ourrc++;
1438             }
1439             (void) fiFree(fi, 0);
1440             /*@switchbreak@*/ break;
1441         }
1442         xx = rpmdbSync(ts->rpmdb);
1443         (void) rpmfiUnlink(psm->fi, "tsInstall");
1444         psm->fi = NULL;
1445         psm->te = NULL;
1446     }
1447     /*@=branchstate@*/
1448     pi = teFreeIterator(pi);
1449
1450     psm->ts = rpmtsUnlink(psm->ts, "tsRun");
1451
1452     /*@-nullstate@*/ /* FIX: ts->flList may be NULL */
1453     if (ourrc)
1454         return -1;
1455     else
1456         return 0;
1457     /*@=nullstate@*/
1458 }