Scaffholding for dumping full headers carried through transaction.
[platform/upstream/rpm.git] / lib / transaction.c
1 /** \ingroup rpmtrans
2  * \file lib/transaction.c
3  */
4
5 #include "system.h"
6
7 #include "psm.h"
8 #include <rpmmacro.h>   /* XXX for rpmExpand */
9
10 #include "fprint.h"
11 #include "misc.h" /* XXX stripTrailingChar, splitString, currentDirectory */
12 #include "rpmdb.h"
13
14 /*@-redecl -exportheadervar@*/
15 /*@unchecked@*/
16 extern const char * chroot_prefix;
17 /*@=redecl =exportheadervar@*/
18
19 /* XXX FIXME: merge with existing (broken?) tests in system.h */
20 /* portability fiddles */
21 #if STATFS_IN_SYS_STATVFS
22 /*@-incondefs@*/
23 # include <sys/statvfs.h>
24 #if defined(__LCLINT__)
25 /*@-declundef -exportheader -protoparammatch @*/ /* LCL: missing annotation */
26 extern int statvfs (const char * file, /*@out@*/ struct statvfs * buf)
27         /*@globals fileSystem @*/
28         /*@modifies *buf, fileSystem @*/;
29 /*@=declundef =exportheader =protoparammatch @*/
30 /*@=incondefs@*/
31 #endif
32 #else
33 # if STATFS_IN_SYS_VFS
34 #  include <sys/vfs.h>
35 # else
36 #  if STATFS_IN_SYS_MOUNT
37 #   include <sys/mount.h>
38 #  else
39 #   if STATFS_IN_SYS_STATFS
40 #    include <sys/statfs.h>
41 #   endif
42 #  endif
43 # endif
44 #endif
45
46 #include "debug.h"
47
48 /*@access FD_t@*/               /* XXX compared with NULL */
49 /*@access Header@*/             /* XXX compared with NULL */
50 /*@access dbiIndexSet@*/
51 /*@access rpmdb@*/
52 /*@access rpmTransactionSet@*/
53 /*@access TFI_t@*/
54 /*@access PSM_t@*/
55 /*@access rpmProblemSet@*/
56 /*@access rpmProblem@*/
57
58 /**
59  */
60 struct diskspaceInfo {
61     dev_t dev;                  /*!< file system device number. */
62     signed long bneeded;        /*!< no. of blocks needed. */
63     signed long ineeded;        /*!< no. of inodes needed. */
64     int bsize;                  /*!< file system block size. */
65     signed long bavail;         /*!< no. of blocks available. */
66     signed long iavail;         /*!< no. of inodes available. */
67 };
68
69 /**
70  * Adjust for root only reserved space. On linux e2fs, this is 5%.
71  */
72 #define adj_fs_blocks(_nb)      (((_nb) * 21) / 20)
73
74 /* argon thought a shift optimization here was a waste of time...  he's
75    probably right :-( */
76 #define BLOCK_ROUND(size, block) (((size) + (block) - 1) / (block))
77
78 #define XSTRCMP(a, b) ((!(a) && !(b)) || ((a) && (b) && !strcmp((a), (b))))
79
80 /**
81  */
82 static /*@null@*/ void * freeFl(rpmTransactionSet ts,
83                 /*@only@*/ /*@null@*/ TFI_t flList)
84         /*@*/
85 {
86     if (flList) {
87         TFI_t fi;
88         int oc;
89
90         /*@-usereleased@*/
91         for (oc = 0, fi = flList; oc < ts->orderCount; oc++, fi++)
92             freeFi(fi);
93         flList = _free(flList);
94         /*@=usereleased@*/
95     }
96     return NULL;
97 }
98
99 void rpmtransSetScriptFd(rpmTransactionSet ts, FD_t fd)
100 {
101     /*@-type@*/ /* FIX: cast? */
102     ts->scriptFd = (fd ? fdLink(fd, "rpmtransSetScriptFd") : NULL);
103     /*@=type@*/
104 }
105
106 int rpmtransGetKeys(const rpmTransactionSet ts, const void *** ep, int * nep)
107 {
108     int rc = 0;
109
110     if (nep) *nep = ts->orderCount;
111     if (ep) {
112         const void ** e;
113         int oc;
114
115         *ep = e = xmalloc(ts->orderCount * sizeof(*e));
116         for (oc = 0; oc < ts->orderCount; oc++, e++) {
117             switch (ts->order[oc].type) {
118             case TR_ADDED:
119                 if (ts->addedPackages.list) {
120                     struct availablePackage * alp;
121                     alp = ts->addedPackages.list + ts->order[oc].u.addedIndex;
122                     *e = alp->key;
123                     /*@switchbreak@*/ break;
124                 }
125                 /*@fallthrough@*/
126             default:
127             case TR_REMOVED:
128                 /*@-mods@*/     /* FIX: double indirection. */
129                 *e = NULL;
130                 /*@=mods@*/
131                 /*@switchbreak@*/ break;
132             }
133         }
134     }
135     return rc;
136 }
137
138 #ifdef  DYING
139 /**
140  */
141 static rpmProblemSet psCreate(void)
142         /*@*/
143 {
144     rpmProblemSet probs;
145
146     probs = xcalloc(1, sizeof(*probs)); /* XXX memory leak */
147     probs->numProblems = probs->numProblemsAlloced = 0;
148     probs->probs = NULL;
149
150     return probs;
151 }
152
153 /**
154  */
155 static void psAppend(rpmProblemSet probs, rpmProblemType type,
156                 const struct availablePackage * alp,
157                 const char * dn, const char *bn,
158                 Header altH, unsigned long ulong1)
159         /*@modifies *probs, alp @*/
160 {
161     rpmProblem p;
162     char *t;
163
164     if (probs->numProblems == probs->numProblemsAlloced) {
165         if (probs->numProblemsAlloced)
166             probs->numProblemsAlloced *= 2;
167         else
168             probs->numProblemsAlloced = 2;
169         probs->probs = xrealloc(probs->probs,
170                         probs->numProblemsAlloced * sizeof(*probs->probs));
171     }
172
173     p = probs->probs + probs->numProblems;
174     probs->numProblems++;
175     memset(p, 0, sizeof(*p));
176     p->type = type;
177     /*@-assignexpose@*/
178     p->key = alp->key;
179     /*@=assignexpose@*/
180     p->ulong1 = ulong1;
181     p->ignoreProblem = 0;
182     p->str1 = NULL;
183     p->h = NULL;
184     p->pkgNEVR = NULL;
185     p->altNEVR = NULL;
186
187     if (dn || bn) {
188         p->str1 =
189             t = xcalloc(1, (dn ? strlen(dn) : 0) + (bn ? strlen(bn) : 0) + 1);
190         if (dn) t = stpcpy(t, dn);
191         if (bn) t = stpcpy(t, bn);
192     }
193
194     if (alp) {
195         p->h = headerLink(alp->h);
196         p->pkgNEVR =
197             t = xcalloc(1, strlen(alp->name) +
198                            strlen(alp->version) +
199                            strlen(alp->release) + sizeof("--"));
200         t = stpcpy(t, alp->name);
201         t = stpcpy(t, "-");
202         t = stpcpy(t, alp->version);
203         t = stpcpy(t, "-");
204         t = stpcpy(t, alp->release);
205     }
206
207     if (altH) {
208         const char * n, * v, * r;
209         (void) headerNVR(altH, &n, &v, &r);
210         p->altNEVR =
211             t = xcalloc(1, strlen(n) + strlen(v) + strlen(r) + sizeof("--"));
212         t = stpcpy(t, n);
213         t = stpcpy(t, "-");
214         t = stpcpy(t, v);
215         t = stpcpy(t, "-");
216         t = stpcpy(t, r);
217     }
218 }
219 #endif  /* DYING */
220
221 /**
222  */
223 static int archOkay(Header h)
224         /*@*/
225 {
226     void * pkgArch;
227     int type, count;
228
229     /* make sure we're trying to install this on the proper architecture */
230     (void) headerGetEntry(h, RPMTAG_ARCH, &type, (void **) &pkgArch, &count);
231 #ifndef DYING
232     if (type == RPM_INT8_TYPE) {
233         int_8 * pkgArchNum;
234         int archNum;
235
236         /* old arch handling */
237         rpmGetArchInfo(NULL, &archNum);
238         pkgArchNum = pkgArch;
239         if (archNum != *pkgArchNum) {
240             return 0;
241         }
242     } else
243 #endif
244     {
245         /* new arch handling */
246         if (!rpmMachineScore(RPM_MACHTABLE_INSTARCH, pkgArch)) {
247             return 0;
248         }
249     }
250
251     return 1;
252 }
253
254 /**
255  */
256 static int osOkay(Header h)
257         /*@*/
258 {
259     void * pkgOs;
260     int type, count;
261
262     /* make sure we're trying to install this on the proper os */
263     (void) headerGetEntry(h, RPMTAG_OS, &type, (void **) &pkgOs, &count);
264 #ifndef DYING
265     if (type == RPM_INT8_TYPE) {
266         /* v1 packages and v2 packages both used improper OS numbers, so just
267            deal with it hope things work */
268         return 1;
269     } else
270 #endif
271     {
272         /* new os handling */
273         if (!rpmMachineScore(RPM_MACHTABLE_INSTOS, pkgOs)) {
274             return 0;
275         }
276     }
277
278     return 1;
279 }
280
281 void rpmProblemSetFree(rpmProblemSet probs)
282 {
283     int i;
284
285     for (i = 0; i < probs->numProblems; i++) {
286         rpmProblem p = probs->probs + i;
287         p->h = headerFree(p->h);
288         p->pkgNEVR = _free(p->pkgNEVR);
289         p->altNEVR = _free(p->altNEVR);
290         p->str1 = _free(p->str1);
291     }
292     free(probs);
293 }
294
295 #ifdef  DYING
296 /**
297  */
298 static /*@observer@*/ const char *const ftstring (fileTypes ft)
299         /*@*/
300 {
301     switch (ft) {
302     case XDIR:  return "directory";
303     case CDEV:  return "char dev";
304     case BDEV:  return "block dev";
305     case LINK:  return "link";
306     case SOCK:  return "sock";
307     case PIPE:  return "fifo/pipe";
308     case REG:   return "file";
309     default:    return "unknown file type";
310     }
311     /*@notreached@*/
312 }
313
314 /**
315  */
316 static fileTypes whatis(uint_16 mode)
317         /*@*/
318 {
319     if (S_ISDIR(mode))  return XDIR;
320     if (S_ISCHR(mode))  return CDEV;
321     if (S_ISBLK(mode))  return BDEV;
322     if (S_ISLNK(mode))  return LINK;
323     if (S_ISSOCK(mode)) return SOCK;
324     if (S_ISFIFO(mode)) return PIPE;
325     return REG;
326 }
327
328 #define alloca_strdup(_s)       strcpy(alloca(strlen(_s)+1), (_s))
329
330 /**
331  * Relocate files in header.
332  * @todo multilib file dispositions need to be checked.
333  * @param ts            transaction set
334  * @param fi            transaction element file info
335  * @param alp           available package
336  * @param origH         package header
337  * @param actions       file dispositions
338  * @return              header with relocated files
339  */
340 static Header relocateFileList(const rpmTransactionSet ts, TFI_t fi,
341                 struct availablePackage * alp,
342                 Header origH, fileAction * actions)
343         /*@modifies ts, fi, alp, origH, actions @*/
344 {
345     HGE_t hge = fi->hge;
346     HAE_t hae = fi->hae;
347     HME_t hme = fi->hme;
348     HFD_t hfd = (fi->hfd ? fi->hfd : headerFreeData);
349     static int _printed = 0;
350     int allowBadRelocate = (ts->ignoreSet & RPMPROB_FILTER_FORCERELOCATE);
351     rpmRelocation * rawRelocations = alp->relocs;
352     rpmRelocation * relocations = NULL;
353     int numRelocations;
354     const char ** validRelocations;
355     rpmTagType validType;
356     int numValid;
357     const char ** baseNames;
358     const char ** dirNames;
359     int_32 * dirIndexes;
360     int_32 * newDirIndexes;
361     int_32 fileCount;
362     int_32 dirCount;
363     uint_32 * fFlags = NULL;
364     uint_16 * fModes = NULL;
365     char * skipDirList;
366     Header h;
367     int nrelocated = 0;
368     int fileAlloced = 0;
369     char * fn = NULL;
370     int haveRelocatedFile = 0;
371     int reldel = 0;
372     int len;
373     int i, j, xx;
374
375     if (!hge(origH, RPMTAG_PREFIXES, &validType,
376                         (void **) &validRelocations, &numValid))
377         numValid = 0;
378
379     numRelocations = 0;
380     if (rawRelocations)
381         while (rawRelocations[numRelocations].newPath ||
382                rawRelocations[numRelocations].oldPath)
383             numRelocations++;
384
385     /*
386      * If no relocations are specified (usually the case), then return the
387      * original header. If there are prefixes, however, then INSTPREFIXES
388      * should be added, but, since relocateFileList() can be called more
389      * than once for the same header, don't bother if already present.
390      */
391     if (rawRelocations == NULL || numRelocations == 0) {
392         if (numValid) {
393             if (!headerIsEntry(origH, RPMTAG_INSTPREFIXES))
394                 xx = hae(origH, RPMTAG_INSTPREFIXES,
395                         validType, validRelocations, numValid);
396             validRelocations = hfd(validRelocations, validType);
397         }
398         /* XXX FIXME multilib file actions need to be checked. */
399         return headerLink(origH);
400     }
401
402     h = headerLink(origH);
403
404     relocations = alloca(sizeof(*relocations) * numRelocations);
405
406     /* Build sorted relocation list from raw relocations. */
407     for (i = 0; i < numRelocations; i++) {
408         char * t;
409
410         /*
411          * Default relocations (oldPath == NULL) are handled in the UI,
412          * not rpmlib.
413          */
414         if (rawRelocations[i].oldPath == NULL) continue; /* XXX can't happen */
415
416         /* FIXME: Trailing /'s will confuse us greatly. Internal ones will 
417            too, but those are more trouble to fix up. :-( */
418         t = alloca_strdup(rawRelocations[i].oldPath);
419         /*@-branchstate@*/
420         relocations[i].oldPath = (t[0] == '/' && t[1] == '\0')
421             ? t
422             : stripTrailingChar(t, '/');
423         /*@=branchstate@*/
424
425         /* An old path w/o a new path is valid, and indicates exclusion */
426         if (rawRelocations[i].newPath) {
427             int del;
428
429             t = alloca_strdup(rawRelocations[i].newPath);
430             /*@-branchstate@*/
431             relocations[i].newPath = (t[0] == '/' && t[1] == '\0')
432                 ? t
433                 : stripTrailingChar(t, '/');
434             /*@=branchstate@*/
435
436             /*@-nullpass@*/     /* FIX:  relocations[i].oldPath == NULL */
437             /* Verify that the relocation's old path is in the header. */
438             for (j = 0; j < numValid; j++)
439                 if (!strcmp(validRelocations[j], relocations[i].oldPath))
440                     /*@innerbreak@*/ break;
441             /* XXX actions check prevents problem from being appended twice. */
442             if (j == numValid && !allowBadRelocate && actions)
443                 psAppend(ts->probs, RPMPROB_BADRELOCATE, alp,
444                          relocations[i].oldPath, NULL, NULL, 0);
445             del =
446                 strlen(relocations[i].newPath) - strlen(relocations[i].oldPath);
447             /*@=nullpass@*/
448
449             if (del > reldel)
450                 reldel = del;
451         } else {
452             relocations[i].newPath = NULL;
453         }
454     }
455
456     /* stupid bubble sort, but it's probably faster here */
457     for (i = 0; i < numRelocations; i++) {
458         int madeSwap;
459         madeSwap = 0;
460         for (j = 1; j < numRelocations; j++) {
461             rpmRelocation tmpReloc;
462             if (relocations[j - 1].oldPath == NULL || /* XXX can't happen */
463                 relocations[j    ].oldPath == NULL || /* XXX can't happen */
464         strcmp(relocations[j - 1].oldPath, relocations[j].oldPath) <= 0)
465                 /*@innercontinue@*/ continue;
466             /*@-usereleased@*/ /* LCL: ??? */
467             tmpReloc = relocations[j - 1];
468             relocations[j - 1] = relocations[j];
469             relocations[j] = tmpReloc;
470             /*@=usereleased@*/
471             madeSwap = 1;
472         }
473         if (!madeSwap) break;
474     }
475
476     if (!_printed) {
477         _printed = 1;
478         rpmMessage(RPMMESS_DEBUG, _("========== relocations\n"));
479         for (i = 0; i < numRelocations; i++) {
480             if (relocations[i].oldPath == NULL) continue; /* XXX can't happen */
481             if (relocations[i].newPath == NULL)
482                 rpmMessage(RPMMESS_DEBUG, _("%5d exclude  %s\n"),
483                         i, relocations[i].oldPath);
484             else
485                 rpmMessage(RPMMESS_DEBUG, _("%5d relocate %s -> %s\n"),
486                         i, relocations[i].oldPath, relocations[i].newPath);
487         }
488     }
489
490     /* Add relocation values to the header */
491     if (numValid) {
492         const char ** actualRelocations;
493         int numActual;
494
495         actualRelocations = xmalloc(numValid * sizeof(*actualRelocations));
496         numActual = 0;
497         for (i = 0; i < numValid; i++) {
498             for (j = 0; j < numRelocations; j++) {
499                 if (relocations[j].oldPath == NULL || /* XXX can't happen */
500                     strcmp(validRelocations[i], relocations[j].oldPath))
501                     /*@innercontinue@*/ continue;
502                 /* On install, a relocate to NULL means skip the path. */
503                 if (relocations[j].newPath) {
504                     actualRelocations[numActual] = relocations[j].newPath;
505                     numActual++;
506                 }
507                 /*@innerbreak@*/ break;
508             }
509             if (j == numRelocations) {
510                 actualRelocations[numActual] = validRelocations[i];
511                 numActual++;
512             }
513         }
514
515         if (numActual)
516             xx = hae(h, RPMTAG_INSTPREFIXES, RPM_STRING_ARRAY_TYPE,
517                        (void **) actualRelocations, numActual);
518
519         actualRelocations = _free(actualRelocations);
520         validRelocations = hfd(validRelocations, validType);
521     }
522
523     xx = hge(h, RPMTAG_BASENAMES, NULL, (void **) &baseNames, &fileCount);
524     xx = hge(h, RPMTAG_DIRINDEXES, NULL, (void **) &dirIndexes, NULL);
525     xx = hge(h, RPMTAG_DIRNAMES, NULL, (void **) &dirNames, &dirCount);
526     xx = hge(h, RPMTAG_FILEFLAGS, NULL, (void **) &fFlags, NULL);
527     xx = hge(h, RPMTAG_FILEMODES, NULL, (void **) &fModes, NULL);
528
529     skipDirList = alloca(dirCount * sizeof(*skipDirList));
530     memset(skipDirList, 0, dirCount * sizeof(*skipDirList));
531
532     newDirIndexes = alloca(sizeof(*newDirIndexes) * fileCount);
533     memcpy(newDirIndexes, dirIndexes, sizeof(*newDirIndexes) * fileCount);
534     dirIndexes = newDirIndexes;
535
536     /*
537      * For all relocations, we go through sorted file/relocation lists 
538      * backwards so that /usr/local relocations take precedence over /usr 
539      * ones.
540      */
541
542     /* Relocate individual paths. */
543
544     for (i = fileCount - 1; i >= 0; i--) {
545         fileTypes ft;
546         int fnlen;
547
548         /*
549          * If only adding libraries of different arch into an already
550          * installed package, skip all other files.
551          */
552         if (alp->multiLib && !isFileMULTILIB((fFlags[i]))) {
553             if (actions) {
554                 actions[i] = FA_SKIPMULTILIB;
555                 rpmMessage(RPMMESS_DEBUG, _("excluding multilib path %s%s\n"), 
556                         dirNames[dirIndexes[i]], baseNames[i]);
557             }
558             continue;
559         }
560
561         len = reldel +
562                 strlen(dirNames[dirIndexes[i]]) + strlen(baseNames[i]) + 1;
563         /*@-branchstate@*/
564         if (len >= fileAlloced) {
565             fileAlloced = len * 2;
566             fn = xrealloc(fn, fileAlloced);
567         }
568         /*@=branchstate@*/
569         *fn = '\0';
570         fnlen = stpcpy( stpcpy(fn, dirNames[dirIndexes[i]]), baseNames[i]) - fn;
571
572         /*
573          * See if this file path needs relocating.
574          */
575         /*
576          * XXX FIXME: Would a bsearch of the (already sorted) 
577          * relocation list be a good idea?
578          */
579         for (j = numRelocations - 1; j >= 0; j--) {
580             if (relocations[j].oldPath == NULL) /* XXX can't happen */
581                 /*@innercontinue@*/ continue;
582             len = strcmp(relocations[j].oldPath, "/")
583                 ? strlen(relocations[j].oldPath)
584                 : 0;
585
586             if (fnlen < len)
587                 /*@innercontinue@*/ continue;
588             /*
589              * Only subdirectories or complete file paths may be relocated. We
590              * don't check for '\0' as our directory names all end in '/'.
591              */
592             if (!(fn[len] == '/' || fnlen == len))
593                 /*@innercontinue@*/ continue;
594
595             if (strncmp(relocations[j].oldPath, fn, len))
596                 /*@innercontinue@*/ continue;
597             /*@innerbreak@*/ break;
598         }
599         if (j < 0) continue;
600
601         ft = whatis(fModes[i]);
602
603         /* On install, a relocate to NULL means skip the path. */
604         if (relocations[j].newPath == NULL) {
605             if (ft == XDIR) {
606                 /* Start with the parent, looking for directory to exclude. */
607                 for (j = dirIndexes[i]; j < dirCount; j++) {
608                     len = strlen(dirNames[j]) - 1;
609                     while (len > 0 && dirNames[j][len-1] == '/') len--;
610                     if (fnlen != len)
611                         /*@innercontinue@*/ continue;
612                     if (strncmp(fn, dirNames[j], fnlen))
613                         /*@innercontinue@*/ continue;
614                     /*@innerbreak@*/ break;
615                 }
616                 if (j < dirCount)
617                     skipDirList[j] = 1;
618             }
619             if (actions) {
620                 actions[i] = FA_SKIPNSTATE;
621                 rpmMessage(RPMMESS_DEBUG, _("excluding %s %s\n"),
622                         ftstring(ft), fn);
623             }
624             continue;
625         }
626
627         /* Relocation on full paths only, please. */
628         if (fnlen != len) continue;
629
630         if (actions)
631             rpmMessage(RPMMESS_DEBUG, _("relocating %s to %s\n"),
632                     fn, relocations[j].newPath);
633         nrelocated++;
634
635         strcpy(fn, relocations[j].newPath);
636         {   char * te = strrchr(fn, '/');
637             if (te) {
638                 if (te > fn) te++;      /* root is special */
639                 fnlen = te - fn;
640             } else
641                 te = fn + strlen(fn);
642             /*@-nullpass -nullderef@*/  /* LCL: te != NULL here. */
643             if (strcmp(baseNames[i], te)) /* basename changed too? */
644                 baseNames[i] = alloca_strdup(te);
645             *te = '\0';                 /* terminate new directory name */
646             /*@=nullpass =nullderef@*/
647         }
648
649         /* Does this directory already exist in the directory list? */
650         for (j = 0; j < dirCount; j++) {
651             if (fnlen != strlen(dirNames[j]))
652                 /*@innercontinue@*/ continue;
653             if (strncmp(fn, dirNames[j], fnlen))
654                 /*@innercontinue@*/ continue;
655             /*@innerbreak@*/ break;
656         }
657         
658         if (j < dirCount) {
659             dirIndexes[i] = j;
660             continue;
661         }
662
663         /* Creating new paths is a pita */
664         if (!haveRelocatedFile) {
665             const char ** newDirList;
666
667             haveRelocatedFile = 1;
668             newDirList = xmalloc((dirCount + 1) * sizeof(*newDirList));
669             for (j = 0; j < dirCount; j++)
670                 newDirList[j] = alloca_strdup(dirNames[j]);
671             dirNames = hfd(dirNames, RPM_STRING_ARRAY_TYPE);
672             dirNames = newDirList;
673         } else {
674             dirNames = xrealloc(dirNames, 
675                                sizeof(*dirNames) * (dirCount + 1));
676         }
677
678         dirNames[dirCount] = alloca_strdup(fn);
679         dirIndexes[i] = dirCount;
680         dirCount++;
681     }
682
683     /* Finish off by relocating directories. */
684     for (i = dirCount - 1; i >= 0; i--) {
685         for (j = numRelocations - 1; j >= 0; j--) {
686
687             if (relocations[j].oldPath == NULL) /* XXX can't happen */
688                 /*@innercontinue@*/ continue;
689             len = strcmp(relocations[j].oldPath, "/")
690                 ? strlen(relocations[j].oldPath)
691                 : 0;
692
693             if (len && strncmp(relocations[j].oldPath, dirNames[i], len))
694                 /*@innercontinue@*/ continue;
695
696             /*
697              * Only subdirectories or complete file paths may be relocated. We
698              * don't check for '\0' as our directory names all end in '/'.
699              */
700             if (dirNames[i][len] != '/')
701                 /*@innercontinue@*/ continue;
702
703             if (relocations[j].newPath) { /* Relocate the path */
704                 const char * s = relocations[j].newPath;
705                 char * t = alloca(strlen(s) + strlen(dirNames[i]) - len + 1);
706
707                 (void) stpcpy( stpcpy(t, s) , dirNames[i] + len);
708                 if (actions)
709                     rpmMessage(RPMMESS_DEBUG,
710                         _("relocating directory %s to %s\n"), dirNames[i], t);
711                 dirNames[i] = t;
712                 nrelocated++;
713             }
714         }
715     }
716
717     /* Save original filenames in header and replace (relocated) filenames. */
718     if (nrelocated) {
719         int c;
720         void * p;
721         rpmTagType t;
722
723         p = NULL;
724         xx = hge(h, RPMTAG_BASENAMES, &t, &p, &c);
725         xx = hae(h, RPMTAG_ORIGBASENAMES, t, p, c);
726         p = hfd(p, t);
727
728         p = NULL;
729         xx = hge(h, RPMTAG_DIRNAMES, &t, &p, &c);
730         xx = hae(h, RPMTAG_ORIGDIRNAMES, t, p, c);
731         p = hfd(p, t);
732
733         p = NULL;
734         xx = hge(h, RPMTAG_DIRINDEXES, &t, &p, &c);
735         xx = hae(h, RPMTAG_ORIGDIRINDEXES, t, p, c);
736         p = hfd(p, t);
737
738         xx = hme(h, RPMTAG_BASENAMES, RPM_STRING_ARRAY_TYPE,
739                           baseNames, fileCount);
740         fi->bnl = hfd(fi->bnl, RPM_STRING_ARRAY_TYPE);
741         xx = hge(h, RPMTAG_BASENAMES, NULL, (void **) &fi->bnl, &fi->fc);
742
743         xx = hme(h, RPMTAG_DIRNAMES, RPM_STRING_ARRAY_TYPE,
744                           dirNames, dirCount);
745         fi->dnl = hfd(fi->dnl, RPM_STRING_ARRAY_TYPE);
746         xx = hge(h, RPMTAG_DIRNAMES, NULL, (void **) &fi->dnl, &fi->dc);
747
748         xx = hme(h, RPMTAG_DIRINDEXES, RPM_INT32_TYPE,
749                           dirIndexes, fileCount);
750         xx = hge(h, RPMTAG_DIRINDEXES, NULL, (void **) &fi->dil, NULL);
751     }
752
753     baseNames = hfd(baseNames, RPM_STRING_ARRAY_TYPE);
754     dirNames = hfd(dirNames, RPM_STRING_ARRAY_TYPE);
755     fn = _free(fn);
756
757     return h;
758 }
759 #endif /* DYING */
760
761 /**
762  * Filter a problem set.
763  * As the problem sets are generated in an order solely dependent
764  * on the ordering of the packages in the transaction, and that
765  * ordering can't be changed, the problem sets must be parallel to
766  * one another. Additionally, the filter set must be a subset of the
767  * target set, given the operations available on transaction set.
768  * This is good, as it lets us perform this trim in linear time, rather
769  * then logarithmic or quadratic.
770  *
771  * @param filter        filter
772  * @param target        problem set
773  * @return              0 no problems, 1 if problems remain
774  */
775 static int psTrim(rpmProblemSet filter, rpmProblemSet target)
776         /*@modifies target @*/
777 {
778     rpmProblem f = filter->probs;
779     rpmProblem t = target->probs;
780     int gotProblems = 0;
781
782     /*@-branchstate@*/
783     while ((f - filter->probs) < filter->numProblems) {
784         if (!f->ignoreProblem) {
785             f++;
786             continue;
787         }
788         while ((t - target->probs) < target->numProblems) {
789             /*@-nullpass@*/     /* LCL: looks good to me */
790             if (f->h == t->h && f->type == t->type && t->key == f->key &&
791                      XSTRCMP(f->str1, t->str1))
792                 /*@innerbreak@*/ break;
793             /*@=nullpass@*/
794             t++;
795             gotProblems = 1;
796         }
797
798         if ((t - target->probs) == target->numProblems) {
799             /* this can't happen ;-) let's be sane if it doesn though */
800             break;
801         }
802
803         t->ignoreProblem = f->ignoreProblem;
804         t++, f++;
805     }
806     /*@=branchstate@*/
807
808     if ((t - target->probs) < target->numProblems)
809         gotProblems = 1;
810
811     return gotProblems;
812 }
813
814 /**
815  */
816 static int sharedCmp(const void * one, const void * two)
817         /*@*/
818 {
819     const struct sharedFileInfo * a = one;
820     const struct sharedFileInfo * b = two;
821
822     if (a->otherPkg < b->otherPkg)
823         return -1;
824     else if (a->otherPkg > b->otherPkg)
825         return 1;
826
827     return 0;
828 }
829
830 /**
831  */
832 static fileAction decideFileFate(const char * dirName,
833                         const char * baseName, short dbMode,
834                         const char * dbMd5, const char * dbLink, short newMode,
835                         const char * newMd5, const char * newLink, int newFlags,
836                         rpmtransFlags transFlags)
837         /*@globals fileSystem @*/
838         /*@modifies fileSystem @*/
839 {
840     char buffer[1024];
841     const char * dbAttr, * newAttr;
842     fileTypes dbWhat, newWhat, diskWhat;
843     struct stat sb;
844     int i, rc;
845     int save = (newFlags & RPMFILE_NOREPLACE) ? FA_ALTNAME : FA_SAVE;
846     char * filespec = alloca(strlen(dirName) + strlen(baseName) + 1);
847
848     (void) stpcpy( stpcpy(filespec, dirName), baseName);
849
850     if (lstat(filespec, &sb)) {
851         /*
852          * The file doesn't exist on the disk. Create it unless the new
853          * package has marked it as missingok, or allfiles is requested.
854          */
855         if (!(transFlags & RPMTRANS_FLAG_ALLFILES) &&
856            (newFlags & RPMFILE_MISSINGOK)) {
857             rpmMessage(RPMMESS_DEBUG, _("%s skipped due to missingok flag\n"),
858                         filespec);
859             return FA_SKIP;
860         } else {
861             return FA_CREATE;
862         }
863     }
864
865     diskWhat = whatis(sb.st_mode);
866     dbWhat = whatis(dbMode);
867     newWhat = whatis(newMode);
868
869     /* RPM >= 2.3.10 shouldn't create config directories -- we'll ignore
870        them in older packages as well */
871     if (newWhat == XDIR) {
872         return FA_CREATE;
873     }
874
875     if (diskWhat != newWhat) {
876         return save;
877     } else if (newWhat != dbWhat && diskWhat != dbWhat) {
878         return save;
879     } else if (dbWhat != newWhat) {
880         return FA_CREATE;
881     } else if (dbWhat != LINK && dbWhat != REG) {
882         return FA_CREATE;
883     }
884
885     if (dbWhat == REG) {
886         rc = mdfile(filespec, buffer);
887
888         if (rc) {
889             /* assume the file has been removed, don't freak */
890             return FA_CREATE;
891         }
892         dbAttr = dbMd5;
893         newAttr = newMd5;
894     } else /* dbWhat == LINK */ {
895         memset(buffer, 0, sizeof(buffer));
896         i = readlink(filespec, buffer, sizeof(buffer) - 1);
897         if (i == -1) {
898             /* assume the file has been removed, don't freak */
899             return FA_CREATE;
900         }
901         dbAttr = dbLink;
902         newAttr = newLink;
903      }
904
905     /* this order matters - we'd prefer to CREATE the file if at all
906        possible in case something else (like the timestamp) has changed */
907
908     if (!strcmp(dbAttr, buffer)) {
909         /* this config file has never been modified, so just replace it */
910         return FA_CREATE;
911     }
912
913     if (!strcmp(dbAttr, newAttr)) {
914         /* this file is the same in all versions of this package */
915         return FA_SKIP;
916     }
917
918     /*
919      * The config file on the disk has been modified, but
920      * the ones in the two packages are different. It would
921      * be nice if RPM was smart enough to at least try and
922      * merge the difference ala CVS, but...
923      */
924     return save;
925 }
926
927 /**
928  */
929 static int filecmp(short mode1, const char * md51, const char * link1,
930                    short mode2, const char * md52, const char * link2)
931         /*@*/
932 {
933     fileTypes what1 = whatis(mode1);
934     fileTypes what2 = whatis(mode2);
935
936     if (what1 != what2) return 1;
937
938     if (what1 == LINK)
939         return strcmp(link1, link2);
940     else if (what1 == REG)
941         return strcmp(md51, md52);
942
943     return 0;
944 }
945
946 /**
947  */
948 /* XXX ts->{probs,rpmdb} modified, could be const ... ts */
949 static int handleInstInstalledFiles(const rpmTransactionSet ts, TFI_t fi,
950                 struct sharedFileInfo * shared,
951                 int sharedCount, int reportConflicts)
952         /*@globals fileSystem @*/
953         /*@modifies ts, fi, fileSystem @*/
954 {
955     HGE_t hge = fi->hge;
956     HFD_t hfd = (fi->hfd ? fi->hfd : headerFreeData);
957     rpmdb db = ts->rpmdb;
958     rpmProblemSet probs = ts->probs;
959     rpmtransFlags transFlags = ts->transFlags;
960     rpmTagType oltype, omtype;
961     Header h;
962     int i;
963     const char ** otherMd5s;
964     const char ** otherLinks;
965     const char * otherStates;
966     uint_32 * otherFlags;
967     uint_32 * otherSizes;
968     uint_16 * otherModes;
969     int numReplaced = 0;
970     int xx;
971
972     rpmdbMatchIterator mi;
973
974     mi = rpmdbInitIterator(db, RPMDBI_PACKAGES, &shared->otherPkg, sizeof(shared->otherPkg));
975     h = rpmdbNextIterator(mi);
976     if (h == NULL) {
977         mi = rpmdbFreeIterator(mi);
978         return 1;
979     }
980
981     xx = hge(h, RPMTAG_FILEMD5S, &omtype, (void **) &otherMd5s, NULL);
982     xx = hge(h, RPMTAG_FILELINKTOS, &oltype, (void **) &otherLinks, NULL);
983     xx = hge(h, RPMTAG_FILESTATES, NULL, (void **) &otherStates, NULL);
984     xx = hge(h, RPMTAG_FILEMODES, NULL, (void **) &otherModes, NULL);
985     xx = hge(h, RPMTAG_FILEFLAGS, NULL, (void **) &otherFlags, NULL);
986     xx = hge(h, RPMTAG_FILESIZES, NULL, (void **) &otherSizes, NULL);
987
988     fi->replaced = xmalloc(sharedCount * sizeof(*fi->replaced));
989
990     for (i = 0; i < sharedCount; i++, shared++) {
991         int otherFileNum, fileNum;
992         otherFileNum = shared->otherFileNum;
993         fileNum = shared->pkgFileNum;
994
995         /* XXX another tedious segfault, assume file state normal. */
996         if (otherStates && otherStates[otherFileNum] != RPMFILE_STATE_NORMAL)
997             continue;
998
999         if (XFA_SKIPPING(fi->actions[fileNum]))
1000             continue;
1001
1002         if (filecmp(otherModes[otherFileNum],
1003                         otherMd5s[otherFileNum],
1004                         otherLinks[otherFileNum],
1005                         fi->fmodes[fileNum],
1006                         fi->fmd5s[fileNum],
1007                         fi->flinks[fileNum])) {
1008             if (reportConflicts)
1009                 psAppend(probs, RPMPROB_FILE_CONFLICT, fi->ap,
1010                         fi->dnl[fi->dil[fileNum]], fi->bnl[fileNum], h, 0);
1011             if (!(otherFlags[otherFileNum] | fi->fflags[fileNum])
1012                         & RPMFILE_CONFIG) {
1013                 /*@-assignexpose@*/
1014                 if (!shared->isRemoved)
1015                     fi->replaced[numReplaced++] = *shared;
1016                 /*@=assignexpose@*/
1017             }
1018         }
1019
1020         if ((otherFlags[otherFileNum] | fi->fflags[fileNum]) & RPMFILE_CONFIG) {
1021             fi->actions[fileNum] = decideFileFate(
1022                         fi->dnl[fi->dil[fileNum]],
1023                         fi->bnl[fileNum],
1024                         otherModes[otherFileNum],
1025                         otherMd5s[otherFileNum],
1026                         otherLinks[otherFileNum],
1027                         fi->fmodes[fileNum],
1028                         fi->fmd5s[fileNum],
1029                         fi->flinks[fileNum],
1030                         fi->fflags[fileNum],
1031                         transFlags);
1032         }
1033
1034         fi->replacedSizes[fileNum] = otherSizes[otherFileNum];
1035     }
1036
1037     otherMd5s = hfd(otherMd5s, omtype);
1038     otherLinks = hfd(otherLinks, oltype);
1039     mi = rpmdbFreeIterator(mi);
1040
1041     fi->replaced = xrealloc(fi->replaced,       /* XXX memory leak */
1042                            sizeof(*fi->replaced) * (numReplaced + 1));
1043     fi->replaced[numReplaced].otherPkg = 0;
1044
1045     return 0;
1046 }
1047
1048 /**
1049  */
1050 /* XXX ts->rpmdb modified, could be const ... ts */
1051 static int handleRmvdInstalledFiles(const rpmTransactionSet ts, TFI_t fi,
1052                 struct sharedFileInfo * shared, int sharedCount)
1053         /*@globals fileSystem @*/
1054         /*@modifies ts, fi, fileSystem @*/
1055 {
1056     HGE_t hge = fi->hge;
1057     rpmdb db = ts->rpmdb;
1058     Header h;
1059     const char * otherStates;
1060     int i, xx;
1061    
1062     rpmdbMatchIterator mi;
1063
1064     mi = rpmdbInitIterator(db, RPMDBI_PACKAGES,
1065                         &shared->otherPkg, sizeof(shared->otherPkg));
1066     h = rpmdbNextIterator(mi);
1067     if (h == NULL) {
1068         mi = rpmdbFreeIterator(mi);
1069         return 1;
1070     }
1071
1072     xx = hge(h, RPMTAG_FILESTATES, NULL, (void **) &otherStates, NULL);
1073
1074     for (i = 0; i < sharedCount; i++, shared++) {
1075         int otherFileNum, fileNum;
1076         otherFileNum = shared->otherFileNum;
1077         fileNum = shared->pkgFileNum;
1078
1079         if (otherStates[otherFileNum] != RPMFILE_STATE_NORMAL)
1080             continue;
1081
1082         fi->actions[fileNum] = FA_SKIP;
1083     }
1084
1085     mi = rpmdbFreeIterator(mi);
1086
1087     return 0;
1088 }
1089
1090 /**
1091  * Update disk space needs on each partition for this package.
1092  */
1093 /* XXX ts->{probs,di} modified, could be const ... ts */
1094 static void handleOverlappedFiles(const rpmTransactionSet ts, TFI_t fi)
1095         /*@globals fileSystem @*/
1096         /*@modifies ts, fi, fileSystem @*/
1097 {
1098     struct diskspaceInfo * dsl = ts->di;
1099     rpmProblemSet probs = (ts->ignoreSet & RPMPROB_FILTER_REPLACENEWFILES)
1100         ? NULL : ts->probs;
1101     hashTable ht = ts->ht;
1102     struct diskspaceInfo * ds = NULL;
1103     uint_32 fixupSize = 0;
1104     char * filespec = NULL;
1105     int fileSpecAlloced = 0;
1106     int i, j;
1107   
1108     for (i = 0; i < fi->fc; i++) {
1109         int otherPkgNum, otherFileNum;
1110         const TFI_t * recs;
1111         int numRecs;
1112
1113         if (XFA_SKIPPING(fi->actions[i]))
1114             continue;
1115
1116         j = strlen(fi->dnl[fi->dil[i]]) + strlen(fi->bnl[i]) + 1;
1117         /*@-branchstate@*/
1118         if (j > fileSpecAlloced) {
1119             fileSpecAlloced = j * 2;
1120             filespec = xrealloc(filespec, fileSpecAlloced);
1121         }
1122         /*@=branchstate@*/
1123
1124         (void) stpcpy( stpcpy( filespec, fi->dnl[fi->dil[i]]), fi->bnl[i]);
1125
1126         if (dsl) {
1127             ds = dsl;
1128             while (ds->bsize && ds->dev != fi->fps[i].entry->dev) ds++;
1129             if (!ds->bsize) ds = NULL;
1130             fixupSize = 0;
1131         }
1132
1133         /*
1134          * Retrieve all records that apply to this file. Note that the
1135          * file info records were built in the same order as the packages
1136          * will be installed and removed so the records for an overlapped
1137          * files will be sorted in exactly the same order.
1138          */
1139         (void) htGetEntry(ht, &fi->fps[i], (const void ***) &recs, &numRecs, NULL);
1140
1141         /*
1142          * If this package is being added, look only at other packages
1143          * being added -- removed packages dance to a different tune.
1144          * If both this and the other package are being added, overlapped
1145          * files must be identical (or marked as a conflict). The
1146          * disposition of already installed config files leads to
1147          * a small amount of extra complexity.
1148          *
1149          * If this package is being removed, then there are two cases that
1150          * need to be worried about:
1151          * If the other package is being added, then skip any overlapped files
1152          * so that this package removal doesn't nuke the overlapped files
1153          * that were just installed.
1154          * If both this and the other package are being removed, then each
1155          * file removal from preceding packages needs to be skipped so that
1156          * the file removal occurs only on the last occurence of an overlapped
1157          * file in the transaction set.
1158          *
1159          */
1160
1161         /* Locate this overlapped file in the set of added/removed packages. */
1162         for (j = 0; j < numRecs && recs[j] != fi; j++)
1163             {};
1164
1165         /* Find what the previous disposition of this file was. */
1166         otherFileNum = -1;                      /* keep gcc quiet */
1167         for (otherPkgNum = j - 1; otherPkgNum >= 0; otherPkgNum--) {
1168             /* Added packages need only look at other added packages. */
1169             if (fi->type == TR_ADDED && recs[otherPkgNum]->type != TR_ADDED)
1170                 /*@innercontinue@*/ continue;
1171
1172             /* TESTME: there are more efficient searches in the world... */
1173             for (otherFileNum = 0; otherFileNum < recs[otherPkgNum]->fc;
1174                  otherFileNum++) {
1175
1176                 /* If the addresses are the same, so are the values. */
1177                 if ((fi->fps + i) == (recs[otherPkgNum]->fps + otherFileNum))
1178                     /*@innerbreak@*/ break;
1179
1180                 /* Otherwise, compare fingerprints by value. */
1181                 /*@-nullpass@*/ /* LCL: looks good to me */
1182                 if (FP_EQUAL(fi->fps[i], recs[otherPkgNum]->fps[otherFileNum]))
1183                     /*@innerbreak@*/ break;
1184                 /*@=nullpass@*/
1185
1186             }
1187             /* XXX is this test still necessary? */
1188             if (recs[otherPkgNum]->actions[otherFileNum] != FA_UNKNOWN)
1189                 /*@innerbreak@*/ break;
1190         }
1191
1192         switch (fi->type) {
1193         case TR_ADDED:
1194           { struct stat sb;
1195             if (otherPkgNum < 0) {
1196                 /* XXX is this test still necessary? */
1197                 if (fi->actions[i] != FA_UNKNOWN)
1198                     /*@switchbreak@*/ break;
1199                 if ((fi->fflags[i] & RPMFILE_CONFIG) && 
1200                         !lstat(filespec, &sb)) {
1201                     /* Here is a non-overlapped pre-existing config file. */
1202                     fi->actions[i] = (fi->fflags[i] & RPMFILE_NOREPLACE)
1203                         ? FA_ALTNAME : FA_BACKUP;
1204                 } else {
1205                     fi->actions[i] = FA_CREATE;
1206                 }
1207                 /*@switchbreak@*/ break;
1208             }
1209
1210             /* Mark added overlapped non-identical files as a conflict. */
1211             if (probs && filecmp(recs[otherPkgNum]->fmodes[otherFileNum],
1212                         recs[otherPkgNum]->fmd5s[otherFileNum],
1213                         recs[otherPkgNum]->flinks[otherFileNum],
1214                         fi->fmodes[i],
1215                         fi->fmd5s[i],
1216                         fi->flinks[i])) {
1217                 psAppend(probs, RPMPROB_NEW_FILE_CONFLICT, fi->ap,
1218                                 filespec, NULL, recs[otherPkgNum]->ap->h, 0);
1219             }
1220
1221             /* Try to get the disk accounting correct even if a conflict. */
1222             fixupSize = recs[otherPkgNum]->fsizes[otherFileNum];
1223
1224             if ((fi->fflags[i] & RPMFILE_CONFIG) && !lstat(filespec, &sb)) {
1225                 /* Here is an overlapped  pre-existing config file. */
1226                 fi->actions[i] = (fi->fflags[i] & RPMFILE_NOREPLACE)
1227                         ? FA_ALTNAME : FA_SKIP;
1228             } else {
1229                 fi->actions[i] = FA_CREATE;
1230             }
1231           } /*@switchbreak@*/ break;
1232         case TR_REMOVED:
1233             if (otherPkgNum >= 0) {
1234                 /* Here is an overlapped added file we don't want to nuke. */
1235                 if (recs[otherPkgNum]->actions[otherFileNum] != FA_ERASE) {
1236                     /* On updates, don't remove files. */
1237                     fi->actions[i] = FA_SKIP;
1238                     /*@switchbreak@*/ break;
1239                 }
1240                 /* Here is an overlapped removed file: skip in previous. */
1241                 recs[otherPkgNum]->actions[otherFileNum] = FA_SKIP;
1242             }
1243             if (XFA_SKIPPING(fi->actions[i]))
1244                 /*@switchbreak@*/ break;
1245             if (fi->fstates && fi->fstates[i] != RPMFILE_STATE_NORMAL)
1246                 /*@switchbreak@*/ break;
1247             if (!(S_ISREG(fi->fmodes[i]) && (fi->fflags[i] & RPMFILE_CONFIG))) {
1248                 fi->actions[i] = FA_ERASE;
1249                 /*@switchbreak@*/ break;
1250             }
1251                 
1252             /* Here is a pre-existing modified config file that needs saving. */
1253             {   char mdsum[50];
1254                 if (!mdfile(filespec, mdsum) && strcmp(fi->fmd5s[i], mdsum)) {
1255                     fi->actions[i] = FA_BACKUP;
1256                     /*@switchbreak@*/ break;
1257                 }
1258             }
1259             fi->actions[i] = FA_ERASE;
1260             /*@switchbreak@*/ break;
1261         }
1262
1263         if (ds) {
1264             uint_32 s = BLOCK_ROUND(fi->fsizes[i], ds->bsize);
1265
1266             switch (fi->actions[i]) {
1267               case FA_BACKUP:
1268               case FA_SAVE:
1269               case FA_ALTNAME:
1270                 ds->ineeded++;
1271                 ds->bneeded += s;
1272                 /*@switchbreak@*/ break;
1273
1274             /*
1275              * FIXME: If two packages share a file (same md5sum), and
1276              * that file is being replaced on disk, will ds->bneeded get
1277              * decremented twice? Quite probably!
1278              */
1279               case FA_CREATE:
1280                 ds->bneeded += s;
1281                 ds->bneeded -= BLOCK_ROUND(fi->replacedSizes[i], ds->bsize);
1282                 /*@switchbreak@*/ break;
1283
1284               case FA_ERASE:
1285                 ds->ineeded--;
1286                 ds->bneeded -= s;
1287                 /*@switchbreak@*/ break;
1288
1289               default:
1290                 /*@switchbreak@*/ break;
1291             }
1292
1293             ds->bneeded -= BLOCK_ROUND(fixupSize, ds->bsize);
1294         }
1295     }
1296     if (filespec) free(filespec);
1297 }
1298
1299 /**
1300  */
1301 static int ensureOlder(struct availablePackage * alp, Header old,
1302                 rpmProblemSet probs)
1303         /*@modifies alp, probs @*/
1304 {
1305     int result, rc = 0;
1306
1307     if (old == NULL) return 1;
1308
1309     result = rpmVersionCompare(old, alp->h);
1310     if (result <= 0)
1311         rc = 0;
1312     else if (result > 0) {
1313         rc = 1;
1314         psAppend(probs, RPMPROB_OLDPACKAGE, alp, NULL, NULL, old, 0);
1315     }
1316
1317     return rc;
1318 }
1319
1320 /**
1321  */
1322 static void skipFiles(const rpmTransactionSet ts, TFI_t fi)
1323         /*@globals rpmGlobalMacroContext @*/
1324         /*@modifies fi, rpmGlobalMacroContext @*/
1325 {
1326     int noDocs = (ts->transFlags & RPMTRANS_FLAG_NODOCS);
1327     char ** netsharedPaths = NULL;
1328     const char ** languages;
1329     const char * dn, * bn;
1330     int dnlen, bnlen, ix;
1331     const char * s;
1332     int * drc;
1333     char * dff;
1334     int i, j;
1335
1336     if (!noDocs)
1337         noDocs = rpmExpandNumeric("%{_excludedocs}");
1338
1339     {   const char *tmpPath = rpmExpand("%{_netsharedpath}", NULL);
1340         /*@-branchstate@*/
1341         if (tmpPath && *tmpPath != '%')
1342             netsharedPaths = splitString(tmpPath, strlen(tmpPath), ':');
1343         /*@=branchstate@*/
1344         tmpPath = _free(tmpPath);
1345     }
1346
1347     s = rpmExpand("%{_install_langs}", NULL);
1348     /*@-branchstate@*/
1349     if (!(s && *s != '%'))
1350         s = _free(s);
1351     if (s) {
1352         languages = (const char **) splitString(s, strlen(s), ':');
1353         s = _free(s);
1354     } else
1355         languages = NULL;
1356     /*@=branchstate@*/
1357
1358     /* Compute directory refcount, skip directory if now empty. */
1359     drc = alloca(fi->dc * sizeof(*drc));
1360     memset(drc, 0, fi->dc * sizeof(*drc));
1361     dff = alloca(fi->dc * sizeof(*dff));
1362     memset(dff, 0, fi->dc * sizeof(*dff));
1363
1364     for (i = 0; i < fi->fc; i++) {
1365         char **nsp;
1366
1367         bn = fi->bnl[i];
1368         bnlen = strlen(bn);
1369         ix = fi->dil[i];
1370         dn = fi->dnl[ix];
1371         dnlen = strlen(dn);
1372
1373         drc[ix]++;
1374
1375         /* Don't bother with skipped files */
1376         if (XFA_SKIPPING(fi->actions[i])) {
1377             drc[ix]--;
1378             continue;
1379         }
1380
1381         /*
1382          * Skip net shared paths.
1383          * Net shared paths are not relative to the current root (though
1384          * they do need to take package relocations into account).
1385          */
1386         for (nsp = netsharedPaths; nsp && *nsp; nsp++) {
1387             int len;
1388
1389             len = strlen(*nsp);
1390             if (dnlen >= len) {
1391                 if (strncmp(dn, *nsp, len))
1392                     /*@innercontinue@*/ continue;
1393                 /* Only directories or complete file paths can be net shared */
1394                 if (!(dn[len] == '/' || dn[len] == '\0'))
1395                     /*@innercontinue@*/ continue;
1396             } else {
1397                 if (len < (dnlen + bnlen))
1398                     /*@innercontinue@*/ continue;
1399                 if (strncmp(dn, *nsp, dnlen))
1400                     /*@innercontinue@*/ continue;
1401                 if (strncmp(bn, (*nsp) + dnlen, bnlen))
1402                     /*@innercontinue@*/ continue;
1403                 len = dnlen + bnlen;
1404                 /* Only directories or complete file paths can be net shared */
1405                 if (!((*nsp)[len] == '/' || (*nsp)[len] == '\0'))
1406                     /*@innercontinue@*/ continue;
1407             }
1408
1409             /*@innerbreak@*/ break;
1410         }
1411
1412         if (nsp && *nsp) {
1413             drc[ix]--;  dff[ix] = 1;
1414             fi->actions[i] = FA_SKIPNETSHARED;
1415             continue;
1416         }
1417
1418         /*
1419          * Skip i18n language specific files.
1420          */
1421         if (fi->flangs && languages && *fi->flangs[i]) {
1422             const char **lang, *l, *le;
1423             for (lang = languages; *lang != NULL; lang++) {
1424                 if (!strcmp(*lang, "all"))
1425                     /*@innerbreak@*/ break;
1426                 for (l = fi->flangs[i]; *l != '\0'; l = le) {
1427                     for (le = l; *le != '\0' && *le != '|'; le++)
1428                         {};
1429                     if ((le-l) > 0 && !strncmp(*lang, l, (le-l)))
1430                         /*@innerbreak@*/ break;
1431                     if (*le == '|') le++;       /* skip over | */
1432                 }
1433                 if (*l != '\0')
1434                     /*@innerbreak@*/ break;
1435             }
1436             if (*lang == NULL) {
1437                 drc[ix]--;      dff[ix] = 1;
1438                 fi->actions[i] = FA_SKIPNSTATE;
1439                 continue;
1440             }
1441         }
1442
1443         /*
1444          * Skip documentation if requested.
1445          */
1446         if (noDocs && (fi->fflags[i] & RPMFILE_DOC)) {
1447             drc[ix]--;  dff[ix] = 1;
1448             fi->actions[i] = FA_SKIPNSTATE;
1449             continue;
1450         }
1451     }
1452
1453     /* Skip (now empty) directories that had skipped files. */
1454     for (j = 0; j < fi->dc; j++) {
1455
1456         if (drc[j]) continue;   /* dir still has files. */
1457         if (!dff[j]) continue;  /* dir was not emptied here. */
1458         
1459         /* Find parent directory and basename. */
1460         dn = fi->dnl[j];        dnlen = strlen(dn) - 1;
1461         bn = dn + dnlen;        bnlen = 0;
1462         while (bn > dn && bn[-1] != '/') {
1463                 bnlen++;
1464                 dnlen--;
1465                 bn--;
1466         }
1467
1468         /* If explicitly included in the package, skip the directory. */
1469         for (i = 0; i < fi->fc; i++) {
1470             const char * dir;
1471
1472             if (XFA_SKIPPING(fi->actions[i]))
1473                 /*@innercontinue@*/ continue;
1474             if (whatis(fi->fmodes[i]) != XDIR)
1475                 /*@innercontinue@*/ continue;
1476             dir = fi->dnl[fi->dil[i]];
1477             if (strlen(dir) != dnlen)
1478                 /*@innercontinue@*/ continue;
1479             if (strncmp(dir, dn, dnlen))
1480                 /*@innercontinue@*/ continue;
1481             if (strlen(fi->bnl[i]) != bnlen)
1482                 /*@innercontinue@*/ continue;
1483             if (strncmp(fi->bnl[i], bn, bnlen))
1484                 /*@innercontinue@*/ continue;
1485             rpmMessage(RPMMESS_DEBUG, _("excluding directory %s\n"), dn);
1486             fi->actions[i] = FA_SKIPNSTATE;
1487             /*@innerbreak@*/ break;
1488         }
1489     }
1490
1491     if (netsharedPaths) freeSplitString(netsharedPaths);
1492 #ifdef  DYING   /* XXX freeFi will deal with this later. */
1493     fi->flangs = _free(fi->flangs);
1494 #endif
1495     if (languages) freeSplitString((char **)languages);
1496 }
1497
1498 /**
1499  * Iterator across transaction elements, forward on install, backward on erase.
1500  */
1501 struct tsIterator_s {
1502 /*@kept@*/ rpmTransactionSet ts;        /*!< transaction set. */
1503     int reverse;                        /*!< reversed traversal? */
1504     int ocsave;                         /*!< last returned iterator index. */
1505     int oc;                             /*!< iterator index. */
1506 };
1507
1508 /**
1509  * Return transaction element order count.
1510  * @param a             transaction element iterator
1511  * @return              element order count
1512  */
1513 static int tsGetOc(void * a)
1514         /*@*/
1515 {
1516     struct tsIterator_s * iter = a;
1517     int oc = iter->ocsave;
1518     return oc;
1519 }
1520
1521 /**
1522  * Return transaction element available package pointer.
1523  * @param a             transaction element iterator
1524  * @return              available package pointer
1525  */
1526 static /*@dependent@*/ struct availablePackage * tsGetAlp(void * a)
1527         /*@*/
1528 {
1529     struct tsIterator_s * iter = a;
1530     struct availablePackage * alp = NULL;
1531     int oc = iter->ocsave;
1532
1533     /*@-branchstate@*/
1534     if (oc != -1) {
1535         rpmTransactionSet ts = iter->ts;
1536         TFI_t fi = ts->flList + oc;
1537         if (ts->addedPackages.list && fi->type == TR_ADDED)
1538             alp = ts->addedPackages.list + ts->order[oc].u.addedIndex;
1539     }
1540     /*@=branchstate@*/
1541     return alp;
1542 }
1543
1544 /**
1545  * Destroy transaction element iterator.
1546  * @param a             transaction element iterator
1547  * @return              NULL always
1548  */
1549 static /*@null@*/ void * tsFreeIterator(/*@only@*//*@null@*/ const void * a)
1550         /*@modifies a @*/
1551 {
1552     return _free(a);
1553 }
1554
1555 /**
1556  * Create transaction element iterator.
1557  * @param a             transaction set
1558  * @return              transaction element iterator
1559  */
1560 static void * tsInitIterator(/*@kept@*/ const void * a)
1561         /*@*/
1562 {
1563     rpmTransactionSet ts = (void *)a;
1564     struct tsIterator_s * iter = NULL;
1565
1566     iter = xcalloc(1, sizeof(*iter));
1567     iter->ts = ts;
1568     iter->reverse = ((ts->transFlags & RPMTRANS_FLAG_REVERSE) ? 1 : 0);
1569     iter->oc = (iter->reverse ? (ts->orderCount - 1) : 0);
1570     iter->ocsave = iter->oc;
1571     return iter;
1572 }
1573
1574 /**
1575  * Return next transaction element's file info.
1576  * @param a             file info iterator
1577  * @return              next index, -1 on termination
1578  */
1579 static /*@dependent@*/ TFI_t tsNextIterator(void * a)
1580         /*@*/
1581 {
1582     struct tsIterator_s * iter = a;
1583     rpmTransactionSet ts = iter->ts;
1584     TFI_t fi = NULL;
1585     int oc = -1;
1586
1587     if (iter->reverse) {
1588         if (iter->oc >= 0)              oc = iter->oc--;
1589     } else {
1590         if (iter->oc < ts->orderCount)  oc = iter->oc++;
1591     }
1592     iter->ocsave = oc;
1593     if (oc != -1)
1594         fi = ts->flList + oc;
1595     return fi;
1596 }
1597
1598 #define NOTIFY(_ts, _al)        if ((_ts)->notify) (void) (_ts)->notify _al
1599
1600 int rpmRunTransactions( rpmTransactionSet ts,
1601                         rpmCallbackFunction notify, rpmCallbackData notifyData,
1602                         rpmProblemSet okProbs, rpmProblemSet * newProbs,
1603                         rpmtransFlags transFlags, rpmprobFilterFlags ignoreSet)
1604 {
1605     int i, j;
1606     int ourrc = 0;
1607     struct availablePackage * alp;
1608     int totalFileCount = 0;
1609     TFI_t fi;
1610     struct diskspaceInfo * dip;
1611     struct sharedFileInfo * shared, * sharedList;
1612     int numShared;
1613     int nexti;
1614     int lastFailed;
1615     int oc;
1616     fingerPrintCache fpc;
1617     struct psm_s psmbuf;
1618     PSM_t psm = &psmbuf;
1619     void * tsi;
1620     int xx;
1621
1622     /* FIXME: what if the same package is included in ts twice? */
1623
1624     ts->transFlags = transFlags;
1625     if (ts->transFlags & RPMTRANS_FLAG_NOSCRIPTS)
1626         ts->transFlags |= (_noTransScripts | _noTransTriggers);
1627     if (ts->transFlags & RPMTRANS_FLAG_NOTRIGGERS)
1628         ts->transFlags |= _noTransTriggers;
1629
1630     /* XXX MULTILIB is broken, as packages can and do execute /sbin/ldconfig. */
1631     if (ts->transFlags & (RPMTRANS_FLAG_JUSTDB | RPMTRANS_FLAG_MULTILIB))
1632         ts->transFlags |= (_noTransScripts | _noTransTriggers);
1633
1634     ts->notify = notify;
1635     ts->notifyData = notifyData;
1636     /*@-assignexpose@*/
1637     ts->probs = *newProbs = psCreate();
1638     /*@=assignexpose@*/
1639     ts->ignoreSet = ignoreSet;
1640     ts->currDir = _free(ts->currDir);
1641     ts->currDir = currentDirectory();
1642     ts->chrootDone = 0;
1643     if (ts->rpmdb) ts->rpmdb->db_chrootDone = 0;
1644     ts->id = (int_32) time(NULL);
1645
1646     memset(psm, 0, sizeof(*psm));
1647     /*@-assignexpose@*/
1648     psm->ts = ts;
1649     /*@=assignexpose@*/
1650
1651     /* Get available space on mounted file systems. */
1652     if (!(ts->ignoreSet & RPMPROB_FILTER_DISKSPACE) &&
1653                 !rpmGetFilesystemList(&ts->filesystems, &ts->filesystemCount)) {
1654         struct stat sb;
1655
1656         ts->di = _free(ts->di);
1657         dip = ts->di = xcalloc((ts->filesystemCount + 1), sizeof(*ts->di));
1658
1659         for (i = 0; (i < ts->filesystemCount) && dip; i++) {
1660 #if STATFS_IN_SYS_STATVFS
1661             struct statvfs sfb;
1662             memset(&sfb, 0, sizeof(sfb));
1663             if (statvfs(ts->filesystems[i], &sfb))
1664 #else
1665             struct statfs sfb;
1666 #  if STAT_STATFS4
1667 /* This platform has the 4-argument version of the statfs call.  The last two
1668  * should be the size of struct statfs and 0, respectively.  The 0 is the
1669  * filesystem type, and is always 0 when statfs is called on a mounted
1670  * filesystem, as we're doing.
1671  */
1672             memset(&sfb, 0, sizeof(sfb));
1673             if (statfs(ts->filesystems[i], &sfb, sizeof(sfb), 0))
1674 #  else
1675             memset(&sfb, 0, sizeof(sfb));
1676             if (statfs(ts->filesystems[i], &sfb))
1677 #  endif
1678 #endif
1679             {
1680                 dip = NULL;
1681             } else {
1682                 ts->di[i].bsize = sfb.f_bsize;
1683                 ts->di[i].bneeded = 0;
1684                 ts->di[i].ineeded = 0;
1685 #ifdef STATFS_HAS_F_BAVAIL
1686                 ts->di[i].bavail = sfb.f_bavail;
1687 #else
1688 /* FIXME: the statfs struct doesn't have a member to tell how many blocks are
1689  * available for non-superusers.  f_blocks - f_bfree is probably too big, but
1690  * it's about all we can do.
1691  */
1692                 ts->di[i].bavail = sfb.f_blocks - sfb.f_bfree;
1693 #endif
1694                 /* XXX Avoid FAT and other file systems that have not inodes. */
1695                 ts->di[i].iavail = !(sfb.f_ffree == 0 && sfb.f_files == 0)
1696                                 ? sfb.f_ffree : -1;
1697
1698                 xx = stat(ts->filesystems[i], &sb);
1699                 ts->di[i].dev = sb.st_dev;
1700             }
1701         }
1702
1703         if (dip) ts->di[i].bsize = 0;
1704     }
1705
1706     /* ===============================================
1707      * For packages being installed:
1708      * - verify package arch/os.
1709      * - verify package epoch:version-release is newer.
1710      * - count files.
1711      * For packages being removed:
1712      * - count files.
1713      */
1714     /* The ordering doesn't matter here */
1715     if (ts->addedPackages.list != NULL)
1716     for (alp = ts->addedPackages.list;
1717         (alp - ts->addedPackages.list) < ts->addedPackages.size;
1718         alp++)
1719     {
1720         if (!archOkay(alp->h) && !(ts->ignoreSet & RPMPROB_FILTER_IGNOREARCH))
1721             psAppend(ts->probs, RPMPROB_BADARCH, alp, NULL, NULL, NULL, 0);
1722
1723         if (!osOkay(alp->h) && !(ts->ignoreSet & RPMPROB_FILTER_IGNOREOS))
1724             psAppend(ts->probs, RPMPROB_BADOS, alp, NULL, NULL, NULL, 0);
1725
1726         if (!(ts->ignoreSet & RPMPROB_FILTER_OLDPACKAGE)) {
1727             rpmdbMatchIterator mi;
1728             Header oldH;
1729             mi = rpmdbInitIterator(ts->rpmdb, RPMTAG_NAME, alp->name, 0);
1730             while ((oldH = rpmdbNextIterator(mi)) != NULL)
1731                 xx = ensureOlder(alp, oldH, ts->probs);
1732             mi = rpmdbFreeIterator(mi);
1733         }
1734
1735         /* XXX multilib should not display "already installed" problems */
1736         if (!(ts->ignoreSet & RPMPROB_FILTER_REPLACEPKG) && !alp->multiLib) {
1737             rpmdbMatchIterator mi;
1738             mi = rpmdbInitIterator(ts->rpmdb, RPMTAG_NAME, alp->name, 0);
1739             xx = rpmdbSetIteratorRE(mi, RPMTAG_VERSION,
1740                         RPMMIRE_DEFAULT, alp->version);
1741             xx = rpmdbSetIteratorRE(mi, RPMTAG_RELEASE,
1742                         RPMMIRE_DEFAULT, alp->release);
1743
1744             while (rpmdbNextIterator(mi) != NULL) {
1745                 psAppend(ts->probs, RPMPROB_PKG_INSTALLED, alp,
1746                         NULL, NULL, NULL, 0);
1747                 /*@innerbreak@*/ break;
1748             }
1749             mi = rpmdbFreeIterator(mi);
1750         }
1751
1752         totalFileCount += alp->filesCount;
1753
1754     }
1755
1756     /* FIXME: it seems a bit silly to read in all of these headers twice */
1757     /* The ordering doesn't matter here */
1758     if (ts->numRemovedPackages > 0) {
1759         rpmdbMatchIterator mi;
1760         Header h;
1761         int fileCount;
1762
1763         mi = rpmdbInitIterator(ts->rpmdb, RPMDBI_PACKAGES, NULL, 0);
1764         xx = rpmdbAppendIterator(mi, ts->removedPackages, ts->numRemovedPackages);
1765         while ((h = rpmdbNextIterator(mi)) != NULL) {
1766             if (headerGetEntry(h, RPMTAG_BASENAMES, NULL, NULL, &fileCount))
1767                 totalFileCount += fileCount;
1768         }
1769         mi = rpmdbFreeIterator(mi);
1770     }
1771
1772     /* ===============================================
1773      * Initialize file list:
1774      */
1775     ts->flEntries = ts->addedPackages.size + ts->numRemovedPackages;
1776     ts->flList = xcalloc(ts->flEntries, sizeof(*ts->flList));
1777
1778     /*
1779      * FIXME?: we'd be better off assembling one very large file list and
1780      * calling fpLookupList only once. I'm not sure that the speedup is
1781      * worth the trouble though.
1782      */
1783     tsi = tsInitIterator(ts);
1784     while ((fi = tsNextIterator(tsi)) != NULL) {
1785         oc = tsGetOc(tsi);
1786         fi->magic = TFIMAGIC;
1787
1788         /* XXX watchout: fi->type must be set for tsGetAlp() to "work" */
1789         fi->type = ts->order[oc].type;
1790         /*@-branchstate@*/
1791         switch (fi->type) {
1792         case TR_ADDED:
1793 #ifdef  DYING
1794             i = ts->order[oc].u.addedIndex;
1795 #endif
1796             /* XXX watchout: fi->type must be set for tsGetAlp() to "work" */
1797             fi->ap = tsGetAlp(tsi);
1798             fi->record = 0;
1799             loadFi(ts, fi, fi->ap->h, 1);
1800 /* XXX free fi->ap->h here */
1801             if (fi->fc == 0)
1802                 continue;
1803
1804 #ifdef  DYING
1805             {   Header foo = relocateFileList(ts, fi, fi->ap, fi->h, fi->actions);
1806                 foo = headerFree(foo);
1807             }
1808 #endif
1809
1810             /* Skip netshared paths, not our i18n files, and excluded docs */
1811             skipFiles(ts, fi);
1812             /*@switchbreak@*/ break;
1813         case TR_REMOVED:
1814             fi->ap = NULL;
1815             fi->record = ts->order[oc].u.removed.dboffset;
1816             /* Retrieve erased package header from the database. */
1817             {   rpmdbMatchIterator mi;
1818
1819                 mi = rpmdbInitIterator(ts->rpmdb, RPMDBI_PACKAGES,
1820                                 &fi->record, sizeof(fi->record));
1821                 if ((fi->h = rpmdbNextIterator(mi)) != NULL)
1822                     fi->h = headerLink(fi->h);
1823                 mi = rpmdbFreeIterator(mi);
1824             }
1825             if (fi->h == NULL) {
1826                 /* ACK! */
1827                 continue;
1828             }
1829             /* XXX header arg unused. */
1830             loadFi(ts, fi, fi->h, 0);
1831             /*@switchbreak@*/ break;
1832         }
1833         /*@=branchstate@*/
1834
1835         if (fi->fc)
1836             fi->fps = xmalloc(fi->fc * sizeof(*fi->fps));
1837     }
1838     tsi = tsFreeIterator(tsi);
1839
1840     if (!ts->chrootDone) {
1841         xx = chdir("/");
1842         /*@-superuser -noeffect @*/
1843         xx = chroot(ts->rootDir);
1844         /*@=superuser =noeffect @*/
1845         ts->chrootDone = 1;
1846         if (ts->rpmdb) ts->rpmdb->db_chrootDone = 1;
1847         /*@-onlytrans@*/
1848         /*@-mods@*/
1849         chroot_prefix = ts->rootDir;
1850         /*@=mods@*/
1851         /*@=onlytrans@*/
1852     }
1853
1854     ts->ht = htCreate(totalFileCount * 2, 0, 0, fpHashFunction, fpEqual);
1855     fpc = fpCacheCreate(totalFileCount);
1856
1857     /* ===============================================
1858      * Add fingerprint for each file not skipped.
1859      */
1860     tsi = tsInitIterator(ts);
1861     while ((fi = tsNextIterator(tsi)) != NULL) {
1862         fpLookupList(fpc, fi->dnl, fi->bnl, fi->dil, fi->fc, fi->fps);
1863         for (i = 0; i < fi->fc; i++) {
1864             if (XFA_SKIPPING(fi->actions[i]))
1865                 /*@innercontinue@*/ continue;
1866             /*@-dependenttrans@*/
1867             htAddEntry(ts->ht, fi->fps + i, fi);
1868             /*@=dependenttrans@*/
1869         }
1870     }
1871     tsi = tsFreeIterator(tsi);
1872
1873     /*@-noeffectuncon @*/ /* FIX: check rc */
1874     NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_START, 6, ts->flEntries,
1875         NULL, ts->notifyData));
1876     /*@=noeffectuncon@*/
1877
1878     /* ===============================================
1879      * Compute file disposition for each package in transaction set.
1880      */
1881     tsi = tsInitIterator(ts);
1882     while ((fi = tsNextIterator(tsi)) != NULL) {
1883         dbiIndexSet * matches;
1884         int knownBad;
1885
1886         /*@-noeffectuncon @*/ /* FIX: check rc */
1887         NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_PROGRESS, (fi - ts->flList),
1888                         ts->flEntries, NULL, ts->notifyData));
1889         /*@=noeffectuncon@*/
1890
1891         if (fi->fc == 0) continue;
1892
1893         /* Extract file info for all files in this package from the database. */
1894         matches = xcalloc(fi->fc, sizeof(*matches));
1895         if (rpmdbFindFpList(ts->rpmdb, fi->fps, matches, fi->fc))
1896             return 1;   /* XXX WTFO? */
1897
1898         numShared = 0;
1899         for (i = 0; i < fi->fc; i++)
1900             numShared += dbiIndexSetCount(matches[i]);
1901
1902         /* Build sorted file info list for this package. */
1903         shared = sharedList = xcalloc((numShared + 1), sizeof(*sharedList));
1904         for (i = 0; i < fi->fc; i++) {
1905             /*
1906              * Take care not to mark files as replaced in packages that will
1907              * have been removed before we will get here.
1908              */
1909             for (j = 0; j < dbiIndexSetCount(matches[i]); j++) {
1910                 int k, ro;
1911                 ro = dbiIndexRecordOffset(matches[i], j);
1912                 knownBad = 0;
1913                 for (k = 0; ro != knownBad && k < ts->orderCount; k++) {
1914                     switch (ts->order[k].type) {
1915                     case TR_REMOVED:
1916                         if (ts->order[k].u.removed.dboffset == ro)
1917                             knownBad = ro;
1918                         /*@switchbreak@*/ break;
1919                     case TR_ADDED:
1920                         /*@switchbreak@*/ break;
1921                     }
1922                 }
1923
1924                 shared->pkgFileNum = i;
1925                 shared->otherPkg = dbiIndexRecordOffset(matches[i], j);
1926                 shared->otherFileNum = dbiIndexRecordFileNumber(matches[i], j);
1927                 shared->isRemoved = (knownBad == ro);
1928                 shared++;
1929             }
1930             matches[i] = dbiFreeIndexSet(matches[i]);
1931         }
1932         numShared = shared - sharedList;
1933         shared->otherPkg = -1;
1934         matches = _free(matches);
1935
1936         /* Sort file info by other package index (otherPkg) */
1937         qsort(sharedList, numShared, sizeof(*shared), sharedCmp);
1938
1939         /* For all files from this package that are in the database ... */
1940         for (i = 0; i < numShared; i = nexti) {
1941             int beingRemoved;
1942
1943             shared = sharedList + i;
1944
1945             /* Find the end of the files in the other package. */
1946             for (nexti = i + 1; nexti < numShared; nexti++) {
1947                 if (sharedList[nexti].otherPkg != shared->otherPkg)
1948                     /*@innerbreak@*/ break;
1949             }
1950
1951             /* Is this file from a package being removed? */
1952             beingRemoved = 0;
1953             for (j = 0; j < ts->numRemovedPackages; j++) {
1954                 if (ts->removedPackages[j] != shared->otherPkg)
1955                     /*@innercontinue@*/ continue;
1956                 beingRemoved = 1;
1957                 /*@innerbreak@*/ break;
1958             }
1959
1960             /* Determine the fate of each file. */
1961             switch (fi->type) {
1962             case TR_ADDED:
1963                 xx = handleInstInstalledFiles(ts, fi, shared, nexti - i,
1964         !(beingRemoved || (ts->ignoreSet & RPMPROB_FILTER_REPLACEOLDFILES)));
1965                 /*@switchbreak@*/ break;
1966             case TR_REMOVED:
1967                 if (!beingRemoved)
1968                     xx = handleRmvdInstalledFiles(ts, fi, shared, nexti - i);
1969                 /*@switchbreak@*/ break;
1970             }
1971         }
1972
1973         free(sharedList);
1974
1975         /* Update disk space needs on each partition for this package. */
1976         handleOverlappedFiles(ts, fi);
1977
1978         /* Check added package has sufficient space on each partition used. */
1979         switch (fi->type) {
1980         case TR_ADDED:
1981             if (!(ts->di && fi->fc))
1982                 /*@switchbreak@*/ break;
1983             for (i = 0; i < ts->filesystemCount; i++) {
1984
1985                 dip = ts->di + i;
1986
1987                 /* XXX Avoid FAT and other file systems that have not inodes. */
1988                 if (dip->iavail <= 0)
1989                     /*@innercontinue@*/ continue;
1990
1991                 if (adj_fs_blocks(dip->bneeded) > dip->bavail)
1992                     psAppend(ts->probs, RPMPROB_DISKSPACE, fi->ap,
1993                                 ts->filesystems[i], NULL, NULL,
1994                    (adj_fs_blocks(dip->bneeded) - dip->bavail) * dip->bsize);
1995
1996                 if (adj_fs_blocks(dip->ineeded) > dip->iavail)
1997                     psAppend(ts->probs, RPMPROB_DISKNODES, fi->ap,
1998                                 ts->filesystems[i], NULL, NULL,
1999                     (adj_fs_blocks(dip->ineeded) - dip->iavail));
2000             }
2001             /*@switchbreak@*/ break;
2002         case TR_REMOVED:
2003             /*@switchbreak@*/ break;
2004         }
2005     }
2006     tsi = tsFreeIterator(tsi);
2007
2008     if (ts->chrootDone) {
2009         /*@-superuser -noeffect @*/
2010         xx = chroot(".");
2011         /*@=superuser =noeffect @*/
2012         ts->chrootDone = 0;
2013         if (ts->rpmdb) ts->rpmdb->db_chrootDone = 0;
2014         /*@-mods@*/
2015         chroot_prefix = NULL;
2016         /*@=mods@*/
2017         xx = chdir(ts->currDir);
2018     }
2019
2020     /*@-noeffectuncon @*/ /* FIX: check rc */
2021     NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_STOP, 6, ts->flEntries,
2022         NULL, ts->notifyData));
2023     /*@=noeffectuncon @*/
2024
2025     /* ===============================================
2026      * Free unused memory as soon as possible.
2027      */
2028
2029     tsi = tsInitIterator(ts);
2030     while ((fi = tsNextIterator(tsi)) != NULL) {
2031         psm->fi = fi;
2032         if (fi->fc == 0)
2033             continue;
2034         fi->fps = _free(fi->fps);
2035     }
2036     tsi = tsFreeIterator(tsi);
2037
2038     fpCacheFree(fpc);
2039     htFree(ts->ht);
2040     ts->ht = NULL;
2041
2042     /* ===============================================
2043      * If unfiltered problems exist, free memory and return.
2044      */
2045     if ((ts->transFlags & RPMTRANS_FLAG_BUILD_PROBS) ||
2046            (ts->probs->numProblems && (!okProbs || psTrim(okProbs, ts->probs))))
2047     {
2048         *newProbs = ts->probs;
2049
2050         ts->flList = freeFl(ts, ts->flList);
2051         ts->flEntries = 0;
2052         /*@-nullstate@*/
2053         return ts->orderCount;
2054         /*@=nullstate@*/
2055     }
2056
2057     /* ===============================================
2058      * Save removed files before erasing.
2059      */
2060     if (ts->transFlags & (RPMTRANS_FLAG_DIRSTASH | RPMTRANS_FLAG_REPACKAGE)) {
2061         tsi = tsInitIterator(ts);
2062         while ((fi = tsNextIterator(tsi)) != NULL) {
2063             psm->fi = fi;
2064             switch (fi->type) {
2065             case TR_ADDED:
2066                 /*@switchbreak@*/ break;
2067             case TR_REMOVED:
2068                 if (ts->transFlags & RPMTRANS_FLAG_REPACKAGE)
2069                     xx = psmStage(psm, PSM_PKGSAVE);
2070                 /*@switchbreak@*/ break;
2071             }
2072         }
2073         tsi = tsFreeIterator(tsi);
2074     }
2075
2076     /* ===============================================
2077      * Install and remove packages.
2078      */
2079
2080     lastFailed = -2;    /* erased packages have -1 */
2081     tsi = tsInitIterator(ts);
2082     while ((fi = tsNextIterator(tsi)) != NULL) {
2083         Header h;
2084         int gotfd;
2085
2086         gotfd = 0;
2087         psm->fi = fi;
2088         switch (fi->type) {
2089         case TR_ADDED:
2090             alp = tsGetAlp(tsi);
2091 assert(alp == fi->ap);
2092             i = alp - ts->addedPackages.list;
2093
2094             rpmMessage(RPMMESS_DEBUG, "========== +++ %s-%s-%s\n",
2095                         fi->name, fi->version, fi->release);
2096             h = (fi->h ? headerLink(fi->h) : NULL);
2097             /*@-branchstate@*/
2098             if (alp->fd == NULL) {
2099                 alp->fd = ts->notify(fi->h, RPMCALLBACK_INST_OPEN_FILE, 0, 0,
2100                             alp->key, ts->notifyData);
2101                 if (alp->fd) {
2102                     rpmRC rpmrc;
2103
2104                     h = headerFree(h);
2105
2106                     /*@-mustmod@*/      /* LCL: segfault */
2107                     rpmrc = rpmReadPackageHeader(alp->fd, &h, NULL, NULL, NULL);
2108                     /*@=mustmod@*/
2109                     if (!(rpmrc == RPMRC_OK || rpmrc == RPMRC_BADSIZE)) {
2110                         /*@-noeffectuncon @*/ /* FIX: check rc */
2111                         (void)ts->notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE,
2112                                         0, 0, alp->key, ts->notifyData);
2113                         /*@=noeffectuncon @*/
2114                         alp->fd = NULL;
2115                         ourrc++;
2116                     } else if (fi->h != NULL) {
2117                         Header foo = relocateFileList(ts, fi, alp, h, NULL);
2118                         h = headerFree(h);
2119                         h = headerLink(foo);
2120                         foo = headerFree(foo);
2121                     }
2122                     if (alp->fd) gotfd = 1;
2123                 }
2124             }
2125             /*@=branchstate@*/
2126
2127             if (alp->fd) {
2128                 Header hsave = NULL;
2129
2130                 if (fi->h) {
2131                     hsave = headerLink(fi->h);
2132                     fi->h = headerFree(fi->h);
2133                     fi->h = headerLink(h);
2134                 } else {
2135 char * fstates = fi->fstates;
2136 fileAction * actions = fi->actions;
2137 fi->fstates = NULL;
2138 fi->actions = NULL;
2139                     freeFi(fi);
2140 oc = tsGetOc(tsi);
2141 fi->magic = TFIMAGIC;
2142 fi->type = ts->order[oc].type;
2143 fi->ap = tsGetAlp(tsi);
2144 fi->record = 0;
2145                     loadFi(ts, fi, h, 1);
2146 fi->fstates = _free(fi->fstates);
2147 fi->fstates = fstates;
2148 fi->actions = _free(fi->actions);
2149 fi->actions = actions;
2150                 }
2151                 if (alp->multiLib)
2152                     ts->transFlags |= RPMTRANS_FLAG_MULTILIB;
2153
2154 assert(alp == fi->ap);
2155                 if (psmStage(psm, PSM_PKGINSTALL)) {
2156                     ourrc++;
2157                     lastFailed = i;
2158                 }
2159                 fi->h = headerFree(fi->h);
2160                 if (hsave) {
2161                     fi->h = headerLink(hsave);
2162                     hsave = headerFree(hsave);
2163                 }
2164             } else {
2165                 ourrc++;
2166                 lastFailed = i;
2167             }
2168
2169             h = headerFree(h);
2170
2171             if (gotfd) {
2172                 /*@-noeffectuncon @*/ /* FIX: check rc */
2173                 (void)ts->notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE, 0, 0,
2174                         alp->key, ts->notifyData);
2175                 /*@=noeffectuncon @*/
2176                 alp->fd = NULL;
2177             }
2178             freeFi(fi);
2179             /*@switchbreak@*/ break;
2180         case TR_REMOVED:
2181             rpmMessage(RPMMESS_DEBUG, "========== --- %s-%s-%s\n",
2182                         fi->name, fi->version, fi->release);
2183             oc = tsGetOc(tsi);
2184             /* If install failed, then we shouldn't erase. */
2185             if (ts->order[oc].u.removed.dependsOnIndex != lastFailed) {
2186                 if (psmStage(psm, PSM_PKGERASE))
2187                     ourrc++;
2188             }
2189             freeFi(fi);
2190             /*@switchbreak@*/ break;
2191         }
2192         xx = rpmdbSync(ts->rpmdb);
2193     }
2194     tsi = tsFreeIterator(tsi);
2195
2196     ts->flList = freeFl(ts, ts->flList);
2197     ts->flEntries = 0;
2198
2199     /*@-nullstate@*/
2200     if (ourrc)
2201         return -1;
2202     else
2203         return 0;
2204     /*@=nullstate@*/
2205 }