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