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