Change all internal uses of rpmtsInitIterator() to use DBI tags
[platform/upstream/rpm.git] / lib / transaction.c
1 /** \ingroup rpmts
2  * \file lib/transaction.c
3  */
4
5 #include "system.h"
6
7 #include <rpm/rpmlib.h>         /* rpmMachineScore, rpmReadPackageFile */
8 #include <rpm/rpmmacro.h>       /* XXX for rpmExpand */
9 #include <rpm/rpmlog.h>
10 #include <rpm/rpmdb.h>
11 #include <rpm/rpmds.h>
12 #include <rpm/rpmfileutil.h>
13 #include <rpm/rpmstring.h>
14
15 #include "lib/fprint.h"
16 #include "lib/misc.h"
17 #include "lib/rpmchroot.h"
18 #include "lib/rpmlock.h"
19 #include "lib/rpmfi_internal.h" /* only internal apis */
20 #include "lib/rpmte_internal.h" /* only internal apis */
21 #include "lib/rpmts_internal.h"
22 #include "rpmio/rpmhook.h"
23
24 /* XXX FIXME: merge with existing (broken?) tests in system.h */
25 /* portability fiddles */
26 #if STATFS_IN_SYS_STATVFS
27 #include <sys/statvfs.h>
28
29 #else
30 # if STATFS_IN_SYS_VFS
31 #  include <sys/vfs.h>
32 # else
33 #  if STATFS_IN_SYS_MOUNT
34 #   include <sys/mount.h>
35 #  else
36 #   if STATFS_IN_SYS_STATFS
37 #    include <sys/statfs.h>
38 #   endif
39 #  endif
40 # endif
41 #endif
42
43 #include "debug.h"
44
45 struct diskspaceInfo_s {
46     char * mntPoint;    /*!< File system mount point */
47     dev_t dev;          /*!< File system device number. */
48     int64_t bneeded;    /*!< No. of blocks needed. */
49     int64_t ineeded;    /*!< No. of inodes needed. */
50     int64_t bsize;      /*!< File system block size. */
51     int64_t bavail;     /*!< No. of blocks available. */
52     int64_t iavail;     /*!< No. of inodes available. */
53     int64_t obneeded;   /*!< Bookkeeping to avoid duplicate reports */
54     int64_t oineeded;   /*!< Bookkeeping to avoid duplicate reports */
55 };
56
57 /* Adjust for root only reserved space. On linux e2fs, this is 5%. */
58 #define adj_fs_blocks(_nb)      (((_nb) * 21) / 20)
59 #define BLOCK_ROUND(size, block) (((size) + (block) - 1) / (block))
60
61 static int rpmtsInitDSI(const rpmts ts)
62 {
63     if (rpmtsFilterFlags(ts) & RPMPROB_FILTER_DISKSPACE)
64         return 0;
65     ts->dsi = _free(ts->dsi);
66     ts->dsi = xcalloc(1, sizeof(*ts->dsi));
67     return 0;
68 }
69
70 static rpmDiskSpaceInfo rpmtsCreateDSI(const rpmts ts, dev_t dev,
71                                        const char * dirName, int count)
72 {
73     rpmDiskSpaceInfo dsi;
74     struct stat sb;
75     char * resolved_path;
76     char mntPoint[PATH_MAX];
77     int rc;
78
79 #if STATFS_IN_SYS_STATVFS
80     struct statvfs sfb;
81     memset(&sfb, 0, sizeof(sfb));
82     rc = statvfs(dirName, &sfb);
83 #else
84     struct statfs sfb;
85     memset(&sfb, 0, sizeof(sfb));
86 #  if STAT_STATFS4
87 /* This platform has the 4-argument version of the statfs call.  The last two
88  * should be the size of struct statfs and 0, respectively.  The 0 is the
89  * filesystem type, and is always 0 when statfs is called on a mounted
90  * filesystem, as we're doing.
91  */
92     rc = statfs(dirName, &sfb, sizeof(sfb), 0);
93 #  else
94     rc = statfs(dirName, &sfb);
95 #  endif
96 #endif
97     if (rc)
98         return NULL;
99
100     rc = stat(dirName, &sb);
101     if (rc)
102         return NULL;
103     if (sb.st_dev != dev)
104         return NULL;
105
106     ts->dsi = xrealloc(ts->dsi, (count + 2) * sizeof(*ts->dsi));
107     dsi = ts->dsi + count;
108     memset(dsi, 0, 2 * sizeof(*dsi));
109
110     dsi->dev = sb.st_dev;
111     dsi->bsize = sfb.f_bsize;
112     if (!dsi->bsize)
113         dsi->bsize = 512;       /* we need a bsize */
114     dsi->bneeded = 0;
115     dsi->ineeded = 0;
116 #ifdef STATFS_HAS_F_BAVAIL
117     dsi->bavail = (sfb.f_flag & ST_RDONLY) ? 0 : sfb.f_bavail;
118 #else
119 /* FIXME: the statfs struct doesn't have a member to tell how many blocks are
120  * available for non-superusers.  f_blocks - f_bfree is probably too big, but
121  * it's about all we can do.
122  */
123     dsi->bavail = sfb.f_blocks - sfb.f_bfree;
124 #endif
125     /* XXX Avoid FAT and other file systems that have not inodes. */
126     /* XXX assigning negative value to unsigned type */
127     dsi->iavail = !(sfb.f_ffree == 0 && sfb.f_files == 0)
128         ? sfb.f_ffree : -1;
129
130     /* Find mount point belonging to this device number */
131     resolved_path = realpath(dirName, mntPoint);
132     if (!resolved_path) {
133         strncpy(mntPoint, dirName, PATH_MAX);
134         mntPoint[PATH_MAX-1] = '\0';
135     }
136     char * end = NULL;
137     while (end != mntPoint) {
138         end = strrchr(mntPoint, '/');
139         if (end == mntPoint) { /* reached "/" */
140             stat("/", &sb);
141             if (dsi->dev != sb.st_dev) {
142                 dsi->mntPoint = xstrdup(mntPoint);
143             } else {
144                 dsi->mntPoint = xstrdup("/");
145             }
146             break;
147         } else if (end) {
148             *end = '\0';
149         } else { /* dirName doesn't start with / - should not happen */
150             dsi->mntPoint = xstrdup(dirName);
151             break;
152         }
153         stat(mntPoint, &sb);
154         if (dsi->dev != sb.st_dev) {
155             *end = '/';
156             dsi->mntPoint = xstrdup(mntPoint);
157             break;
158         }
159     }
160
161     rpmlog(RPMLOG_DEBUG,
162            "0x%08x %8" PRId64 " %12" PRId64 " %12" PRId64" %s\n",
163            (unsigned) dsi->dev, dsi->bsize,
164            dsi->bavail, dsi->iavail,
165            dsi->mntPoint);
166     return dsi;
167 }
168
169 static rpmDiskSpaceInfo rpmtsGetDSI(const rpmts ts, dev_t dev,
170                                     const char *dirName) {
171     rpmDiskSpaceInfo dsi;
172     dsi = ts->dsi;
173     if (dsi) {
174         while (dsi->bsize && dsi->dev != dev)
175             dsi++;
176         if (dsi->bsize == 0) {
177             /* create new entry */
178             dsi = rpmtsCreateDSI(ts, dev, dirName, dsi - ts->dsi);
179         }
180     }
181     return dsi;
182 }
183
184 static void rpmtsUpdateDSI(const rpmts ts, dev_t dev, const char *dirName,
185                 rpm_loff_t fileSize, rpm_loff_t prevSize, rpm_loff_t fixupSize,
186                 rpmFileAction action)
187 {
188     int64_t bneeded;
189     rpmDiskSpaceInfo dsi = rpmtsGetDSI(ts, dev, dirName);
190     if (dsi == NULL)
191         return;
192
193     bneeded = BLOCK_ROUND(fileSize, dsi->bsize);
194
195     switch (action) {
196     case FA_BACKUP:
197     case FA_SAVE:
198     case FA_ALTNAME:
199         dsi->ineeded++;
200         dsi->bneeded += bneeded;
201         break;
202
203     /*
204      * FIXME: If two packages share a file (same md5sum), and
205      * that file is being replaced on disk, will dsi->bneeded get
206      * adjusted twice? Quite probably!
207      */
208     case FA_CREATE:
209         dsi->bneeded += bneeded;
210         dsi->bneeded -= BLOCK_ROUND(prevSize, dsi->bsize);
211         break;
212
213     case FA_ERASE:
214         dsi->ineeded--;
215         dsi->bneeded -= bneeded;
216         break;
217
218     default:
219         break;
220     }
221
222     if (fixupSize)
223         dsi->bneeded -= BLOCK_ROUND(fixupSize, dsi->bsize);
224
225     /* adjust bookkeeping when requirements shrink */
226     if (dsi->bneeded < dsi->obneeded) dsi->obneeded = dsi->bneeded;
227     if (dsi->ineeded < dsi->oineeded) dsi->oineeded = dsi->ineeded;
228 }
229
230 /* return DSI of the device the rpmdb lives on */
231 static rpmDiskSpaceInfo rpmtsDbDSI(const rpmts ts) {
232     const char *dbhome = rpmdbHome(rpmtsGetRdb(ts));
233     struct stat sb;
234     int rc;
235
236     rc = stat(dbhome, &sb);
237     if (rc) {
238         return NULL;
239     }
240     return rpmtsGetDSI(ts, sb.st_dev, dbhome);
241 }
242
243 /* Update DSI for changing size of the rpmdb */
244 static void rpmtsUpdateDSIrpmDBSize(const rpmte p,
245                                     rpmDiskSpaceInfo dsi) {
246     rpm_loff_t headerSize;
247     int64_t bneeded;
248
249     if (dsi==NULL) return;
250
251     headerSize = rpmteHeaderSize(p);
252     bneeded = BLOCK_ROUND(headerSize, dsi->bsize);
253     /* REMOVE doesn't neccessarily shrink the database */
254     if (rpmteType(p) == TR_ADDED) {
255         /* guessing that db grows 4 times more than the header size */
256         dsi->bneeded += (bneeded * 4);
257     }
258 }
259
260
261 static void rpmtsCheckDSIProblems(const rpmts ts, const rpmte te)
262 {
263     rpmDiskSpaceInfo dsi = ts->dsi;
264
265     if (dsi == NULL || !dsi->bsize)
266         return;
267     if (rpmfiFC(rpmteFI(te)) <= 0)
268         return;
269
270     for (; dsi->bsize; dsi++) {
271
272         if (dsi->bavail >= 0 && adj_fs_blocks(dsi->bneeded) > dsi->bavail) {
273             if (dsi->bneeded > dsi->obneeded) {
274                 rpmteAddProblem(te, RPMPROB_DISKSPACE, NULL, dsi->mntPoint,
275                    (adj_fs_blocks(dsi->bneeded) - dsi->bavail) * dsi->bsize);
276                 dsi->obneeded = dsi->bneeded;
277             }
278         }
279
280         if (dsi->iavail >= 0 && adj_fs_blocks(dsi->ineeded) > dsi->iavail) {
281             if (dsi->ineeded > dsi->oineeded) {
282                 rpmteAddProblem(te, RPMPROB_DISKNODES, NULL, dsi->mntPoint,
283                         (adj_fs_blocks(dsi->ineeded) - dsi->iavail));
284                 dsi->oineeded = dsi->ineeded;
285             }
286         }
287     }
288 }
289
290 static void rpmtsFreeDSI(rpmts ts){
291     rpmDiskSpaceInfo dsi;
292     if (ts == NULL)
293         return;
294     dsi = ts->dsi;
295     while (dsi && dsi->bsize != 0) {
296         dsi->mntPoint = _free(dsi->mntPoint);
297         dsi++;
298     }
299
300     ts->dsi = _free(ts->dsi);
301 }
302
303
304 /* Calculate total number of files involved in transaction */
305 static uint64_t countFiles(rpmts ts)
306 {
307     uint64_t fc = 0;
308     rpmtsi pi = rpmtsiInit(ts);
309     rpmte p;
310     while ((p = rpmtsiNext(pi, 0)) != NULL)
311         fc += rpmfiFC(rpmteFI(p));
312     pi = rpmtsiFree(pi);
313     return fc;
314 }
315
316 /**
317  * handleInstInstalledFiles.
318  * @param ts            transaction set
319  * @param p             current transaction element
320  * @param fi            file info set
321  * @param shared        shared file info
322  * @param sharedCount   no. of shared elements
323  * @param reportConflicts
324  */
325 /* XXX only ts->{probs,rpmdb} modified */
326 static int handleInstInstalledFile(const rpmts ts, rpmte p, rpmfi fi,
327                                    Header otherHeader, rpmfi otherFi,
328                                    int beingRemoved)
329 {
330     unsigned int fx = rpmfiFX(fi);
331     rpmfs fs = rpmteGetFileStates(p);
332     int isCfgFile = ((rpmfiFFlags(otherFi) | rpmfiFFlags(fi)) & RPMFILE_CONFIG);
333
334     if (XFA_SKIPPING(rpmfsGetAction(fs, fx)))
335         return 0;
336
337     if (rpmfiCompare(otherFi, fi)) {
338         rpm_color_t tscolor = rpmtsColor(ts);
339         rpm_color_t prefcolor = rpmtsPrefColor(ts);
340         rpm_color_t FColor = rpmfiFColor(fi) & tscolor;
341         rpm_color_t oFColor = rpmfiFColor(otherFi) & tscolor;
342         int rConflicts;
343
344         rConflicts = !(beingRemoved || (rpmtsFilterFlags(ts) & RPMPROB_FILTER_REPLACEOLDFILES));
345         /* Resolve file conflicts to prefer Elf64 (if not forced). */
346         if (tscolor != 0 && FColor != 0 && FColor != oFColor) {
347             if (oFColor & prefcolor) {
348                 rpmfsSetAction(fs, fx, FA_SKIPCOLOR);
349                 rConflicts = 0;
350             } else if (FColor & prefcolor) {
351                 rpmfsSetAction(fs, fx, FA_CREATE);
352                 rConflicts = 0;
353             }
354         }
355
356         if (rConflicts) {
357             char *altNEVR = headerGetAsString(otherHeader, RPMTAG_NEVRA);
358             rpmteAddProblem(p, RPMPROB_FILE_CONFLICT, altNEVR, rpmfiFN(fi),
359                             headerGetInstance(otherHeader));
360             free(altNEVR);
361         }
362
363         /* Save file identifier to mark as state REPLACED. */
364         if ( !(isCfgFile || XFA_SKIPPING(rpmfsGetAction(fs, fx))) ) {
365             if (!beingRemoved)
366                 rpmfsAddReplaced(rpmteGetFileStates(p), rpmfiFX(fi),
367                                  headerGetInstance(otherHeader),
368                                  rpmfiFX(otherFi));
369         }
370     }
371
372     /* Determine config file dispostion, skipping missing files (if any). */
373     if (isCfgFile) {
374         int skipMissing = ((rpmtsFlags(ts) & RPMTRANS_FLAG_ALLFILES) ? 0 : 1);
375         rpmFileAction action = rpmfiDecideFate(otherFi, fi, skipMissing);
376         rpmfsSetAction(fs, fx, action);
377     }
378     rpmfiSetFReplacedSize(fi, rpmfiFSize(otherFi));
379
380     return 0;
381 }
382
383 /**
384  * Update disk space needs on each partition for this package's files.
385  */
386 /* XXX only ts->{probs,di} modified */
387 static void handleOverlappedFiles(rpmts ts, rpmFpHash ht, rpmte p, rpmfi fi)
388 {
389     rpm_loff_t fixupSize = 0;
390     const char * fn;
391     int i, j;
392     rpm_color_t tscolor = rpmtsColor(ts);
393     rpm_color_t prefcolor = rpmtsPrefColor(ts);
394     rpmfs fs = rpmteGetFileStates(p);
395     rpmfs otherFs;
396
397     fi = rpmfiInit(fi, 0);
398     while ((i = rpmfiNext(fi)) >= 0) {
399         rpm_color_t oFColor, FColor;
400         struct fingerPrint_s * fiFps;
401         int otherPkgNum, otherFileNum;
402         rpmfi otherFi;
403         rpmte otherTe;
404         rpmfileAttrs FFlags;
405         rpm_mode_t FMode;
406         struct rpmffi_s * recs;
407         int numRecs;
408
409         if (XFA_SKIPPING(rpmfsGetAction(fs, i)))
410             continue;
411
412         fn = rpmfiFN(fi);
413         fiFps = rpmfiFpsIndex(fi, i);
414         FFlags = rpmfiFFlags(fi);
415         FMode = rpmfiFMode(fi);
416         FColor = rpmfiFColor(fi);
417         FColor &= tscolor;
418
419         fixupSize = 0;
420
421         /*
422          * Retrieve all records that apply to this file. Note that the
423          * file info records were built in the same order as the packages
424          * will be installed and removed so the records for an overlapped
425          * files will be sorted in exactly the same order.
426          */
427         (void) rpmFpHashGetEntry(ht, fiFps, &recs, &numRecs, NULL);
428
429         /*
430          * If this package is being added, look only at other packages
431          * being added -- removed packages dance to a different tune.
432          *
433          * If both this and the other package are being added, overlapped
434          * files must be identical (or marked as a conflict). The
435          * disposition of already installed config files leads to
436          * a small amount of extra complexity.
437          *
438          * If this package is being removed, then there are two cases that
439          * need to be worried about:
440          * If the other package is being added, then skip any overlapped files
441          * so that this package removal doesn't nuke the overlapped files
442          * that were just installed.
443          * If both this and the other package are being removed, then each
444          * file removal from preceding packages needs to be skipped so that
445          * the file removal occurs only on the last occurence of an overlapped
446          * file in the transaction set.
447          *
448          */
449
450         /* Locate this overlapped file in the set of added/removed packages. */
451         for (j = 0; j < numRecs && recs[j].p != p; j++)
452             {};
453
454         /* Find what the previous disposition of this file was. */
455         otherFileNum = -1;                      /* keep gcc quiet */
456         otherFi = NULL;
457         otherTe = NULL;
458         otherFs = NULL;
459
460         for (otherPkgNum = j - 1; otherPkgNum >= 0; otherPkgNum--) {
461             otherTe = recs[otherPkgNum].p;
462             otherFi = rpmteFI(otherTe);
463             otherFileNum = recs[otherPkgNum].fileno;
464             otherFs = rpmteGetFileStates(otherTe);
465
466             /* Added packages need only look at other added packages. */
467             if (rpmteType(p) == TR_ADDED && rpmteType(otherTe) != TR_ADDED)
468                 continue;
469
470             (void) rpmfiSetFX(otherFi, otherFileNum);
471
472             /* XXX Happens iff fingerprint for incomplete package install. */
473             if (rpmfsGetAction(otherFs, otherFileNum) != FA_UNKNOWN)
474                 break;
475         }
476
477         oFColor = rpmfiFColor(otherFi);
478         oFColor &= tscolor;
479
480         switch (rpmteType(p)) {
481         case TR_ADDED:
482           {
483             int reportConflicts =
484                 !(rpmtsFilterFlags(ts) & RPMPROB_FILTER_REPLACENEWFILES);
485             int done = 0;
486
487             if (otherPkgNum < 0) {
488                 /* XXX is this test still necessary? */
489                 rpmFileAction action;
490                 if (rpmfsGetAction(fs, i) != FA_UNKNOWN)
491                     break;
492                 if (rpmfiConfigConflict(fi)) {
493                     /* Here is a non-overlapped pre-existing config file. */
494                     action = (FFlags & RPMFILE_NOREPLACE) ?
495                               FA_ALTNAME : FA_BACKUP;
496                 } else {
497                     action = FA_CREATE;
498                 }
499                 rpmfsSetAction(fs, i, action);
500                 break;
501             }
502
503 assert(otherFi != NULL);
504             /* Mark added overlapped non-identical files as a conflict. */
505             if (rpmfiCompare(otherFi, fi)) {
506                 int rConflicts;
507
508                 rConflicts = reportConflicts;
509                 /* Resolve file conflicts to prefer Elf64 (if not forced) ... */
510                 if (tscolor != 0) {
511                     if (FColor & prefcolor) {
512                         /* ... last file of preferred colour is installed ... */
513                         if (!XFA_SKIPPING(rpmfsGetAction(fs, i)))
514                             rpmfsSetAction(otherFs, otherFileNum, FA_SKIPCOLOR);
515                         rpmfsSetAction(fs, i, FA_CREATE);
516                         rConflicts = 0;
517                     } else
518                     if (oFColor & prefcolor) {
519                         /* ... first file of preferred colour is installed ... */
520                         if (XFA_SKIPPING(rpmfsGetAction(fs, i)))
521                             rpmfsSetAction(otherFs, otherFileNum, FA_CREATE);
522                         rpmfsSetAction(fs, i, FA_SKIPCOLOR);
523                         rConflicts = 0;
524                     }
525                     done = 1;
526                 }
527                 if (rConflicts) {
528                     rpmteAddProblem(p, RPMPROB_NEW_FILE_CONFLICT,
529                                     rpmteNEVRA(otherTe), fn, 0);
530                 }
531             }
532
533             /* Try to get the disk accounting correct even if a conflict. */
534             fixupSize = rpmfiFSize(otherFi);
535
536             if (rpmfiConfigConflict(fi)) {
537                 /* Here is an overlapped  pre-existing config file. */
538                 rpmFileAction action;
539                 action = (FFlags & RPMFILE_NOREPLACE) ? FA_ALTNAME : FA_SKIP;
540                 rpmfsSetAction(fs, i, action);
541             } else {
542                 if (!done)
543                     rpmfsSetAction(fs, i, FA_CREATE);
544             }
545           } break;
546
547         case TR_REMOVED:
548             if (otherPkgNum >= 0) {
549                 assert(otherFi != NULL);
550                 /* Here is an overlapped added file we don't want to nuke. */
551                 if (rpmfsGetAction(otherFs, otherFileNum) != FA_ERASE) {
552                     /* On updates, don't remove files. */
553                     rpmfsSetAction(fs, i, FA_SKIP);
554                     break;
555                 }
556                 /* Here is an overlapped removed file: skip in previous. */
557                 rpmfsSetAction(otherFs, otherFileNum, FA_SKIP);
558             }
559             if (XFA_SKIPPING(rpmfsGetAction(fs, i)))
560                 break;
561             if (rpmfiFState(fi) != RPMFILE_STATE_NORMAL)
562                 break;
563             if (!(S_ISREG(FMode) && (FFlags & RPMFILE_CONFIG))) {
564                 rpmfsSetAction(fs, i, FA_ERASE);
565                 break;
566             }
567                 
568             /* Here is a pre-existing modified config file that needs saving. */
569             {   pgpHashAlgo algo = 0;
570                 size_t diglen = 0;
571                 const unsigned char *digest;
572                 if ((digest = rpmfiFDigest(fi, &algo, &diglen))) {
573                     unsigned char fdigest[diglen];
574                     if (!rpmDoDigest(algo, fn, 0, fdigest, NULL) &&
575                         memcmp(digest, fdigest, diglen)) {
576                         rpmfsSetAction(fs, i, FA_BACKUP);
577                         break;
578                     }
579                 }
580             }
581             rpmfsSetAction(fs, i, FA_ERASE);
582             break;
583         }
584
585         /* Update disk space info for a file. */
586         rpmtsUpdateDSI(ts, fiFps->entry->dev, fiFps->entry->dirName,
587                        rpmfiFSize(fi), rpmfiFReplacedSize(fi),
588                        fixupSize, rpmfsGetAction(fs, i));
589
590     }
591 }
592
593 /**
594  * Ensure that current package is newer than installed package.
595  * @param p             current transaction element
596  * @param h             installed header
597  * @param ps            problem set
598  */
599 static void ensureOlder(const rpmte p, const Header h)
600 {
601     rpmsenseFlags reqFlags = (RPMSENSE_LESS | RPMSENSE_EQUAL);
602     rpmds req;
603
604     req = rpmdsSingle(RPMTAG_REQUIRENAME, rpmteN(p), rpmteEVR(p), reqFlags);
605     if (rpmdsNVRMatchesDep(h, req, _rpmds_nopromote) == 0) {
606         char * altNEVR = headerGetAsString(h, RPMTAG_NEVRA);
607         rpmteAddProblem(p, RPMPROB_OLDPACKAGE, altNEVR, NULL,
608                         headerGetInstance(h));
609         free(altNEVR);
610     }
611     rpmdsFree(req);
612 }
613
614 /**
615  * Check if the curent file in the file iterator is in the
616  * netshardpath and though should be excluded.
617  * @param ts            transaction set
618  * @param fi            file info set
619  * @returns pointer to matching path or NULL
620  */
621 static char ** matchNetsharedpath(const rpmts ts, rpmfi fi)
622 {
623     char ** nsp;
624     const char * dn, * bn;
625     size_t dnlen, bnlen;
626     char * s;
627     bn = rpmfiBN(fi);
628     bnlen = strlen(bn);
629     dn = rpmfiDN(fi);
630     dnlen = strlen(dn);
631     for (nsp = ts->netsharedPaths; nsp && *nsp; nsp++) {
632         size_t len;
633
634         len = strlen(*nsp);
635         if (dnlen >= len) {
636             if (!rstreqn(dn, *nsp, len))
637                 continue;
638             /* Only directories or complete file paths can be net shared */
639             if (!(dn[len] == '/' || dn[len] == '\0'))
640                 continue;
641         } else {
642             if (len < (dnlen + bnlen))
643                 continue;
644             if (!rstreqn(dn, *nsp, dnlen))
645                 continue;
646             /* Insure that only the netsharedpath basename is compared. */
647             if ((s = strchr((*nsp) + dnlen, '/')) != NULL && s[1] != '\0')
648                 continue;
649             if (!rstreqn(bn, (*nsp) + dnlen, bnlen))
650                 continue;
651             len = dnlen + bnlen;
652             /* Only directories or complete file paths can be net shared */
653             if (!((*nsp)[len] == '/' || (*nsp)[len] == '\0'))
654                 continue;
655         }
656
657         break;
658     }
659     return nsp;
660 }
661
662 static void skipEraseFiles(const rpmts ts, rpmte p)
663 {
664     rpmfi fi = rpmteFI(p);
665     rpmfs fs = rpmteGetFileStates(p);
666     int i;
667     char ** nsp;
668     /*
669      * Skip net shared paths.
670      * Net shared paths are not relative to the current root (though
671      * they do need to take package relocations into account).
672      */
673     fi = rpmfiInit(fi, 0);
674     while ((i = rpmfiNext(fi)) >= 0)
675     {
676         nsp = matchNetsharedpath(ts, fi);
677         if (nsp && *nsp) {
678             rpmfsSetAction(fs, i, FA_SKIPNETSHARED);
679         }
680     }
681 }
682
683
684 /**
685  * Skip any files that do not match install policies.
686  * @param ts            transaction set
687  * @param fi            file info set
688  */
689 static void skipInstallFiles(const rpmts ts, rpmte p)
690 {
691     rpm_color_t tscolor = rpmtsColor(ts);
692     rpm_color_t FColor;
693     int noConfigs = (rpmtsFlags(ts) & RPMTRANS_FLAG_NOCONFIGS);
694     int noDocs = (rpmtsFlags(ts) & RPMTRANS_FLAG_NODOCS);
695     const char * dn, * bn;
696     size_t dnlen, bnlen;
697     int * drc;
698     char * dff;
699     int dc;
700     int i, j, ix;
701     rpmfi fi = rpmteFI(p);
702     rpmfs fs = rpmteGetFileStates(p);
703
704     if (!noDocs)
705         noDocs = rpmExpandNumeric("%{_excludedocs}");
706
707     /* Compute directory refcount, skip directory if now empty. */
708     dc = rpmfiDC(fi);
709     drc = xcalloc(dc, sizeof(*drc));
710     dff = xcalloc(dc, sizeof(*dff));
711
712     fi = rpmfiInit(fi, 0);
713     while ((i = rpmfiNext(fi)) >= 0)
714     {
715         char ** nsp;
716         const char *flangs;
717
718         bn = rpmfiBN(fi);
719         bnlen = strlen(bn);
720         ix = rpmfiDX(fi);
721         dn = rpmfiDN(fi);
722         dnlen = strlen(dn);
723
724         drc[ix]++;
725
726         /* Don't bother with skipped files */
727         if (XFA_SKIPPING(rpmfsGetAction(fs, i))) {
728             drc[ix]--; dff[ix] = 1;
729             continue;
730         }
731
732         /* Ignore colored files not in our rainbow. */
733         FColor = rpmfiFColor(fi);
734         if (tscolor && FColor && !(tscolor & FColor)) {
735             drc[ix]--;  dff[ix] = 1;
736             rpmfsSetAction(fs, i, FA_SKIPCOLOR);
737             continue;
738         }
739
740         /*
741          * Skip net shared paths.
742          * Net shared paths are not relative to the current root (though
743          * they do need to take package relocations into account).
744          */
745         nsp = matchNetsharedpath(ts, fi);
746         if (nsp && *nsp) {
747             drc[ix]--;  dff[ix] = 1;
748             rpmfsSetAction(fs, i, FA_SKIPNETSHARED);
749             continue;
750         }
751
752         /*
753          * Skip i18n language specific files.
754          */
755         flangs = (ts->installLangs != NULL) ? rpmfiFLangs(fi) : NULL;
756         if (flangs != NULL && *flangs != '\0') {
757             const char *l, *le;
758             char **lang;
759             for (lang = ts->installLangs; *lang != NULL; lang++) {
760                 for (l = flangs; *l != '\0'; l = le) {
761                     for (le = l; *le != '\0' && *le != '|'; le++)
762                         {};
763                     if ((le-l) > 0 && rstreqn(*lang, l, (le-l)))
764                         break;
765                     if (*le == '|') le++;       /* skip over | */
766                 }
767                 if (*l != '\0')
768                     break;
769             }
770             if (*lang == NULL) {
771                 drc[ix]--;      dff[ix] = 1;
772                 rpmfsSetAction(fs, i, FA_SKIPNSTATE);
773                 continue;
774             }
775         }
776
777         /*
778          * Skip config files if requested.
779          */
780         if (noConfigs && (rpmfiFFlags(fi) & RPMFILE_CONFIG)) {
781             drc[ix]--;  dff[ix] = 1;
782             rpmfsSetAction(fs, i, FA_SKIPNSTATE);
783             continue;
784         }
785
786         /*
787          * Skip documentation if requested.
788          */
789         if (noDocs && (rpmfiFFlags(fi) & RPMFILE_DOC)) {
790             drc[ix]--;  dff[ix] = 1;
791             rpmfsSetAction(fs, i, FA_SKIPNSTATE);
792             continue;
793         }
794     }
795
796     /* Skip (now empty) directories that had skipped files. */
797 #ifndef NOTYET
798     if (fi != NULL)     /* XXX can't happen */
799     for (j = 0; j < dc; j++)
800 #else
801     if ((fi = rpmfiInitD(fi)) != NULL)
802     while (j = rpmfiNextD(fi) >= 0)
803 #endif
804     {
805
806         if (drc[j]) continue;   /* dir still has files. */
807         if (!dff[j]) continue;  /* dir was not emptied here. */
808         
809         /* Find parent directory and basename. */
810         dn = rpmfiDNIndex(fi, j); dnlen = strlen(dn) - 1;
811         bn = dn + dnlen;        bnlen = 0;
812         while (bn > dn && bn[-1] != '/') {
813                 bnlen++;
814                 dnlen--;
815                 bn--;
816         }
817
818         /* If explicitly included in the package, skip the directory. */
819         fi = rpmfiInit(fi, 0);
820         while ((i = rpmfiNext(fi)) >= 0) {
821             const char * fdn, * fbn;
822             rpm_mode_t fFMode;
823
824             if (XFA_SKIPPING(rpmfsGetAction(fs, i)))
825                 continue;
826
827             fFMode = rpmfiFMode(fi);
828
829             if (rpmfiWhatis(fFMode) != XDIR)
830                 continue;
831             fdn = rpmfiDN(fi);
832             if (strlen(fdn) != dnlen)
833                 continue;
834             if (!rstreqn(fdn, dn, dnlen))
835                 continue;
836             fbn = rpmfiBN(fi);
837             if (strlen(fbn) != bnlen)
838                 continue;
839             if (!rstreqn(fbn, bn, bnlen))
840                 continue;
841             rpmlog(RPMLOG_DEBUG, "excluding directory %s\n", dn);
842             rpmfsSetAction(fs, i, FA_SKIPNSTATE);
843             break;
844         }
845     }
846
847     free(drc);
848     free(dff);
849 }
850
851 #undef HASHTYPE
852 #undef HTKEYTYPE
853 #undef HTDATATYPE
854
855 #define HASHTYPE rpmStringSet
856 #define HTKEYTYPE const char *
857 #include "lib/rpmhash.H"
858 #include "lib/rpmhash.C"
859
860 /* Get a rpmdbMatchIterator containing all files in
861  * the rpmdb that share the basename with one from
862  * the transaction.
863  * @param ts            transaction set
864  * @return              rpmdbMatchIterator sorted
865                         by (package, fileNum)
866  */
867 static
868 rpmdbMatchIterator rpmFindBaseNamesInDB(rpmts ts, uint64_t fileCount)
869 {
870     tsMembers tsmem = rpmtsMembers(ts);
871     rpmtsi pi;  rpmte p;
872     rpmfi fi;
873     rpmdbMatchIterator mi;
874     int xx;
875     int oc = 0;
876     const char * baseName;
877
878     rpmStringSet baseNames = rpmStringSetCreate(fileCount, 
879                                         hashFunctionString, strcmp, NULL);
880
881     mi = rpmdbInitIterator(rpmtsGetRdb(ts), RPMDBI_BASENAMES, NULL, 0);
882
883     pi = rpmtsiInit(ts);
884     while ((p = rpmtsiNext(pi, 0)) != NULL) {
885         (void) rpmdbCheckSignals();
886
887         rpmtsNotify(ts, NULL, RPMCALLBACK_TRANS_PROGRESS, oc++, tsmem->orderCount);
888
889         /* Gather all installed headers with matching basename's. */
890         fi = rpmfiInit(rpmteFI(p), 0);
891         while (rpmfiNext(fi) >= 0) {
892             size_t keylen;
893             baseName = rpmfiBN(fi);
894             if (rpmStringSetHasEntry(baseNames, baseName))
895                 continue;
896
897             keylen = strlen(baseName);
898             if (keylen == 0)
899                 keylen++;       /* XXX "/" fixup. */
900             xx = rpmdbExtendIterator(mi, baseName, keylen);
901             rpmStringSetAddEntry(baseNames, baseName);
902          }
903     }
904     pi = rpmtsiFree(pi);
905     rpmStringSetFree(baseNames);
906
907     rpmdbSortIterator(mi);
908     /* iterator is now sorted by (recnum, filenum) */
909     return mi;
910 }
911
912 /* Check files in the transactions against the rpmdb
913  * Lookup all files with the same basename in the rpmdb
914  * and then check for matching finger prints
915  * @param ts            transaction set
916  * @param fpc           global finger print cache
917  */
918 static
919 void checkInstalledFiles(rpmts ts, uint64_t fileCount, rpmFpHash ht, fingerPrintCache fpc)
920 {
921     tsMembers tsmem = rpmtsMembers(ts);
922     rpmte p;
923     rpmfi fi;
924     rpmfs fs;
925     rpmfi otherFi=NULL;
926     int j;
927     int xx;
928     unsigned int fileNum;
929     const char * oldDir;
930
931     rpmdbMatchIterator mi;
932     Header h, newheader;
933
934     int beingRemoved;
935
936     rpmlog(RPMLOG_DEBUG, "computing file dispositions\n");
937
938     mi = rpmFindBaseNamesInDB(ts, fileCount);
939
940     /* For all installed headers with matching basename's ... */
941     if (mi == NULL)
942          return;
943
944     if (rpmdbGetIteratorCount(mi) == 0) {
945         mi = rpmdbFreeIterator(mi);
946         return;
947     }
948
949     /* Loop over all packages from the rpmdb */
950     h = newheader = rpmdbNextIterator(mi);
951     while (h != NULL) {
952         headerGetFlags hgflags = HEADERGET_MINMEM;
953         struct rpmtd_s bnames, dnames, dindexes, ostates;
954         fingerPrint fp;
955         unsigned int installedPkg;
956
957         /* Is this package being removed? */
958         installedPkg = rpmdbGetIteratorOffset(mi);
959         beingRemoved = intHashHasEntry(tsmem->removedPackages, installedPkg);
960
961         h = headerLink(h);
962         headerGet(h, RPMTAG_BASENAMES, &bnames, hgflags);
963         headerGet(h, RPMTAG_DIRNAMES, &dnames, hgflags);
964         headerGet(h, RPMTAG_DIRINDEXES, &dindexes, hgflags);
965         headerGet(h, RPMTAG_FILESTATES, &ostates, hgflags);
966
967         oldDir = NULL;
968         /* loop over all interesting files in that package */
969         do {
970             int gotRecs;
971             struct rpmffi_s * recs;
972             int numRecs;
973             const char * dirName;
974             const char * baseName;
975
976             fileNum = rpmdbGetIteratorFileNum(mi);
977             rpmtdSetIndex(&bnames, fileNum);
978             rpmtdSetIndex(&dindexes, fileNum);
979             rpmtdSetIndex(&dnames, *rpmtdGetUint32(&dindexes));
980             rpmtdSetIndex(&ostates, fileNum);
981
982             dirName = rpmtdGetString(&dnames);
983             baseName = rpmtdGetString(&bnames);
984
985             /* lookup finger print for this file */
986             if ( dirName == oldDir) {
987                 /* directory is the same as last round */
988                 fp.baseName = baseName;
989             } else {
990                 fp = fpLookup(fpc, dirName, baseName, 1);
991                 oldDir = dirName;
992             }
993             /* search for files in the transaction with same finger print */
994             gotRecs = rpmFpHashGetEntry(ht, &fp, &recs, &numRecs, NULL);
995
996             for (j=0; (j<numRecs)&&gotRecs; j++) {
997                 p = recs[j].p;
998                 fi = rpmteFI(p);
999                 fs = rpmteGetFileStates(p);
1000
1001                 /* Determine the fate of each file. */
1002                 switch (rpmteType(p)) {
1003                 case TR_ADDED:
1004                     if (!otherFi) {
1005                         otherFi = rpmfiNew(ts, h, RPMTAG_BASENAMES, RPMFI_KEEPHEADER);
1006                     }
1007                     rpmfiSetFX(fi, recs[j].fileno);
1008                     rpmfiSetFX(otherFi, fileNum);
1009                     xx = handleInstInstalledFile(ts, p, fi, h, otherFi, beingRemoved);
1010                     break;
1011                 case TR_REMOVED:
1012                     if (!beingRemoved) {
1013                         rpmfiSetFX(fi, recs[j].fileno);
1014                         if (*rpmtdGetChar(&ostates) == RPMFILE_STATE_NORMAL)
1015                             rpmfsSetAction(fs, recs[j].fileno, FA_SKIP);
1016                     }
1017                     break;
1018                 }
1019             }
1020
1021             newheader = rpmdbNextIterator(mi);
1022
1023         } while (newheader==h);
1024
1025         otherFi = rpmfiFree(otherFi);
1026         rpmtdFreeData(&ostates);
1027         rpmtdFreeData(&bnames);
1028         rpmtdFreeData(&dnames);
1029         rpmtdFreeData(&dindexes);
1030         headerFree(h);
1031         h = newheader;
1032     }
1033
1034     mi = rpmdbFreeIterator(mi);
1035 }
1036
1037 #define badArch(_a) (rpmMachineScore(RPM_MACHTABLE_INSTARCH, (_a)) == 0)
1038 #define badOs(_a) (rpmMachineScore(RPM_MACHTABLE_INSTOS, (_a)) == 0)
1039
1040 /*
1041  * For packages being installed:
1042  * - verify package arch/os.
1043  * - verify package epoch:version-release is newer.
1044  */
1045 static rpmps checkProblems(rpmts ts)
1046 {
1047     rpm_color_t tscolor = rpmtsColor(ts);
1048     rpmprobFilterFlags probFilter = rpmtsFilterFlags(ts);
1049     rpmtsi pi = rpmtsiInit(ts);
1050     rpmte p;
1051
1052     /* The ordering doesn't matter here */
1053     /* XXX Only added packages need be checked. */
1054     rpmlog(RPMLOG_DEBUG, "sanity checking %d elements\n", rpmtsNElements(ts));
1055     while ((p = rpmtsiNext(pi, TR_ADDED)) != NULL) {
1056         rpmdbMatchIterator mi;
1057
1058         if (!(probFilter & RPMPROB_FILTER_IGNOREARCH) && badArch(rpmteA(p)))
1059             rpmteAddProblem(p, RPMPROB_BADARCH, rpmteA(p), NULL, 0);
1060
1061         if (!(probFilter & RPMPROB_FILTER_IGNOREOS) && badOs(rpmteO(p)))
1062             rpmteAddProblem(p, RPMPROB_BADOS, rpmteO(p), NULL, 0);
1063
1064         if (!(probFilter & RPMPROB_FILTER_OLDPACKAGE)) {
1065             Header h;
1066             mi = rpmtsInitIterator(ts, RPMDBI_NAME, rpmteN(p), 0);
1067             while ((h = rpmdbNextIterator(mi)) != NULL)
1068                 ensureOlder(p, h);
1069             mi = rpmdbFreeIterator(mi);
1070         }
1071
1072         if (!(probFilter & RPMPROB_FILTER_REPLACEPKG)) {
1073             Header h;
1074             mi = rpmtsInitIterator(ts, RPMDBI_NAME, rpmteN(p), 0);
1075             rpmdbSetIteratorRE(mi, RPMTAG_EPOCH, RPMMIRE_STRCMP, rpmteE(p));
1076             rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_STRCMP, rpmteV(p));
1077             rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_STRCMP, rpmteR(p));
1078             if (tscolor) {
1079                 rpmdbSetIteratorRE(mi, RPMTAG_ARCH, RPMMIRE_STRCMP, rpmteA(p));
1080                 rpmdbSetIteratorRE(mi, RPMTAG_OS, RPMMIRE_STRCMP, rpmteO(p));
1081             }
1082
1083             if ((h = rpmdbNextIterator(mi)) != NULL) {
1084                 rpmteAddProblem(p, RPMPROB_PKG_INSTALLED, NULL, NULL,
1085                                 headerGetInstance(h));
1086             }
1087             mi = rpmdbFreeIterator(mi);
1088         }
1089     }
1090     pi = rpmtsiFree(pi);
1091     return rpmtsProblems(ts);
1092 }
1093
1094 /*
1095  * Run pre/post transaction scripts for transaction set
1096  * param ts     Transaction set
1097  * param goal   PKG_PRETRANS/PKG_POSTTRANS
1098  * return       0 on success
1099  */
1100 static int runTransScripts(rpmts ts, pkgGoal goal) 
1101 {
1102     rpmte p;
1103     rpmtsi pi = rpmtsiInit(ts);
1104     while ((p = rpmtsiNext(pi, TR_ADDED)) != NULL) {
1105         rpmteProcess(p, goal);
1106     }
1107     pi = rpmtsiFree(pi);
1108     return 0; /* what to do about failures? */
1109 }
1110
1111 static int rpmtsSetupCollections(rpmts ts)
1112 {
1113     /* seenCollectionsPost and TEs are basically a key-value pair. each item in
1114      * seenCollectionsPost is a collection that has been seen from any package,
1115      * and the associated index in the TEs is the last transaction element
1116      * where that collection was seen. */
1117     ARGV_t seenCollectionsPost = NULL;
1118     rpmte *TEs = NULL;
1119     int numSeenPost = 0;
1120
1121     /* seenCollectionsPre is a list of collections that have been seen from
1122      * only removed packages */
1123     ARGV_t seenCollectionsPre = NULL;
1124     int numSeenPre = 0;
1125
1126     ARGV_const_t collname;
1127     int installing = 1;
1128     int i;
1129
1130     rpmte p;
1131     rpmtsi pi = rpmtsiInit(ts);
1132     while ((p = rpmtsiNext(pi, 0)) != NULL) {
1133         /* detect when we switch from installing to removing packages, and
1134          * update the lastInCollectionAdd lists */
1135         if (installing && rpmteType(p) == TR_REMOVED) {
1136             installing = 0;
1137             for (i = 0; i < numSeenPost; i++) {
1138                 rpmteAddToLastInCollectionAdd(TEs[i], seenCollectionsPost[i]);
1139             }
1140         }
1141
1142         rpmteSetupCollectionPlugins(p);
1143
1144         for (collname = rpmteCollections(p); collname && *collname; collname++) {
1145             /* figure out if we've seen this collection in post before */
1146             for (i = 0; i < numSeenPost && strcmp(*collname, seenCollectionsPost[i]); i++) {
1147             }
1148             if (i < numSeenPost) {
1149                 /* we've seen the collection, update the index */
1150                 TEs[i] = p;
1151             } else {
1152                 /* haven't seen the collection yet, add it */
1153                 argvAdd(&seenCollectionsPost, *collname);
1154                 TEs = xrealloc(TEs, sizeof(*TEs) * (numSeenPost + 1));
1155                 TEs[numSeenPost] = p;
1156                 numSeenPost++;
1157             }
1158
1159             /* figure out if we've seen this collection in pre remove before */
1160             if (installing == 0) {
1161                 for (i = 0; i < numSeenPre && strcmp(*collname, seenCollectionsPre[i]); i++) {
1162                 }
1163                 if (i >= numSeenPre) {
1164                     /* haven't seen this collection, add it */
1165                     rpmteAddToFirstInCollectionRemove(p, *collname);
1166                     argvAdd(&seenCollectionsPre, *collname);
1167                     numSeenPre++;
1168                 }
1169             }
1170         }
1171     }
1172     pi = rpmtsiFree(pi);
1173
1174     /* we've looked at all the rpmte's, update the lastInCollectionAny lists */
1175     for (i = 0; i < numSeenPost; i++) {
1176         rpmteAddToLastInCollectionAny(TEs[i], seenCollectionsPost[i]);
1177         if (installing == 1) {
1178             /* lastInCollectionAdd is only updated above if packages were
1179              * removed. if nothing is removed in the transaction, we need to
1180              * update that list here */
1181             rpmteAddToLastInCollectionAdd(TEs[i], seenCollectionsPost[i]);
1182         }
1183     }
1184
1185     argvFree(seenCollectionsPost);
1186     argvFree(seenCollectionsPre);
1187     _free(TEs);
1188
1189     return 0;
1190 }
1191
1192 /* Add fingerprint for each file not skipped. */
1193 static void addFingerprints(rpmts ts, uint64_t fileCount, rpmFpHash ht, fingerPrintCache fpc)
1194 {
1195     rpmtsi pi;
1196     rpmte p;
1197     rpmfi fi;
1198     int i;
1199
1200     rpmFpHash symlinks = rpmFpHashCreate(fileCount/16+16, fpHashFunction, fpEqual, NULL, NULL);
1201
1202     pi = rpmtsiInit(ts);
1203     while ((p = rpmtsiNext(pi, 0)) != NULL) {
1204         (void) rpmdbCheckSignals();
1205
1206         if ((fi = rpmteFI(p)) == NULL)
1207             continue;   /* XXX can't happen */
1208
1209         (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_FINGERPRINT), 0);
1210         rpmfiFpLookup(fi, fpc);
1211         /* collect symbolic links */
1212         fi = rpmfiInit(fi, 0);
1213         while ((i = rpmfiNext(fi)) >= 0) {
1214             struct rpmffi_s ffi;
1215             char const *linktarget;
1216             linktarget = rpmfiFLink(fi);
1217             if (!(linktarget && *linktarget != '\0'))
1218                 continue;
1219             if (XFA_SKIPPING(rpmfsGetAction(rpmteGetFileStates(p), i)))
1220                 continue;
1221             ffi.p = p;
1222             ffi.fileno = i;
1223             rpmFpHashAddEntry(symlinks, rpmfiFpsIndex(fi, i), ffi);
1224         }
1225         (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_FINGERPRINT), rpmfiFC(fi));
1226
1227     }
1228     pi = rpmtsiFree(pi);
1229
1230     /* ===============================================
1231      * Check fingerprints if they contain symlinks
1232      * and add them to the hash table
1233      */
1234
1235     pi = rpmtsiInit(ts);
1236     while ((p = rpmtsiNext(pi, 0)) != NULL) {
1237         (void) rpmdbCheckSignals();
1238
1239         fi = rpmfiInit(rpmteFI(p), 0);
1240         (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_FINGERPRINT), 0);
1241         while ((i = rpmfiNext(fi)) >= 0) {
1242             if (XFA_SKIPPING(rpmfsGetAction(rpmteGetFileStates(p), i)))
1243                 continue;
1244             fpLookupSubdir(symlinks, ht, fpc, p, i);
1245         }
1246         (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_FINGERPRINT), 0);
1247     }
1248     pi = rpmtsiFree(pi);
1249
1250     rpmFpHashFree(symlinks);
1251 }
1252
1253 static int rpmtsSetup(rpmts ts, rpmprobFilterFlags ignoreSet)
1254 {
1255     rpm_tid_t tid = (rpm_tid_t) time(NULL);
1256     int dbmode = (rpmtsFlags(ts) & RPMTRANS_FLAG_TEST) ?  O_RDONLY : (O_RDWR|O_CREAT);
1257
1258     if (rpmtsFlags(ts) & RPMTRANS_FLAG_NOSCRIPTS)
1259         (void) rpmtsSetFlags(ts, (rpmtsFlags(ts) | _noTransScripts | _noTransTriggers));
1260     if (rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERS)
1261         (void) rpmtsSetFlags(ts, (rpmtsFlags(ts) | _noTransTriggers));
1262
1263     if (rpmtsFlags(ts) & RPMTRANS_FLAG_JUSTDB)
1264         (void) rpmtsSetFlags(ts, (rpmtsFlags(ts) | _noTransScripts | _noTransTriggers));
1265
1266     /* if SELinux isn't enabled, init fails or test run, don't bother... */
1267     if (!is_selinux_enabled() || (rpmtsFlags(ts) & RPMTRANS_FLAG_TEST)) {
1268         rpmtsSetFlags(ts, (rpmtsFlags(ts) | RPMTRANS_FLAG_NOCONTEXTS));
1269     }
1270
1271     if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOCONTEXTS)) {
1272         rpmtsSELabelInit(ts, selinux_file_context_path());
1273     }
1274
1275     /* XXX Make sure the database is open RDWR for package install/erase. */
1276     if (rpmtsOpenDB(ts, dbmode) || rpmChrootSet(rpmtsRootDir(ts)))
1277         return -1;
1278
1279     ts->ignoreSet = ignoreSet;
1280     (void) rpmtsSetTid(ts, tid);
1281
1282     /* Get available space on mounted file systems. */
1283     (void) rpmtsInitDSI(ts);
1284
1285     return 0;
1286 }
1287
1288 static int rpmtsFinish(rpmts ts)
1289 {
1290     if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOCONTEXTS)) {
1291         rpmtsSELabelFini(ts);
1292     }
1293     return rpmChrootSet(NULL);
1294 }
1295
1296 static int rpmtsPrepare(rpmts ts)
1297 {
1298     tsMembers tsmem = rpmtsMembers(ts);
1299     rpmtsi pi;
1300     rpmte p;
1301     rpmfi fi;
1302     int rc = 0;
1303     uint64_t fileCount = countFiles(ts);
1304
1305     fingerPrintCache fpc = fpCacheCreate(fileCount/2 + 10001);
1306     rpmFpHash ht = rpmFpHashCreate(fileCount/2+1, fpHashFunction, fpEqual,
1307                              NULL, NULL);
1308     rpmDiskSpaceInfo dsi;
1309
1310     rpmlog(RPMLOG_DEBUG, "computing %" PRIu64 " file fingerprints\n", fileCount);
1311
1312     /* Skip netshared paths, not our i18n files, and excluded docs */
1313     pi = rpmtsiInit(ts);
1314     while ((p = rpmtsiNext(pi, 0)) != NULL) {
1315         if (rpmfiFC(rpmteFI(p)) == 0)
1316             continue;
1317         if (rpmteType(p) == TR_ADDED) {
1318             skipInstallFiles(ts, p);
1319         } else {
1320             skipEraseFiles(ts, p);
1321         }
1322     }
1323     pi = rpmtsiFree(pi);
1324
1325     /* Open rpmdb & enter chroot for fingerprinting if necessary */
1326     if (rpmdbOpenAll(ts->rdb) || rpmChrootIn()) {
1327         rc = -1;
1328         goto exit;
1329     }
1330     
1331     rpmtsNotify(ts, NULL, RPMCALLBACK_TRANS_START, 6, tsmem->orderCount);
1332     addFingerprints(ts, fileCount, ht, fpc);
1333     /* check against files in the rpmdb */
1334     checkInstalledFiles(ts, fileCount, ht, fpc);
1335
1336     dsi = rpmtsDbDSI(ts);
1337
1338     pi = rpmtsiInit(ts);
1339     while ((p = rpmtsiNext(pi, 0)) != NULL) {
1340         if ((fi = rpmteFI(p)) == NULL)
1341             continue;   /* XXX can't happen */
1342
1343         (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_FINGERPRINT), 0);
1344         /* check files in ts against each other and update disk space
1345            needs on each partition for this package. */
1346         handleOverlappedFiles(ts, ht, p, fi);
1347
1348         rpmtsUpdateDSIrpmDBSize(p, dsi);
1349
1350         /* Check added package has sufficient space on each partition used. */
1351         if (rpmteType(p) == TR_ADDED) {
1352             rpmtsCheckDSIProblems(ts, p);
1353         }
1354         (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_FINGERPRINT), 0);
1355     }
1356     pi = rpmtsiFree(pi);
1357     rpmtsNotify(ts, NULL, RPMCALLBACK_TRANS_STOP, 6, tsmem->orderCount);
1358
1359     /* return from chroot if done earlier */
1360     if (rpmChrootOut())
1361         rc = -1;
1362
1363     /* File info sets, fp caches etc not needed beyond here, free 'em up. */
1364     pi = rpmtsiInit(ts);
1365     while ((p = rpmtsiNext(pi, 0)) != NULL) {
1366         rpmteSetFI(p, NULL);
1367     }
1368     pi = rpmtsiFree(pi);
1369
1370 exit:
1371     ht = rpmFpHashFree(ht);
1372     fpc = fpCacheFree(fpc);
1373     rpmtsFreeDSI(ts);
1374     return rc;
1375 }
1376
1377 /*
1378  * Transaction main loop: install and remove packages
1379  */
1380 static int rpmtsProcess(rpmts ts)
1381 {
1382     rpmtsi pi;  rpmte p;
1383     int rc = 0;
1384
1385     pi = rpmtsiInit(ts);
1386     while ((p = rpmtsiNext(pi, 0)) != NULL) {
1387         int failed;
1388
1389         rpmlog(RPMLOG_DEBUG, "========== +++ %s %s-%s 0x%x\n",
1390                 rpmteNEVR(p), rpmteA(p), rpmteO(p), rpmteColor(p));
1391
1392         failed = rpmteProcess(p, rpmteType(p));
1393         if (failed) {
1394             rpmlog(RPMLOG_ERR, "%s: %s %s\n", rpmteNEVRA(p),
1395                    rpmteTypeString(p), failed > 1 ? _("skipped") : _("failed"));
1396             rc++;
1397         }
1398         (void) rpmdbSync(rpmtsGetRdb(ts));
1399     }
1400     pi = rpmtsiFree(pi);
1401     return rc;
1402 }
1403
1404 int rpmtsRun(rpmts ts, rpmps okProbs, rpmprobFilterFlags ignoreSet)
1405 {
1406     int rc = -1; /* assume failure */
1407     rpmlock lock = NULL;
1408     rpmps tsprobs = NULL;
1409
1410     /* XXX programmer error segfault avoidance. */
1411     if (rpmtsNElements(ts) <= 0) {
1412         goto exit;
1413     }
1414
1415     /* If we are in test mode, then there's no need for transaction lock. */
1416     if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_TEST)) {
1417         if (!(lock = rpmtsAcquireLock(ts))) {
1418             goto exit;
1419         }
1420     }
1421
1422     /* Setup flags and such, open the DB */
1423     if (rpmtsSetup(ts, ignoreSet)) {
1424         goto exit;
1425     }
1426
1427     rpmtsSetupCollections(ts);
1428
1429     /* Check package set for problems */
1430     tsprobs = checkProblems(ts);
1431
1432     /* Run pre-transaction scripts, but only if there are no known
1433      * problems up to this point and not disabled otherwise. */
1434     if (!((rpmtsFlags(ts) & (RPMTRANS_FLAG_BUILD_PROBS|RPMTRANS_FLAG_NOPRE))
1435           || (rpmpsNumProblems(tsprobs)))) {
1436         rpmlog(RPMLOG_DEBUG, "running pre-transaction scripts\n");
1437         runTransScripts(ts, PKG_PRETRANS);
1438     }
1439     tsprobs = rpmpsFree(tsprobs);
1440
1441     /* Compute file disposition for each package in transaction set. */
1442     if (rpmtsPrepare(ts)) {
1443         goto exit;
1444     }
1445     /* Check again for problems (now including file conflicts,  duh */
1446     tsprobs = rpmtsProblems(ts);
1447
1448      /* If unfiltered problems exist, free memory and return. */
1449     if ((rpmtsFlags(ts) & RPMTRANS_FLAG_BUILD_PROBS) || (rpmpsNumProblems(tsprobs))) {
1450         tsMembers tsmem = rpmtsMembers(ts);
1451         rc = tsmem->orderCount;
1452         goto exit;
1453     }
1454
1455     /* Free up memory taken by problem sets */
1456     tsprobs = rpmpsFree(tsprobs);
1457     rpmtsCleanProblems(ts);
1458
1459     /* Actually install and remove packages, get final exit code */
1460     rc = rpmtsProcess(ts) ? -1 : 0;
1461
1462     /* Run post-transaction scripts unless disabled */
1463     if (!(rpmtsFlags(ts) & (RPMTRANS_FLAG_NOPOST))) {
1464         rpmlog(RPMLOG_DEBUG, "running post-transaction scripts\n");
1465         runTransScripts(ts, PKG_POSTTRANS);
1466     }
1467
1468     /* Finish up... */
1469     (void) rpmtsFinish(ts);
1470
1471 exit:
1472     tsprobs = rpmpsFree(tsprobs);
1473     rpmlockFree(lock);
1474     return rc;
1475 }