- linear search on added package provides is dumb.
[platform/upstream/rpm.git] / lib / psm.c
1 /** \ingroup rpmtrans payload
2  * \file lib/psm.c
3  * Package state machine to handle a package from a transaction set.
4  */
5
6 #include "system.h"
7
8 #include "psm.h"
9 #include "rpmal.h"
10 #include <rpmmacro.h>
11 #include <rpmurl.h>
12
13 #include "rpmds.h"
14 #include "depends.h"
15
16 #include "rpmlead.h"            /* writeLead proto */
17 #include "signature.h"          /* signature constants */
18 #include "legacy.h"             /* XXX buildOrigFileList() */
19 #include "ugid.h"               /* XXX unameToUid() and gnameToGid() */
20 #include "misc.h"               /* XXX stripTrailingChar() */
21 #include "rpmdb.h"              /* XXX for db_chrootDone */
22 #include "debug.h"
23
24 /*@unchecked@*/
25 int _fi_debug = 0;
26
27 /*@access Header@*/             /* compared with NULL */
28 /*@access rpmdbMatchIterator@*/ /* compared with NULL */
29 /*@access FSM_t@*/              /* compared with NULL */
30 /*@access FD_t@*/               /* compared with NULL */
31 /*@access rpmdb@*/              /* compared with NULL */
32
33 /*@access rpmTransactionSet@*/
34 /*@access TFI_t@*/
35 /*@access PSM_t@*/
36
37 /*@access alKey@*/
38 /*@access rpmDepSet@*/
39
40 /*@-redecl -declundef -exportheadervar@*/
41 /*@unchecked@*/
42 extern const char * chroot_prefix;
43 /*@=redecl =declundef =exportheadervar@*/
44
45 int rpmVersionCompare(Header first, Header second)
46 {
47     const char * one, * two;
48     int_32 * epochOne, * epochTwo;
49     int rc;
50
51     if (!headerGetEntry(first, RPMTAG_EPOCH, NULL, (void **) &epochOne, NULL))
52         epochOne = NULL;
53     if (!headerGetEntry(second, RPMTAG_EPOCH, NULL, (void **) &epochTwo,
54                         NULL))
55         epochTwo = NULL;
56
57     if (epochOne && !epochTwo)
58         return 1;
59     else if (!epochOne && epochTwo)
60         return -1;
61     else if (epochOne && epochTwo) {
62         if (*epochOne < *epochTwo)
63             return -1;
64         else if (*epochOne > *epochTwo)
65             return 1;
66     }
67
68     rc = headerGetEntry(first, RPMTAG_VERSION, NULL, (void **) &one, NULL);
69     rc = headerGetEntry(second, RPMTAG_VERSION, NULL, (void **) &two, NULL);
70
71     rc = rpmvercmp(one, two);
72     if (rc)
73         return rc;
74
75     rc = headerGetEntry(first, RPMTAG_RELEASE, NULL, (void **) &one, NULL);
76     rc = headerGetEntry(second, RPMTAG_RELEASE, NULL, (void **) &two, NULL);
77
78     return rpmvercmp(one, two);
79 }
80
81 char * fiGetNVR(const TFI_t fi)
82 {
83     char * pkgNVR;
84     char * t;
85
86     if (fi == NULL)
87         return xstrdup("--");
88
89     t = xcalloc(1, (fi->name != NULL ? strlen(fi->name) : 0) +
90                    (fi->version != NULL ? strlen(fi->version) : 0) +
91                    (fi->release != NULL ? strlen(fi->release) : 0) +
92                    sizeof("--"));
93     pkgNVR = t;
94     if (fi->name != NULL)       t = stpcpy(t, fi->name);
95     t = stpcpy(t, "-");
96     if (fi->version != NULL)    t = stpcpy(t, fi->version);
97     t = stpcpy(t, "-");
98     if (fi->release != NULL)    t = stpcpy(t, fi->release);
99     return pkgNVR;
100 }
101
102 /**
103  */
104 static /*@observer@*/ const char *const ftstring (fileTypes ft)
105         /*@*/
106 {
107     switch (ft) {
108     case XDIR:  return "directory";
109     case CDEV:  return "char dev";
110     case BDEV:  return "block dev";
111     case LINK:  return "link";
112     case SOCK:  return "sock";
113     case PIPE:  return "fifo/pipe";
114     case REG:   return "file";
115     default:    return "unknown file type";
116     }
117     /*@notreached@*/
118 }
119
120 fileTypes whatis(uint_16 mode)
121 {
122     if (S_ISDIR(mode))  return XDIR;
123     if (S_ISCHR(mode))  return CDEV;
124     if (S_ISBLK(mode))  return BDEV;
125     if (S_ISLNK(mode))  return LINK;
126     if (S_ISSOCK(mode)) return SOCK;
127     if (S_ISFIFO(mode)) return PIPE;
128     return REG;
129 }
130
131 #define alloca_strdup(_s)       strcpy(alloca(strlen(_s)+1), (_s))
132
133 Header relocateFileList(const rpmTransactionSet ts, TFI_t fi,
134                 Header origH, fileAction * actions)
135 {
136     HGE_t hge = fi->hge;
137     HAE_t hae = fi->hae;
138     HME_t hme = fi->hme;
139     HFD_t hfd = (fi->hfd ? fi->hfd : headerFreeData);
140     static int _printed = 0;
141     int allowBadRelocate = (ts->ignoreSet & RPMPROB_FILTER_FORCERELOCATE);
142     rpmRelocation * relocations = NULL;
143     int numRelocations;
144     const char ** validRelocations;
145     rpmTagType validType;
146     int numValid;
147     const char ** baseNames;
148     const char ** dirNames;
149     int_32 * dirIndexes;
150     int_32 * newDirIndexes;
151     int_32 fileCount;
152     int_32 dirCount;
153     uint_32 * fFlags = NULL;
154     uint_16 * fModes = NULL;
155     char * skipDirList;
156     Header h;
157     int nrelocated = 0;
158     int fileAlloced = 0;
159     char * fn = NULL;
160     int haveRelocatedFile = 0;
161     int reldel = 0;
162     int len;
163     int i, j, xx;
164
165     if (!hge(origH, RPMTAG_PREFIXES, &validType,
166                         (void **) &validRelocations, &numValid))
167         numValid = 0;
168
169     numRelocations = 0;
170     if (fi->relocs)
171         while (fi->relocs[numRelocations].newPath ||
172                fi->relocs[numRelocations].oldPath)
173             numRelocations++;
174
175     /*
176      * If no relocations are specified (usually the case), then return the
177      * original header. If there are prefixes, however, then INSTPREFIXES
178      * should be added, but, since relocateFileList() can be called more
179      * than once for the same header, don't bother if already present.
180      */
181     if (fi->relocs == NULL || numRelocations == 0) {
182         if (numValid) {
183             if (!headerIsEntry(origH, RPMTAG_INSTPREFIXES))
184                 xx = hae(origH, RPMTAG_INSTPREFIXES,
185                         validType, validRelocations, numValid);
186             validRelocations = hfd(validRelocations, validType);
187         }
188         /* XXX FIXME multilib file actions need to be checked. */
189         return headerLink(origH, "relocate(return)");
190     }
191
192     h = headerLink(origH, "relocate(orig)");
193
194     relocations = alloca(sizeof(*relocations) * numRelocations);
195
196     /* Build sorted relocation list from raw relocations. */
197     for (i = 0; i < numRelocations; i++) {
198         char * t;
199
200         /*
201          * Default relocations (oldPath == NULL) are handled in the UI,
202          * not rpmlib.
203          */
204         if (fi->relocs[i].oldPath == NULL) continue; /* XXX can't happen */
205
206         /* FIXME: Trailing /'s will confuse us greatly. Internal ones will 
207            too, but those are more trouble to fix up. :-( */
208         t = alloca_strdup(fi->relocs[i].oldPath);
209         /*@-branchstate@*/
210         relocations[i].oldPath = (t[0] == '/' && t[1] == '\0')
211             ? t
212             : stripTrailingChar(t, '/');
213         /*@=branchstate@*/
214
215         /* An old path w/o a new path is valid, and indicates exclusion */
216         if (fi->relocs[i].newPath) {
217             int del;
218
219             t = alloca_strdup(fi->relocs[i].newPath);
220             /*@-branchstate@*/
221             relocations[i].newPath = (t[0] == '/' && t[1] == '\0')
222                 ? t
223                 : stripTrailingChar(t, '/');
224             /*@=branchstate@*/
225
226             /*@-nullpass@*/     /* FIX:  relocations[i].oldPath == NULL */
227             /* Verify that the relocation's old path is in the header. */
228             for (j = 0; j < numValid; j++)
229                 if (!strcmp(validRelocations[j], relocations[i].oldPath))
230                     /*@innerbreak@*/ break;
231             /* XXX actions check prevents problem from being appended twice. */
232             if (j == numValid && !allowBadRelocate && actions) {
233                 rpmProblemSetAppend(ts->probs, RPMPROB_BADRELOCATE,
234                         fiGetNVR(fi), fi->key,
235                         relocations[i].oldPath, NULL, NULL, 0);
236             }
237             del =
238                 strlen(relocations[i].newPath) - strlen(relocations[i].oldPath);
239             /*@=nullpass@*/
240
241             if (del > reldel)
242                 reldel = del;
243         } else {
244             relocations[i].newPath = NULL;
245         }
246     }
247
248     /* stupid bubble sort, but it's probably faster here */
249     for (i = 0; i < numRelocations; i++) {
250         int madeSwap;
251         madeSwap = 0;
252         for (j = 1; j < numRelocations; j++) {
253             rpmRelocation tmpReloc;
254             if (relocations[j - 1].oldPath == NULL || /* XXX can't happen */
255                 relocations[j    ].oldPath == NULL || /* XXX can't happen */
256         strcmp(relocations[j - 1].oldPath, relocations[j].oldPath) <= 0)
257                 /*@innercontinue@*/ continue;
258             /*@-usereleased@*/ /* LCL: ??? */
259             tmpReloc = relocations[j - 1];
260             relocations[j - 1] = relocations[j];
261             relocations[j] = tmpReloc;
262             /*@=usereleased@*/
263             madeSwap = 1;
264         }
265         if (!madeSwap) break;
266     }
267
268     if (!_printed) {
269         _printed = 1;
270         rpmMessage(RPMMESS_DEBUG, _("========== relocations\n"));
271         for (i = 0; i < numRelocations; i++) {
272             if (relocations[i].oldPath == NULL) continue; /* XXX can't happen */
273             if (relocations[i].newPath == NULL)
274                 rpmMessage(RPMMESS_DEBUG, _("%5d exclude  %s\n"),
275                         i, relocations[i].oldPath);
276             else
277                 rpmMessage(RPMMESS_DEBUG, _("%5d relocate %s -> %s\n"),
278                         i, relocations[i].oldPath, relocations[i].newPath);
279         }
280     }
281
282     /* Add relocation values to the header */
283     if (numValid) {
284         const char ** actualRelocations;
285         int numActual;
286
287         actualRelocations = xmalloc(numValid * sizeof(*actualRelocations));
288         numActual = 0;
289         for (i = 0; i < numValid; i++) {
290             for (j = 0; j < numRelocations; j++) {
291                 if (relocations[j].oldPath == NULL || /* XXX can't happen */
292                     strcmp(validRelocations[i], relocations[j].oldPath))
293                     /*@innercontinue@*/ continue;
294                 /* On install, a relocate to NULL means skip the path. */
295                 if (relocations[j].newPath) {
296                     actualRelocations[numActual] = relocations[j].newPath;
297                     numActual++;
298                 }
299                 /*@innerbreak@*/ break;
300             }
301             if (j == numRelocations) {
302                 actualRelocations[numActual] = validRelocations[i];
303                 numActual++;
304             }
305         }
306
307         if (numActual)
308             xx = hae(h, RPMTAG_INSTPREFIXES, RPM_STRING_ARRAY_TYPE,
309                        (void **) actualRelocations, numActual);
310
311         actualRelocations = _free(actualRelocations);
312         validRelocations = hfd(validRelocations, validType);
313     }
314
315     xx = hge(h, RPMTAG_BASENAMES, NULL, (void **) &baseNames, &fileCount);
316     xx = hge(h, RPMTAG_DIRINDEXES, NULL, (void **) &dirIndexes, NULL);
317     xx = hge(h, RPMTAG_DIRNAMES, NULL, (void **) &dirNames, &dirCount);
318     xx = hge(h, RPMTAG_FILEFLAGS, NULL, (void **) &fFlags, NULL);
319     xx = hge(h, RPMTAG_FILEMODES, NULL, (void **) &fModes, NULL);
320
321     skipDirList = alloca(dirCount * sizeof(*skipDirList));
322     memset(skipDirList, 0, dirCount * sizeof(*skipDirList));
323
324     newDirIndexes = alloca(sizeof(*newDirIndexes) * fileCount);
325     memcpy(newDirIndexes, dirIndexes, sizeof(*newDirIndexes) * fileCount);
326     dirIndexes = newDirIndexes;
327
328     /*
329      * For all relocations, we go through sorted file/relocation lists 
330      * backwards so that /usr/local relocations take precedence over /usr 
331      * ones.
332      */
333
334     /* Relocate individual paths. */
335
336     for (i = fileCount - 1; i >= 0; i--) {
337         fileTypes ft;
338         int fnlen;
339
340         /*
341          * If only adding libraries of different arch into an already
342          * installed package, skip all other files.
343          */
344         if (fi->multiLib && !isFileMULTILIB((fFlags[i]))) {
345             if (actions) {
346                 actions[i] = FA_SKIPMULTILIB;
347                 rpmMessage(RPMMESS_DEBUG, _("excluding multilib path %s%s\n"), 
348                         dirNames[dirIndexes[i]], baseNames[i]);
349             }
350             continue;
351         }
352
353         len = reldel +
354                 strlen(dirNames[dirIndexes[i]]) + strlen(baseNames[i]) + 1;
355         /*@-branchstate@*/
356         if (len >= fileAlloced) {
357             fileAlloced = len * 2;
358             fn = xrealloc(fn, fileAlloced);
359         }
360         /*@=branchstate@*/
361         *fn = '\0';
362         fnlen = stpcpy( stpcpy(fn, dirNames[dirIndexes[i]]), baseNames[i]) - fn;
363
364         /*
365          * See if this file path needs relocating.
366          */
367         /*
368          * XXX FIXME: Would a bsearch of the (already sorted) 
369          * relocation list be a good idea?
370          */
371         for (j = numRelocations - 1; j >= 0; j--) {
372             if (relocations[j].oldPath == NULL) /* XXX can't happen */
373                 /*@innercontinue@*/ continue;
374             len = strcmp(relocations[j].oldPath, "/")
375                 ? strlen(relocations[j].oldPath)
376                 : 0;
377
378             if (fnlen < len)
379                 /*@innercontinue@*/ continue;
380             /*
381              * Only subdirectories or complete file paths may be relocated. We
382              * don't check for '\0' as our directory names all end in '/'.
383              */
384             if (!(fn[len] == '/' || fnlen == len))
385                 /*@innercontinue@*/ continue;
386
387             if (strncmp(relocations[j].oldPath, fn, len))
388                 /*@innercontinue@*/ continue;
389             /*@innerbreak@*/ break;
390         }
391         if (j < 0) continue;
392
393         ft = whatis(fModes[i]);
394
395         /* On install, a relocate to NULL means skip the path. */
396         if (relocations[j].newPath == NULL) {
397             if (ft == XDIR) {
398                 /* Start with the parent, looking for directory to exclude. */
399                 for (j = dirIndexes[i]; j < dirCount; j++) {
400                     len = strlen(dirNames[j]) - 1;
401                     while (len > 0 && dirNames[j][len-1] == '/') len--;
402                     if (fnlen != len)
403                         /*@innercontinue@*/ continue;
404                     if (strncmp(fn, dirNames[j], fnlen))
405                         /*@innercontinue@*/ continue;
406                     /*@innerbreak@*/ break;
407                 }
408                 if (j < dirCount)
409                     skipDirList[j] = 1;
410             }
411             if (actions) {
412                 actions[i] = FA_SKIPNSTATE;
413                 rpmMessage(RPMMESS_DEBUG, _("excluding %s %s\n"),
414                         ftstring(ft), fn);
415             }
416             continue;
417         }
418
419         /* Relocation on full paths only, please. */
420         if (fnlen != len) continue;
421
422         if (actions)
423             rpmMessage(RPMMESS_DEBUG, _("relocating %s to %s\n"),
424                     fn, relocations[j].newPath);
425         nrelocated++;
426
427         strcpy(fn, relocations[j].newPath);
428         {   char * te = strrchr(fn, '/');
429             if (te) {
430                 if (te > fn) te++;      /* root is special */
431                 fnlen = te - fn;
432             } else
433                 te = fn + strlen(fn);
434             /*@-nullpass -nullderef@*/  /* LCL: te != NULL here. */
435             if (strcmp(baseNames[i], te)) /* basename changed too? */
436                 baseNames[i] = alloca_strdup(te);
437             *te = '\0';                 /* terminate new directory name */
438             /*@=nullpass =nullderef@*/
439         }
440
441         /* Does this directory already exist in the directory list? */
442         for (j = 0; j < dirCount; j++) {
443             if (fnlen != strlen(dirNames[j]))
444                 /*@innercontinue@*/ continue;
445             if (strncmp(fn, dirNames[j], fnlen))
446                 /*@innercontinue@*/ continue;
447             /*@innerbreak@*/ break;
448         }
449         
450         if (j < dirCount) {
451             dirIndexes[i] = j;
452             continue;
453         }
454
455         /* Creating new paths is a pita */
456         if (!haveRelocatedFile) {
457             const char ** newDirList;
458
459             haveRelocatedFile = 1;
460             newDirList = xmalloc((dirCount + 1) * sizeof(*newDirList));
461             for (j = 0; j < dirCount; j++)
462                 newDirList[j] = alloca_strdup(dirNames[j]);
463             dirNames = hfd(dirNames, RPM_STRING_ARRAY_TYPE);
464             dirNames = newDirList;
465         } else {
466             dirNames = xrealloc(dirNames, 
467                                sizeof(*dirNames) * (dirCount + 1));
468         }
469
470         dirNames[dirCount] = alloca_strdup(fn);
471         dirIndexes[i] = dirCount;
472         dirCount++;
473     }
474
475     /* Finish off by relocating directories. */
476     for (i = dirCount - 1; i >= 0; i--) {
477         for (j = numRelocations - 1; j >= 0; j--) {
478
479             if (relocations[j].oldPath == NULL) /* XXX can't happen */
480                 /*@innercontinue@*/ continue;
481             len = strcmp(relocations[j].oldPath, "/")
482                 ? strlen(relocations[j].oldPath)
483                 : 0;
484
485             if (len && strncmp(relocations[j].oldPath, dirNames[i], len))
486                 /*@innercontinue@*/ continue;
487
488             /*
489              * Only subdirectories or complete file paths may be relocated. We
490              * don't check for '\0' as our directory names all end in '/'.
491              */
492             if (dirNames[i][len] != '/')
493                 /*@innercontinue@*/ continue;
494
495             if (relocations[j].newPath) { /* Relocate the path */
496                 const char * s = relocations[j].newPath;
497                 char * t = alloca(strlen(s) + strlen(dirNames[i]) - len + 1);
498
499                 (void) stpcpy( stpcpy(t, s) , dirNames[i] + len);
500                 if (actions)
501                     rpmMessage(RPMMESS_DEBUG,
502                         _("relocating directory %s to %s\n"), dirNames[i], t);
503                 dirNames[i] = t;
504                 nrelocated++;
505             }
506         }
507     }
508
509     /* Save original filenames in header and replace (relocated) filenames. */
510     if (nrelocated) {
511         int c;
512         void * p;
513         rpmTagType t;
514
515         p = NULL;
516         xx = hge(h, RPMTAG_BASENAMES, &t, &p, &c);
517         xx = hae(h, RPMTAG_ORIGBASENAMES, t, p, c);
518         p = hfd(p, t);
519
520         p = NULL;
521         xx = hge(h, RPMTAG_DIRNAMES, &t, &p, &c);
522         xx = hae(h, RPMTAG_ORIGDIRNAMES, t, p, c);
523         p = hfd(p, t);
524
525         p = NULL;
526         xx = hge(h, RPMTAG_DIRINDEXES, &t, &p, &c);
527         xx = hae(h, RPMTAG_ORIGDIRINDEXES, t, p, c);
528         p = hfd(p, t);
529
530         xx = hme(h, RPMTAG_BASENAMES, RPM_STRING_ARRAY_TYPE,
531                           baseNames, fileCount);
532         fi->bnl = hfd(fi->bnl, RPM_STRING_ARRAY_TYPE);
533         xx = hge(h, RPMTAG_BASENAMES, NULL, (void **) &fi->bnl, &fi->fc);
534
535         xx = hme(h, RPMTAG_DIRNAMES, RPM_STRING_ARRAY_TYPE,
536                           dirNames, dirCount);
537         fi->dnl = hfd(fi->dnl, RPM_STRING_ARRAY_TYPE);
538         xx = hge(h, RPMTAG_DIRNAMES, NULL, (void **) &fi->dnl, &fi->dc);
539
540         xx = hme(h, RPMTAG_DIRINDEXES, RPM_INT32_TYPE,
541                           dirIndexes, fileCount);
542         xx = hge(h, RPMTAG_DIRINDEXES, NULL, (void **) &fi->dil, NULL);
543     }
544
545     baseNames = hfd(baseNames, RPM_STRING_ARRAY_TYPE);
546     dirNames = hfd(dirNames, RPM_STRING_ARRAY_TYPE);
547     fn = _free(fn);
548
549     return h;
550 }
551
552 fnpyKey rpmfiGetKey(TFI_t fi)
553 {
554 /*@-compdef -retexpose -usereleased@*/
555     return fi->key;
556 /*@=compdef =retexpose =usereleased@*/
557 }
558
559 TFI_t XrpmfiUnlink(TFI_t fi, const char * msg, const char * fn, unsigned ln)
560 {
561 /*@-modfilesys@*/
562 if (_fi_debug)
563 fprintf(stderr, "--> fi %p -- %d %s at %s:%u\n", fi, fi->nrefs, msg, fn, ln);
564 /*@=modfilesys@*/
565     fi->nrefs--;
566     return NULL;
567 }
568
569 TFI_t XrpmfiLink(TFI_t fi, const char * msg, const char * fn, unsigned ln)
570 {
571     fi->nrefs++;
572 /*@-modfilesys@*/
573 if (_fi_debug)
574 fprintf(stderr, "--> fi %p ++ %d %s at %s:%u\n", fi, fi->nrefs, msg, fn, ln);
575 /*@=modfilesys@*/
576     /*@-refcounttrans@*/ return fi; /*@=refcounttrans@*/
577 }
578
579 void loadFi(const rpmTransactionSet ts, TFI_t fi, Header h, int keep_header)
580 {
581     HGE_t hge;
582     HFD_t hfd;
583     uint_32 * uip;
584     int len;
585     int rc;
586     int i;
587     
588     if (fi->fsm == NULL)
589         fi->fsm = newFSM();
590
591     /* XXX avoid gcc noise on pointer (4th arg) cast(s) */
592     hge = (keep_header && fi->type == TR_ADDED)
593         ? (HGE_t) headerGetEntryMinMemory : (HGE_t) headerGetEntry;
594     fi->hge = hge;
595     fi->hae = (HAE_t) headerAddEntry;
596     fi->hme = (HME_t) headerModifyEntry;
597     fi->hre = (HRE_t) headerRemoveEntry;
598     fi->hfd = hfd = headerFreeData;
599
600     /*@-branchstate@*/
601     if (h && fi->h == NULL)     fi->h = headerLink(h, "loadFi");
602     /*@=branchstate@*/
603
604     /* Duplicate name-version-release so that headers can be free'd. */
605     rc = hge(fi->h, RPMTAG_NAME, NULL, (void **) &fi->name, NULL);
606     fi->name = xstrdup(fi->name);
607     rc = hge(fi->h, RPMTAG_VERSION, NULL, (void **) &fi->version, NULL);
608     fi->version = xstrdup(fi->version);
609     rc = hge(fi->h, RPMTAG_RELEASE, NULL, (void **) &fi->release, NULL);
610     fi->release = xstrdup(fi->release);
611
612     /* -1 means not found */
613     rc = hge(fi->h, RPMTAG_EPOCH, NULL, (void **) &uip, NULL);
614     fi->epoch = (rc ? *uip : -1);
615
616     /* 0 means unknown */
617     rc = hge(fi->h, RPMTAG_ARCHIVESIZE, NULL, (void **) &uip, NULL);
618     fi->archiveSize = (rc ? *uip : 0);
619
620     if (!hge(fi->h, RPMTAG_BASENAMES, NULL, (void **) &fi->bnl, &fi->fc)) {
621         fi->dc = 0;
622         fi->fc = 0;
623         return;
624     }
625
626     rc = hge(fi->h, RPMTAG_DIRNAMES, NULL, (void **) &fi->dnl, &fi->dc);
627
628     rc = hge(fi->h, RPMTAG_DIRINDEXES, NULL, (void **) &fi->dil, NULL);
629     rc = hge(fi->h, RPMTAG_FILEMODES, NULL, (void **) &fi->fmodes, NULL);
630     rc = hge(fi->h, RPMTAG_FILEFLAGS, NULL, (void **) &fi->fflags, NULL);
631     rc = hge(fi->h, RPMTAG_FILESIZES, NULL, (void **) &fi->fsizes, NULL);
632
633     /* XXX initialized to NULL for TR_ADDED? */
634     rc = hge(fi->h, RPMTAG_FILESTATES, NULL, (void **) &fi->fstates, NULL);
635
636     fi->action = FA_UNKNOWN;
637     fi->flags = 0;
638
639     /* actions is initialized earlier for added packages */
640     if (fi->actions == NULL)
641         fi->actions = xcalloc(fi->fc, sizeof(*fi->actions));
642
643     fi->keep_header = keep_header;
644     switch (fi->type) {
645     case TR_ADDED:
646         fi->mapflags =
647                 CPIO_MAP_PATH | CPIO_MAP_MODE | CPIO_MAP_UID | CPIO_MAP_GID;
648         rc = hge(fi->h, RPMTAG_FILELINKTOS, NULL, (void **) &fi->flinks, NULL);
649         rc = hge(fi->h, RPMTAG_FILELANGS, NULL, (void **) &fi->flangs, NULL);
650
651         rc = hge(fi->h, RPMTAG_FILEMD5S, NULL, (void **) &fi->fmd5s, NULL);
652
653         rc = hge(fi->h, RPMTAG_FILEMTIMES, NULL, (void **) &fi->fmtimes, NULL);
654         rc = hge(fi->h, RPMTAG_FILERDEVS, NULL, (void **) &fi->frdevs, NULL);
655
656         /* 0 makes for noops */
657         fi->replacedSizes = xcalloc(fi->fc, sizeof(*fi->replacedSizes));
658
659         if (ts != NULL && fi->h != NULL)
660         {   Header foo = relocateFileList(ts, fi, fi->h, fi->actions);
661             foo = headerFree(foo, "loadFi TR_ADDED relocate");
662         }
663
664     if (!fi->keep_header) {
665         fi->fmtimes = memcpy(xmalloc(fi->fc * sizeof(*fi->fmtimes)),
666                                 fi->fmtimes, fi->fc * sizeof(*fi->fmtimes));
667         fi->frdevs = memcpy(xmalloc(fi->fc * sizeof(*fi->frdevs)),
668                                 fi->frdevs, fi->fc * sizeof(*fi->frdevs));
669
670         fi->fsizes = memcpy(xmalloc(fi->fc * sizeof(*fi->fsizes)),
671                                 fi->fsizes, fi->fc * sizeof(*fi->fsizes));
672         fi->fflags = memcpy(xmalloc(fi->fc * sizeof(*fi->fflags)),
673                                 fi->fflags, fi->fc * sizeof(*fi->fflags));
674         fi->fmodes = memcpy(xmalloc(fi->fc * sizeof(*fi->fmodes)),
675                                 fi->fmodes, fi->fc * sizeof(*fi->fmodes));
676         /* XXX there's a tedious segfault here for some version(s) of rpm */
677         if (fi->fstates)
678             fi->fstates = memcpy(xmalloc(fi->fc * sizeof(*fi->fstates)),
679                                 fi->fstates, fi->fc * sizeof(*fi->fstates));
680         else
681             fi->fstates = xcalloc(1, fi->fc * sizeof(*fi->fstates));
682         fi->dil = memcpy(xmalloc(fi->fc * sizeof(*fi->dil)),
683                                 fi->dil, fi->fc * sizeof(*fi->dil));
684         fi->h = headerFree(fi->h, "loadFi TR_ADDED");
685     }
686
687         break;
688     case TR_REMOVED:
689         fi->mapflags = 
690                 CPIO_MAP_ABSOLUTE | CPIO_MAP_ADDDOT | CPIO_ALL_HARDLINKS |
691                 CPIO_MAP_PATH | CPIO_MAP_MODE | CPIO_MAP_UID | CPIO_MAP_GID;
692         rc = hge(fi->h, RPMTAG_FILEMD5S, NULL, (void **) &fi->fmd5s, NULL);
693         rc = hge(fi->h, RPMTAG_FILELINKTOS, NULL, (void **) &fi->flinks, NULL);
694         fi->fsizes = memcpy(xmalloc(fi->fc * sizeof(*fi->fsizes)),
695                                 fi->fsizes, fi->fc * sizeof(*fi->fsizes));
696         fi->fflags = memcpy(xmalloc(fi->fc * sizeof(*fi->fflags)),
697                                 fi->fflags, fi->fc * sizeof(*fi->fflags));
698         fi->fmodes = memcpy(xmalloc(fi->fc * sizeof(*fi->fmodes)),
699                                 fi->fmodes, fi->fc * sizeof(*fi->fmodes));
700         /* XXX there's a tedious segfault here for some version(s) of rpm */
701         if (fi->fstates)
702             fi->fstates = memcpy(xmalloc(fi->fc * sizeof(*fi->fstates)),
703                                 fi->fstates, fi->fc * sizeof(*fi->fstates));
704         else
705             fi->fstates = xcalloc(1, fi->fc * sizeof(*fi->fstates));
706         fi->dil = memcpy(xmalloc(fi->fc * sizeof(*fi->dil)),
707                                 fi->dil, fi->fc * sizeof(*fi->dil));
708         fi->h = headerFree(fi->h, "loadFi TR_REMOVED");
709         break;
710     }
711
712     fi->dnlmax = -1;
713     for (i = 0; i < fi->dc; i++) {
714         if ((len = strlen(fi->dnl[i])) > fi->dnlmax)
715             fi->dnlmax = len;
716     }
717
718     fi->bnlmax = -1;
719     for (i = 0; i < fi->fc; i++) {
720         if ((len = strlen(fi->bnl[i])) > fi->bnlmax)
721             fi->bnlmax = len;
722     }
723
724     fi->dperms = 0755;
725     fi->fperms = 0644;
726
727     return;
728 }
729
730 void freeFi(TFI_t fi)
731 {
732     HFD_t hfd = (fi->hfd ? fi->hfd : headerFreeData);
733
734     switch (fi->type) {
735     case TR_ADDED:
736         if (!fi->keep_header) {
737             fi->fmtimes = hfd(fi->fmtimes, -1);
738             fi->frdevs = hfd(fi->frdevs, -1);
739             fi->fsizes = hfd(fi->fsizes, -1);
740             fi->fflags = hfd(fi->fflags, -1);
741             fi->fmodes = hfd(fi->fmodes, -1);
742             fi->fstates = hfd(fi->fstates, -1);
743             fi->dil = hfd(fi->dil, -1);
744         }
745         break;
746     case TR_REMOVED:
747         fi->fsizes = hfd(fi->fsizes, -1);
748         fi->fflags = hfd(fi->fflags, -1);
749         fi->fmodes = hfd(fi->fmodes, -1);
750         fi->fstates = hfd(fi->fstates, -1);
751         fi->dil = hfd(fi->dil, -1);
752         break;
753     }
754
755     fi->fsm = freeFSM(fi->fsm);
756
757     fi->apath = _free(fi->apath);
758     fi->fuids = _free(fi->fuids);
759     fi->fgids = _free(fi->fgids);
760     fi->fmapflags = _free(fi->fmapflags);
761
762     fi->bnl = hfd(fi->bnl, -1);
763     fi->dnl = hfd(fi->dnl, -1);
764     fi->obnl = hfd(fi->obnl, -1);
765     fi->odnl = hfd(fi->odnl, -1);
766     fi->flinks = hfd(fi->flinks, -1);
767     fi->fmd5s = hfd(fi->fmd5s, -1);
768     fi->fuser = hfd(fi->fuser, -1);
769     fi->fgroup = hfd(fi->fgroup, -1);
770     fi->flangs = hfd(fi->flangs, -1);
771
772     fi->name = _free(fi->name);
773     fi->version = _free(fi->version);
774     fi->release = _free(fi->release);
775     fi->actions = _free(fi->actions);
776     fi->replacedSizes = _free(fi->replacedSizes);
777     fi->replaced = _free(fi->replaced);
778
779     fi->h = headerFree(fi->h, "freeFi");
780
781     /*@-nullstate@*/ /* FIX: fi->{name,version,release,actions,...,h} NULL */
782     return;
783     /*@=nullstate@*/
784 }
785
786 /*@observer@*/ const char *const fiTypeString(TFI_t fi)
787 {
788     switch(fi->type) {
789     case TR_ADDED:      return " install";
790     case TR_REMOVED:    return "   erase";
791     default:            return "???";
792     }
793     /*@noteached@*/
794 }
795
796 /**
797  * Macros to be defined from per-header tag values.
798  * @todo Should other macros be added from header when installing a package?
799  */
800 /*@observer@*/ /*@unchecked@*/
801 static struct tagMacro {
802 /*@observer@*/ /*@null@*/ const char *  macroname; /*!< Macro name to define. */
803     rpmTag      tag;            /*!< Header tag to use for value. */
804 } tagMacros[] = {
805     { "name",           RPMTAG_NAME },
806     { "version",        RPMTAG_VERSION },
807     { "release",        RPMTAG_RELEASE },
808     { "epoch",          RPMTAG_EPOCH },
809     { NULL, 0 }
810 };
811
812 /**
813  * Define per-header macros.
814  * @param fi            transaction element file info
815  * @param h             header
816  * @return              0 always
817  */
818 static int rpmInstallLoadMacros(TFI_t fi, Header h)
819         /*@globals rpmGlobalMacroContext, internalState @*/
820         /*@modifies rpmGlobalMacroContext, internalState @*/
821 {
822     HGE_t hge = (HGE_t) fi->hge;
823     struct tagMacro * tagm;
824     union {
825 /*@unused@*/ void * ptr;
826 /*@unused@*/ const char ** argv;
827         const char * str;
828         int_32 * i32p;
829     } body;
830     char numbuf[32];
831     rpmTagType type;
832
833     for (tagm = tagMacros; tagm->macroname != NULL; tagm++) {
834         if (!hge(h, tagm->tag, &type, (void **) &body, NULL))
835             continue;
836         switch (type) {
837         case RPM_INT32_TYPE:
838             sprintf(numbuf, "%d", *body.i32p);
839             addMacro(NULL, tagm->macroname, NULL, numbuf, -1);
840             /*@switchbreak@*/ break;
841         case RPM_STRING_TYPE:
842             addMacro(NULL, tagm->macroname, NULL, body.str, -1);
843             /*@switchbreak@*/ break;
844         case RPM_NULL_TYPE:
845         case RPM_CHAR_TYPE:
846         case RPM_INT8_TYPE:
847         case RPM_INT16_TYPE:
848         case RPM_BIN_TYPE:
849         case RPM_STRING_ARRAY_TYPE:
850         case RPM_I18NSTRING_TYPE:
851         default:
852             /*@switchbreak@*/ break;
853         }
854     }
855     return 0;
856 }
857
858 /**
859  * Copy file data from h to newH.
860  * @param h             header from
861  * @param newH          header to
862  * @param actions       array of file dispositions
863  * @return              0 on success, 1 on failure
864  */
865 static int mergeFiles(TFI_t fi, Header h, Header newH)
866         /*@modifies h @*/
867 {
868     HGE_t hge = (HGE_t)fi->hge;
869     HME_t hme = (HME_t)fi->hme;
870     HFD_t hfd = (fi->hfd ? fi->hfd : headerFreeData);
871     fileAction * actions = fi->actions;
872     int i, j, k, fc, xx;
873     rpmTagType type = 0;
874     int_32 count = 0;
875     int_32 dirNamesCount, dirCount;
876     void * data, * newdata;
877     int_32 * dirIndexes, * newDirIndexes;
878     uint_32 * fileSizes, fileSize;
879     const char ** dirNames;
880     const char ** newDirNames;
881     static rpmTag mergeTags[] = {
882         RPMTAG_FILESIZES,
883         RPMTAG_FILESTATES,
884         RPMTAG_FILEMODES,
885         RPMTAG_FILERDEVS,
886         RPMTAG_FILEMTIMES,
887         RPMTAG_FILEMD5S,
888         RPMTAG_FILELINKTOS,
889         RPMTAG_FILEFLAGS,
890         RPMTAG_FILEUSERNAME,
891         RPMTAG_FILEGROUPNAME,
892         RPMTAG_FILEVERIFYFLAGS,
893         RPMTAG_FILEDEVICES,
894         RPMTAG_FILEINODES,
895         RPMTAG_FILELANGS,
896         RPMTAG_BASENAMES,
897         0,
898     };
899     static rpmTag requireTags[] = {
900         RPMTAG_REQUIRENAME, RPMTAG_REQUIREVERSION, RPMTAG_REQUIREFLAGS,
901         RPMTAG_PROVIDENAME, RPMTAG_PROVIDEVERSION, RPMTAG_PROVIDEFLAGS,
902         RPMTAG_CONFLICTNAME, RPMTAG_CONFLICTVERSION, RPMTAG_CONFLICTFLAGS
903     };
904
905     xx = hge(h, RPMTAG_SIZE, NULL, (void **) &fileSizes, NULL);
906     fileSize = *fileSizes;
907     xx = hge(newH, RPMTAG_FILESIZES, NULL, (void **) &fileSizes, &count);
908     for (i = 0, fc = 0; i < count; i++)
909         if (actions[i] != FA_SKIPMULTILIB) {
910             fc++;
911             fileSize += fileSizes[i];
912         }
913     xx = hme(h, RPMTAG_SIZE, RPM_INT32_TYPE, &fileSize, 1);
914
915     /*@-sizeoftype@*/
916     for (i = 0; mergeTags[i]; i++) {
917         if (!hge(newH, mergeTags[i], &type, (void **) &data, &count))
918             continue;
919         switch (type) {
920         case RPM_CHAR_TYPE:
921         case RPM_INT8_TYPE:
922             newdata = xcalloc(fc, sizeof(int_8));
923             for (j = 0, k = 0; j < count; j++)
924                 if (actions[j] != FA_SKIPMULTILIB)
925                         ((int_8 *) newdata)[k++] = ((int_8 *) data)[j];
926             xx = headerAddOrAppendEntry(h, mergeTags[i], type, newdata, fc);
927             free (newdata);
928             /*@switchbreak@*/ break;
929         case RPM_INT16_TYPE:
930             newdata = xcalloc(fc, sizeof(int_16));
931             for (j = 0, k = 0; j < count; j++)
932                 if (actions[j] != FA_SKIPMULTILIB)
933                     ((int_16 *) newdata)[k++] = ((int_16 *) data)[j];
934             xx = headerAddOrAppendEntry(h, mergeTags[i], type, newdata, fc);
935             free (newdata);
936             /*@switchbreak@*/ break;
937         case RPM_INT32_TYPE:
938             newdata = xcalloc(fc, sizeof(int_32));
939             for (j = 0, k = 0; j < count; j++)
940                 if (actions[j] != FA_SKIPMULTILIB)
941                     ((int_32 *) newdata)[k++] = ((int_32 *) data)[j];
942             xx = headerAddOrAppendEntry(h, mergeTags[i], type, newdata, fc);
943             free (newdata);
944             /*@switchbreak@*/ break;
945         case RPM_STRING_ARRAY_TYPE:
946             newdata = xcalloc(fc, sizeof(char *));
947             for (j = 0, k = 0; j < count; j++)
948                 if (actions[j] != FA_SKIPMULTILIB)
949                     ((char **) newdata)[k++] = ((char **) data)[j];
950             xx = headerAddOrAppendEntry(h, mergeTags[i], type, newdata, fc);
951             free (newdata);
952             /*@switchbreak@*/ break;
953         default:
954             rpmError(RPMERR_DATATYPE, _("Data type %d not supported\n"),
955                         (int) type);
956             return 1;
957             /*@notreached@*/ /*@switchbreak@*/ break;
958         }
959         data = hfd(data, type);
960     }
961     /*@=sizeoftype@*/
962     xx = hge(newH, RPMTAG_DIRINDEXES, NULL, (void **) &newDirIndexes, &count);
963     xx = hge(newH, RPMTAG_DIRNAMES, NULL, (void **) &newDirNames, NULL);
964     xx = hge(h, RPMTAG_DIRINDEXES, NULL, (void **) &dirIndexes, NULL);
965     xx = hge(h, RPMTAG_DIRNAMES, NULL, (void **) &data, &dirNamesCount);
966
967     dirNames = xcalloc(dirNamesCount + fc, sizeof(*dirNames));
968     for (i = 0; i < dirNamesCount; i++)
969         dirNames[i] = ((char **) data)[i];
970     dirCount = dirNamesCount;
971     newdata = xcalloc(fc, sizeof(*newDirIndexes));
972     for (i = 0, k = 0; i < count; i++) {
973         if (actions[i] == FA_SKIPMULTILIB)
974             continue;
975         for (j = 0; j < dirCount; j++)
976             if (!strcmp(dirNames[j], newDirNames[newDirIndexes[i]]))
977                 /*@innerbreak@*/ break;
978         if (j == dirCount)
979             dirNames[dirCount++] = newDirNames[newDirIndexes[i]];
980         ((int_32 *) newdata)[k++] = j;
981     }
982     xx = headerAddOrAppendEntry(h, RPMTAG_DIRINDEXES, RPM_INT32_TYPE, newdata, fc);
983     if (dirCount > dirNamesCount)
984         xx = headerAddOrAppendEntry(h, RPMTAG_DIRNAMES, RPM_STRING_ARRAY_TYPE,
985                                dirNames + dirNamesCount,
986                                dirCount - dirNamesCount);
987     data = hfd(data, -1);
988     newDirNames = hfd(newDirNames, -1);
989     free (newdata);
990     free (dirNames);
991
992     for (i = 0; i < 9; i += 3) {
993         const char **Names, **EVR, **newNames, **newEVR;
994         rpmTagType nnt, nvt, rnt;
995         uint_32 *Flags, *newFlags;
996         int Count = 0, newCount = 0;
997
998         if (!hge(newH, requireTags[i], &nnt, (void **) &newNames, &newCount))
999             continue;
1000
1001         xx = hge(newH, requireTags[i+1], &nvt, (void **) &newEVR, NULL);
1002         xx = hge(newH, requireTags[i+2], NULL, (void **) &newFlags, NULL);
1003         if (hge(h, requireTags[i], &rnt, (void **) &Names, &Count))
1004         {
1005             xx = hge(h, requireTags[i+1], NULL, (void **) &EVR, NULL);
1006             xx = hge(h, requireTags[i+2], NULL, (void **) &Flags, NULL);
1007             for (j = 0; j < newCount; j++)
1008                 for (k = 0; k < Count; k++)
1009                     if (!strcmp (newNames[j], Names[k])
1010                         && !strcmp (newEVR[j], EVR[k])
1011                         && (newFlags[j] & RPMSENSE_SENSEMASK) ==
1012                            (Flags[k] & RPMSENSE_SENSEMASK))
1013                     {
1014                         newNames[j] = NULL;
1015                         /*@innerbreak@*/ break;
1016                     }
1017         }
1018         for (j = 0, k = 0; j < newCount; j++) {
1019             if (!newNames[j] || !isDependsMULTILIB(newFlags[j]))
1020                 /*@innercontinue@*/ continue;
1021             if (j != k) {
1022                 newNames[k] = newNames[j];
1023                 newEVR[k] = newEVR[j];
1024                 newFlags[k] = newFlags[j];
1025             }
1026             k++;
1027         }
1028         if (k) {
1029             xx = headerAddOrAppendEntry(h, requireTags[i],
1030                                        RPM_STRING_ARRAY_TYPE, newNames, k);
1031             xx = headerAddOrAppendEntry(h, requireTags[i+1],
1032                                        RPM_STRING_ARRAY_TYPE, newEVR, k);
1033             xx = headerAddOrAppendEntry(h, requireTags[i+2], RPM_INT32_TYPE,
1034                                        newFlags, k);
1035         }
1036         newNames = hfd(newNames, nnt);
1037         newEVR = hfd(newEVR, nvt);
1038         Names = hfd(Names, rnt);
1039     }
1040     return 0;
1041 }
1042
1043 /**
1044  * Mark files in database shared with this package as "replaced".
1045  * @param psm           package state machine data
1046  * @return              0 always
1047  */
1048 static int markReplacedFiles(const PSM_t psm)
1049         /*@globals fileSystem@*/
1050         /*@modifies fileSystem @*/
1051 {
1052     const rpmTransactionSet ts = psm->ts;
1053     TFI_t fi = psm->fi;
1054     HGE_t hge = (HGE_t)fi->hge;
1055     const struct sharedFileInfo * replaced = fi->replaced;
1056     const struct sharedFileInfo * sfi;
1057     rpmdbMatchIterator mi;
1058     Header h;
1059     unsigned int * offsets;
1060     unsigned int prev;
1061     int num, xx;
1062
1063     if (!(fi->fc > 0 && fi->replaced))
1064         return 0;
1065
1066     num = prev = 0;
1067     for (sfi = replaced; sfi->otherPkg; sfi++) {
1068         if (prev && prev == sfi->otherPkg)
1069             continue;
1070         prev = sfi->otherPkg;
1071         num++;
1072     }
1073     if (num == 0)
1074         return 0;
1075
1076     offsets = alloca(num * sizeof(*offsets));
1077     num = prev = 0;
1078     for (sfi = replaced; sfi->otherPkg; sfi++) {
1079         if (prev && prev == sfi->otherPkg)
1080             continue;
1081         prev = sfi->otherPkg;
1082         offsets[num++] = sfi->otherPkg;
1083     }
1084
1085     mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES, NULL, 0);
1086     xx = rpmdbAppendIterator(mi, offsets, num);
1087     xx = rpmdbSetIteratorRewrite(mi, 1);
1088
1089     sfi = replaced;
1090     while ((h = rpmdbNextIterator(mi)) != NULL) {
1091         char * secStates;
1092         int modified;
1093         int count;
1094
1095         modified = 0;
1096
1097         if (!hge(h, RPMTAG_FILESTATES, NULL, (void **)&secStates, &count))
1098             continue;
1099         
1100         prev = rpmdbGetIteratorOffset(mi);
1101         num = 0;
1102         while (sfi->otherPkg && sfi->otherPkg == prev) {
1103             assert(sfi->otherFileNum < count);
1104             if (secStates[sfi->otherFileNum] != RPMFILE_STATE_REPLACED) {
1105                 secStates[sfi->otherFileNum] = RPMFILE_STATE_REPLACED;
1106                 if (modified == 0) {
1107                     /* Modified header will be rewritten. */
1108                     modified = 1;
1109                     xx = rpmdbSetIteratorModified(mi, modified);
1110                 }
1111                 num++;
1112             }
1113             sfi++;
1114         }
1115     }
1116     mi = rpmdbFreeIterator(mi);
1117
1118     return 0;
1119 }
1120
1121 /**
1122  * Create directory if it does not exist, make sure path is writable.
1123  * @note This will only create last component of directory path.
1124  * @param dpath         directory path
1125  * @param dname         directory use
1126  * @return              rpmRC return code
1127  */
1128 static rpmRC chkdir (const char * dpath, const char * dname)
1129         /*@globals fileSystem@*/
1130         /*@modifies fileSystem @*/
1131 {
1132     struct stat st;
1133     int rc;
1134
1135     if ((rc = Stat(dpath, &st)) < 0) {
1136         int ut = urlPath(dpath, NULL);
1137         switch (ut) {
1138         case URL_IS_PATH:
1139         case URL_IS_UNKNOWN:
1140             if (errno != ENOENT)
1141                 break;
1142             /*@fallthrough@*/
1143         case URL_IS_FTP:
1144         case URL_IS_HTTP:
1145             rc = Mkdir(dpath, 0755);
1146             break;
1147         case URL_IS_DASH:
1148             break;
1149         }
1150         if (rc < 0) {
1151             rpmError(RPMERR_CREATE, _("cannot create %%%s %s\n"),
1152                         dname, dpath);
1153             return RPMRC_FAIL;
1154         }
1155     }
1156     if ((rc = Access(dpath, W_OK))) {
1157         rpmError(RPMERR_CREATE, _("cannot write to %%%s %s\n"), dname, dpath);
1158         return RPMRC_FAIL;
1159     }
1160     return RPMRC_OK;
1161 }
1162
1163 rpmRC rpmInstallSourcePackage(rpmTransactionSet ts,
1164                 FD_t fd,
1165                 const char ** specFilePtr,
1166                 rpmCallbackFunction notify, rpmCallbackData notifyData,
1167                 const char ** cookie)
1168 {
1169     TFI_t fi = xcalloc(sizeof(*fi), 1);
1170     const char * _sourcedir = NULL;
1171     const char * _specdir = NULL;
1172     const char * specFile = NULL;
1173     HGE_t hge;
1174     HFD_t hfd;
1175     Header h = NULL;
1176     struct psm_s psmbuf;
1177     PSM_t psm = &psmbuf;
1178     int isSource;
1179     rpmRC rc;
1180     int i;
1181     alKey pkgKey = (alKey)0;
1182
1183     /*@-mods -temptrans -assignexpose@*/
1184     ts->notify = notify;
1185     ts->notifyData = notifyData;
1186     /*@=mods =temptrans =assignexpose@*/
1187
1188     /*@-mustmod@*/      /* LCL: segfault */
1189     rc = rpmReadPackageFile(ts, fd, "InstallSourcePackage", &h);
1190     /*@=mustmod@*/
1191     if (!(rc == RPMRC_OK || rc == RPMRC_BADSIZE) || h == NULL) {
1192         goto exit;
1193     }
1194     rc = RPMRC_OK;                              /* XXX HACK */
1195     isSource = headerIsEntry(h, RPMTAG_SOURCEPACKAGE);
1196
1197     if (!isSource) {
1198         rpmError(RPMERR_NOTSRPM, _("source package expected, binary found\n"));
1199         rc = RPMRC_FAIL;
1200         goto exit;
1201     }
1202
1203     /* XXX don't bother with fd, linked directly into fi below. */
1204     (void) rpmtransAddPackage(ts, h, NULL, NULL, 0, NULL);
1205
1206     fi->type = TR_ADDED;
1207
1208     fi->h = alGetHeader(ts->addedPackages, pkgKey, 1);
1209     /* XXX can't happen */
1210     if (fi->h == NULL) {
1211         rc = RPMRC_FAIL;
1212         goto exit;
1213     }
1214
1215     fi->multiLib = 0;   /* MULTILIB for src.rpm's? */
1216
1217 #ifdef  DYING
1218     /*@-kepttrans@*/
1219     fi->key = alGetKey(ts->addedPackages, pkgKey);
1220     /*@=kepttrans@*/
1221     fi->relocs = alGetRelocs(ts->addedPackages, pkgKey);
1222     fi->fd = alGetFd(ts->addedPackages, pkgKey);
1223 #else
1224     fi->key = NULL;
1225     /* XXX don't bother with fd, linked directly into fi below. */
1226 /*@i@*/ fi->fd = fd;
1227     fi->relocs = NULL;
1228 #endif
1229
1230     /* XXX header arg unused. */
1231     loadFi(ts, fi, fi->h, 1);
1232     hge = fi->hge;
1233     hfd = (fi->hfd ? fi->hfd : headerFreeData);
1234     h = headerFree(h, "InstallSourcePackage");
1235
1236     (void) rpmInstallLoadMacros(fi, fi->h);
1237
1238     memset(psm, 0, sizeof(*psm));
1239     /*@-assignexpose@*/
1240     psm->ts = rpmtsLink(ts, "InstallSourcePackage");
1241     psm->fi = fi;
1242     /*@=assignexpose@*/
1243
1244     if (cookie) {
1245         *cookie = NULL;
1246         if (hge(fi->h, RPMTAG_COOKIE, NULL, (void **) cookie, NULL))
1247             *cookie = xstrdup(*cookie);
1248     }
1249
1250     /* XXX FIXME: can't do endian neutral MD5 verification yet. */
1251     fi->fmd5s = hfd(fi->fmd5s, -1);
1252
1253     /* XXX FIXME: don't do per-file mapping, force global flags. */
1254     fi->fmapflags = _free(fi->fmapflags);
1255     fi->mapflags = CPIO_MAP_PATH | CPIO_MAP_MODE | CPIO_MAP_UID | CPIO_MAP_GID;
1256
1257     fi->uid = getuid();
1258     fi->gid = getgid();
1259     fi->astriplen = 0;
1260     fi->striplen = 0;
1261
1262     fi->fuids = xcalloc(sizeof(*fi->fuids), fi->fc);
1263     fi->fgids = xcalloc(sizeof(*fi->fgids), fi->fc);
1264     for (i = 0; i < fi->fc; i++) {
1265         fi->fuids[i] = fi->uid;
1266         fi->fgids[i] = fi->gid;
1267     }
1268
1269     for (i = 0; i < fi->fc; i++) {
1270         fi->actions[i] = FA_CREATE;
1271     }
1272
1273     i = fi->fc;
1274
1275     if (fi->h != NULL) {        /* XXX can't happen */
1276         rpmBuildFileList(fi->h, &fi->apath, NULL);
1277
1278         if (headerIsEntry(fi->h, RPMTAG_COOKIE))
1279             for (i = 0; i < fi->fc; i++)
1280                 if (fi->fflags[i] & RPMFILE_SPECFILE) break;
1281     }
1282
1283     if (i == fi->fc) {
1284         /* Find the spec file by name. */
1285         for (i = 0; i < fi->fc; i++) {
1286             const char * t = fi->apath[i];
1287             t += strlen(fi->apath[i]) - 5;
1288             if (!strcmp(t, ".spec")) break;
1289         }
1290     }
1291
1292     _sourcedir = rpmGenPath(ts->rootDir, "%{_sourcedir}", "");
1293     rc = chkdir(_sourcedir, "sourcedir");
1294     if (rc) {
1295         rc = RPMRC_FAIL;
1296         goto exit;
1297     }
1298
1299     _specdir = rpmGenPath(ts->rootDir, "%{_specdir}", "");
1300     rc = chkdir(_specdir, "specdir");
1301     if (rc) {
1302         rc = RPMRC_FAIL;
1303         goto exit;
1304     }
1305
1306     /* Build dnl/dil with {_sourcedir, _specdir} as values. */
1307     if (i < fi->fc) {
1308         int speclen = strlen(_specdir) + 2;
1309         int sourcelen = strlen(_sourcedir) + 2;
1310         char * t;
1311
1312         fi->dnl = hfd(fi->dnl, -1);
1313
1314         fi->dc = 2;
1315         fi->dnl = xmalloc(fi->dc * sizeof(*fi->dnl) + fi->fc * sizeof(*fi->dil) +
1316                         speclen + sourcelen);
1317         fi->dil = (int *)(fi->dnl + fi->dc);
1318         memset(fi->dil, 0, fi->fc * sizeof(*fi->dil));
1319         fi->dil[i] = 1;
1320         /*@-dependenttrans@*/
1321         fi->dnl[0] = t = (char *)(fi->dil + fi->fc);
1322         fi->dnl[1] = t = stpcpy( stpcpy(t, _sourcedir), "/") + 1;
1323         /*@=dependenttrans@*/
1324         (void) stpcpy( stpcpy(t, _specdir), "/");
1325
1326         t = xmalloc(speclen + strlen(fi->bnl[i]) + 1);
1327         (void) stpcpy( stpcpy( stpcpy(t, _specdir), "/"), fi->bnl[i]);
1328         specFile = t;
1329     } else {
1330         rpmError(RPMERR_NOSPEC, _("source package contains no .spec file\n"));
1331         rc = RPMRC_FAIL;
1332         goto exit;
1333     }
1334
1335     psm->goal = PSM_PKGINSTALL;
1336
1337     /*@-compmempass@*/  /* FIX: psm->fi->dnl should be owned. */
1338     rc = psmStage(psm, PSM_PROCESS);
1339
1340     (void) psmStage(psm, PSM_FINI);
1341     /*@=compmempass@*/
1342
1343     if (rc) rc = RPMRC_FAIL;
1344
1345 exit:
1346     if (specFilePtr && specFile && rc == RPMRC_OK)
1347         *specFilePtr = specFile;
1348     else
1349         specFile = _free(specFile);
1350
1351     _specdir = _free(_specdir);
1352     _sourcedir = _free(_sourcedir);
1353
1354     if (h) h = headerFree(h, "InstallSourcePackage exit");
1355
1356     if (fi) {
1357         freeFi(fi);
1358         /*@-refcounttrans@*/ /* FIX: fi needs to be only */
1359         fi = _free(fi);
1360         /*@=refcounttrans@*/
1361     }
1362
1363     psm->ts = rpmtsUnlink(ts, "InstallSourcePackage");
1364
1365     return rc;
1366 }
1367
1368 /*@observer@*/ /*@unchecked@*/
1369 static char * SCRIPT_PATH = "PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin";
1370
1371 /**
1372  * Return scriptlet name from tag.
1373  * @param tag           scriptlet tag
1374  * @return              name of scriptlet
1375  */
1376 static /*@observer@*/ const char * const tag2sln(int tag)
1377         /*@*/
1378 {
1379     switch (tag) {
1380     case RPMTAG_PREIN:          return "%pre";
1381     case RPMTAG_POSTIN:         return "%post";
1382     case RPMTAG_PREUN:          return "%preun";
1383     case RPMTAG_POSTUN:         return "%postun";
1384     case RPMTAG_VERIFYSCRIPT:   return "%verify";
1385     }
1386     return "%unknownscript";
1387 }
1388
1389 /**
1390  * Run scriptlet with args.
1391  *
1392  * Run a script with an interpreter. If the interpreter is not specified,
1393  * /bin/sh will be used. If the interpreter is /bin/sh, then the args from
1394  * the header will be ignored, passing instead arg1 and arg2.
1395  * 
1396  * @param psm           package state machine data
1397  * @param h             header
1398  * @param sln           name of scriptlet section
1399  * @param progArgc      no. of args from header
1400  * @param progArgv      args from header, progArgv[0] is the interpreter to use
1401  * @param script        scriptlet from header
1402  * @param arg1          no. instances of package installed after scriptlet exec
1403  *                      (-1 is no arg)
1404  * @param arg2          ditto, but for the target package
1405  * @return              0 on success, 1 on error
1406  */
1407 static int runScript(PSM_t psm, Header h,
1408                 const char * sln,
1409                 int progArgc, const char ** progArgv, 
1410                 const char * script, int arg1, int arg2)
1411         /*@globals rpmGlobalMacroContext,
1412                 fileSystem, internalState@*/
1413         /*@modifies psm, rpmGlobalMacroContext, fileSystem, internalState @*/
1414 {
1415     const rpmTransactionSet ts = psm->ts;
1416     TFI_t fi = psm->fi;
1417     HGE_t hge = fi->hge;
1418     HFD_t hfd = (fi->hfd ? fi->hfd : headerFreeData);
1419     const char ** argv = NULL;
1420     int argc = 0;
1421     const char ** prefixes = NULL;
1422     int numPrefixes;
1423     rpmTagType ipt;
1424     const char * oldPrefix;
1425     int maxPrefixLength;
1426     int len;
1427     char * prefixBuf = NULL;
1428     pid_t child;
1429     int status = 0;
1430     const char * fn = NULL;
1431     int i, xx;
1432     int freePrefixes = 0;
1433     FD_t out;
1434     rpmRC rc = RPMRC_OK;
1435     const char *n, *v, *r;
1436
1437     if (progArgv == NULL && script == NULL)
1438         return 0;
1439
1440     rpmMessage(RPMMESS_DEBUG, _("%s: running %s scriptlet\n"),
1441                 psm->stepName, tag2sln(psm->scriptTag));
1442
1443     if (!progArgv) {
1444         argv = alloca(5 * sizeof(*argv));
1445         argv[0] = "/bin/sh";
1446         argc = 1;
1447     } else {
1448         argv = alloca((progArgc + 4) * sizeof(*argv));
1449         memcpy(argv, progArgv, progArgc * sizeof(*argv));
1450         argc = progArgc;
1451     }
1452
1453     xx = headerNVR(h, &n, &v, &r);
1454     if (hge(h, RPMTAG_INSTPREFIXES, &ipt, (void **) &prefixes, &numPrefixes)) {
1455         freePrefixes = 1;
1456     } else if (hge(h, RPMTAG_INSTALLPREFIX, NULL, (void **) &oldPrefix, NULL)) {
1457         prefixes = &oldPrefix;
1458         numPrefixes = 1;
1459     } else {
1460         numPrefixes = 0;
1461     }
1462
1463     maxPrefixLength = 0;
1464     for (i = 0; i < numPrefixes; i++) {
1465         len = strlen(prefixes[i]);
1466         if (len > maxPrefixLength) maxPrefixLength = len;
1467     }
1468     prefixBuf = alloca(maxPrefixLength + 50);
1469
1470     if (script) {
1471         FD_t fd;
1472         /*@-branchstate@*/
1473         if (makeTempFile((!ts->chrootDone ? ts->rootDir : "/"), &fn, &fd)) {
1474             if (freePrefixes) free(prefixes);
1475             return 1;
1476         }
1477         /*@=branchstate@*/
1478
1479         if (rpmIsDebug() &&
1480             (!strcmp(argv[0], "/bin/sh") || !strcmp(argv[0], "/bin/bash")))
1481         {
1482             static const char set_x[] = "set -x\n";
1483             xx = Fwrite(set_x, sizeof(set_x[0]), sizeof(set_x)-1, fd);
1484         }
1485
1486         xx = Fwrite(script, sizeof(script[0]), strlen(script), fd);
1487         xx = Fclose(fd);
1488
1489         {   const char * sn = fn;
1490             if (!ts->chrootDone &&
1491                 !(ts->rootDir[0] == '/' && ts->rootDir[1] == '\0'))
1492             {
1493                 sn += strlen(ts->rootDir)-1;
1494             }
1495             argv[argc++] = sn;
1496         }
1497
1498         if (arg1 >= 0) {
1499             char *av = alloca(20);
1500             sprintf(av, "%d", arg1);
1501             argv[argc++] = av;
1502         }
1503         if (arg2 >= 0) {
1504             char *av = alloca(20);
1505             sprintf(av, "%d", arg2);
1506             argv[argc++] = av;
1507         }
1508     }
1509
1510     argv[argc] = NULL;
1511
1512     if (ts->scriptFd != NULL) {
1513         if (rpmIsVerbose()) {
1514             out = fdDup(Fileno(ts->scriptFd));
1515         } else {
1516             out = Fopen("/dev/null", "w.fdio");
1517             if (Ferror(out)) {
1518                 out = fdDup(Fileno(ts->scriptFd));
1519             }
1520         }
1521     } else {
1522         out = fdDup(STDOUT_FILENO);
1523 #ifdef  DYING
1524         out = fdLink(out, "runScript persist");
1525 #endif
1526     }
1527     if (out == NULL) return 1;  /* XXX can't happen */
1528     
1529     if (!(child = fork())) {
1530         const char * rootDir;
1531         int pipes[2];
1532
1533         pipes[0] = pipes[1] = 0;
1534         /* make stdin inaccessible */
1535         xx = pipe(pipes);
1536         xx = close(pipes[1]);
1537         xx = dup2(pipes[0], STDIN_FILENO);
1538         xx = close(pipes[0]);
1539
1540         /*@-branchstate@*/
1541         if (ts->scriptFd != NULL) {
1542             int sfdno = Fileno(ts->scriptFd);
1543             int ofdno = Fileno(out);
1544             if (sfdno != STDERR_FILENO)
1545                 xx = dup2(sfdno, STDERR_FILENO);
1546             if (ofdno != STDOUT_FILENO)
1547                 xx = dup2(ofdno, STDOUT_FILENO);
1548             /* make sure we don't close stdin/stderr/stdout by mistake! */
1549             if (ofdno > STDERR_FILENO && ofdno != sfdno) {
1550                 xx = Fclose (out);
1551             }
1552             if (sfdno > STDERR_FILENO) {
1553                 xx = Fclose (ts->scriptFd);
1554             }
1555         }
1556         /*@=branchstate@*/
1557
1558         /*@-branchstate@*/
1559         {   const char *ipath = rpmExpand("PATH=%{_install_script_path}", NULL);
1560             const char *path = SCRIPT_PATH;
1561
1562             if (ipath && ipath[5] != '%')
1563                 path = ipath;
1564
1565             xx = doputenv(path);
1566             /*@-modobserver@*/
1567             ipath = _free(ipath);
1568             /*@=modobserver@*/
1569         }
1570         /*@=branchstate@*/
1571
1572         for (i = 0; i < numPrefixes; i++) {
1573             sprintf(prefixBuf, "RPM_INSTALL_PREFIX%d=%s", i, prefixes[i]);
1574             xx = doputenv(prefixBuf);
1575
1576             /* backwards compatibility */
1577             if (i == 0) {
1578                 sprintf(prefixBuf, "RPM_INSTALL_PREFIX=%s", prefixes[i]);
1579                 xx = doputenv(prefixBuf);
1580             }
1581         }
1582
1583         if ((rootDir = ts->rootDir) != NULL)    /* XXX can't happen */
1584         switch(urlIsURL(rootDir)) {
1585         case URL_IS_PATH:
1586             rootDir += sizeof("file://") - 1;
1587             rootDir = strchr(rootDir, '/');
1588             /*@fallthrough@*/
1589         case URL_IS_UNKNOWN:
1590             if (!ts->chrootDone && !(rootDir[0] == '/' && rootDir[1] == '\0')) {
1591                 /*@-superuser -noeffect @*/
1592                 xx = chroot(rootDir);
1593                 /*@=superuser =noeffect @*/
1594             }
1595             xx = chdir("/");
1596             xx = execv(argv[0], (char *const *)argv);
1597             break;
1598         default:
1599             break;
1600         }
1601
1602         _exit(-1);
1603         /*@notreached@*/
1604     }
1605
1606     if (waitpid(child, &status, 0) < 0) {
1607         rpmError(RPMERR_SCRIPT,
1608      _("execution of %s scriptlet from %s-%s-%s failed, waitpid returned %s\n"),
1609                  sln, n, v, r, strerror (errno));
1610         /* XXX what to do here? */
1611         rc = RPMRC_OK;
1612     } else {
1613         if (!WIFEXITED(status) || WEXITSTATUS(status)) {
1614             rpmError(RPMERR_SCRIPT,
1615      _("execution of %s scriptlet from %s-%s-%s failed, exit status %d\n"),
1616                      sln, n, v, r, WEXITSTATUS(status));
1617             rc = RPMRC_FAIL;
1618         }
1619     }
1620
1621     if (freePrefixes) prefixes = hfd(prefixes, ipt);
1622
1623     xx = Fclose(out);   /* XXX dup'd STDOUT_FILENO */
1624     
1625     /*@-branchstate@*/
1626     if (script) {
1627         if (!rpmIsDebug())
1628             xx = unlink(fn);
1629         fn = _free(fn);
1630     }
1631     /*@=branchstate@*/
1632
1633     return rc;
1634 }
1635
1636 /**
1637  * Retrieve and run scriptlet from header.
1638  * @param psm           package state machine data
1639  * @return              rpmRC return code
1640  */
1641 static rpmRC runInstScript(PSM_t psm)
1642         /*@globals rpmGlobalMacroContext,
1643                 fileSystem, internalState @*/
1644         /*@modifies psm, rpmGlobalMacroContext,
1645                 fileSystem, internalState @*/
1646 {
1647     TFI_t fi = psm->fi;
1648     HGE_t hge = fi->hge;
1649     HFD_t hfd = (fi->hfd ? fi->hfd : headerFreeData);
1650     void ** progArgv;
1651     int progArgc;
1652     const char ** argv;
1653     rpmTagType ptt, stt;
1654     const char * script;
1655     rpmRC rc = RPMRC_OK;
1656     int xx;
1657
1658     /*
1659      * headerGetEntry() sets the data pointer to NULL if the entry does
1660      * not exist.
1661      */
1662     xx = hge(fi->h, psm->scriptTag, &stt, (void **) &script, NULL);
1663     xx = hge(fi->h, psm->progTag, &ptt, (void **) &progArgv, &progArgc);
1664     if (progArgv == NULL && script == NULL)
1665         goto exit;
1666
1667     /*@-branchstate@*/
1668     if (progArgv && ptt == RPM_STRING_TYPE) {
1669         argv = alloca(sizeof(*argv));
1670         *argv = (const char *) progArgv;
1671     } else {
1672         argv = (const char **) progArgv;
1673     }
1674     /*@=branchstate@*/
1675
1676     if (fi->h != NULL)  /* XXX can't happen */
1677     rc = runScript(psm, fi->h, tag2sln(psm->scriptTag), progArgc, argv,
1678                 script, psm->scriptArg, -1);
1679
1680 exit:
1681     progArgv = hfd(progArgv, ptt);
1682     script = hfd(script, stt);
1683     return rc;
1684 }
1685
1686 /**
1687  * @param psm           package state machine data
1688  * @param sourceH
1689  * @param triggeredH
1690  * @param arg2
1691  * @param triggersAlreadyRun
1692  * @return
1693  */
1694 static int handleOneTrigger(PSM_t psm, Header sourceH, Header triggeredH,
1695                         int arg2, unsigned char * triggersAlreadyRun)
1696         /*@globals rpmGlobalMacroContext,
1697                 fileSystem, internalState@*/
1698         /*@modifies psm, triggeredH, *triggersAlreadyRun, rpmGlobalMacroContext,
1699                 fileSystem, internalState @*/
1700 {
1701     int scareMem = 1;
1702     const rpmTransactionSet ts = psm->ts;
1703     TFI_t fi = psm->fi;
1704     HGE_t hge = fi->hge;
1705     HFD_t hfd = (fi->hfd ? fi->hfd : headerFreeData);
1706     rpmDepSet trigger = NULL;
1707     const char ** triggerScripts;
1708     const char ** triggerProgs;
1709     int_32 * triggerIndices;
1710     const char * sourceName;
1711     rpmRC rc = RPMRC_OK;
1712     int xx;
1713
1714     xx = headerNVR(sourceH, &sourceName, NULL, NULL);
1715
1716     trigger = dsiInit(dsNew(triggeredH, RPMTAG_TRIGGERNAME, scareMem));
1717     if (trigger != NULL)
1718     while (dsiNext(trigger) >= 0) {
1719         rpmTagType tit, tst, tpt;
1720         const char * Name;
1721         int_32 Flags = dsiGetFlags(trigger);
1722 #ifdef  LEGACY
1723         int skip;
1724 #endif
1725
1726         if ((Name = dsiGetN(trigger)) == NULL)
1727             continue;   /* XXX can't happen */
1728
1729         if (strcmp(Name, sourceName))
1730             continue;
1731         if (!(Flags & psm->sense))
1732             continue;
1733
1734 #ifdef  LEGACY
1735         /*
1736          * For some reason, the TRIGGERVERSION stuff includes the name of
1737          * the package which the trigger is based on. We need to skip
1738          * over that here. I suspect that we'll change our minds on this
1739          * and remove that, so I'm going to just 'do the right thing'.
1740          */
1741         skip = strlen(Name);
1742         if (!strncmp(trigger->EVR[trigger->i], trigger->N[trigger->i], skip) &&
1743             (trigger->EVR[trigger->i][skip] == '-'))
1744             skip++;
1745         else
1746             skip = 0;
1747 #endif
1748
1749         if (!headerMatchesDepFlags(sourceH, trigger))
1750             continue;
1751
1752         if (!(  hge(triggeredH, RPMTAG_TRIGGERINDEX, &tit,
1753                        (void **) &triggerIndices, NULL) &&
1754                 hge(triggeredH, RPMTAG_TRIGGERSCRIPTS, &tst,
1755                        (void **) &triggerScripts, NULL) &&
1756                 hge(triggeredH, RPMTAG_TRIGGERSCRIPTPROG, &tpt,
1757                        (void **) &triggerProgs, NULL))
1758             )
1759             continue;
1760
1761         {   int arg1;
1762             int index;
1763
1764             arg1 = rpmdbCountPackages(ts->rpmdb, Name);
1765             if (arg1 < 0) {
1766                 /* XXX W2DO? fails as "execution of script failed" */
1767                 rc = RPMRC_FAIL;
1768             } else {
1769                 arg1 += psm->countCorrection;
1770                 index = triggerIndices[trigger->i];
1771                 if (triggersAlreadyRun == NULL ||
1772                     triggersAlreadyRun[index] == 0)
1773                 {
1774                     rc = runScript(psm, triggeredH, "%trigger", 1,
1775                             triggerProgs + index, triggerScripts[index], 
1776                             arg1, arg2);
1777                     if (triggersAlreadyRun != NULL)
1778                         triggersAlreadyRun[index] = 1;
1779                 }
1780             }
1781         }
1782
1783         triggerIndices = hfd(triggerIndices, tit);
1784         triggerScripts = hfd(triggerScripts, tst);
1785         triggerProgs = hfd(triggerProgs, tpt);
1786
1787         /*
1788          * Each target/source header pair can only result in a single
1789          * script being run.
1790          */
1791         break;
1792     }
1793
1794     trigger = dsFree(trigger);
1795
1796     return rc;
1797 }
1798
1799 /**
1800  * Run trigger scripts in the database that are fired by this header.
1801  * @param psm           package state machine data
1802  * @return              0 on success, 1 on error
1803  */
1804 static int runTriggers(PSM_t psm)
1805         /*@globals rpmGlobalMacroContext,
1806                 fileSystem, internalState @*/
1807         /*@modifies psm, rpmGlobalMacroContext,
1808                 fileSystem, internalState @*/
1809 {
1810     const rpmTransactionSet ts = psm->ts;
1811     TFI_t fi = psm->fi;
1812     int numPackage;
1813     rpmRC rc = RPMRC_OK;
1814
1815     numPackage = rpmdbCountPackages(ts->rpmdb, fi->name) + psm->countCorrection;
1816     if (numPackage < 0)
1817         return 1;
1818
1819     if (fi->h != NULL)  /* XXX can't happen */
1820     {   Header triggeredH;
1821         rpmdbMatchIterator mi;
1822         int countCorrection = psm->countCorrection;
1823
1824         psm->countCorrection = 0;
1825         mi = rpmtsInitIterator(ts, RPMTAG_TRIGGERNAME, fi->name, 0);
1826         while((triggeredH = rpmdbNextIterator(mi)) != NULL) {
1827             rc |= handleOneTrigger(psm, fi->h, triggeredH, numPackage, NULL);
1828         }
1829
1830         mi = rpmdbFreeIterator(mi);
1831         psm->countCorrection = countCorrection;
1832     }
1833
1834     return rc;
1835 }
1836
1837 /**
1838  * Run triggers from this header that are fired by headers in the database.
1839  * @param psm           package state machine data
1840  * @return              0 on success, 1 on error
1841  */
1842 static int runImmedTriggers(PSM_t psm)
1843         /*@globals rpmGlobalMacroContext,
1844                 fileSystem, internalState @*/
1845         /*@modifies psm, rpmGlobalMacroContext,
1846                 fileSystem, internalState @*/
1847 {
1848     const rpmTransactionSet ts = psm->ts;
1849     TFI_t fi = psm->fi;
1850     HGE_t hge = fi->hge;
1851     HFD_t hfd = (fi->hfd ? fi->hfd : headerFreeData);
1852     const char ** triggerNames;
1853     int numTriggers;
1854     int_32 * triggerIndices;
1855     rpmTagType tnt, tit;
1856     int numTriggerIndices;
1857     unsigned char * triggersRun;
1858     rpmRC rc = RPMRC_OK;
1859
1860     if (fi->h == NULL)  return 0;       /* XXX can't happen */
1861
1862     if (!(      hge(fi->h, RPMTAG_TRIGGERNAME, &tnt,
1863                         (void **) &triggerNames, &numTriggers) &&
1864                 hge(fi->h, RPMTAG_TRIGGERINDEX, &tit,
1865                         (void **) &triggerIndices, &numTriggerIndices))
1866         )
1867         return 0;
1868
1869     triggersRun = alloca(sizeof(*triggersRun) * numTriggerIndices);
1870     memset(triggersRun, 0, sizeof(*triggersRun) * numTriggerIndices);
1871
1872     {   Header sourceH = NULL;
1873         int i;
1874
1875         for (i = 0; i < numTriggers; i++) {
1876             rpmdbMatchIterator mi;
1877
1878             if (triggersRun[triggerIndices[i]] != 0) continue;
1879         
1880             mi = rpmtsInitIterator(ts, RPMTAG_NAME, triggerNames[i], 0);
1881
1882             while((sourceH = rpmdbNextIterator(mi)) != NULL) {
1883                 rc |= handleOneTrigger(psm, sourceH, fi->h, 
1884                                 rpmdbGetIteratorCount(mi),
1885                                 triggersRun);
1886             }
1887
1888             mi = rpmdbFreeIterator(mi);
1889         }
1890     }
1891     triggerIndices = hfd(triggerIndices, tit);
1892     triggerNames = hfd(triggerNames, tnt);
1893     return rc;
1894 }
1895
1896 /*@observer@*/ static const char *const pkgStageString(pkgStage a)
1897         /*@*/
1898 {
1899     switch(a) {
1900     case PSM_UNKNOWN:           return "unknown";
1901
1902     case PSM_PKGINSTALL:        return "  install";
1903     case PSM_PKGERASE:          return "    erase";
1904     case PSM_PKGCOMMIT:         return "   commit";
1905     case PSM_PKGSAVE:           return "repackage";
1906
1907     case PSM_INIT:              return "init";
1908     case PSM_PRE:               return "pre";
1909     case PSM_PROCESS:           return "process";
1910     case PSM_POST:              return "post";
1911     case PSM_UNDO:              return "undo";
1912     case PSM_FINI:              return "fini";
1913
1914     case PSM_CREATE:            return "create";
1915     case PSM_NOTIFY:            return "notify";
1916     case PSM_DESTROY:           return "destroy";
1917     case PSM_COMMIT:            return "commit";
1918
1919     case PSM_CHROOT_IN:         return "chrootin";
1920     case PSM_CHROOT_OUT:        return "chrootout";
1921     case PSM_SCRIPT:            return "script";
1922     case PSM_TRIGGERS:          return "triggers";
1923     case PSM_IMMED_TRIGGERS:    return "immedtriggers";
1924
1925     case PSM_RPMIO_FLAGS:       return "rpmioflags";
1926
1927     case PSM_RPMDB_LOAD:        return "rpmdbload";
1928     case PSM_RPMDB_ADD:         return "rpmdbadd";
1929     case PSM_RPMDB_REMOVE:      return "rpmdbremove";
1930
1931     default:                    return "???";
1932     }
1933     /*@noteached@*/
1934 }
1935
1936 /**
1937  * @todo Packages w/o files never get a callback, hence don't get displayed
1938  * on install with -v.
1939  */
1940 /*@-nullpass@*/ /* FIX: testing null annotation for fi->h */
1941 int psmStage(PSM_t psm, pkgStage stage)
1942 {
1943     const rpmTransactionSet ts = psm->ts;
1944     TFI_t fi = psm->fi;
1945     HGE_t hge = fi->hge;
1946     HME_t hme = fi->hme;
1947     HFD_t hfd = (fi->hfd ? fi->hfd : headerFreeData);
1948     rpmRC rc = psm->rc;
1949     int saveerrno;
1950     int xx;
1951
1952     /*@-branchstate@*/
1953     switch (stage) {
1954     case PSM_UNKNOWN:
1955         break;
1956     case PSM_INIT:
1957         rpmMessage(RPMMESS_DEBUG, _("%s: %s-%s-%s has %d files, test = %d\n"),
1958                 psm->stepName, fi->name, fi->version, fi->release,
1959                 fi->fc, (ts->transFlags & RPMTRANS_FLAG_TEST));
1960
1961         /*
1962          * When we run scripts, we pass an argument which is the number of 
1963          * versions of this package that will be installed when we are
1964          * finished.
1965          */
1966         psm->npkgs_installed = rpmdbCountPackages(ts->rpmdb, fi->name);
1967         if (psm->npkgs_installed < 0) {
1968             rc = RPMRC_FAIL;
1969             break;
1970         }
1971
1972         if (psm->goal == PSM_PKGINSTALL) {
1973             psm->scriptArg = psm->npkgs_installed + 1;
1974
1975 assert(psm->mi == NULL);
1976             psm->mi = rpmtsInitIterator(ts, RPMTAG_NAME, fi->name, 0);
1977             xx = rpmdbSetIteratorRE(psm->mi, RPMTAG_VERSION,
1978                         RPMMIRE_DEFAULT, fi->version);
1979             xx = rpmdbSetIteratorRE(psm->mi, RPMTAG_RELEASE,
1980                         RPMMIRE_DEFAULT, fi->release);
1981
1982             while ((psm->oh = rpmdbNextIterator(psm->mi))) {
1983                 fi->record = rpmdbGetIteratorOffset(psm->mi);
1984                 if (ts->transFlags & RPMTRANS_FLAG_MULTILIB)
1985                     psm->oh = headerCopy(psm->oh);
1986                 else
1987                     psm->oh = NULL;
1988                 /*@loopbreak@*/ break;
1989             }
1990             psm->mi = rpmdbFreeIterator(psm->mi);
1991             rc = RPMRC_OK;
1992
1993             if (fi->fc > 0 && fi->fstates == NULL) {
1994                 fi->fstates = xmalloc(sizeof(*fi->fstates) * fi->fc);
1995                 memset(fi->fstates, RPMFILE_STATE_NORMAL, fi->fc);
1996             }
1997
1998             if (fi->fc <= 0)                            break;
1999             if (ts->transFlags & RPMTRANS_FLAG_JUSTDB)  break;
2000         
2001             /*
2002              * Old format relocateable packages need the entire default
2003              * prefix stripped to form the cpio list, while all other packages
2004              * need the leading / stripped.
2005              */
2006             {   const char * p;
2007                 rc = hge(fi->h, RPMTAG_DEFAULTPREFIX, NULL, (void **) &p, NULL);
2008                 fi->striplen = (rc ? strlen(p) + 1 : 1); 
2009             }
2010             fi->mapflags =
2011                 CPIO_MAP_PATH | CPIO_MAP_MODE | CPIO_MAP_UID | CPIO_MAP_GID;
2012         
2013             if (headerIsEntry(fi->h, RPMTAG_ORIGBASENAMES))
2014                 buildOrigFileList(fi->h, &fi->apath, NULL);
2015             else
2016                 rpmBuildFileList(fi->h, &fi->apath, NULL);
2017         
2018             if (fi->fuser == NULL)
2019                 xx = hge(fi->h, RPMTAG_FILEUSERNAME, NULL,
2020                                 (void **) &fi->fuser, NULL);
2021             if (fi->fgroup == NULL)
2022                 xx = hge(fi->h, RPMTAG_FILEGROUPNAME, NULL,
2023                                 (void **) &fi->fgroup, NULL);
2024             if (fi->fuids == NULL)
2025                 fi->fuids = xcalloc(sizeof(*fi->fuids), fi->fc);
2026             if (fi->fgids == NULL)
2027                 fi->fgids = xcalloc(sizeof(*fi->fgids), fi->fc);
2028             rc = RPMRC_OK;
2029         }
2030         if (psm->goal == PSM_PKGERASE || psm->goal == PSM_PKGSAVE) {
2031             psm->scriptArg = psm->npkgs_installed - 1;
2032         
2033             /* Retrieve installed header. */
2034             rc = psmStage(psm, PSM_RPMDB_LOAD);
2035         }
2036         if (psm->goal == PSM_PKGSAVE) {
2037             /* Open output package for writing. */
2038             {   const char * bfmt = rpmGetPath("%{_repackage_name_fmt}", NULL);
2039                 const char * pkgbn =
2040                         headerSprintf(fi->h, bfmt, rpmTagTable, rpmHeaderFormats, NULL);
2041
2042                 bfmt = _free(bfmt);
2043                 psm->pkgURL = rpmGenPath("%{?_repackage_root:%{_repackage_root}}",
2044                                          "%{?_repackage_dir:%{_repackage_dir}}",
2045                                         pkgbn);
2046                 pkgbn = _free(pkgbn);
2047                 (void) urlPath(psm->pkgURL, &psm->pkgfn);
2048                 psm->fd = Fopen(psm->pkgfn, "w.ufdio");
2049                 if (psm->fd == NULL || Ferror(psm->fd)) {
2050                     rc = RPMRC_FAIL;
2051                     break;
2052                 }
2053             }
2054         }
2055         break;
2056     case PSM_PRE:
2057         if (ts->transFlags & RPMTRANS_FLAG_TEST)        break;
2058
2059         /* Change root directory if requested and not already done. */
2060         rc = psmStage(psm, PSM_CHROOT_IN);
2061
2062         if (psm->goal == PSM_PKGINSTALL) {
2063             psm->scriptTag = RPMTAG_PREIN;
2064             psm->progTag = RPMTAG_PREINPROG;
2065
2066             if (!(ts->transFlags & RPMTRANS_FLAG_NOTRIGGERPREIN)) {
2067                 /* XXX FIXME: implement %triggerprein. */
2068             }
2069
2070             if (!(ts->transFlags & RPMTRANS_FLAG_NOPRE)) {
2071                 rc = psmStage(psm, PSM_SCRIPT);
2072                 if (rc) {
2073                     rpmError(RPMERR_SCRIPT,
2074                         _("%s: %s scriptlet failed (%d), skipping %s-%s-%s\n"),
2075                         psm->stepName, tag2sln(psm->scriptTag), rc,
2076                         fi->name, fi->version, fi->release);
2077                     break;
2078                 }
2079             }
2080         }
2081
2082         if (psm->goal == PSM_PKGERASE) {
2083             psm->scriptTag = RPMTAG_PREUN;
2084             psm->progTag = RPMTAG_PREUNPROG;
2085             psm->sense = RPMSENSE_TRIGGERUN;
2086             psm->countCorrection = -1;
2087
2088             if (!(ts->transFlags & RPMTRANS_FLAG_NOTRIGGERUN)) {
2089                 /* Run triggers in other package(s) this package sets off. */
2090                 rc = psmStage(psm, PSM_TRIGGERS);
2091                 if (rc) break;
2092
2093                 /* Run triggers in this package other package(s) set off. */
2094                 rc = psmStage(psm, PSM_IMMED_TRIGGERS);
2095                 if (rc) break;
2096             }
2097
2098             if (!(ts->transFlags & RPMTRANS_FLAG_NOPREUN))
2099                 rc = psmStage(psm, PSM_SCRIPT);
2100         }
2101         if (psm->goal == PSM_PKGSAVE) {
2102             /* Regenerate original header. */
2103             {   void * uh = NULL;
2104                 int_32 uht, uhc;
2105
2106                 if (headerGetEntry(fi->h, RPMTAG_HEADERIMMUTABLE, &uht, &uh, &uhc)) {
2107                     psm->oh = headerCopyLoad(uh);
2108                     uh = hfd(uh, uht);
2109                 } else {
2110                     psm->oh = headerLink(fi->h, "PSM_PKGSAVE_PRE)");
2111                 }
2112             }
2113
2114             /* Add remove transaction id to header. */
2115             if (psm->oh)
2116             {   int_32 tid = ts->id;
2117                 xx = headerAddEntry(psm->oh, RPMTAG_REMOVETID,
2118                         RPM_INT32_TYPE, &tid, 1);
2119             }
2120
2121             /* Retrieve type of payload compression. */
2122             /*@-nullstate@*/    /* FIX: psm->oh may be NULL */
2123             rc = psmStage(psm, PSM_RPMIO_FLAGS);
2124             /*@=nullstate@*/
2125
2126             /* Write the lead section into the package. */
2127             {   int archnum = -1;
2128                 int osnum = -1;
2129                 struct rpmlead lead;
2130
2131 #ifndef DYING
2132                 rpmGetArchInfo(NULL, &archnum);
2133                 rpmGetOsInfo(NULL, &osnum);
2134 #endif
2135
2136                 memset(&lead, 0, sizeof(lead));
2137                 /* XXX Set package version conditioned on noDirTokens. */
2138                 lead.major = 4;
2139                 lead.minor = 0;
2140                 lead.type = RPMLEAD_BINARY;
2141                 lead.archnum = archnum;
2142                 lead.osnum = osnum;
2143                 lead.signature_type = RPMSIGTYPE_HEADERSIG;
2144
2145                 {   char buf[256];
2146                     sprintf(buf, "%s-%s-%s", fi->name, fi->version, fi->release);
2147                     strncpy(lead.name, buf, sizeof(lead.name));
2148                 }
2149
2150                 rc = writeLead(psm->fd, &lead);
2151                 if (rc) {
2152                     rpmError(RPMERR_NOSPACE, _("Unable to write package: %s\n"),
2153                          Fstrerror(psm->fd));
2154                     rc = RPMRC_FAIL;
2155                     break;
2156                 }
2157             }
2158
2159             /* Write the signature section into the package. */
2160             {   Header sig = headerRegenSigHeader(fi->h);
2161                 rc = rpmWriteSignature(psm->fd, sig);
2162                 sig = rpmFreeSignature(sig);
2163                 if (rc) break;
2164             }
2165
2166             /* Write the metadata section into the package. */
2167             rc = headerWrite(psm->fd, psm->oh, HEADER_MAGIC_YES);
2168             if (rc) break;
2169         }
2170         break;
2171     case PSM_PROCESS:
2172         if (ts->transFlags & RPMTRANS_FLAG_TEST)        break;
2173
2174         if (psm->goal == PSM_PKGINSTALL) {
2175             int i;
2176
2177             if (fi->fc <= 0)                            break;
2178             if (ts->transFlags & RPMTRANS_FLAG_JUSTDB)  break;
2179
2180             for (i = 0; i < fi->fc; i++) {
2181                 uid_t uid;
2182                 gid_t gid;
2183
2184                 uid = fi->uid;
2185                 gid = fi->gid;
2186                 if (fi->fuser && unameToUid(fi->fuser[i], &uid)) {
2187                     rpmMessage(RPMMESS_WARNING,
2188                         _("user %s does not exist - using root\n"),
2189                         fi->fuser[i]);
2190                     uid = 0;
2191                     /* XXX this diddles header memory. */
2192                     fi->fmodes[i] &= ~S_ISUID;  /* turn off the suid bit */
2193                 }
2194
2195                 if (fi->fgroup && gnameToGid(fi->fgroup[i], &gid)) {
2196                     rpmMessage(RPMMESS_WARNING,
2197                         _("group %s does not exist - using root\n"),
2198                         fi->fgroup[i]);
2199                     gid = 0;
2200                     /* XXX this diddles header memory. */
2201                     fi->fmodes[i] &= ~S_ISGID;  /* turn off the sgid bit */
2202                 }
2203                 if (fi->fuids) fi->fuids[i] = uid;
2204                 if (fi->fgids) fi->fgids[i] = gid;
2205             }
2206
2207             /* Retrieve type of payload compression. */
2208             rc = psmStage(psm, PSM_RPMIO_FLAGS);
2209
2210             if (fi->fd == NULL) {       /* XXX can't happen */
2211                 rc = RPMRC_FAIL;
2212                 break;
2213             }
2214             /*@-nullpass@*/     /* LCL: fi->fd != NULL here. */
2215             psm->cfd = Fdopen(fdDup(Fileno(fi->fd)), psm->rpmio_flags);
2216             /*@=nullpass@*/
2217             if (psm->cfd == NULL) {     /* XXX can't happen */
2218                 rc = RPMRC_FAIL;
2219                 break;
2220             }
2221
2222             rc = fsmSetup(fi->fsm, FSM_PKGINSTALL, ts, fi,
2223                         psm->cfd, NULL, &psm->failedFile);
2224             xx = fsmTeardown(fi->fsm);
2225
2226             saveerrno = errno; /* XXX FIXME: Fclose with libio destroys errno */
2227             xx = Fclose(psm->cfd);
2228             psm->cfd = NULL;
2229             /*@-mods@*/
2230             errno = saveerrno; /* XXX FIXME: Fclose with libio destroys errno */
2231             /*@=mods@*/
2232
2233             if (!rc)
2234                 rc = psmStage(psm, PSM_COMMIT);
2235
2236             if (rc) {
2237                 rpmError(RPMERR_CPIO,
2238                         _("unpacking of archive failed%s%s: %s\n"),
2239                         (psm->failedFile != NULL ? _(" on file ") : ""),
2240                         (psm->failedFile != NULL ? psm->failedFile : ""),
2241                         cpioStrerror(rc));
2242                 rc = RPMRC_FAIL;
2243                 break;
2244             }
2245             psm->what = RPMCALLBACK_INST_PROGRESS;
2246             psm->amount = (fi->archiveSize ? fi->archiveSize : 100);
2247             psm->total = psm->amount;
2248             xx = psmStage(psm, PSM_NOTIFY);
2249         }
2250         if (psm->goal == PSM_PKGERASE) {
2251
2252             if (fi->fc <= 0)                            break;
2253             if (ts->transFlags & RPMTRANS_FLAG_JUSTDB)  break;
2254             if (ts->transFlags & RPMTRANS_FLAG_APPLYONLY)       break;
2255
2256             psm->what = RPMCALLBACK_UNINST_START;
2257             psm->amount = fi->fc;       /* XXX W2DO? looks wrong. */
2258             psm->total = fi->fc;
2259             xx = psmStage(psm, PSM_NOTIFY);
2260
2261             rc = fsmSetup(fi->fsm, FSM_PKGERASE, ts, fi,
2262                         NULL, NULL, &psm->failedFile);
2263             xx = fsmTeardown(fi->fsm);
2264
2265             psm->what = RPMCALLBACK_UNINST_STOP;
2266             psm->amount = 0;            /* XXX W2DO? looks wrong. */
2267             psm->total = fi->fc;
2268             xx = psmStage(psm, PSM_NOTIFY);
2269
2270         }
2271         if (psm->goal == PSM_PKGSAVE) {
2272             fileAction * actions = fi->actions;
2273             fileAction action = fi->action;
2274
2275             fi->action = FA_COPYOUT;
2276             fi->actions = NULL;
2277
2278             if (psm->fd == NULL) {      /* XXX can't happen */
2279                 rc = RPMRC_FAIL;
2280                 break;
2281             }
2282             /*@-nullpass@*/     /* LCL: psm->fd != NULL here. */
2283             xx = Fflush(psm->fd);
2284             psm->cfd = Fdopen(fdDup(Fileno(psm->fd)), psm->rpmio_flags);
2285             /*@=nullpass@*/
2286             if (psm->cfd == NULL) {     /* XXX can't happen */
2287                 rc = RPMRC_FAIL;
2288                 break;
2289             }
2290
2291             rc = fsmSetup(fi->fsm, FSM_PKGBUILD, ts, fi, psm->cfd,
2292                         NULL, &psm->failedFile);
2293             xx = fsmTeardown(fi->fsm);
2294
2295             saveerrno = errno; /* XXX FIXME: Fclose with libio destroys errno */
2296             xx = Fclose(psm->cfd);
2297             psm->cfd = NULL;
2298             /*@-mods@*/
2299             errno = saveerrno;
2300             /*@=mods@*/
2301
2302             fi->action = action;
2303             fi->actions = actions;
2304         }
2305         break;
2306     case PSM_POST:
2307         if (ts->transFlags & RPMTRANS_FLAG_TEST)        break;
2308
2309         if (psm->goal == PSM_PKGINSTALL) {
2310             int_32 installTime = (int_32) time(NULL);
2311
2312             if (fi->h == NULL) break;   /* XXX can't happen */
2313             if (fi->fstates != NULL && fi->fc > 0)
2314                 xx = headerAddEntry(fi->h, RPMTAG_FILESTATES, RPM_CHAR_TYPE,
2315                                 fi->fstates, fi->fc);
2316
2317             xx = headerAddEntry(fi->h, RPMTAG_INSTALLTIME, RPM_INT32_TYPE,
2318                                 &installTime, 1);
2319
2320             if (ts->transFlags & RPMTRANS_FLAG_MULTILIB) {
2321                 uint_32 multiLib, * newMultiLib, * p;
2322
2323                 if (hge(fi->h, RPMTAG_MULTILIBS, NULL,
2324                                 (void **) &newMultiLib, NULL) &&
2325                     hge(psm->oh, RPMTAG_MULTILIBS, NULL,
2326                                 (void **) &p, NULL))
2327                 {
2328                     multiLib = *p;
2329                     multiLib |= *newMultiLib;
2330                     xx = hme(psm->oh, RPMTAG_MULTILIBS, RPM_INT32_TYPE,
2331                                       &multiLib, 1);
2332                 }
2333                 rc = mergeFiles(fi, psm->oh, fi->h);
2334                 if (rc) break;
2335             }
2336
2337
2338             /*
2339              * If this package has already been installed, remove it from
2340              * the database before adding the new one.
2341              */
2342             if (fi->record && !(ts->transFlags & RPMTRANS_FLAG_APPLYONLY)) {
2343                 rc = psmStage(psm, PSM_RPMDB_REMOVE);
2344                 if (rc) break;
2345             }
2346
2347             rc = psmStage(psm, PSM_RPMDB_ADD);
2348             if (rc) break;
2349
2350             psm->scriptTag = RPMTAG_POSTIN;
2351             psm->progTag = RPMTAG_POSTINPROG;
2352             psm->sense = RPMSENSE_TRIGGERIN;
2353             psm->countCorrection = 0;
2354
2355             if (!(ts->transFlags & RPMTRANS_FLAG_NOPOST)) {
2356                 rc = psmStage(psm, PSM_SCRIPT);
2357                 if (rc) break;
2358             }
2359             if (!(ts->transFlags & RPMTRANS_FLAG_NOTRIGGERIN)) {
2360                 /* Run triggers in other package(s) this package sets off. */
2361                 rc = psmStage(psm, PSM_TRIGGERS);
2362                 if (rc) break;
2363
2364                 /* Run triggers in this package other package(s) set off. */
2365                 rc = psmStage(psm, PSM_IMMED_TRIGGERS);
2366                 if (rc) break;
2367             }
2368
2369             if (!(ts->transFlags & RPMTRANS_FLAG_APPLYONLY))
2370                 rc = markReplacedFiles(psm);
2371
2372         }
2373         if (psm->goal == PSM_PKGERASE) {
2374
2375             psm->scriptTag = RPMTAG_POSTUN;
2376             psm->progTag = RPMTAG_POSTUNPROG;
2377             psm->sense = RPMSENSE_TRIGGERPOSTUN;
2378             psm->countCorrection = -1;
2379
2380             if (!(ts->transFlags & RPMTRANS_FLAG_NOPOSTUN)) {
2381                 rc = psmStage(psm, PSM_SCRIPT);
2382                 /* XXX WTFO? postun failures don't cause erasure failure. */
2383             }
2384
2385             if (!(ts->transFlags & RPMTRANS_FLAG_NOTRIGGERPOSTUN)) {
2386                 /* Run triggers in other package(s) this package sets off. */
2387                 rc = psmStage(psm, PSM_TRIGGERS);
2388                 if (rc) break;
2389             }
2390
2391             if (!(ts->transFlags & RPMTRANS_FLAG_APPLYONLY))
2392                 rc = psmStage(psm, PSM_RPMDB_REMOVE);
2393         }
2394         if (psm->goal == PSM_PKGSAVE) {
2395         }
2396
2397         /* Restore root directory if changed. */
2398         xx = psmStage(psm, PSM_CHROOT_OUT);
2399         break;
2400     case PSM_UNDO:
2401         break;
2402     case PSM_FINI:
2403         /* Restore root directory if changed. */
2404         xx = psmStage(psm, PSM_CHROOT_OUT);
2405
2406         if (psm->fd) {
2407             saveerrno = errno; /* XXX FIXME: Fclose with libio destroys errno */
2408             xx = Fclose(psm->fd);
2409             psm->fd = NULL;
2410             /*@-mods@*/
2411             errno = saveerrno;
2412             /*@=mods@*/
2413         }
2414
2415         if (psm->goal == PSM_PKGSAVE) {
2416             if (!rc) {
2417                 rpmMessage(RPMMESS_VERBOSE, _("Wrote: %s\n"),
2418                         (psm->pkgURL ? psm->pkgURL : "???"));
2419             }
2420         }
2421
2422         if (rc) {
2423             if (psm->failedFile)
2424                 rpmError(RPMERR_CPIO,
2425                         _("%s failed on file %s: %s\n"),
2426                         psm->stepName, psm->failedFile, cpioStrerror(rc));
2427             else
2428                 rpmError(RPMERR_CPIO, _("%s failed: %s\n"),
2429                         psm->stepName, cpioStrerror(rc));
2430         }
2431
2432         if (fi->h && (psm->goal == PSM_PKGERASE || psm->goal == PSM_PKGSAVE))
2433             fi->h = headerFree(fi->h, "PSM_PKGSAVE_POST fi->h");
2434         psm->oh = headerFree(psm->oh, "PSM_PKGSAVE_POST psm->oh");
2435         psm->pkgURL = _free(psm->pkgURL);
2436         psm->rpmio_flags = _free(psm->rpmio_flags);
2437         psm->failedFile = _free(psm->failedFile);
2438
2439         fi->fgids = _free(fi->fgids);
2440         fi->fuids = _free(fi->fuids);
2441         fi->fgroup = hfd(fi->fgroup, -1);
2442         fi->fuser = hfd(fi->fuser, -1);
2443         fi->apath = _free(fi->apath);
2444         fi->fstates = _free(fi->fstates);
2445         break;
2446
2447     case PSM_PKGINSTALL:
2448     case PSM_PKGERASE:
2449     case PSM_PKGSAVE:
2450         psm->goal = stage;
2451         psm->rc = RPMRC_OK;
2452         psm->stepName = pkgStageString(stage);
2453
2454         rc = psmStage(psm, PSM_INIT);
2455         if (!rc) rc = psmStage(psm, PSM_PRE);
2456         if (!rc) rc = psmStage(psm, PSM_PROCESS);
2457         if (!rc) rc = psmStage(psm, PSM_POST);
2458         xx = psmStage(psm, PSM_FINI);
2459         break;
2460     case PSM_PKGCOMMIT:
2461         break;
2462
2463     case PSM_CREATE:
2464         break;
2465     case PSM_NOTIFY:
2466         if (ts && ts->notify) {
2467             /*@-noeffectuncon @*/ /* FIX: check rc */
2468             (void) ts->notify(fi->h, psm->what, psm->amount, psm->total,
2469                                 fi->key, ts->notifyData);
2470             /*@=noeffectuncon @*/
2471         }
2472         break;
2473     case PSM_DESTROY:
2474         break;
2475     case PSM_COMMIT:
2476         if (!(ts->transFlags & RPMTRANS_FLAG_PKGCOMMIT)) break;
2477         if (ts->transFlags & RPMTRANS_FLAG_APPLYONLY) break;
2478
2479         rc = fsmSetup(fi->fsm, FSM_PKGCOMMIT, ts, fi,
2480                         NULL, NULL, &psm->failedFile);
2481         xx = fsmTeardown(fi->fsm);
2482         break;
2483
2484     case PSM_CHROOT_IN:
2485         /* Change root directory if requested and not already done. */
2486         if (ts->rootDir && !ts->chrootDone && !psm->chrootDone) {
2487             static int _loaded = 0;
2488
2489             /*
2490              * This loads all of the name services libraries, in case we
2491              * don't have access to them in the chroot().
2492              */
2493             if (!_loaded) {
2494                 (void)getpwnam("root");
2495                 endpwent();
2496                 _loaded++;
2497             }
2498
2499             xx = chdir("/");
2500             /*@-superuser@*/
2501             rc = chroot(ts->rootDir);
2502             /*@=superuser@*/
2503             psm->chrootDone = ts->chrootDone = 1;
2504             if (ts->rpmdb != NULL) ts->rpmdb->db_chrootDone = 1;
2505             /*@-onlytrans@*/
2506             /*@-mods@*/
2507             chroot_prefix = ts->rootDir;
2508             /*@=mods@*/
2509             /*@=onlytrans@*/
2510         }
2511         break;
2512     case PSM_CHROOT_OUT:
2513         /* Restore root directory if changed. */
2514         if (psm->chrootDone) {
2515             /*@-superuser@*/
2516             rc = chroot(".");
2517             /*@=superuser@*/
2518             psm->chrootDone = ts->chrootDone = 0;
2519             if (ts->rpmdb != NULL) ts->rpmdb->db_chrootDone = 0;
2520             /*@-mods@*/
2521             chroot_prefix = NULL;
2522             /*@=mods@*/
2523             xx = chdir(ts->currDir);
2524         }
2525         break;
2526     case PSM_SCRIPT:    /* Run current package scriptlets. */
2527         rc = runInstScript(psm);
2528         break;
2529     case PSM_TRIGGERS:
2530         /* Run triggers in other package(s) this package sets off. */
2531         rc = runTriggers(psm);
2532         break;
2533     case PSM_IMMED_TRIGGERS:    
2534         /* Run triggers in this package other package(s) set off. */
2535         rc = runImmedTriggers(psm);
2536         break;
2537
2538     case PSM_RPMIO_FLAGS:
2539     {   const char * payload_compressor = NULL;
2540         char * t;
2541
2542         if (!hge(fi->h, RPMTAG_PAYLOADCOMPRESSOR, NULL,
2543                             (void **) &payload_compressor, NULL))
2544             payload_compressor = "gzip";
2545         psm->rpmio_flags = t = xmalloc(sizeof("w9.gzdio"));
2546         *t = '\0';
2547         t = stpcpy(t, ((psm->goal == PSM_PKGSAVE) ? "w9" : "r"));
2548         if (!strcmp(payload_compressor, "gzip"))
2549             t = stpcpy(t, ".gzdio");
2550         if (!strcmp(payload_compressor, "bzip2"))
2551             t = stpcpy(t, ".bzdio");
2552         rc = RPMRC_OK;
2553     }   break;
2554
2555     case PSM_RPMDB_LOAD:
2556 assert(psm->mi == NULL);
2557         psm->mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES,
2558                                 &fi->record, sizeof(fi->record));
2559
2560         fi->h = rpmdbNextIterator(psm->mi);
2561         if (fi->h)
2562             fi->h = headerLink(fi->h, "PSM_RPMDB_LOAD)");
2563 else {
2564 fprintf(stderr, "*** PSM_RDB_LOAD: header #%u not found\n", fi->record);
2565 }
2566         psm->mi = rpmdbFreeIterator(psm->mi);
2567         rc = (fi->h ? RPMRC_OK : RPMRC_FAIL);
2568         break;
2569     case PSM_RPMDB_ADD:
2570         if (ts->transFlags & RPMTRANS_FLAG_TEST)        break;
2571         if (fi->h != NULL)      /* XXX can't happen */
2572         rc = rpmdbAdd(ts->rpmdb, ts->id, fi->h);
2573         break;
2574     case PSM_RPMDB_REMOVE:
2575         if (ts->transFlags & RPMTRANS_FLAG_TEST)        break;
2576         rc = rpmdbRemove(ts->rpmdb, ts->id, fi->record);
2577         break;
2578
2579     default:
2580         break;
2581     }
2582     /*@=branchstate@*/
2583
2584     /*@-nullstate@*/    /* FIX: psm->oh and psm->fi->h may be NULL. */
2585     return rc;
2586     /*@=nullstate@*/
2587 }
2588 /*@=nullpass@*/