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