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