Be more intelligent about storing file user and group names
[platform/upstream/rpm.git] / lib / rpmfi.c
1 /** \ingroup rpmdep
2  * \file lib/rpmfi.c
3  * Routines to handle file info tag sets.
4  */
5
6 #include "system.h"
7
8 #include <rpm/rpmlog.h>
9 #include <rpm/rpmts.h>
10 #include <rpm/rpmfileutil.h>    /* XXX rpmDoDigest */
11 #include <rpm/rpmstring.h>
12 #include <rpm/rpmmacro.h>       /* XXX rpmCleanPath */
13 #include <rpm/rpmds.h>
14
15 #include "lib/rpmfi_internal.h"
16 #include "lib/rpmte_internal.h" /* relocations */
17 #include "lib/cpio.h"   /* XXX CPIO_FOO */
18 #include "lib/fsm.h"    /* XXX newFSM() */
19
20 #include "debug.h"
21
22 /*
23  * Simple and stupid user + groupname "cache."
24  * Store each unique user and group name string just once, retrieve
25  * by index value. As the number of unique names is typically very low,
26  * the dumb linear lookup appears to be fast enough and hash table seems
27  * like an overkill.
28  */
29 struct ugcache_s {
30     char **uniq;
31     ugidx_t num;
32 } ugcache = { NULL, 0 };
33
34 static ugidx_t ugcachePut(const char *str)
35 {
36     int found = 0;
37     ugidx_t ret;
38
39     for (ugidx_t i = 0; i < ugcache.num; i++) {
40         if (strcmp(str, ugcache.uniq[i]) == 0) {
41             ret = i;
42             found = 1;
43             break;
44         }
45     }
46     if (!found) {
47         /* blow up on index wraparound */
48         assert((ugidx_t)(ugcache.num + 1) > ugcache.num);
49         ugcache.uniq = xrealloc(ugcache.uniq, 
50                                 sizeof(ugcache.uniq) * (ugcache.num+1));
51         ugcache.uniq[ugcache.num] = xstrdup(str);
52         ret = ugcache.num;
53         ugcache.num++;
54     }
55     return ret;
56 }
57
58 static const char *ugcacheGet(ugidx_t idx)
59 {
60     const char *name = NULL;
61     if (idx >= 0 && idx < ugcache.num && ugcache.uniq != NULL)
62         name = ugcache.uniq[idx];
63     return name;
64 }
65     
66 int _rpmfi_debug = 0;
67
68 rpmfi rpmfiUnlink(rpmfi fi, const char * msg)
69 {
70     if (fi == NULL) return NULL;
71 if (_rpmfi_debug && msg != NULL)
72 fprintf(stderr, "--> fi %p -- %d %s\n", fi, fi->nrefs, msg);
73     fi->nrefs--;
74     return NULL;
75 }
76
77 rpmfi rpmfiLink(rpmfi fi, const char * msg)
78 {
79     if (fi == NULL) return NULL;
80     fi->nrefs++;
81 if (_rpmfi_debug && msg != NULL)
82 fprintf(stderr, "--> fi %p ++ %d %s\n", fi, fi->nrefs, msg);
83     return fi;
84 }
85
86 rpm_count_t rpmfiFC(rpmfi fi)
87 {
88     return (fi != NULL ? fi->fc : 0);
89 }
90
91 rpm_count_t rpmfiDC(rpmfi fi)
92 {
93     return (fi != NULL ? fi->dc : 0);
94 }
95
96 #ifdef  NOTYET
97 int rpmfiDI(rpmfi fi)
98 {
99 }
100 #endif
101
102 int rpmfiFX(rpmfi fi)
103 {
104     return (fi != NULL ? fi->i : -1);
105 }
106
107 int rpmfiSetFX(rpmfi fi, int fx)
108 {
109     int i = -1;
110
111     if (fi != NULL && fx >= 0 && fx < fi->fc) {
112         i = fi->i;
113         fi->i = fx;
114         fi->j = fi->dil[fi->i];
115     }
116     return i;
117 }
118
119 int rpmfiDX(rpmfi fi)
120 {
121     return (fi != NULL ? fi->j : -1);
122 }
123
124 int rpmfiSetDX(rpmfi fi, int dx)
125 {
126     int j = -1;
127
128     if (fi != NULL && dx >= 0 && dx < fi->dc) {
129         j = fi->j;
130         fi->j = dx;
131     }
132     return j;
133 }
134
135 const char * rpmfiBN(rpmfi fi)
136 {
137     const char * BN = NULL;
138
139     if (fi != NULL && fi->i >= 0 && fi->i < fi->fc) {
140         if (fi->bnl != NULL)
141             BN = fi->bnl[fi->i];
142     }
143     return BN;
144 }
145
146 const char * rpmfiDN(rpmfi fi)
147 {
148     const char * DN = NULL;
149
150     if (fi != NULL && fi->j >= 0 && fi->j < fi->dc) {
151         if (fi->dnl != NULL)
152             DN = fi->dnl[fi->j];
153     }
154     return DN;
155 }
156
157 const char * rpmfiFN(rpmfi fi)
158 {
159     const char * FN = "";
160
161     if (fi != NULL && fi->i >= 0 && fi->i < fi->fc) {
162         char * t;
163         if (fi->fn == NULL) {
164             size_t dnlmax = 0, bnlmax = 0, len;
165             for (int i = 0; i < fi->dc; i++) {
166                 if ((len = strlen(fi->dnl[i])) > dnlmax)
167                     dnlmax = len;
168             }
169             for (int i = 0; i < fi->fc; i++) {
170                 if ((len = strlen(fi->bnl[i])) > bnlmax)
171                     bnlmax = len;
172             }
173             fi->fn = xmalloc(dnlmax + bnlmax + 1);
174         }
175         FN = t = fi->fn;
176         *t = '\0';
177         t = stpcpy(t, fi->dnl[fi->dil[fi->i]]);
178         t = stpcpy(t, fi->bnl[fi->i]);
179     }
180     return FN;
181 }
182
183 rpmfileAttrs rpmfiFFlags(rpmfi fi)
184 {
185     rpmfileAttrs FFlags = 0;
186
187     if (fi != NULL && fi->i >= 0 && fi->i < fi->fc) {
188         if (fi->fflags != NULL)
189             FFlags = fi->fflags[fi->i];
190     }
191     return FFlags;
192 }
193
194 rpmVerifyAttrs rpmfiVFlags(rpmfi fi)
195 {
196     rpmVerifyAttrs VFlags = 0;
197
198     if (fi != NULL && fi->i >= 0 && fi->i < fi->fc) {
199         if (fi->vflags != NULL)
200             VFlags = fi->vflags[fi->i];
201     }
202     return VFlags;
203 }
204
205 rpm_mode_t rpmfiFMode(rpmfi fi)
206 {
207     rpm_mode_t fmode = 0;
208
209     if (fi != NULL && fi->i >= 0 && fi->i < fi->fc) {
210         if (fi->fmodes != NULL)
211             fmode = fi->fmodes[fi->i];
212     }
213     return fmode;
214 }
215
216 rpmfileState rpmfiFState(rpmfi fi)
217 {
218     rpmfileState fstate = RPMFILE_STATE_MISSING;
219
220     if (fi != NULL && fi->i >= 0 && fi->i < fi->fc) {
221         if (fi->fstates != NULL)
222             fstate = fi->fstates[fi->i];
223     }
224     return fstate;
225 }
226
227 const unsigned char * rpmfiMD5(rpmfi fi)
228 {
229     const unsigned char *digest;
230     pgpHashAlgo algo = 0;
231
232     digest = rpmfiFDigest(fi, &algo, NULL);
233     return (algo == PGPHASHALGO_MD5) ? digest : NULL;
234 }
235
236 const unsigned char * rpmfiFDigest(rpmfi fi, pgpHashAlgo *algo, size_t *len)
237 {
238     const unsigned char *digest = NULL;
239
240     if (fi != NULL && fi->i >= 0 && fi->i < fi->fc) {
241         size_t diglen = rpmDigestLength(fi->digestalgo);
242         if (fi->digests != NULL)
243             digest = fi->digests + (diglen * fi->i);
244         if (len) 
245             *len = diglen;
246         if (algo) 
247             *algo = fi->digestalgo;
248     }
249     return digest;
250 }
251
252 char * rpmfiFDigestHex(rpmfi fi, pgpHashAlgo *algo)
253 {
254     size_t diglen = 0;
255     char *fdigest = NULL;
256     const unsigned char *digest = rpmfiFDigest(fi, algo, &diglen);
257     if (digest) {
258         fdigest = pgpHexStr(digest, diglen);
259     }
260     return fdigest;
261 }
262
263 const char * rpmfiFLink(rpmfi fi)
264 {
265     const char * flink = NULL;
266
267     if (fi != NULL && fi->i >= 0 && fi->i < fi->fc) {
268         if (fi->flinks != NULL)
269             flink = fi->flinks[fi->i];
270     }
271     return flink;
272 }
273
274 rpm_loff_t rpmfiFSize(rpmfi fi)
275 {
276     rpm_loff_t fsize = 0;
277
278     if (fi != NULL && fi->i >= 0 && fi->i < fi->fc) {
279         if (fi->fsizes != NULL)
280             fsize = fi->fsizes[fi->i];
281     }
282     return fsize;
283 }
284
285 rpm_rdev_t rpmfiFRdev(rpmfi fi)
286 {
287     rpm_rdev_t frdev = 0;
288
289     if (fi != NULL && fi->i >= 0 && fi->i < fi->fc) {
290         if (fi->frdevs != NULL)
291             frdev = fi->frdevs[fi->i];
292     }
293     return frdev;
294 }
295
296 rpm_ino_t rpmfiFInode(rpmfi fi)
297 {
298     rpm_ino_t finode = 0;
299
300     if (fi != NULL && fi->i >= 0 && fi->i < fi->fc) {
301         if (fi->finodes != NULL)
302             finode = fi->finodes[fi->i];
303     }
304     return finode;
305 }
306
307 rpm_color_t rpmfiColor(rpmfi fi)
308 {
309     rpm_color_t color = 0;
310
311     if (fi != NULL && fi->fcolors != NULL) {
312         for (int i = 0; i < fi->fc; i++)
313             color |= fi->fcolors[i];
314         /* XXX ignore all but lsnibble for now. */
315         color &= 0xf;
316     }
317     return color;
318 }
319
320 rpm_color_t rpmfiFColor(rpmfi fi)
321 {
322     rpm_color_t fcolor = 0;
323
324     if (fi != NULL && fi->i >= 0 && fi->i < fi->fc) {
325         if (fi->fcolors != NULL)
326             /* XXX ignore all but lsnibble for now. */
327             fcolor = (fi->fcolors[fi->i] & 0x0f);
328     }
329     return fcolor;
330 }
331
332 const char * rpmfiFClass(rpmfi fi)
333 {
334     const char * fclass = NULL;
335     int cdictx;
336
337     if (fi != NULL && fi->fcdictx != NULL && fi->i >= 0 && fi->i < fi->fc) {
338         cdictx = fi->fcdictx[fi->i];
339         if (fi->cdict != NULL && cdictx >= 0 && cdictx < fi->ncdict)
340             fclass = fi->cdict[cdictx];
341     }
342     return fclass;
343 }
344
345 uint32_t rpmfiFDepends(rpmfi fi, const uint32_t ** fddictp)
346 {
347     int fddictx = -1;
348     int fddictn = 0;
349     const uint32_t * fddict = NULL;
350
351     if (fi != NULL && fi->i >= 0 && fi->i < fi->fc) {
352         if (fi->fddictn != NULL)
353             fddictn = fi->fddictn[fi->i];
354         if (fddictn > 0 && fi->fddictx != NULL)
355             fddictx = fi->fddictx[fi->i];
356         if (fi->ddict != NULL && fddictx >= 0 && (fddictx+fddictn) <= fi->nddict)
357             fddict = fi->ddict + fddictx;
358     }
359     if (fddictp)
360         *fddictp = fddict;
361     return fddictn;
362 }
363
364 uint32_t rpmfiFNlink(rpmfi fi)
365 {
366     uint32_t nlink = 0;
367
368     if (fi != NULL && fi->i >= 0 && fi->i < fi->fc) {
369         /* XXX rpm-2.3.12 has not RPMTAG_FILEINODES */
370         if (fi->finodes && fi->frdevs) {
371             rpm_ino_t finode = fi->finodes[fi->i];
372             rpm_rdev_t frdev = fi->frdevs[fi->i];
373             int j;
374
375             for (j = 0; j < fi->fc; j++) {
376                 if (fi->frdevs[j] == frdev && fi->finodes[j] == finode)
377                     nlink++;
378             }
379         }
380     }
381     return nlink;
382 }
383
384 rpm_time_t rpmfiFMtime(rpmfi fi)
385 {
386     rpm_time_t fmtime = 0;
387
388     if (fi != NULL && fi->i >= 0 && fi->i < fi->fc) {
389         if (fi->fmtimes != NULL)
390             fmtime = fi->fmtimes[fi->i];
391     }
392     return fmtime;
393 }
394
395 const char * rpmfiFUser(rpmfi fi)
396 {
397     const char * fuser = NULL;
398
399     if (fi != NULL && fi->i >= 0 && fi->i < fi->fc) {
400         if (fi->fuser != NULL)
401             fuser = ugcacheGet(fi->fuser[fi->i]);
402     }
403     return fuser;
404 }
405
406 const char * rpmfiFGroup(rpmfi fi)
407 {
408     const char * fgroup = NULL;
409
410     if (fi != NULL && fi->i >= 0 && fi->i < fi->fc) {
411         if (fi->fgroup != NULL)
412             fgroup = ugcacheGet(fi->fgroup[fi->i]);
413     }
414     return fgroup;
415 }
416
417 const char * rpmfiFCaps(rpmfi fi)
418 {
419     const char *fcaps = NULL;
420     if (fi != NULL && fi->i >= 0 && fi->i < fi->fc) {
421         fcaps = fi->fcaps ? fi->fcaps[fi->i] : "";
422     }
423     return fcaps;
424 }
425
426 rpmFileAction rpmfiFAction(rpmfi fi)
427 {
428     rpmFileAction action;
429     if (fi != NULL && fi->actions != NULL && fi->i >= 0 && fi->i < fi->fc) {
430         action = fi->actions[fi->i];
431     } else {
432         action = FA_UNKNOWN;
433     }
434     return action;
435 }
436
437 void rpmfiSetFAction(rpmfi fi, rpmFileAction action)
438 {
439     if (fi != NULL && fi->actions != NULL && fi->i >= 0 && fi->i < fi->fc) {
440         fi->actions[fi->i] = action;
441     }   
442 }
443
444 int rpmfiNext(rpmfi fi)
445 {
446     int i = -1;
447
448     if (fi != NULL && ++fi->i >= 0) {
449         if (fi->i < fi->fc) {
450             i = fi->i;
451             if (fi->dil != NULL)
452                 fi->j = fi->dil[fi->i];
453         } else
454             fi->i = -1;
455
456 if (_rpmfi_debug  < 0 && i != -1)
457 fprintf(stderr, "*** fi %p\t%s[%d] %s%s\n", fi, (fi->Type ? fi->Type : "?Type?"), i, (i >= 0 ? fi->dnl[fi->j] : ""), (i >= 0 ? fi->bnl[fi->i] : ""));
458
459     }
460
461     return i;
462 }
463
464 rpmfi rpmfiInit(rpmfi fi, int fx)
465 {
466     if (fi != NULL) {
467         if (fx >= 0 && fx < fi->fc) {
468             fi->i = fx - 1;
469             fi->j = -1;
470         }
471     }
472
473     return fi;
474 }
475
476 int rpmfiNextD(rpmfi fi)
477 {
478     int j = -1;
479
480     if (fi != NULL && ++fi->j >= 0) {
481         if (fi->j < fi->dc)
482             j = fi->j;
483         else
484             fi->j = -1;
485
486 if (_rpmfi_debug  < 0 && j != -1)
487 fprintf(stderr, "*** fi %p\t%s[%d]\n", fi, (fi->Type ? fi->Type : "?Type?"), j);
488
489     }
490
491     return j;
492 }
493
494 rpmfi rpmfiInitD(rpmfi fi, int dx)
495 {
496     if (fi != NULL) {
497         if (dx >= 0 && dx < fi->fc)
498             fi->j = dx - 1;
499         else
500             fi = NULL;
501     }
502
503     return fi;
504 }
505
506 /**
507  * Identify a file type.
508  * @param ft            file type
509  * @return              string to identify a file type
510  */
511 static
512 const char * ftstring (rpmFileTypes ft)
513 {
514     switch (ft) {
515     case XDIR:  return "directory";
516     case CDEV:  return "char dev";
517     case BDEV:  return "block dev";
518     case LINK:  return "link";
519     case SOCK:  return "sock";
520     case PIPE:  return "fifo/pipe";
521     case REG:   return "file";
522     default:    return "unknown file type";
523     }
524 }
525
526 rpmFileTypes rpmfiWhatis(rpm_mode_t mode)
527 {
528     if (S_ISDIR(mode))  return XDIR;
529     if (S_ISCHR(mode))  return CDEV;
530     if (S_ISBLK(mode))  return BDEV;
531     if (S_ISLNK(mode))  return LINK;
532     if (S_ISSOCK(mode)) return SOCK;
533     if (S_ISFIFO(mode)) return PIPE;
534     return REG;
535 }
536
537 int rpmfiCompare(const rpmfi afi, const rpmfi bfi)
538 {
539     rpmFileTypes awhat = rpmfiWhatis(rpmfiFMode(afi));
540     rpmFileTypes bwhat = rpmfiWhatis(rpmfiFMode(bfi));
541
542     if ((rpmfiFFlags(afi) & RPMFILE_GHOST) ||
543         (rpmfiFFlags(bfi) & RPMFILE_GHOST)) return 0;
544
545     if (awhat != bwhat) return 1;
546
547     if (awhat == LINK) {
548         const char * alink = rpmfiFLink(afi);
549         const char * blink = rpmfiFLink(bfi);
550         if (alink == blink) return 0;
551         if (alink == NULL) return 1;
552         if (blink == NULL) return -1;
553         return strcmp(alink, blink);
554     } else if (awhat == REG) {
555         size_t adiglen, bdiglen;
556         pgpHashAlgo aalgo, balgo;
557         const unsigned char * adigest = rpmfiFDigest(afi, &aalgo, &adiglen);
558         const unsigned char * bdigest = rpmfiFDigest(bfi, &balgo, &bdiglen);
559         if (adigest == bdigest) return 0;
560         if (adigest == NULL) return 1;
561         if (bdigest == NULL) return -1;
562         /* can't meaningfully compare different hash types */
563         if (aalgo != balgo || adiglen != bdiglen) return -1;
564         return memcmp(adigest, bdigest, adiglen);
565     }
566
567     return 0;
568 }
569
570 rpmFileAction rpmfiDecideFate(const rpmfi ofi, rpmfi nfi, int skipMissing)
571 {
572     const char * fn = rpmfiFN(nfi);
573     rpmfileAttrs newFlags = rpmfiFFlags(nfi);
574     char buffer[1024];
575     rpmFileTypes dbWhat, newWhat, diskWhat;
576     struct stat sb;
577     int save = (newFlags & RPMFILE_NOREPLACE) ? FA_ALTNAME : FA_SAVE;
578
579     if (lstat(fn, &sb)) {
580         /*
581          * The file doesn't exist on the disk. Create it unless the new
582          * package has marked it as missingok, or allfiles is requested.
583          */
584         if (skipMissing && (newFlags & RPMFILE_MISSINGOK)) {
585             rpmlog(RPMLOG_DEBUG, "%s skipped due to missingok flag\n",
586                         fn);
587             return FA_SKIP;
588         } else {
589             return FA_CREATE;
590         }
591     }
592
593     diskWhat = rpmfiWhatis((rpm_mode_t)sb.st_mode);
594     dbWhat = rpmfiWhatis(rpmfiFMode(ofi));
595     newWhat = rpmfiWhatis(rpmfiFMode(nfi));
596
597     /*
598      * RPM >= 2.3.10 shouldn't create config directories -- we'll ignore
599      * them in older packages as well.
600      */
601     if (newWhat == XDIR)
602         return FA_CREATE;
603
604     if (diskWhat != newWhat && dbWhat != REG && dbWhat != LINK)
605         return save;
606     else if (newWhat != dbWhat && diskWhat != dbWhat)
607         return save;
608     else if (dbWhat != newWhat)
609         return FA_CREATE;
610     else if (dbWhat != LINK && dbWhat != REG)
611         return FA_CREATE;
612
613     /*
614      * This order matters - we'd prefer to CREATE the file if at all
615      * possible in case something else (like the timestamp) has changed.
616      */
617     memset(buffer, 0, sizeof(buffer));
618     if (dbWhat == REG) {
619         pgpHashAlgo oalgo, nalgo;
620         size_t odiglen, ndiglen;
621         const unsigned char * odigest, * ndigest;
622         odigest = rpmfiFDigest(ofi, &oalgo, &odiglen);
623         if (diskWhat == REG) {
624             if (rpmDoDigest(oalgo, fn, 0, 
625                 (unsigned char *)buffer, NULL))
626                 return FA_CREATE;       /* assume file has been removed */
627             if (odigest && !memcmp(odigest, buffer, odiglen))
628                 return FA_CREATE;       /* unmodified config file, replace. */
629         }
630         ndigest = rpmfiFDigest(nfi, &nalgo, &ndiglen);
631         /* XXX can't compare different hash types, what should we do here? */
632         if (oalgo != nalgo || odiglen != ndiglen)
633             return FA_CREATE;
634         if (odigest && ndigest && !memcmp(odigest, ndigest, odiglen))
635             return FA_SKIP;     /* identical file, don't bother. */
636     } else /* dbWhat == LINK */ {
637         const char * oFLink, * nFLink;
638         oFLink = rpmfiFLink(ofi);
639         if (diskWhat == LINK) {
640             if (readlink(fn, buffer, sizeof(buffer) - 1) == -1)
641                 return FA_CREATE;       /* assume file has been removed */
642             if (oFLink && !strcmp(oFLink, buffer))
643                 return FA_CREATE;       /* unmodified config file, replace. */
644         }
645         nFLink = rpmfiFLink(nfi);
646         if (oFLink && nFLink && !strcmp(oFLink, nFLink))
647             return FA_SKIP;     /* identical file, don't bother. */
648     }
649
650     /*
651      * The config file on the disk has been modified, but
652      * the ones in the two packages are different. It would
653      * be nice if RPM was smart enough to at least try and
654      * merge the difference ala CVS, but...
655      */
656     return save;
657 }
658
659 int rpmfiConfigConflict(const rpmfi fi)
660 {
661     const char * fn = rpmfiFN(fi);
662     rpmfileAttrs flags = rpmfiFFlags(fi);
663     char buffer[1024];
664     rpmFileTypes newWhat, diskWhat;
665     struct stat sb;
666
667     if (!(flags & RPMFILE_CONFIG) || lstat(fn, &sb)) {
668         return 0;
669     }
670
671     diskWhat = rpmfiWhatis((rpm_mode_t)sb.st_mode);
672     newWhat = rpmfiWhatis(rpmfiFMode(fi));
673
674     if (newWhat != LINK && newWhat != REG)
675         return 1;
676
677     if (diskWhat != newWhat)
678         return 1;
679     
680     memset(buffer, 0, sizeof(buffer));
681     if (newWhat == REG) {
682         pgpHashAlgo algo;
683         size_t diglen;
684         const unsigned char *ndigest = rpmfiFDigest(fi, &algo, &diglen);
685         if (rpmDoDigest(algo, fn, 0, (unsigned char *)buffer, NULL))
686             return 0;   /* assume file has been removed */
687         if (ndigest && !memcmp(ndigest, buffer, diglen))
688             return 0;   /* unmodified config file */
689     } else /* newWhat == LINK */ {
690         const char * nFLink;
691         if (readlink(fn, buffer, sizeof(buffer) - 1) == -1)
692             return 0;   /* assume file has been removed */
693         nFLink = rpmfiFLink(fi);
694         if (nFLink && !strcmp(nFLink, buffer))
695             return 0;   /* unmodified config file */
696     }
697
698     return 1;
699 }
700
701 const char * rpmfiTypeString(rpmfi fi)
702 {
703     switch(rpmteType(fi->te)) {
704     case TR_ADDED:      return " install";
705     case TR_REMOVED:    return "   erase";
706     default:            return "???";
707     }
708 }
709
710 static char **duparray(char ** src, int size)
711 {
712     char **dest = xmalloc((size+1) * sizeof(*dest));
713     for (int i = 0; i < size; i++) {
714         dest[i] = xstrdup(src[i]);
715     }
716     free(src);
717     return dest;
718 }
719
720 /**
721  * Relocate files in header.
722  * @todo multilib file dispositions need to be checked.
723  * @param ts            transaction set
724  * @param fi            transaction element file info
725  * @param origH         package header
726  * @param actions       file dispositions
727  * @return              header with relocated files
728  */
729 static
730 Header relocateFileList(const rpmts ts, rpmfi fi,
731                 Header origH, rpmFileAction * actions)
732 {
733     rpmte p = rpmtsRelocateElement(ts);
734     static int _printed = 0;
735     int allowBadRelocate = (rpmtsFilterFlags(ts) & RPMPROB_FILTER_FORCERELOCATE);
736     rpmRelocation * relocations = NULL;
737     int numRelocations;
738     char ** baseNames;
739     char ** dirNames;
740     uint32_t * dirIndexes;
741     uint32_t * newDirIndexes;
742     rpm_count_t fileCount, dirCount, numValid = 0;
743     rpm_color_t * fColors = NULL;
744     rpm_color_t * dColors = NULL;
745     rpm_mode_t * fModes = NULL;
746     Header h;
747     int nrelocated = 0;
748     int fileAlloced = 0;
749     char * fn = NULL;
750     int haveRelocatedBase = 0;
751     int reldel = 0;
752     int len;
753     int i, j;
754     struct rpmtd_s validRelocs;
755     struct rpmtd_s bnames, dnames, dindexes, fcolors, fmodes;
756
757     
758     if (headerGet(origH, RPMTAG_PREFIXES, &validRelocs, HEADERGET_MINMEM)) 
759         numValid = rpmtdCount(&validRelocs);
760
761 assert(p != NULL);
762     numRelocations = 0;
763     if (p->relocs)
764         while (p->relocs[numRelocations].newPath ||
765                p->relocs[numRelocations].oldPath)
766             numRelocations++;
767
768     /*
769      * If no relocations are specified (usually the case), then return the
770      * original header. If there are prefixes, however, then INSTPREFIXES
771      * should be added, but, since relocateFileList() can be called more
772      * than once for the same header, don't bother if already present.
773      */
774     if (p->relocs == NULL || numRelocations == 0) {
775         if (numValid) {
776             if (!headerIsEntry(origH, RPMTAG_INSTPREFIXES)) {
777                 rpmtdSetTag(&validRelocs, RPMTAG_INSTPREFIXES);
778                 headerPut(origH, &validRelocs, HEADERPUT_DEFAULT);
779             }
780             rpmtdFreeData(&validRelocs);
781         }
782         /* XXX FIXME multilib file actions need to be checked. */
783         return headerLink(origH);
784     }
785
786     h = headerLink(origH);
787
788     relocations = xmalloc(sizeof(*relocations) * numRelocations);
789
790     /* Build sorted relocation list from raw relocations. */
791     for (i = 0; i < numRelocations; i++) {
792         char * t;
793
794         /*
795          * Default relocations (oldPath == NULL) are handled in the UI,
796          * not rpmlib.
797          */
798         if (p->relocs[i].oldPath == NULL) continue; /* XXX can't happen */
799
800         /* FIXME: Trailing /'s will confuse us greatly. Internal ones will 
801            too, but those are more trouble to fix up. :-( */
802         t = xstrdup(p->relocs[i].oldPath);
803         relocations[i].oldPath = (t[0] == '/' && t[1] == '\0')
804             ? t
805             : stripTrailingChar(t, '/');
806
807         /* An old path w/o a new path is valid, and indicates exclusion */
808         if (p->relocs[i].newPath) {
809             int del;
810             int valid = 0;
811             const char *validprefix;
812
813             t = xstrdup(p->relocs[i].newPath);
814             relocations[i].newPath = (t[0] == '/' && t[1] == '\0')
815                 ? t
816                 : stripTrailingChar(t, '/');
817
818                 /* FIX:  relocations[i].oldPath == NULL */
819             /* Verify that the relocation's old path is in the header. */
820             rpmtdInit(&validRelocs);
821             while ((validprefix = rpmtdNextString(&validRelocs))) {
822                 if (strcmp(validprefix, relocations[i].oldPath) == 0) {
823                     valid = 1;
824                     break;
825                 }
826             }
827
828             /* XXX actions check prevents problem from being appended twice. */
829             if (!valid && !allowBadRelocate && actions) {
830                 rpmps ps = rpmtsProblems(ts);
831                 rpmpsAppend(ps, RPMPROB_BADRELOCATE,
832                         rpmteNEVRA(p), rpmteKey(p),
833                         relocations[i].oldPath, NULL, NULL, 0);
834                 ps = rpmpsFree(ps);
835             }
836             del =
837                 strlen(relocations[i].newPath) - strlen(relocations[i].oldPath);
838
839             if (del > reldel)
840                 reldel = del;
841         } else {
842             relocations[i].newPath = NULL;
843         }
844     }
845
846     /* stupid bubble sort, but it's probably faster here */
847     for (i = 0; i < numRelocations; i++) {
848         int madeSwap;
849         madeSwap = 0;
850         for (j = 1; j < numRelocations; j++) {
851             rpmRelocation tmpReloc;
852             if (relocations[j - 1].oldPath == NULL || /* XXX can't happen */
853                 relocations[j    ].oldPath == NULL || /* XXX can't happen */
854         strcmp(relocations[j - 1].oldPath, relocations[j].oldPath) <= 0)
855                 continue;
856             /* LCL: ??? */
857             tmpReloc = relocations[j - 1];
858             relocations[j - 1] = relocations[j];
859             relocations[j] = tmpReloc;
860             madeSwap = 1;
861         }
862         if (!madeSwap) break;
863     }
864
865     if (!_printed) {
866         _printed = 1;
867         rpmlog(RPMLOG_DEBUG, "========== relocations\n");
868         for (i = 0; i < numRelocations; i++) {
869             if (relocations[i].oldPath == NULL) continue; /* XXX can't happen */
870             if (relocations[i].newPath == NULL)
871                 rpmlog(RPMLOG_DEBUG, "%5d exclude  %s\n",
872                         i, relocations[i].oldPath);
873             else
874                 rpmlog(RPMLOG_DEBUG, "%5d relocate %s -> %s\n",
875                         i, relocations[i].oldPath, relocations[i].newPath);
876         }
877     }
878
879     /* Add relocation values to the header */
880     if (numValid) {
881         const char *validprefix;
882         const char ** actualRelocations;
883         rpm_count_t numActual;
884
885         actualRelocations = xmalloc(numValid * sizeof(*actualRelocations));
886         numActual = 0;
887         rpmtdInit(&validRelocs);
888         while ((validprefix = rpmtdNextString(&validRelocs))) {
889             for (j = 0; j < numRelocations; j++) {
890                 if (relocations[j].oldPath == NULL || /* XXX can't happen */
891                     strcmp(validprefix, relocations[j].oldPath))
892                     continue;
893                 /* On install, a relocate to NULL means skip the path. */
894                 if (relocations[j].newPath) {
895                     actualRelocations[numActual] = relocations[j].newPath;
896                     numActual++;
897                 }
898                 break;
899             }
900             if (j == numRelocations) {
901                 actualRelocations[numActual] = validprefix;
902                 numActual++;
903             }
904         }
905
906         if (numActual) {
907             headerPutStringArray(h, RPMTAG_INSTPREFIXES,
908                                      actualRelocations, numActual);
909         }
910
911         actualRelocations = _free(actualRelocations);
912         rpmtdFreeData(&validRelocs);
913     }
914
915     headerGet(h, RPMTAG_BASENAMES, &bnames, fi->scareFlags);
916     headerGet(h, RPMTAG_DIRINDEXES, &dindexes, fi->scareFlags);
917     headerGet(h, RPMTAG_DIRNAMES, &dnames, fi->scareFlags);
918     headerGet(h, RPMTAG_FILECOLORS, &fcolors, fi->scareFlags);
919     headerGet(h, RPMTAG_FILEMODES, &fmodes, fi->scareFlags);
920     /* TODO XXX ugh.. use rpmtd iterators & friends instead */
921     baseNames = bnames.data;
922     dirIndexes = dindexes.data;
923     fColors = fcolors.data;
924     fModes = fmodes.data;
925     fileCount = rpmtdCount(&bnames);
926     dirCount = rpmtdCount(&dnames);
927     /* XXX TODO: use rpmtdDup() instead */
928     dirNames = dnames.data = duparray(dnames.data, dirCount);
929     dnames.flags |= RPMTD_PTR_ALLOCED;
930
931     dColors = xcalloc(dirCount, sizeof(*dColors));
932
933     newDirIndexes = xmalloc(sizeof(*newDirIndexes) * fileCount);
934     memcpy(newDirIndexes, dirIndexes, sizeof(*newDirIndexes) * fileCount);
935     dirIndexes = newDirIndexes;
936
937     /*
938      * For all relocations, we go through sorted file/relocation lists 
939      * backwards so that /usr/local relocations take precedence over /usr 
940      * ones.
941      */
942
943     /* Relocate individual paths. */
944
945     for (i = fileCount - 1; i >= 0; i--) {
946         rpmFileTypes ft;
947         int fnlen;
948
949         len = reldel +
950                 strlen(dirNames[dirIndexes[i]]) + strlen(baseNames[i]) + 1;
951         if (len >= fileAlloced) {
952             fileAlloced = len * 2;
953             fn = xrealloc(fn, fileAlloced);
954         }
955
956 assert(fn != NULL);             /* XXX can't happen */
957         *fn = '\0';
958         fnlen = stpcpy( stpcpy(fn, dirNames[dirIndexes[i]]), baseNames[i]) - fn;
959
960 if (fColors != NULL) {
961 /* XXX pkgs may not have unique dirNames, so color all dirNames that match. */
962 for (j = 0; j < dirCount; j++) {
963 if (strcmp(dirNames[dirIndexes[i]], dirNames[j])) continue;
964 dColors[j] |= fColors[i];
965 }
966 }
967
968         /*
969          * See if this file path needs relocating.
970          */
971         /*
972          * XXX FIXME: Would a bsearch of the (already sorted) 
973          * relocation list be a good idea?
974          */
975         for (j = numRelocations - 1; j >= 0; j--) {
976             if (relocations[j].oldPath == NULL) /* XXX can't happen */
977                 continue;
978             len = strcmp(relocations[j].oldPath, "/")
979                 ? strlen(relocations[j].oldPath)
980                 : 0;
981
982             if (fnlen < len)
983                 continue;
984             /*
985              * Only subdirectories or complete file paths may be relocated. We
986              * don't check for '\0' as our directory names all end in '/'.
987              */
988             if (!(fn[len] == '/' || fnlen == len))
989                 continue;
990
991             if (strncmp(relocations[j].oldPath, fn, len))
992                 continue;
993             break;
994         }
995         if (j < 0) continue;
996
997 /* FIX: fModes may be NULL */
998         ft = rpmfiWhatis(fModes[i]);
999
1000         /* On install, a relocate to NULL means skip the path. */
1001         if (relocations[j].newPath == NULL) {
1002             if (ft == XDIR) {
1003                 /* Start with the parent, looking for directory to exclude. */
1004                 for (j = dirIndexes[i]; j < dirCount; j++) {
1005                     len = strlen(dirNames[j]) - 1;
1006                     while (len > 0 && dirNames[j][len-1] == '/') len--;
1007                     if (fnlen != len)
1008                         continue;
1009                     if (strncmp(fn, dirNames[j], fnlen))
1010                         continue;
1011                     break;
1012                 }
1013             }
1014             if (actions) {
1015                 actions[i] = FA_SKIPNSTATE;
1016                 rpmlog(RPMLOG_DEBUG, "excluding %s %s\n",
1017                         ftstring(ft), fn);
1018             }
1019             continue;
1020         }
1021
1022         /* Relocation on full paths only, please. */
1023         if (fnlen != len) continue;
1024
1025         if (actions)
1026             rpmlog(RPMLOG_DEBUG, "relocating %s to %s\n",
1027                     fn, relocations[j].newPath);
1028         nrelocated++;
1029
1030         strcpy(fn, relocations[j].newPath);
1031         {   char * te = strrchr(fn, '/');
1032             if (te) {
1033                 if (te > fn) te++;      /* root is special */
1034                 fnlen = te - fn;
1035             } else
1036                 te = fn + strlen(fn);
1037             if (strcmp(baseNames[i], te)) { /* basename changed too? */
1038                 if (!haveRelocatedBase) {
1039                     /* XXX TODO: use rpmtdDup() instead */
1040                     bnames.data = baseNames = duparray(baseNames, fileCount);
1041                     bnames.flags |= RPMTD_PTR_ALLOCED;
1042                     haveRelocatedBase = 1;
1043                 }
1044                 free(baseNames[i]);
1045                 baseNames[i] = xstrdup(te);
1046             }
1047             *te = '\0';                 /* terminate new directory name */
1048         }
1049
1050         /* Does this directory already exist in the directory list? */
1051         for (j = 0; j < dirCount; j++) {
1052             if (fnlen != strlen(dirNames[j]))
1053                 continue;
1054             if (strncmp(fn, dirNames[j], fnlen))
1055                 continue;
1056             break;
1057         }
1058         
1059         if (j < dirCount) {
1060             dirIndexes[i] = j;
1061             continue;
1062         }
1063
1064         /* Creating new paths is a pita */
1065         dirNames = dnames.data = xrealloc(dnames.data, 
1066                                sizeof(*dirNames) * (dirCount + 1));
1067
1068         dirNames[dirCount] = xstrdup(fn);
1069         dirIndexes[i] = dirCount;
1070         dirCount++;
1071         dnames.count++;
1072     }
1073
1074     /* Finish off by relocating directories. */
1075     for (i = dirCount - 1; i >= 0; i--) {
1076         for (j = numRelocations - 1; j >= 0; j--) {
1077
1078             if (relocations[j].oldPath == NULL) /* XXX can't happen */
1079                 continue;
1080             len = strcmp(relocations[j].oldPath, "/")
1081                 ? strlen(relocations[j].oldPath)
1082                 : 0;
1083
1084             if (len && strncmp(relocations[j].oldPath, dirNames[i], len))
1085                 continue;
1086
1087             /*
1088              * Only subdirectories or complete file paths may be relocated. We
1089              * don't check for '\0' as our directory names all end in '/'.
1090              */
1091             if (dirNames[i][len] != '/')
1092                 continue;
1093
1094             if (relocations[j].newPath) { /* Relocate the path */
1095                 char *t = NULL;
1096                 rstrscat(&t, relocations[j].newPath, (dirNames[i] + len), NULL);
1097                 /* Unfortunatly rpmCleanPath strips the trailing slash.. */
1098                 (void) rpmCleanPath(t);
1099                 rstrcat(&t, "/");
1100
1101                 if (actions)
1102                     rpmlog(RPMLOG_DEBUG,
1103                         "relocating directory %s to %s\n", dirNames[i], t);
1104                 free(dirNames[i]);
1105                 dirNames[i] = t;
1106                 nrelocated++;
1107             }
1108         }
1109     }
1110
1111     /* Save original filenames in header and replace (relocated) filenames. */
1112     if (nrelocated) {
1113         struct rpmtd_s td;
1114
1115         headerGet(h, RPMTAG_BASENAMES, &td, HEADERGET_MINMEM);
1116         rpmtdSetTag(&td, RPMTAG_ORIGBASENAMES);
1117         headerPut(h, &td, HEADERPUT_DEFAULT);
1118         rpmtdFreeData(&td);
1119
1120         headerGet(h, RPMTAG_DIRNAMES, &td, HEADERGET_MINMEM);
1121         rpmtdSetTag(&td, RPMTAG_ORIGDIRNAMES);
1122         headerPut(h, &td, HEADERPUT_DEFAULT);
1123         rpmtdFreeData(&td);
1124
1125         headerGet(h, RPMTAG_DIRINDEXES, &td, HEADERGET_MINMEM);
1126         rpmtdSetTag(&td, RPMTAG_ORIGDIRINDEXES);
1127         headerPut(h, &td, HEADERPUT_DEFAULT);
1128         rpmtdFreeData(&td);
1129
1130         if (rpmtdFromStringArray(&td, RPMTAG_BASENAMES, (const char**) baseNames, fileCount)) {
1131             headerMod(h, &td);
1132         }
1133         free(fi->bnl);
1134         headerGet(h, RPMTAG_BASENAMES, &td, fi->scareFlags);
1135         fi->fc = rpmtdCount(&td);
1136         fi->bnl = td.data;
1137
1138         if (rpmtdFromStringArray(&td, RPMTAG_DIRNAMES, (const char**) dirNames, dirCount)) {
1139             headerMod(h, &td);
1140         }
1141         free(fi->dnl);
1142         headerGet(h, RPMTAG_DIRNAMES, &td, fi->scareFlags);
1143         fi->dc = rpmtdCount(&td);
1144         fi->dnl = td.data;
1145
1146         if (rpmtdFromUint32(&td, RPMTAG_DIRINDEXES, dirIndexes, fileCount)) {
1147             headerMod(h, &td);
1148         }
1149         headerGet(h, RPMTAG_DIRINDEXES, &td, fi->scareFlags);
1150         /* Ugh, nasty games with how dil is alloced depending on scareMem */
1151         if (fi->scareFlags & HEADERGET_ALLOC)
1152             free(fi->dil);
1153         fi->dil = td.data;
1154     }
1155
1156     rpmtdFreeData(&bnames);
1157     rpmtdFreeData(&dnames);
1158     rpmtdFreeData(&dindexes);
1159     rpmtdFreeData(&fcolors);
1160     rpmtdFreeData(&fmodes);
1161     free(fn);
1162     for (i = 0; i < numRelocations; i++) {
1163         free(relocations[i].oldPath);
1164         free(relocations[i].newPath);
1165     }
1166     free(relocations);
1167     free(newDirIndexes);
1168     free(dColors);
1169
1170     return h;
1171 }
1172
1173 rpmfi rpmfiFree(rpmfi fi)
1174 {
1175     if (fi == NULL) return NULL;
1176
1177     if (fi->nrefs > 1)
1178         return rpmfiUnlink(fi, fi->Type);
1179
1180 if (_rpmfi_debug < 0)
1181 fprintf(stderr, "*** fi %p\t%s[%d]\n", fi, fi->Type, fi->fc);
1182
1183     if (fi->fc > 0) {
1184         fi->bnl = _free(fi->bnl);
1185         fi->dnl = _free(fi->dnl);
1186
1187         fi->flinks = _free(fi->flinks);
1188         fi->flangs = _free(fi->flangs);
1189         fi->digests = _free(fi->digests);
1190         fi->fcaps = _free(fi->fcaps);
1191
1192         fi->cdict = _free(fi->cdict);
1193
1194         fi->fuser = _free(fi->fuser);
1195         fi->fgroup = _free(fi->fgroup);
1196
1197         fi->fstates = _free(fi->fstates);
1198
1199         if (!(fi->fiflags & RPMFI_KEEPHEADER) && fi->h == NULL) {
1200             fi->fmtimes = _constfree(fi->fmtimes);
1201             fi->fmodes = _free(fi->fmodes);
1202             fi->fflags = _constfree(fi->fflags);
1203             fi->vflags = _constfree(fi->vflags);
1204             fi->fsizes = _constfree(fi->fsizes);
1205             fi->frdevs = _constfree(fi->frdevs);
1206             fi->finodes = _constfree(fi->finodes);
1207             fi->dil = _free(fi->dil);
1208
1209             fi->fcolors = _constfree(fi->fcolors);
1210             fi->fcdictx = _constfree(fi->fcdictx);
1211             fi->ddict = _constfree(fi->ddict);
1212             fi->fddictx = _constfree(fi->fddictx);
1213             fi->fddictn = _constfree(fi->fddictn);
1214
1215         }
1216     }
1217
1218     fi->fsm = freeFSM(fi->fsm);
1219
1220     fi->fn = _free(fi->fn);
1221     fi->apath = _free(fi->apath);
1222
1223     fi->actions = _free(fi->actions);
1224     fi->replacedSizes = _free(fi->replacedSizes);
1225     fi->replaced = _free(fi->replaced);
1226
1227     fi->h = headerFree(fi->h);
1228
1229     (void) rpmfiUnlink(fi, fi->Type);
1230     memset(fi, 0, sizeof(*fi));         /* XXX trash and burn */
1231     fi = _free(fi);
1232
1233     return NULL;
1234 }
1235
1236 /* Helper to convert user+group names into indexes to name cache */
1237 static ugidx_t *cacheNames(Header h, rpmTag tag)
1238 {
1239     ugidx_t *idx = NULL;
1240     struct rpmtd_s td;
1241     if (headerGet(h, tag, &td, HEADERGET_MINMEM)) {
1242        idx = xmalloc(sizeof(*idx) * rpmtdCount(&td));
1243        int i = 0;
1244        const char *str;
1245        while ((str = rpmtdNextString(&td))) {
1246            idx[i++] = ugcachePut(str);
1247        }
1248        rpmtdFreeData(&td);
1249     }
1250     return idx;
1251 }
1252
1253 #define _hgfi(_h, _tag, _td, _flags, _data) \
1254     if (headerGet((_h), (_tag), (_td), (_flags))) \
1255         _data = (td.data)
1256
1257 rpmfi rpmfiNew(const rpmts ts, Header h, rpmTag tagN, rpmfiFlags flags)
1258 {
1259     rpmte p;
1260     rpmfi fi = NULL;
1261     const char * Type;
1262     rpm_loff_t *asize = NULL;
1263     unsigned char * t;
1264     int isBuild, isSource;
1265     struct rpmtd_s fdigests, digalgo;
1266     struct rpmtd_s td;
1267     headerGetFlags scareFlags = (flags & RPMFI_KEEPHEADER) ? 
1268                                 HEADERGET_MINMEM : HEADERGET_ALLOC;
1269     headerGetFlags defFlags = HEADERGET_ALLOC;
1270
1271     if (tagN == RPMTAG_BASENAMES) {
1272         Type = "Files";
1273     } else {
1274         Type = "?Type?";
1275         goto exit;
1276     }
1277
1278     fi = xcalloc(1, sizeof(*fi));
1279     if (fi == NULL)     /* XXX can't happen */
1280         goto exit;
1281
1282     fi->magic = RPMFIMAGIC;
1283     fi->Type = Type;
1284     fi->i = -1;
1285     fi->tagN = tagN;
1286
1287     fi->fiflags = flags;
1288     fi->scareFlags = scareFlags;
1289
1290     fi->h = (fi->fiflags & RPMFI_KEEPHEADER) ? headerLink(h) : NULL;
1291
1292     if (headerGet(h, RPMTAG_LONGARCHIVESIZE, &td, HEADERGET_EXT)) {
1293         asize = rpmtdGetUint64(&td);
1294     }
1295     /* 0 means unknown */
1296     fi->archiveSize = asize ? *asize : 0;
1297     rpmtdFreeData(&td);
1298     
1299     /* Archive size is not set when this gets called from build */
1300     isBuild = (asize == NULL);
1301     isSource = headerIsSource(h);
1302     if (isBuild) fi->fiflags |= RPMFI_ISBUILD;
1303     if (isSource) fi->fiflags |= RPMFI_ISSOURCE;
1304
1305     /* See if we have pre/posttrans scripts. */
1306     fi->transscripts |= (headerIsEntry(h, RPMTAG_PRETRANS) &&
1307                          headerIsEntry(h, RPMTAG_PRETRANSPROG)) ?
1308                         RPMFI_HAVE_PRETRANS : 0;
1309     fi->transscripts |= (headerIsEntry(h, RPMTAG_POSTTRANS) &&
1310                          headerIsEntry(h, RPMTAG_POSTTRANSPROG)) ?
1311                         RPMFI_HAVE_POSTTRANS : 0;
1312
1313     _hgfi(h, RPMTAG_BASENAMES, &td, defFlags, fi->bnl);
1314     fi->fc = rpmtdCount(&td);
1315     if (fi->fc == 0) {
1316         goto exit;
1317     }
1318
1319     _hgfi(h, RPMTAG_DIRNAMES, &td, defFlags, fi->dnl);
1320     fi->dc = rpmtdCount(&td);
1321     _hgfi(h, RPMTAG_DIRINDEXES, &td, scareFlags, fi->dil);
1322     if (!(flags & RPMFI_NOFILEMODES))
1323         _hgfi(h, RPMTAG_FILEMODES, &td, scareFlags, fi->fmodes);
1324     if (!(flags & RPMFI_NOFILEFLAGS))
1325         _hgfi(h, RPMTAG_FILEFLAGS, &td, scareFlags, fi->fflags);
1326     if (!(flags & RPMFI_NOFILEVERIFYFLAGS))
1327         _hgfi(h, RPMTAG_FILEVERIFYFLAGS, &td, scareFlags, fi->vflags);
1328     if (!(flags & RPMFI_NOFILESIZES))
1329         _hgfi(h, RPMTAG_FILESIZES, &td, scareFlags, fi->fsizes);
1330
1331     if (!(flags & RPMFI_NOFILECOLORS))
1332         _hgfi(h, RPMTAG_FILECOLORS, &td, scareFlags, fi->fcolors);
1333
1334     if (!(flags & RPMFI_NOFILECLASS)) {
1335         _hgfi(h, RPMTAG_CLASSDICT, &td, scareFlags, fi->cdict);
1336         fi->ncdict = rpmtdCount(&td);
1337         _hgfi(h, RPMTAG_FILECLASS, &td, scareFlags, fi->fcdictx);
1338     }
1339     if (!(flags & RPMFI_NOFILEDEPS)) {
1340         _hgfi(h, RPMTAG_DEPENDSDICT, &td, scareFlags, fi->ddict);
1341         fi->nddict = rpmtdCount(&td);
1342         _hgfi(h, RPMTAG_FILEDEPENDSX, &td, scareFlags, fi->fddictx);
1343         _hgfi(h, RPMTAG_FILEDEPENDSN, &td, scareFlags, fi->fddictn);
1344     }
1345
1346     /*
1347      * For installed packages, get the states here. For to-be-installed
1348      * packages fi->fstates is lazily created through rpmfiSetFState().
1349      * XXX file states not needed at all by TR_REMOVED.
1350      */
1351     if (!(flags & RPMFI_NOFILESTATES))
1352         _hgfi(h, RPMTAG_FILESTATES, &td, defFlags, fi->fstates);
1353
1354     if (!(flags & RPMFI_NOFILECAPS))
1355         _hgfi(h, RPMTAG_FILECAPS, &td, defFlags, fi->fcaps);
1356
1357     /* For build and src.rpm's the file actions are known at this point */
1358     fi->actions = xcalloc(fi->fc, sizeof(*fi->actions));
1359     if (isBuild) {
1360         for (int i = 0; i < fi->fc; i++) {
1361             int ghost = fi->fflags[i] & RPMFILE_GHOST;
1362             fi->actions[i] = ghost ? FA_SKIP : FA_COPYOUT;
1363         }
1364     } else if (isSource) {
1365         for (int i = 0; i < fi->fc; i++) {
1366             fi->actions[i] = FA_CREATE;
1367         }
1368     }
1369
1370     if (!(flags & RPMFI_NOFILELINKTOS))
1371         _hgfi(h, RPMTAG_FILELINKTOS, &td, defFlags, fi->flinks);
1372     /* FILELANGS are only interesting when installing */
1373     if ((headerGetInstance(h) == 0) && !(flags & RPMFI_NOFILELANGS))
1374         _hgfi(h, RPMTAG_FILELANGS, &td, defFlags, fi->flangs);
1375
1376     /* See if the package has non-md5 file digests */
1377     fi->digestalgo = PGPHASHALGO_MD5;
1378     if (headerGet(h, RPMTAG_FILEDIGESTALGO, &digalgo, HEADERGET_MINMEM)) {
1379         pgpHashAlgo *algo = rpmtdGetUint32(&digalgo);
1380         /* Hmm, what to do with unknown digest algorithms? */
1381         if (algo && rpmDigestLength(*algo) != 0) {
1382             fi->digestalgo = *algo;
1383         }
1384     }
1385
1386     fi->digests = NULL;
1387     /* grab hex digests from header and store in binary format */
1388     if (!(flags & RPMFI_NOFILEDIGESTS) &&
1389         headerGet(h, RPMTAG_FILEDIGESTS, &fdigests, HEADERGET_MINMEM)) {
1390         const char *fdigest;
1391         size_t diglen = rpmDigestLength(fi->digestalgo);
1392         fi->digests = t = xmalloc(rpmtdCount(&fdigests) * diglen);
1393
1394         while ((fdigest = rpmtdNextString(&fdigests))) {
1395             if (!(fdigest && *fdigest != '\0')) {
1396                 memset(t, 0, diglen);
1397                 t += diglen;
1398                 continue;
1399             }
1400             for (int j = 0; j < diglen; j++, t++, fdigest += 2)
1401                 *t = (rnibble(fdigest[0]) << 4) | rnibble(fdigest[1]);
1402         }
1403         rpmtdFreeData(&fdigests);
1404     }
1405
1406     /* XXX TR_REMOVED doesn;t need fmtimes, frdevs, finodes */
1407     if (!(flags & RPMFI_NOFILEMTIMES))
1408         _hgfi(h, RPMTAG_FILEMTIMES, &td, scareFlags, fi->fmtimes);
1409     if (!(flags & RPMFI_NOFILERDEVS))
1410         _hgfi(h, RPMTAG_FILERDEVS, &td, scareFlags, fi->frdevs);
1411     if (!(flags & RPMFI_NOFILEINODES))
1412         _hgfi(h, RPMTAG_FILEINODES, &td, scareFlags, fi->finodes);
1413
1414     if (!(flags & RPMFI_NOFILEUSER)) 
1415         fi->fuser = cacheNames(h, RPMTAG_FILEUSERNAME);
1416     if (!(flags & RPMFI_NOFILEGROUP)) 
1417         fi->fgroup = cacheNames(h, RPMTAG_FILEGROUPNAME);
1418
1419     if (ts != NULL)
1420     if (fi != NULL)
1421     if ((p = rpmtsRelocateElement(ts)) != NULL && rpmteType(p) == TR_ADDED
1422      && !headerIsSource(h)
1423      && !headerIsEntry(h, RPMTAG_ORIGBASENAMES))
1424     {
1425         Header foo;
1426
1427         /* FIX: fi-digests undefined */
1428         foo = relocateFileList(ts, fi, h, fi->actions);
1429         fi->h = headerFree(fi->h);
1430         fi->h = headerLink(foo);
1431         foo = headerFree(foo);
1432     }
1433
1434     if (!(fi->fiflags & RPMFI_KEEPHEADER)) {
1435         fi->h = headerFree(fi->h);
1436     }
1437
1438     /* lazily alloced from rpmfiFN() */
1439     fi->fn = NULL;
1440
1441 exit:
1442 if (_rpmfi_debug < 0)
1443 fprintf(stderr, "*** fi %p\t%s[%d]\n", fi, Type, (fi ? fi->fc : 0));
1444
1445     /* FIX: rpmfi null annotations */
1446     return rpmfiLink(fi, (fi ? fi->Type : NULL));
1447 }
1448
1449 rpmfi rpmfiUpdateState(rpmfi fi, rpmts ts, rpmte p)
1450 {
1451     char * fstates = fi->fstates;
1452     rpmFileAction * actions = fi->actions;
1453     sharedFileInfo replaced = fi->replaced;
1454     rpmte savep;
1455     int numShared = 0;
1456
1457     if (replaced != NULL) {
1458         for (; replaced->otherPkg; replaced++) {
1459             numShared++;
1460         }
1461         if (numShared > 0) {
1462             replaced = xcalloc(numShared + 1, sizeof(*fi->replaced));
1463             memcpy(replaced, fi->replaced, 
1464                    sizeof(*fi->replaced) * (numShared + 1));
1465         }
1466     }
1467
1468     fi->fstates = NULL;
1469     fi->actions = NULL;
1470     fi->replaced = NULL;
1471     /* FIX: fi->actions is NULL */
1472     fi = rpmfiFree(fi);
1473
1474     savep = rpmtsSetRelocateElement(ts, p);
1475     fi = rpmfiNew(ts, p->h, RPMTAG_BASENAMES, RPMFI_KEEPHEADER);
1476     (void) rpmtsSetRelocateElement(ts, savep);
1477
1478     if (fi != NULL) {   /* XXX can't happen */
1479         fi->te = p;
1480         free(fi->fstates);
1481         fi->fstates = fstates;
1482         free(fi->actions);
1483         fi->actions = actions;
1484         if (replaced != NULL)
1485             fi->replaced = replaced;
1486         p->fi = fi;
1487     }
1488     return fi;
1489 }
1490
1491 void rpmfiSetFState(rpmfi fi, int ix, rpmfileState state)
1492 {
1493     if (fi != NULL && ix >= 0 && ix < fi->fc) {
1494         if (fi->fstates == NULL) {
1495             fi->fstates = xmalloc(sizeof(*fi->fstates) * fi->fc);
1496             memset(fi->fstates, RPMFILE_STATE_MISSING, fi->fc);
1497         }
1498         fi->fstates[ix] = state;
1499     }
1500 }
1501
1502 void rpmfiSetFReplacedSize(rpmfi fi, rpm_loff_t newsize)
1503 {
1504     if (fi != NULL && fi->i >= 0 && fi->i < fi->fc) {
1505         if (fi->replacedSizes == NULL) {
1506             fi->replacedSizes = xcalloc(fi->fc, sizeof(*fi->replacedSizes));
1507         }
1508         /* XXX watch out, replacedSizes is not rpm_loff_t (yet) */
1509         fi->replacedSizes[fi->i] = (rpm_off_t) newsize;
1510     }
1511 }
1512
1513 rpm_loff_t rpmfiFReplacedSize(rpmfi fi)
1514 {
1515     rpm_loff_t rsize = 0;
1516     if (fi != NULL && fi->i >= 0 && fi->i < fi->fc) {
1517         if (fi->replacedSizes) {
1518             rsize = fi->replacedSizes[fi->i];
1519         }
1520     }
1521     return rsize;
1522 }
1523
1524 FSM_t rpmfiFSM(rpmfi fi)
1525 {
1526     if (fi != NULL && fi->fsm == NULL) {
1527         cpioMapFlags mapflags;
1528         /* Figure out mapflags: 
1529          * - path, mode, uid and gid are used by everything
1530          * - all binary packages get SBIT_CHECK set
1531          * - if archive size is not known, we're only building this package,
1532          *   different rules apply 
1533          */
1534         mapflags = CPIO_MAP_PATH | CPIO_MAP_MODE | CPIO_MAP_UID | CPIO_MAP_GID;
1535         if (fi->fiflags & RPMFI_ISBUILD) {
1536             mapflags |= CPIO_MAP_TYPE;
1537             if (fi->fiflags & RPMFI_ISSOURCE) mapflags |= CPIO_FOLLOW_SYMLINKS;
1538         } else {
1539             if (!(fi->fiflags & RPMFI_ISSOURCE)) mapflags |= CPIO_SBIT_CHECK;
1540         }
1541         fi->fsm = newFSM(mapflags);
1542     }
1543     return (fi != NULL) ? fi->fsm : NULL;
1544 }