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