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