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