Add macro %isu_package to generate ISU Package
[platform/upstream/rpm.git] / lib / transaction.c
1 /** \ingroup rpmts
2  * \file lib/transaction.c
3  */
4
5 #include "system.h"
6
7 #include <inttypes.h>
8 #include <libgen.h>
9
10 #include <rpm/rpmlib.h>         /* rpmMachineScore, rpmReadPackageFile */
11 #include <rpm/rpmmacro.h>       /* XXX for rpmExpand */
12 #include <rpm/rpmlog.h>
13 #include <rpm/rpmdb.h>
14 #include <rpm/rpmds.h>
15 #include <rpm/rpmfileutil.h>
16 #include <rpm/rpmstring.h>
17 #include <rpm/rpmsq.h>
18
19 #include "lib/fprint.h"
20 #include "lib/misc.h"
21 #include "lib/rpmchroot.h"
22 #include "lib/rpmlock.h"
23 #include "lib/rpmds_internal.h"
24 #include "lib/rpmfi_internal.h" /* only internal apis */
25 #include "lib/rpmte_internal.h" /* only internal apis */
26 #include "lib/rpmts_internal.h"
27 #include "rpmio/rpmhook.h"
28 #include "lib/rpmtriggers.h"
29
30 #include "lib/rpmplugins.h"
31
32 /* XXX FIXME: merge with existing (broken?) tests in system.h */
33 /* portability fiddles */
34 #if STATFS_IN_SYS_STATVFS
35 #include <sys/statvfs.h>
36
37 #else
38 # if STATFS_IN_SYS_VFS
39 #  include <sys/vfs.h>
40 # else
41 #  if STATFS_IN_SYS_MOUNT
42 #   include <sys/mount.h>
43 #  else
44 #   if STATFS_IN_SYS_STATFS
45 #    include <sys/statfs.h>
46 #   endif
47 #  endif
48 # endif
49 #endif
50
51 #include "debug.h"
52
53 struct diskspaceInfo_s {
54     char * mntPoint;    /*!< File system mount point */
55     dev_t dev;          /*!< File system device number. */
56     int64_t bneeded;    /*!< No. of blocks needed. */
57     int64_t ineeded;    /*!< No. of inodes needed. */
58     int64_t bsize;      /*!< File system block size. */
59     int64_t bavail;     /*!< No. of blocks available. */
60     int64_t iavail;     /*!< No. of inodes available. */
61     int64_t obneeded;   /*!< Bookkeeping to avoid duplicate reports */
62     int64_t oineeded;   /*!< Bookkeeping to avoid duplicate reports */
63     int64_t bdelta;     /*!< Delta for temporary space need on updates */
64     int64_t idelta;     /*!< Delta for temporary inode need on updates */
65 };
66
67 /* Adjust for root only reserved space. On linux e2fs, this is 5%. */
68 #define adj_fs_blocks(_nb)      (((_nb) * 21) / 20)
69 #define BLOCK_ROUND(size, block) (((size) + (block) - 1) / (block))
70
71 static char *getMntPoint(const char *dirName, dev_t dev)
72 {
73     char mntPoint[PATH_MAX];
74     char *resolved_path = realpath(dirName, mntPoint);
75     char *end = NULL;
76     struct stat sb;
77     char *res = NULL;
78
79     if (!resolved_path) {
80         strncpy(mntPoint, dirName, PATH_MAX);
81         mntPoint[PATH_MAX-1] = '\0';
82     }
83
84     while (end != mntPoint) {
85         end = strrchr(mntPoint, '/');
86         if (end == mntPoint) { /* reached "/" */
87             stat("/", &sb);
88             if (dev != sb.st_dev) {
89                 res = xstrdup(mntPoint);
90             } else {
91                 res = xstrdup("/");
92             }
93             break;
94         } else if (end) {
95             *end = '\0';
96         } else { /* dirName doesn't start with / - should not happen */
97             res = xstrdup(dirName);
98             break;
99         }
100         stat(mntPoint, &sb);
101         if (dev != sb.st_dev) {
102             *end = '/';
103             res = xstrdup(mntPoint);
104             break;
105         }
106     }
107     return res;
108 }
109
110 static int rpmtsInitDSI(const rpmts ts)
111 {
112     if (rpmtsFilterFlags(ts) & RPMPROB_FILTER_DISKSPACE)
113         return 0;
114     ts->dsi = _free(ts->dsi);
115     ts->dsi = xcalloc(1, sizeof(*ts->dsi));
116     return 0;
117 }
118
119 static rpmDiskSpaceInfo rpmtsCreateDSI(const rpmts ts, dev_t dev,
120                                        const char * dirName, int count)
121 {
122     rpmDiskSpaceInfo dsi;
123     struct stat sb;
124     int rc;
125
126 #if STATFS_IN_SYS_STATVFS
127     struct statvfs sfb;
128     memset(&sfb, 0, sizeof(sfb));
129     rc = statvfs(dirName, &sfb);
130 #else
131     struct statfs sfb;
132     memset(&sfb, 0, sizeof(sfb));
133 #  if STAT_STATFS4
134 /* This platform has the 4-argument version of the statfs call.  The last two
135  * should be the size of struct statfs and 0, respectively.  The 0 is the
136  * filesystem type, and is always 0 when statfs is called on a mounted
137  * filesystem, as we're doing.
138  */
139     rc = statfs(dirName, &sfb, sizeof(sfb), 0);
140 #  else
141     rc = statfs(dirName, &sfb);
142 #  endif
143 #endif
144     if (rc)
145         return NULL;
146
147     rc = stat(dirName, &sb);
148     if (rc)
149         return NULL;
150     if (sb.st_dev != dev)
151         return NULL;
152
153     ts->dsi = xrealloc(ts->dsi, (count + 2) * sizeof(*ts->dsi));
154     dsi = ts->dsi + count;
155     memset(dsi, 0, 2 * sizeof(*dsi));
156
157     dsi->dev = sb.st_dev;
158     dsi->bsize = sfb.f_bsize;
159     if (!dsi->bsize)
160         dsi->bsize = 512;       /* we need a bsize */
161     dsi->bneeded = 0;
162     dsi->ineeded = 0;
163 #ifdef STATFS_HAS_F_BAVAIL
164     dsi->bavail = (sfb.f_flag & ST_RDONLY) ? 0 : sfb.f_bavail;
165 #else
166 /* FIXME: the statfs struct doesn't have a member to tell how many blocks are
167  * available for non-superusers.  f_blocks - f_bfree is probably too big, but
168  * it's about all we can do.
169  */
170     dsi->bavail = sfb.f_blocks - sfb.f_bfree;
171 #endif
172     /* XXX Avoid FAT and other file systems that have not inodes. */
173     /* XXX assigning negative value to unsigned type */
174     dsi->iavail = !(sfb.f_ffree == 0 && sfb.f_files == 0)
175         ? sfb.f_ffree : -1;
176
177     /* Find mount point belonging to this device number */
178     dsi->mntPoint = getMntPoint(dirName, dsi->dev);
179
180     /* normalize block size to 4096 bytes if it is too big. */
181     if (dsi->bsize > 4096) {
182         uint64_t old_size = dsi->bavail * dsi->bsize;
183         rpmlog(RPMLOG_DEBUG,
184                 "dubious blocksize % " PRId64 " on %s, normalizing to 4096\n",
185                 dsi->bsize, dsi->mntPoint);
186         dsi->bsize = 4096; /* Assume 4k block size */
187         dsi->bavail = old_size / dsi->bsize;
188     }
189
190     rpmlog(RPMLOG_DEBUG,
191            "0x%08x %8" PRId64 " %12" PRId64 " %12" PRId64" %s\n",
192            (unsigned) dsi->dev, dsi->bsize,
193            dsi->bavail, dsi->iavail,
194            dsi->mntPoint);
195     return dsi;
196 }
197
198 static rpmDiskSpaceInfo rpmtsGetDSI(const rpmts ts, dev_t dev,
199                                     const char *dirName) {
200     rpmDiskSpaceInfo dsi;
201     dsi = ts->dsi;
202     if (dsi) {
203         while (dsi->bsize && dsi->dev != dev)
204             dsi++;
205         if (dsi->bsize == 0) {
206             /* create new entry */
207             dsi = rpmtsCreateDSI(ts, dev, dirName, dsi - ts->dsi);
208         }
209     }
210     return dsi;
211 }
212
213 static void rpmtsUpdateDSI(const rpmts ts, dev_t dev, const char *dirName,
214                 rpm_loff_t fileSize, rpm_loff_t prevSize, rpm_loff_t fixupSize,
215                 rpmFileAction action)
216 {
217     int64_t bneeded;
218     rpmDiskSpaceInfo dsi = rpmtsGetDSI(ts, dev, dirName);
219     if (dsi == NULL)
220         return;
221
222     bneeded = BLOCK_ROUND(fileSize, dsi->bsize);
223
224     switch (action) {
225     case FA_BACKUP:
226     case FA_SAVE:
227     case FA_ALTNAME:
228         dsi->ineeded++;
229         dsi->bneeded += bneeded;
230         break;
231
232     case FA_CREATE:
233         dsi->bneeded += bneeded;
234         dsi->ineeded++;
235         if (prevSize) {
236             dsi->bdelta += BLOCK_ROUND(prevSize, dsi->bsize);
237             dsi->idelta++;
238         }
239         if (fixupSize) {
240             dsi->bdelta += BLOCK_ROUND(fixupSize, dsi->bsize);
241             dsi->idelta++;
242         }
243
244         break;
245
246     case FA_ERASE:
247         dsi->ineeded--;
248         dsi->bneeded -= bneeded;
249         break;
250
251     default:
252         break;
253     }
254
255     /* adjust bookkeeping when requirements shrink */
256     if (dsi->bneeded < dsi->obneeded) dsi->obneeded = dsi->bneeded;
257     if (dsi->ineeded < dsi->oineeded) dsi->oineeded = dsi->ineeded;
258 }
259
260 static void rpmtsCheckDSIProblems(const rpmts ts, const rpmte te)
261 {
262     rpmDiskSpaceInfo dsi = ts->dsi;
263
264     if (dsi == NULL || !dsi->bsize)
265         return;
266
267     for (; dsi->bsize; dsi++) {
268
269         if (dsi->bavail >= 0 && adj_fs_blocks(dsi->bneeded) > dsi->bavail) {
270             if (dsi->bneeded > dsi->obneeded) {
271                 rpmteAddProblem(te, RPMPROB_DISKSPACE, NULL, dsi->mntPoint,
272                    (adj_fs_blocks(dsi->bneeded) - dsi->bavail) * dsi->bsize);
273                 dsi->obneeded = dsi->bneeded;
274             }
275         }
276
277         if (dsi->iavail >= 0 && adj_fs_blocks(dsi->ineeded) > dsi->iavail) {
278             if (dsi->ineeded > dsi->oineeded) {
279                 rpmteAddProblem(te, RPMPROB_DISKNODES, NULL, dsi->mntPoint,
280                         (adj_fs_blocks(dsi->ineeded) - dsi->iavail));
281                 dsi->oineeded = dsi->ineeded;
282             }
283         }
284
285         /* Adjust for temporary -> final disk consumption */
286         dsi->bneeded -= dsi->bdelta;
287         dsi->bdelta = 0;
288         dsi->ineeded -= dsi->idelta;
289         dsi->idelta = 0;
290     }
291 }
292
293 static void rpmtsFreeDSI(rpmts ts)
294 {
295     rpmDiskSpaceInfo dsi;
296     if (ts == NULL)
297         return;
298     dsi = ts->dsi;
299     while (dsi && dsi->bsize != 0) {
300         dsi->mntPoint = _free(dsi->mntPoint);
301         dsi++;
302     }
303
304     ts->dsi = _free(ts->dsi);
305 }
306
307
308 /* Calculate total number of files involved in transaction */
309 static uint64_t countFiles(rpmts ts)
310 {
311     uint64_t fc = 0;
312     rpmtsi pi = rpmtsiInit(ts);
313     rpmte p;
314     rpmfiles files;
315     while ((p = rpmtsiNext(pi, 0)) != NULL) {
316         files = rpmteFiles(p);
317         fc += rpmfilesFC(files);
318         rpmfilesFree(files);
319     }
320     rpmtsiFree(pi);
321     return fc;
322 }
323
324 static int handleRemovalConflict(rpmfiles fi, int fx, rpmfiles ofi, int ofx)
325 {
326     int rConflicts = 0; /* Removed files don't conflict, normally */
327     rpmFileTypes ft = rpmfiWhatis(rpmfilesFMode(fi, fx));
328     rpmFileTypes oft = rpmfiWhatis(rpmfilesFMode(ofi, ofx));
329     struct stat sb;
330     char *fn = NULL;
331
332     if (oft == XDIR) {
333         /* We can't handle directory changing to anything else */
334         if (ft != XDIR)
335             rConflicts = 1;
336     } else if (oft == LINK) {
337         /* We can't correctly handle directory symlink changing to directory */
338         if (ft == XDIR) {
339             fn = rpmfilesFN(fi, fx);
340             if (stat(fn, &sb) == 0 && S_ISDIR(sb.st_mode))
341                 rConflicts = 1;
342         }
343     }
344
345     /*
346      * ...but if the conflicting item is either not on disk, or has
347      * already been changed to the new type, we should be ok afterall.
348      */
349     if (rConflicts) {
350         if (fn == NULL)
351             fn = rpmfilesFN(fi, fx);
352         if (lstat(fn, &sb) || rpmfiWhatis(sb.st_mode) == ft)
353             rConflicts = 0;
354     }
355
356     free(fn);
357     return rConflicts;
358 }
359
360 /*
361  * Elf files can be "colored", and if enabled in the transaction, the
362  * color can be used to resolve conflicts between elf-64bit and elf-32bit
363  * files to the hosts preferred type, by default 64bit. The non-preferred
364  * type is overwritten or never installed at all and thus the conflict
365  * magically disappears. This is infamously nasty "rpm magic" and entirely
366  * unnecessary with careful packaging.
367  */
368 static int handleColorConflict(rpmts ts,
369                                rpmfs fs, rpmfiles fi, int fx,
370                                rpmfs ofs, rpmfiles ofi, int ofx)
371 {
372     int rConflicts = 1;
373     rpm_color_t tscolor = rpmtsColor(ts);
374
375     if (tscolor != 0) {
376         rpm_color_t fcolor = rpmfilesFColor(fi, fx) & tscolor;
377         rpm_color_t ofcolor = rpmfilesFColor(ofi, ofx) & tscolor;
378
379         if (fcolor != 0 && ofcolor != 0 && fcolor != ofcolor) {
380             rpm_color_t prefcolor = rpmtsPrefColor(ts);
381
382             if (fcolor & prefcolor) {
383                 if (ofs && !XFA_SKIPPING(rpmfsGetAction(fs, fx)))
384                     rpmfsSetAction(ofs, ofx, FA_SKIPCOLOR);
385                 rpmfsSetAction(fs, fx, FA_CREATE);
386                 rConflicts = 0;
387             } else if (ofcolor & prefcolor) {
388                 if (ofs && XFA_SKIPPING(rpmfsGetAction(fs, fx)))
389                     rpmfsSetAction(ofs, ofx, FA_CREATE);
390                 rpmfsSetAction(fs, fx, FA_SKIPCOLOR);
391                 rConflicts = 0;
392             }
393         }
394     }
395
396     return rConflicts;
397 }
398
399 /**
400  * handleInstInstalledFiles.
401  * @param ts            transaction set
402  * @param p             current transaction element
403  * @param fi            file info set
404  * @param fx            file index
405  * @param otherHeader   header containing the matching file
406  * @param otherFi       matching file info set
407  * @param ofx           matching file index
408  * @param beingRemoved  file being removed (installed otherwise)
409  */
410 /* XXX only ts->{probs,rpmdb} modified */
411 static void handleInstInstalledFile(const rpmts ts, rpmte p, rpmfiles fi, int fx,
412                                    Header otherHeader, rpmfiles otherFi, int ofx,
413                                    int beingRemoved)
414 {
415     rpmfs fs = rpmteGetFileStates(p);
416     int isCfgFile = ((rpmfilesFFlags(otherFi, ofx) | rpmfilesFFlags(fi, fx)) & RPMFILE_CONFIG);
417
418     if (XFA_SKIPPING(rpmfsGetAction(fs, fx)))
419         return;
420
421     if (rpmfilesCompare(otherFi, ofx, fi, fx)) {
422         int rConflicts = 1;
423         char rState = RPMFILE_STATE_REPLACED;
424
425         /*
426          * There are some removal conflicts we can't handle. However
427          * if the package has a %pretrans scriptlet, it might be able to
428          * fix the conflict. Let it through on test-transaction to allow
429          * eg yum to get past it, if the conflict is present on the actual
430          * transaction we'll abort. Behaving differently on test is nasty,
431          * but its still better than barfing in middle of large transaction.
432          */
433         if (beingRemoved) {
434             rConflicts = handleRemovalConflict(fi, fx, otherFi, ofx);
435             if (rConflicts && rpmteHaveTransScript(p, RPMTAG_PRETRANS)) {
436                 if (rpmtsFlags(ts) & RPMTRANS_FLAG_TEST)
437                     rConflicts = 0;
438             }
439         }
440
441         if (rConflicts) {
442             /* If enabled, resolve colored conflicts to preferred type */
443             rConflicts = handleColorConflict(ts, fs, fi, fx,
444                                              NULL, otherFi, ofx);
445             /* If resolved, we need to adjust in-rpmdb state too */
446             if (rConflicts == 0 && rpmfsGetAction(fs, fx) == FA_CREATE)
447                 rState = RPMFILE_STATE_WRONGCOLOR;
448         }
449
450
451         if (rConflicts) {
452                 char *path = rpmfilesFN(fi, fx);
453                 /* Call file conflict hook for all plugins */
454                 rpmpluginsCallFileConflict(ts->plugins, ts, path, otherHeader, otherFi, rConflicts);
455         }
456
457         /* Somebody used The Force, lets shut up... */
458         if (rpmtsFilterFlags(ts) & RPMPROB_FILTER_REPLACEOLDFILES) {
459             rConflicts = 0;
460         }
461
462
463         if (rConflicts) {
464             char *altNEVR = headerGetAsString(otherHeader, RPMTAG_NEVRA);
465             char *fn = rpmfilesFN(fi, fx);
466             rpmteAddProblem(p, RPMPROB_FILE_CONFLICT, altNEVR, fn,
467                             headerGetInstance(otherHeader));
468             free(fn);
469             free(altNEVR);
470         }
471
472         /* Save file identifier to mark as state REPLACED. */
473         if ( !(isCfgFile || XFA_SKIPPING(rpmfsGetAction(fs, fx))) ) {
474             if (!beingRemoved)
475                 rpmfsAddReplaced(rpmteGetFileStates(p), fx, rState,
476                                  headerGetInstance(otherHeader), ofx);
477         }
478     }
479
480     /* Determine config file disposition, skipping missing files (if any). */
481     if (isCfgFile) {
482         int skipMissing = ((rpmtsFlags(ts) & RPMTRANS_FLAG_ALLFILES) ? 0 : 1);
483         rpmFileAction action;
484         action = rpmfilesDecideFate(otherFi, ofx, fi, fx, skipMissing);
485         rpmfsSetAction(fs, fx, action);
486     }
487
488     /* Skip already existing files - if 'minimize_writes' is set. */
489     if ((!isCfgFile) && (rpmfsGetAction(fs, fx) == FA_UNKNOWN)  && ts->min_writes) {
490         if (rpmfileContentsEqual(otherFi, ofx, fi, fx)) {
491            rpmfsSetAction(fs, fx, FA_TOUCH);
492         }
493     }
494
495     rpmfilesSetFReplacedSize(fi, fx, rpmfilesFSize(otherFi, ofx));
496 }
497
498 /**
499  * Update disk space needs on each partition for this package's files.
500  */
501 /* XXX only ts->{probs,di} modified */
502 static void handleOverlappedFiles(rpmts ts, fingerPrintCache fpc, rpmte p, rpmfiles fi)
503 {
504     rpm_loff_t fixupSize = 0;
505     int i, j;
506     rpmfs fs = rpmteGetFileStates(p);
507     rpmfs otherFs;
508     rpm_count_t fc = rpmfilesFC(fi);
509     int reportConflicts = !(rpmtsFilterFlags(ts) & RPMPROB_FILTER_REPLACENEWFILES);
510     fingerPrint * fpList = rpmfilesFps(fi);
511
512     for (i = 0; i < fc; i++) {
513         struct fingerPrint_s * fiFps;
514         int otherPkgNum, otherFileNum;
515         rpmfiles otherFi;
516         rpmte otherTe;
517         rpmfileAttrs FFlags;
518         struct rpmffi_s * recs;
519         int numRecs;
520
521         if (XFA_SKIPPING(rpmfsGetAction(fs, i)))
522             continue;
523
524         FFlags = rpmfilesFFlags(fi, i);
525
526         fixupSize = 0;
527
528         /*
529          * Retrieve all records that apply to this file. Note that the
530          * file info records were built in the same order as the packages
531          * will be installed and removed so the records for an overlapped
532          * files will be sorted in exactly the same order.
533          */
534         fiFps = fpCacheGetByFp(fpc, fpList, i, &recs, &numRecs);
535
536         /*
537          * If this package is being added, look only at other packages
538          * being added -- removed packages dance to a different tune.
539          *
540          * If both this and the other package are being added, overlapped
541          * files must be identical (or marked as a conflict). The
542          * disposition of already installed config files leads to
543          * a small amount of extra complexity.
544          *
545          * If this package is being removed, then there are two cases that
546          * need to be worried about:
547          * If the other package is being added, then skip any overlapped files
548          * so that this package removal doesn't nuke the overlapped files
549          * that were just installed.
550          * If both this and the other package are being removed, then each
551          * file removal from preceding packages needs to be skipped so that
552          * the file removal occurs only on the last occurrence of an overlapped
553          * file in the transaction set.
554          *
555          */
556
557         /*
558          * Locate this overlapped file in the set of added/removed packages,
559          * including the package owning it: a package can have self-conflicting
560          * files when directory symlinks are present. Don't compare a file
561          * with itself though...
562          */
563         for (j = 0; j < numRecs && !(recs[j].p == p && recs[j].fileno == i); j++)
564             {};
565
566         /* Find what the previous disposition of this file was. */
567         otherFileNum = -1;                      /* keep gcc quiet */
568         otherFi = NULL;
569         otherTe = NULL;
570         otherFs = NULL;
571
572         for (otherPkgNum = j - 1; otherPkgNum >= 0; otherPkgNum--) {
573             otherTe = recs[otherPkgNum].p;
574             otherFileNum = recs[otherPkgNum].fileno;
575             otherFs = rpmteGetFileStates(otherTe);
576
577             /* Added packages need only look at other added packages. */
578             if (rpmteType(p) == TR_ADDED && rpmteType(otherTe) != TR_ADDED)
579                 continue;
580
581             /* XXX Happens iff fingerprint for incomplete package install. */
582             if (rpmfsGetAction(otherFs, otherFileNum) != FA_UNKNOWN) {
583                 otherFi = rpmteFiles(otherTe);
584                 break;
585             }
586         }
587
588         switch (rpmteType(p)) {
589         case TR_ADDED:
590             if (otherPkgNum < 0) {
591                 /* XXX is this test still necessary? */
592                 rpmFileAction action;
593                 if (rpmfsGetAction(fs, i) != FA_UNKNOWN)
594                     break;
595                 if (rpmfilesConfigConflict(fi, i)) {
596                     /* Here is a non-overlapped pre-existing config file. */
597                     action = (FFlags & RPMFILE_NOREPLACE) ?
598                               FA_ALTNAME : FA_BACKUP;
599                 } else {
600                     action = FA_CREATE;
601                 }
602                 rpmfsSetAction(fs, i, action);
603                 break;
604             }
605
606 assert(otherFi != NULL);
607             /* Mark added overlapped non-identical files as a conflict. */
608             if (rpmfilesCompare(otherFi, otherFileNum, fi, i)) {
609                 int rConflicts;
610
611                 /* If enabled, resolve colored conflicts to preferred type */
612                 rConflicts = handleColorConflict(ts, fs, fi, i,
613                                                 otherFs, otherFi, otherFileNum);
614
615                 if (rConflicts && reportConflicts) {
616                     char *fn = rpmfilesFN(fi, i);
617                     rpmteAddProblem(p, RPMPROB_NEW_FILE_CONFLICT,
618                                     rpmteNEVRA(otherTe), fn, 0);
619                     free(fn);
620                 }
621             } else {
622                 /* Skip create on all but the first instance of a shared file */
623                 rpmFileAction oaction = rpmfsGetAction(otherFs, otherFileNum);
624                 if (oaction != FA_UNKNOWN && !XFA_SKIPPING(oaction)) {
625                     rpmfileAttrs oflags;
626                     /* ...but ghosts aren't really created so... */
627                     oflags = rpmfilesFFlags(otherFi, otherFileNum);
628                     if (!(oflags & RPMFILE_GHOST)) {
629                         rpmfsSetAction(fs, i, FA_SKIP);
630                     }
631                 /* if the other file is color skipped then skip this file too */
632                 } else if (oaction == FA_SKIPCOLOR) {
633                     rpmfsSetAction(fs, i, FA_SKIPCOLOR);
634                 }
635             }
636
637             /* Skipped files dont need fixup size or backups, %config or not */
638             if (XFA_SKIPPING(rpmfsGetAction(fs, i)))
639                 break;
640
641             /* Try to get the disk accounting correct even if a conflict. */
642             fixupSize = rpmfilesFSize(otherFi, otherFileNum);
643
644             if (rpmfilesConfigConflict(fi, i)) {
645                 /* Here is an overlapped  pre-existing config file. */
646                 rpmFileAction action;
647                 action = (FFlags & RPMFILE_NOREPLACE) ? FA_ALTNAME : FA_SKIP;
648                 rpmfsSetAction(fs, i, action);
649             } else {
650                 /* If not decided yet, create it */
651                 if (rpmfsGetAction(fs, i) == FA_UNKNOWN)
652                     rpmfsSetAction(fs, i, FA_CREATE);
653             }
654             break;
655
656         case TR_REMOVED:
657             if (otherPkgNum >= 0) {
658                 assert(otherFi != NULL);
659                 /* Here is an overlapped added file we don't want to nuke. */
660                 if (rpmfsGetAction(otherFs, otherFileNum) != FA_ERASE) {
661                     /* On updates, don't remove files. */
662                     rpmfsSetAction(fs, i, FA_SKIP);
663                     break;
664                 }
665                 /* Here is an overlapped removed file: skip in previous. */
666                 rpmfsSetAction(otherFs, otherFileNum, FA_SKIP);
667             }
668             if (XFA_SKIPPING(rpmfsGetAction(fs, i)))
669                 break;
670             if (rpmfilesFState(fi, i) != RPMFILE_STATE_NORMAL) {
671                 rpmfsSetAction(fs, i, FA_SKIP);
672                 break;
673             }
674                 
675             /* Pre-existing modified config files need to be saved. */
676             if (rpmfilesConfigConflict(fi, i)) {
677                 rpmfsSetAction(fs, i, FA_SAVE);
678                 break;
679             }
680         
681             /* Otherwise, we can just erase. */
682             rpmfsSetAction(fs, i, FA_ERASE);
683             break;
684         }
685         rpmfilesFree(otherFi);
686
687         /* Update disk space info for a file. */
688         rpmtsUpdateDSI(ts, fpEntryDev(fpc, fiFps), fpEntryDir(fpc, fiFps),
689                        rpmfilesFSize(fi, i), rpmfilesFReplacedSize(fi, i),
690                        fixupSize, rpmfsGetAction(fs, i));
691
692     }
693 }
694
695 /**
696  * Ensure that current package is newer than installed package.
697  * @param tspool        transaction string pool
698  * @param p             current transaction element
699  * @param h             installed header
700  */
701 static void ensureOlder(rpmstrPool tspool, const rpmte p, const Header h)
702 {
703     rpmsenseFlags reqFlags = (RPMSENSE_LESS | RPMSENSE_EQUAL);
704     rpmds req;
705
706     req = rpmdsSinglePool(tspool, RPMTAG_REQUIRENAME,
707                           rpmteN(p), rpmteEVR(p), reqFlags);
708     if (rpmdsMatches(tspool, h, -1, req, 1, _rpmds_nopromote) == 0) {
709         char * altNEVR = headerGetAsString(h, RPMTAG_NEVRA);
710         rpmteAddProblem(p, RPMPROB_OLDPACKAGE, altNEVR, NULL,
711                         headerGetInstance(h));
712         free(altNEVR);
713     }
714     rpmdsFree(req);
715 }
716
717 /**
718  * Check if the curent file in the file iterator is in the
719  * netshardpath and though should be excluded.
720  * @param ts            transaction set
721  * @param fi            file info set
722  * @returns 1 if path is net shared path, otherwise 0
723  */
724 static int matchNetsharedpath(const rpmts ts, rpmfi fi)
725 {
726     char ** nsp;
727     const char * dn, * bn;
728     size_t dnlen, bnlen;
729     char * s;
730     bn = rpmfiBN(fi);
731     bnlen = strlen(bn);
732     dn = rpmfiDN(fi);
733     dnlen = strlen(dn);
734     for (nsp = ts->netsharedPaths; nsp && *nsp; nsp++) {
735         size_t len;
736
737         len = strlen(*nsp);
738         if (dnlen >= len) {
739             if (!rstreqn(dn, *nsp, len))
740                 continue;
741             /* Only directories or complete file paths can be net shared */
742             if (!(dn[len] == '/' || dn[len] == '\0'))
743                 continue;
744         } else {
745             if (len < (dnlen + bnlen))
746                 continue;
747             if (!rstreqn(dn, *nsp, dnlen))
748                 continue;
749             /* Insure that only the netsharedpath basename is compared. */
750             if ((s = strchr((*nsp) + dnlen, '/')) != NULL && s[1] != '\0')
751                 continue;
752             if (!rstreqn(bn, (*nsp) + dnlen, bnlen))
753                 continue;
754             len = dnlen + bnlen;
755             /* Only directories or complete file paths can be net shared */
756             if (!((*nsp)[len] == '/' || (*nsp)[len] == '\0'))
757                 continue;
758         }
759
760         break;
761     }
762     return (nsp != NULL && *nsp != NULL);
763 }
764
765 static void skipEraseFiles(const rpmts ts, rpmfiles files, rpmfs fs)
766 {
767     int i;
768     /*
769      * Skip net shared paths.
770      * Net shared paths are not relative to the current root (though
771      * they do need to take package relocations into account).
772      */
773     if (ts->netsharedPaths) {
774         rpmfi fi = rpmfilesIter(files, RPMFI_ITER_FWD);
775         while ((i = rpmfiNext(fi)) >= 0)
776         {
777             if (matchNetsharedpath(ts, fi))
778                 rpmfsSetAction(fs, i, FA_SKIPNETSHARED);
779         }
780         rpmfiFree(fi);
781     }
782 }
783
784
785 /**
786  * Skip any files that do not match install policies.
787  * @param ts            transaction set
788  * @param files         file info set
789  * @param fs            file states
790  */
791 static void skipInstallFiles(const rpmts ts, rpmfiles files, rpmfs fs)
792 {
793     rpm_color_t tscolor = rpmtsColor(ts);
794     rpm_color_t FColor;
795     int noConfigs = (rpmtsFlags(ts) & RPMTRANS_FLAG_NOCONFIGS);
796     int noDocs = (rpmtsFlags(ts) & RPMTRANS_FLAG_NODOCS);
797     int * drc;
798     char * dff;
799     int dc;
800     int i, j, ix;
801     rpmfi fi = rpmfilesIter(files, RPMFI_ITER_FWD);
802
803     if (!noDocs)
804         noDocs = rpmExpandNumeric("%{_excludedocs}");
805
806     /* Compute directory refcount, skip directory if now empty. */
807     dc = rpmfiDC(fi);
808     drc = xcalloc(dc, sizeof(*drc));
809     dff = xcalloc(dc, sizeof(*dff));
810
811     fi = rpmfiInit(fi, 0);
812     while ((i = rpmfiNext(fi)) >= 0) {
813         const char *flangs;
814
815         ix = rpmfiDX(fi);
816         drc[ix]++;
817
818         /* Don't bother with skipped files */
819         /* XXX FIXME: --excludepath on %license should not be permitted */
820         if (XFA_SKIPPING(rpmfsGetAction(fs, i))) {
821             drc[ix]--; dff[ix] = 1;
822             continue;
823         }
824
825         /* Ignore colored files not in our rainbow. */
826         FColor = rpmfiFColor(fi);
827         if (tscolor && FColor && !(tscolor & FColor)) {
828             drc[ix]--;  dff[ix] = 1;
829             rpmfsSetAction(fs, i, FA_SKIPCOLOR);
830             continue;
831         }
832
833         /*
834          * Skip net shared paths.
835          * Net shared paths are not relative to the current root (though
836          * they do need to take package relocations into account).
837          */
838         if (ts->netsharedPaths) {
839             if (matchNetsharedpath(ts, fi)) {
840                 drc[ix]--;      dff[ix] = 1;
841                 rpmfsSetAction(fs, i, FA_SKIPNETSHARED);
842                 continue;
843             }
844         }
845
846         /*
847          * In general, excluding license files is not permitted. In case
848          * of SKIPNETSHARED and SKIPCOLOR the file is expected to be
849          * there via other means however so that is ok.
850          */
851         if (rpmfiFFlags(fi) & RPMFILE_LICENSE)
852             continue;
853
854         /*
855          * Skip i18n language specific files.
856          */
857         flangs = (ts->installLangs != NULL) ? rpmfiFLangs(fi) : NULL;
858         if (flangs != NULL && *flangs != '\0') {
859             const char *l, *le;
860             char **lang;
861             for (lang = ts->installLangs; *lang != NULL; lang++) {
862                 for (l = flangs; *l != '\0'; l = le) {
863                     for (le = l; *le != '\0' && *le != '|'; le++)
864                         {};
865                     if ((le-l) > 0 && rstreqn(*lang, l, (le-l)))
866                         break;
867                     if (*le == '|') le++;       /* skip over | */
868                 }
869                 if (*l != '\0')
870                     break;
871             }
872             if (*lang == NULL) {
873                 drc[ix]--;      dff[ix] = 1;
874                 rpmfsSetAction(fs, i, FA_SKIPNSTATE);
875                 continue;
876             }
877         }
878
879         /*
880          * Skip config files if requested.
881          */
882         if (noConfigs && (rpmfiFFlags(fi) & RPMFILE_CONFIG)) {
883             drc[ix]--;  dff[ix] = 1;
884             rpmfsSetAction(fs, i, FA_SKIPNSTATE);
885             continue;
886         }
887
888         /*
889          * Skip documentation if requested.
890          */
891         if (noDocs && (rpmfiFFlags(fi) & RPMFILE_DOC)) {
892             drc[ix]--;  dff[ix] = 1;
893             rpmfsSetAction(fs, i, FA_SKIPNSTATE);
894             continue;
895         }
896     }
897
898     /* Skip (now empty) directories that had skipped files. */
899     /* Iterate over dirs in reversed order to solve subdirs at first */
900     for (j = dc - 1; j >= 0; j--) {
901         const char * dn, * bn;
902         size_t dnlen, bnlen;
903
904         if (drc[j]) continue;   /* dir still has files. */
905         if (!dff[j]) continue;  /* dir was not emptied here. */
906         
907         /* Find parent directory and basename. */
908         dn = rpmfilesDN(files, j); dnlen = strlen(dn) - 1;
909         bn = dn + dnlen;        bnlen = 0;
910         while (bn > dn && bn[-1] != '/') {
911                 bnlen++;
912                 dnlen--;
913                 bn--;
914         }
915
916         /* If explicitly included in the package, skip the directory. */
917         fi = rpmfiInit(fi, 0);
918         while ((i = rpmfiNext(fi)) >= 0) {
919             const char * fdn, * fbn;
920             rpm_mode_t fFMode;
921
922             if (XFA_SKIPPING(rpmfsGetAction(fs, i)))
923                 continue;
924
925             fFMode = rpmfiFMode(fi);
926
927             if (rpmfiWhatis(fFMode) != XDIR)
928                 continue;
929             fdn = rpmfiDN(fi);
930             if (strlen(fdn) != dnlen)
931                 continue;
932             if (!rstreqn(fdn, dn, dnlen))
933                 continue;
934             fbn = rpmfiBN(fi);
935             if (strlen(fbn) != bnlen)
936                 continue;
937             if (!rstreqn(fbn, bn, bnlen))
938                 continue;
939             rpmlog(RPMLOG_DEBUG, "excluding directory %s\n", dn);
940             rpmfsSetAction(fs, i, FA_SKIPNSTATE);
941             ix = rpmfiDX(fi);
942             /* Decrease count of files for parent directory */
943             drc[ix]--;
944             /* Mark directory because something was removed from them */
945             dff[ix] = 1;
946             break;
947         }
948     }
949
950     free(drc);
951     free(dff);
952     rpmfiFree(fi);
953 }
954
955 #undef HASHTYPE
956 #undef HTKEYTYPE
957 #undef HTDATATYPE
958
959 #define HASHTYPE rpmStringSet
960 #define HTKEYTYPE rpmsid
961 #include "lib/rpmhash.H"
962 #include "lib/rpmhash.C"
963
964 static unsigned int sidHash(rpmsid sid)
965 {
966     return sid;
967 }
968
969 static int sidCmp(rpmsid a, rpmsid b)
970 {
971     return (a != b);
972 }
973
974 /* Get a rpmdbMatchIterator containing all files in
975  * the rpmdb that share the basename with one from
976  * the transaction.
977  * @param ts            transaction set
978  * @return              rpmdbMatchIterator sorted
979                         by (package, fileNum)
980  */
981 static
982 rpmdbMatchIterator rpmFindBaseNamesInDB(rpmts ts, uint64_t fileCount)
983 {
984     tsMembers tsmem = rpmtsMembers(ts);
985     rpmstrPool tspool = rpmtsPool(ts);
986     rpmtsi pi;  rpmte p;
987     rpmfiles files;
988     rpmfi fi;
989     rpmdbMatchIterator mi;
990     int oc = 0;
991     const char * baseName;
992     rpmsid baseNameId;
993
994     rpmStringSet baseNames = rpmStringSetCreate(fileCount, 
995                                         sidHash, sidCmp, NULL);
996
997     mi = rpmdbNewIterator(rpmtsGetRdb(ts), RPMDBI_BASENAMES);
998
999     pi = rpmtsiInit(ts);
1000     while ((p = rpmtsiNext(pi, 0)) != NULL) {
1001         (void) rpmsqPoll();
1002
1003         rpmtsNotify(ts, NULL, RPMCALLBACK_TRANS_PROGRESS, oc++, tsmem->orderCount);
1004
1005         /* Gather all installed headers with matching basename's. */
1006         files = rpmteFiles(p);
1007         fi = rpmfilesIter(files, RPMFI_ITER_FWD);
1008         while (rpmfiNext(fi) >= 0) {
1009             size_t keylen;
1010
1011             baseNameId = rpmfiBNId(fi);
1012
1013             if (rpmStringSetHasEntry(baseNames, baseNameId))
1014                 continue;
1015
1016             keylen = rpmstrPoolStrlen(tspool, baseNameId);
1017             baseName = rpmstrPoolStr(tspool, baseNameId);
1018             if (keylen == 0)
1019                 keylen++;       /* XXX "/" fixup. */
1020             rpmdbExtendIterator(mi, baseName, keylen);
1021             rpmStringSetAddEntry(baseNames, baseNameId);
1022         }
1023         rpmfiFree(fi);
1024         rpmfilesFree(files);
1025     }
1026     rpmtsiFree(pi);
1027     rpmStringSetFree(baseNames);
1028
1029     rpmdbSortIterator(mi);
1030     /* iterator is now sorted by (recnum, filenum) */
1031     return mi;
1032 }
1033
1034 /* Check files in the transactions against the rpmdb
1035  * Lookup all files with the same basename in the rpmdb
1036  * and then check for matching finger prints
1037  * @param ts            transaction set
1038  * @param fpc           global finger print cache
1039  */
1040 static
1041 void checkInstalledFiles(rpmts ts, uint64_t fileCount, fingerPrintCache fpc)
1042 {
1043     tsMembers tsmem = rpmtsMembers(ts);
1044     rpmte p;
1045     rpmfiles fi;
1046     rpmfs fs;
1047     int j;
1048     unsigned int fileNum;
1049
1050     rpmdbMatchIterator mi;
1051     Header h, newheader;
1052
1053     rpmlog(RPMLOG_DEBUG, "computing file dispositions\n");
1054
1055     mi = rpmFindBaseNamesInDB(ts, fileCount);
1056
1057     /* For all installed headers with matching basename's ... */
1058     if (mi == NULL)
1059          return;
1060
1061     if (rpmdbGetIteratorCount(mi) == 0) {
1062         mi = rpmdbFreeIterator(mi);
1063         return;
1064     }
1065
1066     /* Loop over all packages from the rpmdb */
1067     h = newheader = rpmdbNextIterator(mi);
1068     while (h != NULL) {
1069         headerGetFlags hgflags = HEADERGET_MINMEM;
1070         struct rpmtd_s bnames, dnames, dindexes, ostates;
1071         fingerPrint *fpp = NULL;
1072         unsigned int installedPkg;
1073         int beingRemoved = 0;
1074         rpmfiles otherFi = NULL;
1075         rpmte *removedPkg = NULL;
1076
1077         /* Is this package being removed? */
1078         installedPkg = rpmdbGetIteratorOffset(mi);
1079         if (packageHashGetEntry(tsmem->removedPackages, installedPkg,
1080                                 &removedPkg, NULL, NULL)) {
1081             beingRemoved = 1;
1082             otherFi = rpmteFiles(removedPkg[0]);
1083         }
1084
1085         h = headerLink(h);
1086         /* For packages being removed we can use its rpmfi to avoid all this */
1087         if (!beingRemoved) {
1088             headerGet(h, RPMTAG_BASENAMES, &bnames, hgflags);
1089             headerGet(h, RPMTAG_DIRNAMES, &dnames, hgflags);
1090             headerGet(h, RPMTAG_DIRINDEXES, &dindexes, hgflags);
1091             headerGet(h, RPMTAG_FILESTATES, &ostates, hgflags);
1092         }
1093
1094         /* loop over all interesting files in that package */
1095         do {
1096             int fpIx;
1097             struct rpmffi_s * recs;
1098             int numRecs;
1099             const char * dirName;
1100             const char * baseName;
1101
1102             /* lookup finger print for this file */
1103             fileNum = rpmdbGetIteratorFileNum(mi);
1104             if (!beingRemoved) {
1105                 rpmtdSetIndex(&bnames, fileNum);
1106                 rpmtdSetIndex(&dindexes, fileNum);
1107                 rpmtdSetIndex(&dnames, *rpmtdGetUint32(&dindexes));
1108                 rpmtdSetIndex(&ostates, fileNum);
1109
1110                 dirName = rpmtdGetString(&dnames);
1111                 baseName = rpmtdGetString(&bnames);
1112
1113                 fpLookup(fpc, dirName, baseName, &fpp);
1114                 fpIx = 0;
1115             } else {
1116                 fpp = rpmfilesFps(otherFi);
1117                 fpIx = fileNum;
1118             }
1119
1120             /* search for files in the transaction with same finger print */
1121             fpCacheGetByFp(fpc, fpp, fpIx, &recs, &numRecs);
1122
1123             for (j = 0; j < numRecs; j++) {
1124                 p = recs[j].p;
1125                 fi = rpmteFiles(p);
1126                 fs = rpmteGetFileStates(p);
1127
1128                 /* Determine the fate of each file. */
1129                 switch (rpmteType(p)) {
1130                 case TR_ADDED:
1131                     if (!otherFi) {
1132                         /* XXX What to do if this fails? */
1133                         otherFi = rpmfilesNew(NULL, h, RPMTAG_BASENAMES, RPMFI_KEEPHEADER);
1134                     }
1135                     handleInstInstalledFile(ts, p, fi, recs[j].fileno,
1136                                             h, otherFi, fileNum, beingRemoved);
1137                     break;
1138                 case TR_REMOVED:
1139                     if (!beingRemoved) {
1140                         if (*rpmtdGetChar(&ostates) == RPMFILE_STATE_NORMAL)
1141                             rpmfsSetAction(fs, recs[j].fileno, FA_SKIP);
1142                     }
1143                     break;
1144                 }
1145                 rpmfilesFree(fi);
1146             }
1147
1148             newheader = rpmdbNextIterator(mi);
1149
1150         } while (newheader==h);
1151
1152         otherFi = rpmfilesFree(otherFi);
1153         if (!beingRemoved) {
1154             rpmtdFreeData(&ostates);
1155             rpmtdFreeData(&bnames);
1156             rpmtdFreeData(&dnames);
1157             rpmtdFreeData(&dindexes);
1158             free(fpp);
1159         }
1160         headerFree(h);
1161         h = newheader;
1162     }
1163
1164     rpmdbFreeIterator(mi);
1165 }
1166
1167 #define badArch(_a) (rpmMachineScore(RPM_MACHTABLE_INSTARCH, (_a)) == 0)
1168 #define badOs(_a) (rpmMachineScore(RPM_MACHTABLE_INSTOS, (_a)) == 0)
1169
1170 /*
1171  * For packages being installed:
1172  * - verify package arch/os.
1173  * - verify package epoch:version-release is newer.
1174  */
1175 static rpmps checkProblems(rpmts ts)
1176 {
1177     rpm_color_t tscolor = rpmtsColor(ts);
1178     rpmprobFilterFlags probFilter = rpmtsFilterFlags(ts);
1179     rpmstrPool tspool = rpmtsPool(ts);
1180     rpmtsi pi = rpmtsiInit(ts);
1181     rpmte p;
1182
1183     /* The ordering doesn't matter here */
1184     /* XXX Only added packages need be checked. */
1185     rpmlog(RPMLOG_DEBUG, "sanity checking %d elements\n", rpmtsNElements(ts));
1186     while ((p = rpmtsiNext(pi, TR_ADDED)) != NULL) {
1187
1188         if (!(probFilter & RPMPROB_FILTER_IGNOREARCH) && badArch(rpmteA(p)))
1189             rpmteAddProblem(p, RPMPROB_BADARCH, rpmteA(p), NULL, 0);
1190
1191         if (!(probFilter & RPMPROB_FILTER_IGNOREOS) && badOs(rpmteO(p)))
1192             rpmteAddProblem(p, RPMPROB_BADOS, rpmteO(p), NULL, 0);
1193
1194         if (!(probFilter & RPMPROB_FILTER_OLDPACKAGE)) {
1195             Header h;
1196             rpmdbMatchIterator mi;
1197             mi = rpmtsInitIterator(ts, RPMDBI_NAME, rpmteN(p), 0);
1198             while ((h = rpmdbNextIterator(mi)) != NULL)
1199                 ensureOlder(tspool, p, h);
1200             rpmdbFreeIterator(mi);
1201         }
1202
1203         if (!(probFilter & RPMPROB_FILTER_REPLACEPKG)) {
1204             Header h;
1205             rpmdbMatchIterator mi;
1206             mi = rpmtsPrunedIterator(ts, RPMDBI_NAME, rpmteN(p), 1);
1207             rpmdbSetIteratorRE(mi, RPMTAG_EPOCH, RPMMIRE_STRCMP, rpmteE(p));
1208             rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_STRCMP, rpmteV(p));
1209             rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_STRCMP, rpmteR(p));
1210             if (tscolor) {
1211                 rpmdbSetIteratorRE(mi, RPMTAG_ARCH, RPMMIRE_STRCMP, rpmteA(p));
1212                 rpmdbSetIteratorRE(mi, RPMTAG_OS, RPMMIRE_STRCMP, rpmteO(p));
1213             }
1214
1215             if ((h = rpmdbNextIterator(mi)) != NULL) {
1216                 rpmteAddProblem(p, RPMPROB_PKG_INSTALLED, NULL, NULL,
1217                                 headerGetInstance(h));
1218             }
1219             rpmdbFreeIterator(mi);
1220         }
1221
1222         if (!(probFilter & RPMPROB_FILTER_FORCERELOCATE))
1223             rpmteAddRelocProblems(p);
1224     }
1225     rpmtsiFree(pi);
1226     return rpmtsProblems(ts);
1227 }
1228
1229 /*
1230  * Run pre/post transaction scripts for transaction set
1231  * param ts     Transaction set
1232  * param goal   PKG_PRETRANS/PKG_POSTTRANS
1233  * return       0 on success
1234  */
1235 static int runTransScripts(rpmts ts, pkgGoal goal) 
1236 {
1237     int rc = 0;
1238     rpmte p;
1239     rpmtsi pi = rpmtsiInit(ts);
1240     rpmElementTypes types = TR_ADDED;
1241     int i = 0;
1242     if (goal == PKG_TRANSFILETRIGGERUN)
1243         types = TR_REMOVED;
1244
1245     while ((p = rpmtsiNext(pi, types)) != NULL) {
1246         rc += rpmteProcess(p, goal, i++);
1247     }
1248     rpmtsiFree(pi);
1249     return rc;
1250 }
1251
1252 static int rpmtsSetup(rpmts ts, rpmprobFilterFlags ignoreSet)
1253 {
1254     rpm_tid_t tid = (rpm_tid_t) time(NULL);
1255     int dbmode = (rpmtsFlags(ts) & RPMTRANS_FLAG_TEST) ?  O_RDONLY : (O_RDWR|O_CREAT);
1256
1257     if (rpmtsFlags(ts) & RPMTRANS_FLAG_NOSCRIPTS)
1258         (void) rpmtsSetFlags(ts, (rpmtsFlags(ts) | _noTransScripts | _noTransTriggers));
1259     if (rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERS)
1260         (void) rpmtsSetFlags(ts, (rpmtsFlags(ts) | _noTransTriggers));
1261
1262     if (rpmtsFlags(ts) & (RPMTRANS_FLAG_JUSTDB | RPMTRANS_FLAG_TEST))
1263         (void) rpmtsSetFlags(ts, (rpmtsFlags(ts) | _noTransScripts | _noTransTriggers));
1264
1265     /* 
1266      * Make sure the database is open RDWR for package install/erase.
1267      * Note that we initialize chroot state here even if it's just "/" as
1268      * this ensures we can successfully perform open(".") which is
1269      * required to reliably restore cwd after Lua scripts.
1270      */ 
1271     if (rpmtsOpenDB(ts, dbmode) || rpmChrootSet(rpmtsRootDir(ts)))
1272         return -1;
1273
1274     ts->ignoreSet = ignoreSet;
1275     (void) rpmtsSetTid(ts, tid);
1276
1277     /* Get available space on mounted file systems. */
1278     (void) rpmtsInitDSI(ts);
1279
1280     return 0;
1281 }
1282
1283 static int rpmtsFinish(rpmts ts)
1284 {
1285     return rpmChrootSet(NULL);
1286 }
1287
1288 static int rpmtsPrepare(rpmts ts)
1289 {
1290     tsMembers tsmem = rpmtsMembers(ts);
1291     rpmtsi pi;
1292     rpmte p;
1293     int rc = 0;
1294     uint64_t fileCount = countFiles(ts);
1295     const char *dbhome = NULL;
1296     struct stat dbstat;
1297
1298     fingerPrintCache fpc = fpCacheCreate(fileCount/2 + 10001, rpmtsPool(ts));
1299
1300     rpmlog(RPMLOG_DEBUG, "computing %" PRIu64 " file fingerprints\n", fileCount);
1301
1302     /* Reset actions, set skip for netshared paths and excluded files */
1303     pi = rpmtsiInit(ts);
1304     while ((p = rpmtsiNext(pi, 0)) != NULL) {
1305         rpmfiles files = rpmteFiles(p);
1306         if (rpmfilesFC(files) > 0) {
1307             rpmfs fs = rpmteGetFileStates(p);
1308             /* Ensure clean state, this could get called more than once. */
1309             rpmfsResetActions(fs);
1310             if (rpmteType(p) == TR_ADDED) {
1311                 skipInstallFiles(ts, files, fs);
1312             } else {
1313                 skipEraseFiles(ts, files, fs);
1314             }
1315         }
1316         rpmfilesFree(files);
1317     }
1318     rpmtsiFree(pi);
1319
1320     /* Open rpmdb & enter chroot for fingerprinting if necessary */
1321     if (rpmdbOpenAll(ts->rdb) || rpmChrootIn()) {
1322         rc = -1;
1323         goto exit;
1324     }
1325     
1326     rpmtsNotify(ts, NULL, RPMCALLBACK_TRANS_START, 6, tsmem->orderCount);
1327     /* Add fingerprint for each file not skipped. */
1328     fpCachePopulate(fpc, ts, fileCount);
1329     /* check against files in the rpmdb */
1330     checkInstalledFiles(ts, fileCount, fpc);
1331
1332     dbhome = rpmdbHome(rpmtsGetRdb(ts));
1333     /* If we can't stat, ignore db growth. Probably not right but... */
1334     if (dbhome && stat(dbhome, &dbstat))
1335         dbhome = NULL;
1336
1337     pi = rpmtsiInit(ts);
1338     while ((p = rpmtsiNext(pi, 0)) != NULL) {
1339         rpmfiles files = rpmteFiles(p);;
1340         if (files == 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, fpc, p, files);
1347
1348         /* Check added package has sufficient space on each partition used. */
1349         if (rpmteType(p) == TR_ADDED) {
1350             /*
1351              * Try to estimate space needed for rpmdb growth: guess that the
1352              * db grows 4 times the header size (indexes and all).
1353              */
1354             if (dbhome) {
1355                 int64_t hsize = rpmteHeaderSize(p) * 4;
1356                 rpmtsUpdateDSI(ts, dbstat.st_dev, dbhome,
1357                                hsize, 0, 0, FA_CREATE);
1358             }
1359
1360             rpmtsCheckDSIProblems(ts, p);
1361         }
1362         (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_FINGERPRINT), 0);
1363         rpmfilesFree(files);
1364     }
1365     rpmtsiFree(pi);
1366     rpmtsNotify(ts, NULL, RPMCALLBACK_TRANS_STOP, 6, tsmem->orderCount);
1367
1368     /* return from chroot if done earlier */
1369     if (rpmChrootOut())
1370         rc = -1;
1371
1372     /* On actual transaction, file info sets are not needed after this */
1373     if (!(rpmtsFlags(ts) & (RPMTRANS_FLAG_TEST|RPMTRANS_FLAG_BUILD_PROBS))) {
1374         pi = rpmtsiInit(ts);
1375         while ((p = rpmtsiNext(pi, 0)) != NULL) {
1376             rpmteCleanFiles(p);
1377         }
1378         rpmtsiFree(pi);
1379     }
1380
1381 exit:
1382     fpCacheFree(fpc);
1383     rpmtsFreeDSI(ts);
1384     return rc;
1385 }
1386
1387 /*
1388  * Transaction main loop: install and remove packages
1389  */
1390 static int rpmtsProcess(rpmts ts)
1391 {
1392     rpmtsi pi;  rpmte p;
1393     int rc = 0;
1394     int i = 0;
1395
1396     pi = rpmtsiInit(ts);
1397     while ((p = rpmtsiNext(pi, 0)) != NULL) {
1398         int failed;
1399
1400         rpmlog(RPMLOG_DEBUG, "========== +++ %s %s-%s 0x%x\n",
1401                 rpmteNEVR(p), rpmteA(p), rpmteO(p), rpmteColor(p));
1402
1403         failed = rpmteProcess(p, rpmteType(p), i++);
1404         if (failed) {
1405             rpmlog(RPMLOG_ERR, "%s: %s %s\n", rpmteNEVRA(p),
1406                    rpmteTypeString(p), failed > 1 ? _("skipped") : _("failed"));
1407             rc++;
1408         }
1409     }
1410     rpmtsiFree(pi);
1411     return rc;
1412 }
1413
1414 rpmRC rpmtsSetupTransactionPlugins(rpmts ts)
1415 {
1416     rpmRC rc = RPMRC_OK;
1417     ARGV_t files = NULL;
1418     int nfiles = 0;
1419     char *dsoPath = NULL;
1420
1421     /*
1422      * Assume allocated equals initialized. There are some oddball cases
1423      * (verification of non-installed package) where this is not true
1424      * currently but that's not a new issue.
1425      */
1426     if ((rpmtsFlags(ts) & RPMTRANS_FLAG_NOPLUGINS) || rpmPluginsGetCount(rpmtsPlugins(ts)) != 0)
1427         return RPMRC_OK;
1428
1429     dsoPath = rpmExpand("%{__plugindir}/*.so", NULL);
1430     if (rpmGlob(dsoPath, &nfiles, &files) == 0) {
1431         rpmPlugins tsplugins = rpmtsPlugins(ts);
1432         for (int i = 0; i < nfiles; i++) {
1433             char *bn = basename(files[i]);
1434             bn[strlen(bn)-strlen(".so")] = '\0';
1435             if (rpmpluginsAddPlugin(tsplugins, "transaction", bn) == RPMRC_FAIL)
1436             {
1437             /* any configured plugin failing to load is a failure */
1438                 // temporally make the loading policy relaxed: no failures
1439                 //refer to commit id: 3959da1846227711d97c17495aa4779e653a1b3a
1440                 //rc = RPMRC_FAIL;
1441             }
1442         }
1443         files = argvFree(files);
1444     }
1445     free(dsoPath);
1446
1447     return rc;
1448 }
1449 /**
1450  * Run a scriptlet with args.
1451  *
1452  * Run a script with an interpreter. If the interpreter is not specified,
1453  * /bin/sh will be used. If the interpreter is /bin/sh, then the args from
1454  * the header will be ignored, passing instead arg1 and arg2.
1455  *
1456  * @param ts            transaction set
1457  * @param te            transaction element
1458  * @param prefixes      install prefixes
1459  * @param script        scriptlet from header
1460  * @param arg1          no. instances of package installed after scriptlet exec
1461  *                      (-1 is no arg)
1462  * @param arg2          ditto, but for the target package
1463  * @return              0 on success
1464  */
1465 rpmRC runScript(rpmts ts, rpmte te, Header h, ARGV_const_t prefixes,
1466                        rpmScript script, int arg1, int arg2)
1467 {
1468     rpmte xte = te;
1469     rpmRC stoprc, rc = RPMRC_OK;
1470     rpmTagVal stag = rpmScriptTag(script);
1471     FD_t sfd = NULL;
1472     int warn_only = (stag != RPMTAG_PREIN &&
1473                      stag != RPMTAG_PREUN &&
1474                      stag != RPMTAG_PRETRANS &&
1475                      stag != RPMTAG_VERIFYSCRIPT);
1476
1477     /* Fake up a transaction element for triggers from rpmdb */
1478     if (te == NULL) {
1479         te = rpmteNew(ts, h, TR_REMOVED, NULL, NULL);
1480         rpmteSetHeader(te, h);
1481     }
1482
1483     sfd = rpmtsNotify(ts, te, RPMCALLBACK_SCRIPT_START, stag, 0);
1484     if (sfd == NULL)
1485         sfd = rpmtsScriptFd(ts);
1486
1487     rpmswEnter(rpmtsOp(ts, RPMTS_OP_SCRIPTLETS), 0);
1488     rc = rpmScriptRun(script, arg1, arg2, sfd,
1489                       prefixes, warn_only, rpmtsPlugins(ts));
1490     rpmswExit(rpmtsOp(ts, RPMTS_OP_SCRIPTLETS), 0);
1491
1492     /* Map warn-only errors to "notfound" for script stop callback */
1493     stoprc = (rc != RPMRC_OK && warn_only) ? RPMRC_NOTFOUND : rc;
1494     rpmtsNotify(ts, te, RPMCALLBACK_SCRIPT_STOP, stag, stoprc);
1495
1496     /*
1497      * Notify callback for all errors. "total" abused for warning/error,
1498      * rc only reflects whether the condition prevented install/erase
1499      * (which is only happens with %prein and %preun scriptlets) or not.
1500      */
1501     if (rc != RPMRC_OK) {
1502         if (warn_only) {
1503             rc = RPMRC_OK;
1504         }
1505         rpmtsNotify(ts, te, RPMCALLBACK_SCRIPT_ERROR, stag, rc);
1506     }
1507
1508     if (te != xte)
1509         rpmteFree(te);
1510
1511     return rc;
1512 }
1513
1514 int rpmtsRun(rpmts ts, rpmps okProbs, rpmprobFilterFlags ignoreSet)
1515 {
1516     int rc = -1; /* assume failure */
1517     tsMembers tsmem = rpmtsMembers(ts);
1518     rpmtxn txn = NULL;
1519     rpmps tsprobs = NULL;
1520     int TsmPreDone = 0; /* TsmPre hook hasn't been called */
1521     /* Ignore SIGPIPE for the duration of transaction */
1522     rpmsqAction_t oact = rpmsqSetAction(SIGPIPE, RPMSQ_IGN);
1523     
1524     /* Force default 022 umask during transaction for consistent results */
1525     mode_t oldmask = umask(022);
1526
1527     /* Empty transaction, nothing to do */
1528     if (rpmtsNElements(ts) <= 0) {
1529         rc = 0;
1530         goto exit;
1531     }
1532
1533     /* If we are in test mode, then there's no need for transaction lock. */
1534     if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_TEST)) {
1535         if (!(txn = rpmtxnBegin(ts, RPMTXN_WRITE))) {
1536             goto exit;
1537         }
1538     }
1539
1540     /* Setup flags and such, open the DB */
1541     if (rpmtsSetup(ts, ignoreSet)) {
1542         goto exit;
1543     }
1544
1545     /* Check package set for problems */
1546     tsprobs = checkProblems(ts);
1547
1548     /* Run pre transaction hook for all plugins */
1549     TsmPreDone = 1;
1550     if (rpmpluginsCallTsmPre(rpmtsPlugins(ts), ts) == RPMRC_FAIL) {
1551         goto exit;
1552     }
1553
1554     /* Run %pretrans scripts, but only if there are no known problems up to
1555      * this point and not disabled otherwise. This is evil as it runs before
1556      * fingerprinting and problem checking and is best avoided.
1557      */
1558     if (!((rpmtsFlags(ts) & (RPMTRANS_FLAG_BUILD_PROBS|RPMTRANS_FLAG_NOPRETRANS))
1559           || (rpmpsNumProblems(tsprobs)))) {
1560         rpmlog(RPMLOG_DEBUG, "running pre-transaction scripts\n");
1561         runTransScripts(ts, PKG_PRETRANS);
1562     }
1563     tsprobs = rpmpsFree(tsprobs);
1564
1565     /* Compute file disposition for each package in transaction set. */
1566     if (rpmtsPrepare(ts)) {
1567         goto exit;
1568     }
1569     /* Check again for problems (now including file conflicts,  duh */
1570     tsprobs = rpmtsProblems(ts);
1571
1572      /* If unfiltered problems exist, free memory and return. */
1573     if ((rpmtsFlags(ts) & RPMTRANS_FLAG_BUILD_PROBS) || (rpmpsNumProblems(tsprobs))) {
1574         rc = tsmem->orderCount;
1575         goto exit;
1576     }
1577
1578     /* Free up memory taken by problem sets */
1579     tsprobs = rpmpsFree(tsprobs);
1580     rpmtsCleanProblems(ts);
1581
1582     /*
1583      * Free up the global string pool unless we expect it to be needed
1584      * again. During the transaction, private pools will be used for
1585      * rpmfi's etc.
1586      */
1587     if (!(rpmtsFlags(ts) & (RPMTRANS_FLAG_TEST|RPMTRANS_FLAG_BUILD_PROBS)))
1588         tsmem->pool = rpmstrPoolFree(tsmem->pool);
1589
1590     /* Run %transfiletriggerun scripts unless disabled */
1591     if (!(rpmtsFlags(ts) & (RPMTRANS_FLAG_BUILD_PROBS|RPMTRANS_FLAG_NOPRETRANS|
1592         RPMTRANS_FLAG_NOTRIGGERUN))) {
1593
1594         runFileTriggers(ts, NULL, RPMSENSE_TRIGGERUN,
1595                         RPMSCRIPT_TRANSFILETRIGGER, 0);
1596         runTransScripts(ts, PKG_TRANSFILETRIGGERUN);
1597     }
1598
1599     /* Actually install and remove packages, get final exit code */
1600     rc = rpmtsProcess(ts) ? -1 : 0;
1601
1602     /* Run %posttrans scripts unless disabled */
1603     if (!(rpmtsFlags(ts) & (RPMTRANS_FLAG_NOPOSTTRANS))) {
1604         rpmlog(RPMLOG_DEBUG, "running post-transaction scripts\n");
1605         runTransScripts(ts, PKG_POSTTRANS);
1606     }
1607
1608     /* Run %transfiletriggerpostun scripts unless disabled */
1609     if (!(rpmtsFlags(ts) & (RPMTRANS_FLAG_NOPOSTTRANS|RPMTRANS_FLAG_NOTRIGGERIN))) {
1610         runFileTriggers(ts, NULL, RPMSENSE_TRIGGERIN, RPMSCRIPT_TRANSFILETRIGGER, 0);
1611     }
1612     if (!(rpmtsFlags(ts) & (RPMTRANS_FLAG_NOPOSTTRANS|RPMTRANS_FLAG_NOTRIGGERPOSTUN))) {
1613         runPostUnTransFileTrigs(ts);
1614     }
1615
1616     /* Run %transfiletriggerin scripts unless disabled */
1617     if (!(rpmtsFlags(ts) & (RPMTRANS_FLAG_NOPOSTTRANS|RPMTRANS_FLAG_NOTRIGGERIN))) {
1618         runTransScripts(ts, PKG_TRANSFILETRIGGERIN);
1619     }
1620 exit:
1621     /* Run post transaction hook for all plugins */
1622     if (TsmPreDone) /* If TsmPre hook has been called, call the TsmPost hook */
1623         rpmpluginsCallTsmPost(rpmtsPlugins(ts), ts, rc);
1624
1625     /* Finish up... */
1626     (void) umask(oldmask);
1627     (void) rpmtsFinish(ts);
1628     rpmpsFree(tsprobs);
1629     rpmtxnEnd(txn);
1630     /* Restore SIGPIPE *after* unblocking signals in rpmtxnEnd() */
1631     rpmsqSetAction(SIGPIPE, oact);
1632     return rc;
1633 }