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