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