allow rpm to custom systemd installation
[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
19 #include "debug.h"
20
21 static rpmfi rpmfiUnlink(rpmfi fi)
22 {
23     if (fi)
24         fi->nrefs--;
25     return NULL;
26 }
27
28 rpmfi rpmfiLink(rpmfi fi)
29 {
30     if (fi)
31         fi->nrefs++;
32     return fi;
33 }
34
35 rpm_count_t rpmfiFC(rpmfi fi)
36 {
37     return (fi != NULL ? fi->fc : 0);
38 }
39
40 rpm_count_t rpmfiDC(rpmfi fi)
41 {
42     return (fi != NULL ? fi->dc : 0);
43 }
44
45 #ifdef  NOTYET
46 int rpmfiDI(rpmfi fi)
47 {
48 }
49 #endif
50
51 int rpmfiFX(rpmfi fi)
52 {
53     return (fi != NULL ? fi->i : -1);
54 }
55
56 int rpmfiSetFX(rpmfi fi, int fx)
57 {
58     int i = -1;
59
60     if (fi != NULL && fx >= 0 && fx < fi->fc) {
61         i = fi->i;
62         fi->i = fx;
63         fi->j = fi->dil[fi->i];
64     }
65     return i;
66 }
67
68 int rpmfiDX(rpmfi fi)
69 {
70     return (fi != NULL ? fi->j : -1);
71 }
72
73 int rpmfiSetDX(rpmfi fi, int dx)
74 {
75     int j = -1;
76
77     if (fi != NULL && dx >= 0 && dx < fi->dc) {
78         j = fi->j;
79         fi->j = dx;
80     }
81     return j;
82 }
83
84 int rpmfiDIIndex(rpmfi fi, int dx)
85 {
86     int j = -1;
87     if (fi != NULL && dx >= 0 && dx < fi->fc) {
88         if (fi->dil != NULL)
89             j = fi->dil[dx];
90     }
91     return j;
92 }
93
94 rpmsid rpmfiBNIdIndex(rpmfi fi, int ix)
95 {
96     rpmsid id = 0;
97     if (fi != NULL && ix >= 0 && ix < fi->fc) {
98         if (fi->bnid != NULL)
99             id = fi->bnid[ix];
100     }
101     return id;
102 }
103
104 rpmsid rpmfiDNIdIndex(rpmfi fi, int jx)
105 {
106     rpmsid id = 0;
107     if (fi != NULL && jx >= 0 && jx < fi->fc) {
108         if (fi->dnid != NULL)
109             id = fi->dnid[jx];
110     }
111     return id;
112 }
113
114 const char * rpmfiBNIndex(rpmfi fi, int ix)
115 {
116     const char * BN = NULL;
117
118     if (fi != NULL && ix >= 0 && ix < fi->fc) {
119         if (fi->bnid != NULL)
120             BN = rpmstrPoolStr(fi->pool, fi->bnid[ix]);
121     }
122     return BN;
123 }
124
125 const char * rpmfiDNIndex(rpmfi fi, int jx)
126 {
127     const char * DN = NULL;
128
129     if (fi != NULL && jx >= 0 && jx < fi->dc) {
130         if (fi->dnid != NULL)
131             DN = rpmstrPoolStr(fi->pool, fi->dnid[jx]);
132     }
133     return DN;
134 }
135
136 char * rpmfiFNIndex(rpmfi fi, int ix)
137 {
138     char *fn = NULL;
139     if (fi != NULL && ix >= 0 && ix < fi->fc) {
140         fn = rstrscat(NULL, rpmstrPoolStr(fi->pool, fi->dnid[fi->dil[ix]]),
141                             rpmstrPoolStr(fi->pool, fi->bnid[ix]), NULL);
142     }
143     return fn;
144 }
145
146 rpmfileAttrs rpmfiFFlagsIndex(rpmfi fi, int ix)
147 {
148     rpmfileAttrs FFlags = 0;
149
150     if (fi != NULL && ix >= 0 && ix < fi->fc) {
151         if (fi->fflags != NULL)
152             FFlags = fi->fflags[ix];
153     }
154     return FFlags;
155 }
156
157 rpmVerifyAttrs rpmfiVFlagsIndex(rpmfi fi, int ix)
158 {
159     rpmVerifyAttrs VFlags = 0;
160
161     if (fi != NULL && ix >= 0 && ix < fi->fc) {
162         if (fi->vflags != NULL)
163             VFlags = fi->vflags[ix];
164     }
165     return VFlags;
166 }
167
168 rpm_mode_t rpmfiFModeIndex(rpmfi fi, int ix)
169 {
170     rpm_mode_t fmode = 0;
171
172     if (fi != NULL && ix >= 0 && ix < fi->fc) {
173         if (fi->fmodes != NULL)
174             fmode = fi->fmodes[ix];
175     }
176     return fmode;
177 }
178
179 rpmfileState rpmfiFStateIndex(rpmfi fi, int ix)
180 {
181     rpmfileState fstate = RPMFILE_STATE_MISSING;
182
183     if (fi != NULL && ix >= 0 && ix < fi->fc) {
184         if (fi->fstates != NULL)
185             fstate = fi->fstates[ix];
186     }
187     return fstate;
188 }
189
190 const unsigned char * rpmfiMD5(rpmfi fi)
191 {
192     const unsigned char *digest;
193     int algo = 0;
194
195     digest = rpmfiFDigest(fi, &algo, NULL);
196     return (algo == PGPHASHALGO_MD5) ? digest : NULL;
197 }
198
199 int rpmfiDigestAlgo(rpmfi fi)
200 {
201     return fi ? fi->digestalgo : 0;
202 }
203
204 const unsigned char * rpmfiFDigestIndex(rpmfi fi, int ix, int *algo, size_t *len)
205 {
206     const unsigned char *digest = NULL;
207
208     if (fi != NULL && ix >= 0 && ix < fi->fc) {
209         size_t diglen = rpmDigestLength(fi->digestalgo);
210         if (fi->digests != NULL)
211             digest = fi->digests + (diglen * ix);
212         if (len) 
213             *len = diglen;
214         if (algo) 
215             *algo = fi->digestalgo;
216     }
217     return digest;
218 }
219
220 char * rpmfiFDigestHex(rpmfi fi, int *algo)
221 {
222     size_t diglen = 0;
223     char *fdigest = NULL;
224     const unsigned char *digest = rpmfiFDigest(fi, algo, &diglen);
225     if (digest) {
226         fdigest = pgpHexStr(digest, diglen);
227     }
228     return fdigest;
229 }
230
231 const char * rpmfiFLinkIndex(rpmfi fi, int ix)
232 {
233     const char * flink = NULL;
234
235     if (fi != NULL && ix >= 0 && ix < fi->fc) {
236         if (fi->flinks != NULL)
237             flink = rpmstrPoolStr(fi->pool, fi->flinks[ix]);
238     }
239     return flink;
240 }
241
242 rpm_loff_t rpmfiFSizeIndex(rpmfi fi, int ix)
243 {
244     rpm_loff_t fsize = 0;
245
246     if (fi != NULL && ix >= 0 && ix < fi->fc) {
247         if (fi->fsizes != NULL)
248             fsize = fi->fsizes[ix];
249     }
250     return fsize;
251 }
252
253 rpm_rdev_t rpmfiFRdevIndex(rpmfi fi, int ix)
254 {
255     rpm_rdev_t frdev = 0;
256
257     if (fi != NULL && ix >= 0 && ix < fi->fc) {
258         if (fi->frdevs != NULL)
259             frdev = fi->frdevs[ix];
260     }
261     return frdev;
262 }
263
264 rpm_ino_t rpmfiFInodeIndex(rpmfi fi, int ix)
265 {
266     rpm_ino_t finode = 0;
267
268     if (fi != NULL && ix >= 0 && ix < fi->fc) {
269         if (fi->finodes != NULL)
270             finode = fi->finodes[ix];
271     }
272     return finode;
273 }
274
275 rpm_color_t rpmfiColor(rpmfi fi)
276 {
277     rpm_color_t color = 0;
278
279     if (fi != NULL && fi->fcolors != NULL) {
280         for (int i = 0; i < fi->fc; i++)
281             color |= fi->fcolors[i];
282         /* XXX ignore all but lsnibble for now. */
283         color &= 0xf;
284     }
285     return color;
286 }
287
288 rpm_color_t rpmfiFColorIndex(rpmfi fi, int ix)
289 {
290     rpm_color_t fcolor = 0;
291
292     if (fi != NULL && ix >= 0 && ix < fi->fc) {
293         if (fi->fcolors != NULL)
294             /* XXX ignore all but lsnibble for now. */
295             fcolor = (fi->fcolors[ix] & 0x0f);
296     }
297     return fcolor;
298 }
299
300 const char * rpmfiFClassIndex(rpmfi fi, int ix)
301 {
302     const char * fclass = NULL;
303     int cdictx;
304
305     if (fi != NULL && fi->fcdictx != NULL && ix >= 0 && ix < fi->fc) {
306         cdictx = fi->fcdictx[ix];
307         if (fi->cdict != NULL && cdictx >= 0 && cdictx < fi->ncdict)
308             fclass = fi->cdict[cdictx];
309     }
310     return fclass;
311 }
312
313 uint32_t rpmfiFDependsIndex(rpmfi fi, int ix, const uint32_t ** fddictp)
314 {
315     int fddictx = -1;
316     int fddictn = 0;
317     const uint32_t * fddict = NULL;
318
319     if (fi != NULL && ix >= 0 && ix < fi->fc) {
320         if (fi->fddictn != NULL)
321             fddictn = fi->fddictn[ix];
322         if (fddictn > 0 && fi->fddictx != NULL)
323             fddictx = fi->fddictx[ix];
324         if (fi->ddict != NULL && fddictx >= 0 && (fddictx+fddictn) <= fi->nddict)
325             fddict = fi->ddict + fddictx;
326     }
327     if (fddictp)
328         *fddictp = fddict;
329     return fddictn;
330 }
331
332 uint32_t rpmfiFNlinkIndex(rpmfi fi, int ix)
333 {
334     uint32_t nlink = 0;
335
336     if (fi != NULL && ix >= 0 && ix < fi->fc) {
337         /* XXX rpm-2.3.12 has not RPMTAG_FILEINODES */
338         if (fi->finodes && fi->frdevs) {
339             rpm_ino_t finode = fi->finodes[ix];
340             rpm_rdev_t frdev = fi->frdevs[ix];
341             int j;
342
343             for (j = 0; j < fi->fc; j++) {
344                 if (fi->frdevs[j] == frdev && fi->finodes[j] == finode)
345                     nlink++;
346             }
347         }
348     }
349     return nlink;
350 }
351
352 rpm_time_t rpmfiFMtimeIndex(rpmfi fi, int ix)
353 {
354     rpm_time_t fmtime = 0;
355
356     if (fi != NULL && ix >= 0 && ix < fi->fc) {
357         if (fi->fmtimes != NULL)
358             fmtime = fi->fmtimes[ix];
359     }
360     return fmtime;
361 }
362
363 const char * rpmfiFUserIndex(rpmfi fi, int ix)
364 {
365     const char * fuser = NULL;
366
367     if (fi != NULL && ix >= 0 && ix < fi->fc) {
368         if (fi->fuser != NULL)
369             fuser = rpmstrPoolStr(fi->pool, fi->fuser[ix]);
370     }
371     return fuser;
372 }
373
374 const char * rpmfiFGroupIndex(rpmfi fi, int ix)
375 {
376     const char * fgroup = NULL;
377
378     if (fi != NULL && ix >= 0 && ix < fi->fc) {
379         if (fi->fgroup != NULL)
380             fgroup = rpmstrPoolStr(fi->pool, fi->fgroup[ix]);
381     }
382     return fgroup;
383 }
384
385 const char * rpmfiFCapsIndex(rpmfi fi, int ix)
386 {
387     const char *fcaps = NULL;
388     if (fi != NULL && ix >= 0 && ix < fi->fc) {
389         fcaps = fi->fcaps ? fi->fcaps[ix] : "";
390     }
391     return fcaps;
392 }
393
394 const char * rpmfiFLangsIndex(rpmfi fi, int ix)
395 {
396     const char *flangs = NULL;
397     if (fi != NULL && fi->flangs != NULL && ix >= 0 && ix < fi->fc) {
398         flangs = rpmstrPoolStr(fi->pool, fi->flangs[ix]);
399     }
400     return flangs;
401 }
402
403 struct fingerPrint_s *rpmfiFps(rpmfi fi)
404 {
405     return (fi != NULL) ? fi->fps : NULL;
406 }
407
408 int rpmfiNext(rpmfi fi)
409 {
410     int i = -1;
411
412     if (fi != NULL && ++fi->i >= 0) {
413         if (fi->i < fi->fc) {
414             i = fi->i;
415             if (fi->dil != NULL)
416                 fi->j = fi->dil[fi->i];
417         } else
418             fi->i = -1;
419     }
420
421     return i;
422 }
423
424 rpmfi rpmfiInit(rpmfi fi, int fx)
425 {
426     if (fi != NULL) {
427         if (fx >= 0 && fx < fi->fc) {
428             fi->i = fx - 1;
429             fi->j = -1;
430         }
431     }
432
433     return fi;
434 }
435
436 int rpmfiNextD(rpmfi fi)
437 {
438     int j = -1;
439
440     if (fi != NULL && ++fi->j >= 0) {
441         if (fi->j < fi->dc)
442             j = fi->j;
443         else
444             fi->j = -1;
445     }
446
447     return j;
448 }
449
450 rpmfi rpmfiInitD(rpmfi fi, int dx)
451 {
452     if (fi != NULL) {
453         if (dx >= 0 && dx < fi->fc)
454             fi->j = dx - 1;
455         else
456             fi = NULL;
457     }
458
459     return fi;
460 }
461
462 /**
463  * Identify a file type.
464  * @param ft            file type
465  * @return              string to identify a file type
466  */
467 static
468 const char * ftstring (rpmFileTypes ft)
469 {
470     switch (ft) {
471     case XDIR:  return "directory";
472     case CDEV:  return "char dev";
473     case BDEV:  return "block dev";
474     case LINK:  return "link";
475     case SOCK:  return "sock";
476     case PIPE:  return "fifo/pipe";
477     case REG:   return "file";
478     default:    return "unknown file type";
479     }
480 }
481
482 rpmFileTypes rpmfiWhatis(rpm_mode_t mode)
483 {
484     if (S_ISDIR(mode))  return XDIR;
485     if (S_ISCHR(mode))  return CDEV;
486     if (S_ISBLK(mode))  return BDEV;
487     if (S_ISLNK(mode))  return LINK;
488     if (S_ISSOCK(mode)) return SOCK;
489     if (S_ISFIFO(mode)) return PIPE;
490     return REG;
491 }
492
493 int rpmfiCompareIndex(rpmfi afi, int aix, rpmfi bfi, int bix)
494 {
495     mode_t amode = rpmfiFModeIndex(afi, aix);
496     mode_t bmode = rpmfiFModeIndex(bfi, bix);
497     rpmFileTypes awhat = rpmfiWhatis(amode);
498
499     if ((rpmfiFFlagsIndex(afi, aix) & RPMFILE_GHOST) ||
500         (rpmfiFFlagsIndex(bfi, bix) & RPMFILE_GHOST)) return 0;
501
502     if (amode != bmode) return 1;
503
504     if (awhat == LINK || awhat == REG) {
505         if (rpmfiFSizeIndex(afi, aix) != rpmfiFSizeIndex(bfi, bix))
506             return 1;
507     }
508
509     if (!rstreq(rpmfiFUserIndex(afi, aix), rpmfiFUserIndex(bfi, bix)))
510         return 1;
511     if (!rstreq(rpmfiFGroupIndex(afi, aix), rpmfiFGroupIndex(bfi, bix)))
512         return 1;
513
514     if (awhat == LINK) {
515         const char * alink = rpmfiFLinkIndex(afi, aix);
516         const char * blink = rpmfiFLinkIndex(bfi, bix);
517         if (alink == blink) return 0;
518         if (alink == NULL) return 1;
519         if (blink == NULL) return -1;
520         return strcmp(alink, blink);
521     } else if (awhat == REG) {
522         size_t adiglen, bdiglen;
523         int aalgo, balgo;
524         const unsigned char * adigest, * bdigest;
525         adigest = rpmfiFDigestIndex(afi, aix, &aalgo, &adiglen);
526         bdigest = rpmfiFDigestIndex(bfi, bix, &balgo, &bdiglen);
527         if (adigest == bdigest) return 0;
528         if (adigest == NULL) return 1;
529         if (bdigest == NULL) return -1;
530         /* can't meaningfully compare different hash types */
531         if (aalgo != balgo || adiglen != bdiglen) return -1;
532         return memcmp(adigest, bdigest, adiglen);
533     } else if (awhat == CDEV || awhat == BDEV) {
534         if (rpmfiFRdevIndex(afi, aix) != rpmfiFRdevIndex(bfi, bix))
535             return 1;
536     }
537
538     return 0;
539 }
540
541 rpmFileAction rpmfiDecideFateIndex(rpmfi ofi, int oix, rpmfi nfi, int nix,
542                                    int skipMissing)
543 {
544     char * fn = rpmfiFNIndex(nfi, nix);
545     rpmfileAttrs newFlags = rpmfiFFlagsIndex(nfi, nix);
546     char buffer[1024];
547     rpmFileTypes dbWhat, newWhat, diskWhat;
548     struct stat sb;
549     int save = (newFlags & RPMFILE_NOREPLACE) ? FA_ALTNAME : FA_SAVE;
550     int action = FA_CREATE; /* assume we can create */
551
552     /* If the new file is a ghost, leave whatever might be on disk alone. */
553     if (newFlags & RPMFILE_GHOST) {
554         action = FA_SKIP;
555         goto exit;
556     }
557
558     if (lstat(fn, &sb)) {
559         /*
560          * The file doesn't exist on the disk. Create it unless the new
561          * package has marked it as missingok, or allfiles is requested.
562          */
563         if (skipMissing && (newFlags & RPMFILE_MISSINGOK)) {
564             rpmlog(RPMLOG_DEBUG, "%s skipped due to missingok flag\n",
565                         fn);
566             action = FA_SKIP;
567             goto exit;
568         } else {
569             goto exit;
570         }
571     }
572
573     diskWhat = rpmfiWhatis((rpm_mode_t)sb.st_mode);
574     dbWhat = rpmfiWhatis(rpmfiFModeIndex(ofi, oix));
575     newWhat = rpmfiWhatis(rpmfiFModeIndex(nfi, nix));
576
577     /*
578      * This order matters - we'd prefer to CREATE the file if at all
579      * possible in case something else (like the timestamp) has changed.
580      * Only regular files and symlinks might need a backup, everything
581      * else falls through here with FA_CREATE.
582      */
583     memset(buffer, 0, sizeof(buffer));
584     if (dbWhat == REG) {
585         int oalgo, nalgo;
586         size_t odiglen, ndiglen;
587         const unsigned char * odigest, * ndigest;
588
589         /* See if the file on disk is identical to the one in old pkg */
590         odigest = rpmfiFDigestIndex(ofi, oix, &oalgo, &odiglen);
591         if (diskWhat == REG) {
592             if (rpmDoDigest(oalgo, fn, 0, (unsigned char *)buffer, NULL))
593                 goto exit;      /* assume file has been removed */
594             if (odigest && memcmp(odigest, buffer, odiglen) == 0)
595                 goto exit;      /* unmodified config file, replace. */
596         }
597
598         /* See if the file on disk is identical to the one in new pkg */
599         ndigest = rpmfiFDigestIndex(nfi, nix, &nalgo, &ndiglen);
600         if (diskWhat == REG && newWhat == REG) {
601             /* hash algo changed in new, recalculate digest */
602             if (oalgo != nalgo)
603                 if (rpmDoDigest(nalgo, fn, 0, (unsigned char *)buffer, NULL))
604                     goto exit;          /* assume file has been removed */
605             if (ndigest && memcmp(ndigest, buffer, ndiglen) == 0)
606                 goto exit;              /* file identical in new, replace. */
607         }
608
609         /* If file can be determined identical in old and new pkg, let it be */
610         if (newWhat == REG && oalgo == nalgo && odiglen == ndiglen) {
611             if (odigest && ndigest && memcmp(odigest, ndigest, odiglen) == 0) {
612                 action = FA_SKIP; /* identical file, dont bother */
613                 goto exit;
614             }
615         }
616         
617         /* ...but otherwise a backup will be needed */
618         action = save;
619     } else if (dbWhat == LINK) {
620         const char * oFLink, * nFLink;
621
622         /* See if the link on disk is identical to the one in old pkg */
623         oFLink = rpmfiFLinkIndex(ofi, oix);
624         if (diskWhat == LINK) {
625             ssize_t link_len = readlink(fn, buffer, sizeof(buffer) - 1);
626             if (link_len == -1)
627                 goto exit;              /* assume file has been removed */
628             buffer[link_len] = '\0';
629             if (oFLink && rstreq(oFLink, buffer))
630                 goto exit;              /* unmodified config file, replace. */
631         }
632
633         /* See if the link on disk is identical to the one in new pkg */
634         nFLink = rpmfiFLinkIndex(nfi, nix);
635         if (diskWhat == LINK && newWhat == LINK) {
636             if (nFLink && rstreq(nFLink, buffer))
637                 goto exit;              /* unmodified config file, replace. */
638         }
639
640         /* If link is identical in old and new pkg, let it be */
641         if (newWhat == LINK && oFLink && nFLink && rstreq(oFLink, nFLink)) {
642             action = FA_SKIP;           /* identical file, don't bother. */
643             goto exit;
644         }
645
646         /* ...but otherwise a backup will be needed */
647         action = save;
648     }
649
650 exit:
651     free(fn);
652     return action;
653 }
654
655 int rpmfiConfigConflictIndex(rpmfi fi, int ix)
656 {
657     char * fn = NULL;
658     rpmfileAttrs flags = rpmfiFFlagsIndex(fi, ix);
659     char buffer[1024];
660     rpmFileTypes newWhat, diskWhat;
661     struct stat sb;
662     int rc = 0;
663
664     /* Non-configs are not config conflicts. */
665     if (!(flags & RPMFILE_CONFIG))
666         return 0;
667
668     /* Only links and regular files can be %config, this is kinda moot */
669     /* XXX: Why are we returning 1 here? */
670     newWhat = rpmfiWhatis(rpmfiFModeIndex(fi, ix));
671     if (newWhat != LINK && newWhat != REG)
672         return 1;
673
674     /* If it's not on disk, there's nothing to be saved */
675     fn = rpmfiFNIndex(fi, ix);
676     if (lstat(fn, &sb))
677         goto exit;
678
679     /*
680      * Preserve legacy behavior: an existing %ghost %config is considered
681      * "modified" but unlike regular %config, its never removed and
682      * never backed up. Whether this actually makes sense is a whole
683      * another question, but this is very long-standing behavior that
684      * people might be depending on. The resulting FA_ALTNAME etc action
685      * is special-cased in FSM to avoid actually creating backups on ghosts.
686      */
687     if (flags & RPMFILE_GHOST) {
688         rc = 1;
689         goto exit;
690     }
691
692     /* Files of different types obviously are not identical */
693     diskWhat = rpmfiWhatis((rpm_mode_t)sb.st_mode);
694     if (diskWhat != newWhat) {
695         rc = 1;
696         goto exit;
697     }
698
699     /* Files of different sizes obviously are not identical */
700     if (rpmfiFSizeIndex(fi, ix) != sb.st_size) {
701         rc = 1;
702         goto exit;
703     }
704     
705     memset(buffer, 0, sizeof(buffer));
706     if (newWhat == REG) {
707         int algo;
708         size_t diglen;
709         const unsigned char *ndigest = rpmfiFDigestIndex(fi,ix, &algo, &diglen);
710         if (rpmDoDigest(algo, fn, 0, (unsigned char *)buffer, NULL))
711             goto exit;  /* assume file has been removed */
712         if (ndigest && memcmp(ndigest, buffer, diglen) == 0)
713             goto exit;  /* unmodified config file */
714     } else /* newWhat == LINK */ {
715         const char * nFLink;
716         ssize_t link_len = readlink(fn, buffer, sizeof(buffer) - 1);
717         if (link_len == -1)
718             goto exit;  /* assume file has been removed */
719         buffer[link_len] = '\0';
720         nFLink = rpmfiFLinkIndex(fi, ix);
721         if (nFLink && rstreq(nFLink, buffer))
722             goto exit;  /* unmodified config file */
723     }
724
725     rc = 1;
726
727 exit:
728     free(fn);
729     return rc;
730 }
731
732 static char **duparray(char ** src, int size)
733 {
734     char **dest = xmalloc((size+1) * sizeof(*dest));
735     for (int i = 0; i < size; i++) {
736         dest[i] = xstrdup(src[i]);
737     }
738     free(src);
739     return dest;
740 }
741
742 static int addPrefixes(Header h, rpmRelocation *relocations, int numRelocations)
743 {
744     struct rpmtd_s validRelocs;
745     const char *validprefix;
746     const char ** actualRelocations;
747     int numActual = 0;
748
749     headerGet(h, RPMTAG_PREFIXES, &validRelocs, HEADERGET_MINMEM);
750     /*
751      * If no relocations are specified (usually the case), then return the
752      * original header. If there are prefixes, however, then INSTPREFIXES
753      * should be added for RPM_INSTALL_PREFIX environ variables in scriptlets, 
754      * but, since relocateFileList() can be called more than once for 
755      * the same header, don't bother if already present.
756      */
757     if (relocations == NULL || numRelocations == 0) {
758         if (rpmtdCount(&validRelocs) > 0) {
759             if (!headerIsEntry(h, RPMTAG_INSTPREFIXES)) {
760                 rpmtdSetTag(&validRelocs, RPMTAG_INSTPREFIXES);
761                 headerPut(h, &validRelocs, HEADERPUT_DEFAULT);
762             }
763             rpmtdFreeData(&validRelocs);
764         }
765         return 0;
766     }
767
768     actualRelocations = xmalloc(rpmtdCount(&validRelocs) * sizeof(*actualRelocations));
769     rpmtdInit(&validRelocs);
770     while ((validprefix = rpmtdNextString(&validRelocs))) {
771         int j;
772         for (j = 0; j < numRelocations; j++) {
773             if (relocations[j].oldPath == NULL || /* XXX can't happen */
774                 !rstreq(validprefix, relocations[j].oldPath))
775                 continue;
776             /* On install, a relocate to NULL means skip the path. */
777             if (relocations[j].newPath) {
778                 actualRelocations[numActual] = relocations[j].newPath;
779                 numActual++;
780             }
781             break;
782         }
783         if (j == numRelocations) {
784             actualRelocations[numActual] = validprefix;
785             numActual++;
786         }
787     }
788     rpmtdFreeData(&validRelocs);
789
790     if (numActual) {
791         headerPutStringArray(h, RPMTAG_INSTPREFIXES, actualRelocations, numActual);
792     }
793     free(actualRelocations);
794     return numActual;
795 }
796
797 static void saveRelocs(Header h, rpmtd bnames, rpmtd dnames, rpmtd dindexes)
798 {
799         struct rpmtd_s td;
800         headerGet(h, RPMTAG_BASENAMES, &td, HEADERGET_MINMEM);
801         rpmtdSetTag(&td, RPMTAG_ORIGBASENAMES);
802         headerPut(h, &td, HEADERPUT_DEFAULT);
803         rpmtdFreeData(&td);
804
805         headerGet(h, RPMTAG_DIRNAMES, &td, HEADERGET_MINMEM);
806         rpmtdSetTag(&td, RPMTAG_ORIGDIRNAMES);
807         headerPut(h, &td, HEADERPUT_DEFAULT);
808         rpmtdFreeData(&td);
809
810         headerGet(h, RPMTAG_DIRINDEXES, &td, HEADERGET_MINMEM);
811         rpmtdSetTag(&td, RPMTAG_ORIGDIRINDEXES);
812         headerPut(h, &td, HEADERPUT_DEFAULT);
813         rpmtdFreeData(&td);
814
815         headerMod(h, bnames);
816         headerMod(h, dnames);
817         headerMod(h, dindexes);
818 }
819
820 void rpmRelocateFileList(rpmRelocation *relocations, int numRelocations, 
821                          rpmfs fs, Header h)
822 {
823     static int _printed = 0;
824     char ** baseNames;
825     char ** dirNames;
826     uint32_t * dirIndexes;
827     rpm_count_t fileCount, dirCount;
828     int nrelocated = 0;
829     int fileAlloced = 0;
830     char * fn = NULL;
831     int haveRelocatedBase = 0;
832     size_t maxlen = 0;
833     int i, j;
834     struct rpmtd_s bnames, dnames, dindexes, fmodes;
835
836     addPrefixes(h, relocations, numRelocations);
837
838     if (!_printed) {
839         _printed = 1;
840         rpmlog(RPMLOG_DEBUG, "========== relocations\n");
841         for (i = 0; i < numRelocations; i++) {
842             if (relocations[i].oldPath == NULL) continue; /* XXX can't happen */
843             if (relocations[i].newPath == NULL)
844                 rpmlog(RPMLOG_DEBUG, "%5d exclude  %s\n",
845                         i, relocations[i].oldPath);
846             else
847                 rpmlog(RPMLOG_DEBUG, "%5d relocate %s -> %s\n",
848                         i, relocations[i].oldPath, relocations[i].newPath);
849         }
850     }
851
852     for (i = 0; i < numRelocations; i++) {
853         if (relocations[i].newPath == NULL) continue;
854         size_t len = strlen(relocations[i].newPath);
855         if (len > maxlen) maxlen = len;
856     }
857
858     headerGet(h, RPMTAG_BASENAMES, &bnames, HEADERGET_MINMEM);
859     headerGet(h, RPMTAG_DIRINDEXES, &dindexes, HEADERGET_ALLOC);
860     headerGet(h, RPMTAG_DIRNAMES, &dnames, HEADERGET_MINMEM);
861     headerGet(h, RPMTAG_FILEMODES, &fmodes, HEADERGET_MINMEM);
862     /* TODO XXX ugh.. use rpmtd iterators & friends instead */
863     baseNames = bnames.data;
864     dirIndexes = dindexes.data;
865     fileCount = rpmtdCount(&bnames);
866     dirCount = rpmtdCount(&dnames);
867     /* XXX TODO: use rpmtdDup() instead */
868     dirNames = dnames.data = duparray(dnames.data, dirCount);
869     dnames.flags |= RPMTD_PTR_ALLOCED;
870
871     /*
872      * For all relocations, we go through sorted file/relocation lists 
873      * backwards so that /usr/local relocations take precedence over /usr 
874      * ones.
875      */
876
877     /* Relocate individual paths. */
878
879     for (i = fileCount - 1; i >= 0; i--) {
880         rpmFileTypes ft;
881         int fnlen;
882
883         size_t len = maxlen +
884                 strlen(dirNames[dirIndexes[i]]) + strlen(baseNames[i]) + 1;
885         if (len >= fileAlloced) {
886             fileAlloced = len * 2;
887             fn = xrealloc(fn, fileAlloced);
888         }
889
890 assert(fn != NULL);             /* XXX can't happen */
891         *fn = '\0';
892         fnlen = stpcpy( stpcpy(fn, dirNames[dirIndexes[i]]), baseNames[i]) - fn;
893
894         /*
895          * See if this file path needs relocating.
896          */
897         /*
898          * XXX FIXME: Would a bsearch of the (already sorted) 
899          * relocation list be a good idea?
900          */
901         for (j = numRelocations - 1; j >= 0; j--) {
902             if (relocations[j].oldPath == NULL) /* XXX can't happen */
903                 continue;
904             len = !rstreq(relocations[j].oldPath, "/")
905                 ? strlen(relocations[j].oldPath)
906                 : 0;
907
908             if (fnlen < len)
909                 continue;
910             /*
911              * Only subdirectories or complete file paths may be relocated. We
912              * don't check for '\0' as our directory names all end in '/'.
913              */
914             if (!(fn[len] == '/' || fnlen == len))
915                 continue;
916
917             if (!rstreqn(relocations[j].oldPath, fn, len))
918                 continue;
919             break;
920         }
921         if (j < 0) continue;
922
923         rpmtdSetIndex(&fmodes, i);
924         ft = rpmfiWhatis(rpmtdGetNumber(&fmodes));
925
926         /* On install, a relocate to NULL means skip the path. */
927         if (relocations[j].newPath == NULL) {
928             if (ft == XDIR) {
929                 /* Start with the parent, looking for directory to exclude. */
930                 for (j = dirIndexes[i]; j < dirCount; j++) {
931                     len = strlen(dirNames[j]) - 1;
932                     while (len > 0 && dirNames[j][len-1] == '/') len--;
933                     if (fnlen != len)
934                         continue;
935                     if (!rstreqn(fn, dirNames[j], fnlen))
936                         continue;
937                     break;
938                 }
939             }
940             rpmfsSetAction(fs, i, FA_SKIPNSTATE);
941             rpmlog(RPMLOG_DEBUG, "excluding %s %s\n",
942                    ftstring(ft), fn);
943             continue;
944         }
945
946         /* Relocation on full paths only, please. */
947         if (fnlen != len) continue;
948
949         rpmlog(RPMLOG_DEBUG, "relocating %s to %s\n",
950                fn, relocations[j].newPath);
951         nrelocated++;
952
953         strcpy(fn, relocations[j].newPath);
954         {   char * te = strrchr(fn, '/');
955             if (te) {
956                 if (te > fn) te++;      /* root is special */
957                 fnlen = te - fn;
958             } else
959                 te = fn + strlen(fn);
960             if (!rstreq(baseNames[i], te)) { /* basename changed too? */
961                 if (!haveRelocatedBase) {
962                     /* XXX TODO: use rpmtdDup() instead */
963                     bnames.data = baseNames = duparray(baseNames, fileCount);
964                     bnames.flags |= RPMTD_PTR_ALLOCED;
965                     haveRelocatedBase = 1;
966                 }
967                 free(baseNames[i]);
968                 baseNames[i] = xstrdup(te);
969             }
970             *te = '\0';                 /* terminate new directory name */
971         }
972
973         /* Does this directory already exist in the directory list? */
974         for (j = 0; j < dirCount; j++) {
975             if (fnlen != strlen(dirNames[j]))
976                 continue;
977             if (!rstreqn(fn, dirNames[j], fnlen))
978                 continue;
979             break;
980         }
981         
982         if (j < dirCount) {
983             dirIndexes[i] = j;
984             continue;
985         }
986
987         /* Creating new paths is a pita */
988         dirNames = dnames.data = xrealloc(dnames.data, 
989                                sizeof(*dirNames) * (dirCount + 1));
990
991         dirNames[dirCount] = xstrdup(fn);
992         dirIndexes[i] = dirCount;
993         dirCount++;
994         dnames.count++;
995     }
996
997     /* Finish off by relocating directories. */
998     for (i = dirCount - 1; i >= 0; i--) {
999         for (j = numRelocations - 1; j >= 0; j--) {
1000
1001             if (relocations[j].oldPath == NULL) /* XXX can't happen */
1002                 continue;
1003             size_t len = !rstreq(relocations[j].oldPath, "/")
1004                 ? strlen(relocations[j].oldPath)
1005                 : 0;
1006
1007             if (len && !rstreqn(relocations[j].oldPath, dirNames[i], len))
1008                 continue;
1009
1010             /*
1011              * Only subdirectories or complete file paths may be relocated. We
1012              * don't check for '\0' as our directory names all end in '/'.
1013              */
1014             if (dirNames[i][len] != '/')
1015                 continue;
1016
1017             if (relocations[j].newPath) { /* Relocate the path */
1018                 char *t = NULL;
1019                 rstrscat(&t, relocations[j].newPath, (dirNames[i] + len), NULL);
1020                 /* Unfortunatly rpmCleanPath strips the trailing slash.. */
1021                 (void) rpmCleanPath(t);
1022                 rstrcat(&t, "/");
1023
1024                 rpmlog(RPMLOG_DEBUG,
1025                        "relocating directory %s to %s\n", dirNames[i], t);
1026                 free(dirNames[i]);
1027                 dirNames[i] = t;
1028                 nrelocated++;
1029             }
1030         }
1031     }
1032
1033     /* Save original filenames in header and replace (relocated) filenames. */
1034     if (nrelocated) {
1035         saveRelocs(h, &bnames, &dnames, &dindexes);
1036     }
1037
1038     rpmtdFreeData(&bnames);
1039     rpmtdFreeData(&dnames);
1040     rpmtdFreeData(&dindexes);
1041     rpmtdFreeData(&fmodes);
1042     free(fn);
1043 }
1044
1045 rpmfi rpmfiFree(rpmfi fi)
1046 {
1047     if (fi == NULL) return NULL;
1048
1049     if (fi->nrefs > 1)
1050         return rpmfiUnlink(fi);
1051
1052     if (fi->fc > 0) {
1053         fi->bnid = _free(fi->bnid);
1054         fi->dnid = _free(fi->dnid);
1055         fi->dil = _free(fi->dil);
1056
1057         fi->flinks = _free(fi->flinks);
1058         fi->flangs = _free(fi->flangs);
1059         fi->digests = _free(fi->digests);
1060         fi->fcaps = _free(fi->fcaps);
1061
1062         fi->cdict = _free(fi->cdict);
1063
1064         fi->fuser = _free(fi->fuser);
1065         fi->fgroup = _free(fi->fgroup);
1066
1067         fi->fstates = _free(fi->fstates);
1068         fi->fps = _free(fi->fps);
1069
1070         fi->pool = rpmstrPoolFree(fi->pool);
1071
1072         /* these point to header memory if KEEPHEADER is used, dont free */
1073         if (!(fi->fiflags & RPMFI_KEEPHEADER) && fi->h == NULL) {
1074             fi->fmtimes = _free(fi->fmtimes);
1075             fi->fmodes = _free(fi->fmodes);
1076             fi->fflags = _free(fi->fflags);
1077             fi->vflags = _free(fi->vflags);
1078             fi->fsizes = _free(fi->fsizes);
1079             fi->frdevs = _free(fi->frdevs);
1080             fi->finodes = _free(fi->finodes);
1081
1082             fi->fcolors = _free(fi->fcolors);
1083             fi->fcdictx = _free(fi->fcdictx);
1084             fi->ddict = _free(fi->ddict);
1085             fi->fddictx = _free(fi->fddictx);
1086             fi->fddictn = _free(fi->fddictn);
1087
1088         }
1089     }
1090
1091     fi->fn = _free(fi->fn);
1092     fi->apath = _free(fi->apath);
1093
1094     fi->replacedSizes = _free(fi->replacedSizes);
1095
1096     fi->h = headerFree(fi->h);
1097
1098     (void) rpmfiUnlink(fi);
1099     memset(fi, 0, sizeof(*fi));         /* XXX trash and burn */
1100     fi = _free(fi);
1101
1102     return NULL;
1103 }
1104
1105 static rpmsid * tag2pool(rpmstrPool pool, Header h, rpmTag tag)
1106 {
1107     rpmsid *sids = NULL;
1108     struct rpmtd_s td;
1109     if (headerGet(h, tag, &td, HEADERGET_MINMEM)) {
1110         sids = rpmtdToPool(&td, pool);
1111         rpmtdFreeData(&td);
1112     }
1113     return sids;
1114 }
1115
1116 /* validate a indexed tag data triplet (such as file bn/dn/dx) */
1117 static int indexSane(rpmtd xd, rpmtd yd, rpmtd zd)
1118 {
1119     int sane = 0;
1120     uint32_t xc = rpmtdCount(xd);
1121     uint32_t yc = rpmtdCount(yd);
1122     uint32_t zc = rpmtdCount(zd);
1123
1124     /* check that the amount of data in each is sane */
1125     if (xc > 0 && yc > 0 && yc <= xc && zc == xc) {
1126         uint32_t * i;
1127         /* ...and that the indexes are within bounds */
1128         while ((i = rpmtdNextUint32(zd))) {
1129             if (*i >= yc)
1130                 break;
1131         }
1132         /* unless the loop runs to finish, the data is broken */
1133         sane = (i == NULL);
1134     }
1135     return sane;
1136 }
1137
1138 #define _hgfi(_h, _tag, _td, _flags, _data) \
1139     if (headerGet((_h), (_tag), (_td), (_flags))) \
1140         _data = (td.data)
1141
1142 static int rpmfiPopulate(rpmfi fi, Header h, rpmfiFlags flags)
1143 {
1144     headerGetFlags scareFlags = (flags & RPMFI_KEEPHEADER) ? 
1145                                 HEADERGET_MINMEM : HEADERGET_ALLOC;
1146     headerGetFlags defFlags = HEADERGET_ALLOC;
1147     struct rpmtd_s fdigests, digalgo, td;
1148     unsigned char * t;
1149
1150     /* XXX TODO: all these should be sanity checked, ugh... */
1151     if (!(flags & RPMFI_NOFILEMODES))
1152         _hgfi(h, RPMTAG_FILEMODES, &td, scareFlags, fi->fmodes);
1153     if (!(flags & RPMFI_NOFILEFLAGS))
1154         _hgfi(h, RPMTAG_FILEFLAGS, &td, scareFlags, fi->fflags);
1155     if (!(flags & RPMFI_NOFILEVERIFYFLAGS))
1156         _hgfi(h, RPMTAG_FILEVERIFYFLAGS, &td, scareFlags, fi->vflags);
1157     if (!(flags & RPMFI_NOFILESIZES))
1158         _hgfi(h, RPMTAG_FILESIZES, &td, scareFlags, fi->fsizes);
1159
1160     if (!(flags & RPMFI_NOFILECOLORS))
1161         _hgfi(h, RPMTAG_FILECOLORS, &td, scareFlags, fi->fcolors);
1162
1163     if (!(flags & RPMFI_NOFILECLASS)) {
1164         _hgfi(h, RPMTAG_CLASSDICT, &td, scareFlags, fi->cdict);
1165         fi->ncdict = rpmtdCount(&td);
1166         _hgfi(h, RPMTAG_FILECLASS, &td, scareFlags, fi->fcdictx);
1167     }
1168     if (!(flags & RPMFI_NOFILEDEPS)) {
1169         _hgfi(h, RPMTAG_DEPENDSDICT, &td, scareFlags, fi->ddict);
1170         fi->nddict = rpmtdCount(&td);
1171         _hgfi(h, RPMTAG_FILEDEPENDSX, &td, scareFlags, fi->fddictx);
1172         _hgfi(h, RPMTAG_FILEDEPENDSN, &td, scareFlags, fi->fddictn);
1173     }
1174
1175     if (!(flags & RPMFI_NOFILESTATES))
1176         _hgfi(h, RPMTAG_FILESTATES, &td, defFlags, fi->fstates);
1177
1178     if (!(flags & RPMFI_NOFILECAPS))
1179         _hgfi(h, RPMTAG_FILECAPS, &td, defFlags, fi->fcaps);
1180
1181     if (!(flags & RPMFI_NOFILELINKTOS))
1182         fi->flinks = tag2pool(fi->pool, h, RPMTAG_FILELINKTOS);
1183     /* FILELANGS are only interesting when installing */
1184     if ((headerGetInstance(h) == 0) && !(flags & RPMFI_NOFILELANGS))
1185         fi->flangs = tag2pool(fi->pool, h, RPMTAG_FILELANGS);
1186
1187     /* See if the package has non-md5 file digests */
1188     fi->digestalgo = PGPHASHALGO_MD5;
1189     if (headerGet(h, RPMTAG_FILEDIGESTALGO, &digalgo, HEADERGET_MINMEM)) {
1190         uint32_t *algo = rpmtdGetUint32(&digalgo);
1191         /* Hmm, what to do with unknown digest algorithms? */
1192         if (algo && rpmDigestLength(*algo) != 0) {
1193             fi->digestalgo = *algo;
1194         }
1195     }
1196
1197     fi->digests = NULL;
1198     /* grab hex digests from header and store in binary format */
1199     if (!(flags & RPMFI_NOFILEDIGESTS) &&
1200         headerGet(h, RPMTAG_FILEDIGESTS, &fdigests, HEADERGET_MINMEM)) {
1201         const char *fdigest;
1202         size_t diglen = rpmDigestLength(fi->digestalgo);
1203         fi->digests = t = xmalloc(rpmtdCount(&fdigests) * diglen);
1204
1205         while ((fdigest = rpmtdNextString(&fdigests))) {
1206             if (!(fdigest && *fdigest != '\0')) {
1207                 memset(t, 0, diglen);
1208                 t += diglen;
1209                 continue;
1210             }
1211             for (int j = 0; j < diglen; j++, t++, fdigest += 2)
1212                 *t = (rnibble(fdigest[0]) << 4) | rnibble(fdigest[1]);
1213         }
1214         rpmtdFreeData(&fdigests);
1215     }
1216
1217     /* XXX TR_REMOVED doesn;t need fmtimes, frdevs, finodes */
1218     if (!(flags & RPMFI_NOFILEMTIMES))
1219         _hgfi(h, RPMTAG_FILEMTIMES, &td, scareFlags, fi->fmtimes);
1220     if (!(flags & RPMFI_NOFILERDEVS))
1221         _hgfi(h, RPMTAG_FILERDEVS, &td, scareFlags, fi->frdevs);
1222     if (!(flags & RPMFI_NOFILEINODES))
1223         _hgfi(h, RPMTAG_FILEINODES, &td, scareFlags, fi->finodes);
1224
1225     if (!(flags & RPMFI_NOFILEUSER)) 
1226         fi->fuser = tag2pool(fi->pool, h, RPMTAG_FILEUSERNAME);
1227     if (!(flags & RPMFI_NOFILEGROUP)) 
1228         fi->fgroup = tag2pool(fi->pool, h, RPMTAG_FILEGROUPNAME);
1229
1230     /* TODO: validate and return a real error */
1231     return 0;
1232 }
1233
1234 rpmfi rpmfiNewPool(rpmstrPool pool, Header h, rpmTagVal tagN, rpmfiFlags flags)
1235 {
1236     rpmfi fi = xcalloc(1, sizeof(*fi)); 
1237     struct rpmtd_s bn, dn, dx;
1238
1239     fi->magic = RPMFIMAGIC;
1240     fi->i = -1;
1241     fi->fiflags = flags;
1242
1243     /*
1244      * Grab and validate file triplet data. Headers with no files simply
1245      * fall through here and an empty file set is returned.
1246      */
1247     if (headerGet(h, RPMTAG_BASENAMES, &bn, HEADERGET_MINMEM)) {
1248         headerGet(h, RPMTAG_DIRNAMES, &dn, HEADERGET_MINMEM);
1249         headerGet(h, RPMTAG_DIRINDEXES, &dx, HEADERGET_ALLOC);
1250
1251         if (indexSane(&bn, &dn, &dx)) {
1252             /* private or shared pool? */
1253             fi->pool = (pool != NULL) ? rpmstrPoolLink(pool) :
1254                                         rpmstrPoolCreate();
1255
1256             /* init the file triplet data */
1257             fi->fc = rpmtdCount(&bn);
1258             fi->dc = rpmtdCount(&dn);
1259             fi->bnid = rpmtdToPool(&bn, fi->pool);
1260             fi->dnid = rpmtdToPool(&dn, fi->pool);
1261             /* steal index data from the td (pooh...) */
1262             fi->dil = dx.data;
1263             dx.data = NULL;
1264
1265             /* populate the rest of the stuff */
1266             rpmfiPopulate(fi, h, flags);
1267
1268             /* freeze the pool to save memory, but only if private pool */
1269             if (fi->pool != pool)
1270                 rpmstrPoolFreeze(fi->pool, 0);
1271
1272             fi->h = (fi->fiflags & RPMFI_KEEPHEADER) ? headerLink(h) : NULL;
1273         } else {
1274             /* broken data, free and return NULL */
1275             fi = _free(fi);
1276         }
1277         rpmtdFreeData(&bn);
1278         rpmtdFreeData(&dn);
1279         rpmtdFreeData(&dx);
1280     }
1281
1282     return rpmfiLink(fi);
1283 }
1284
1285 rpmfi rpmfiNew(const rpmts ts, Header h, rpmTagVal tagN, rpmfiFlags flags)
1286 {
1287     return rpmfiNewPool(NULL, h, tagN, flags);
1288 }
1289
1290 void rpmfiSetFReplacedSizeIndex(rpmfi fi, int ix, rpm_loff_t newsize)
1291 {
1292     if (fi != NULL && ix >= 0 && ix < fi->fc) {
1293         if (fi->replacedSizes == NULL) {
1294             fi->replacedSizes = xcalloc(fi->fc, sizeof(*fi->replacedSizes));
1295         }
1296         /* XXX watch out, replacedSizes is not rpm_loff_t (yet) */
1297         fi->replacedSizes[ix] = (rpm_off_t) newsize;
1298     }
1299 }
1300
1301 rpm_loff_t rpmfiFReplacedSizeIndex(rpmfi fi, int ix)
1302 {
1303     rpm_loff_t rsize = 0;
1304     if (fi != NULL && ix >= 0 && ix < fi->fc) {
1305         if (fi->replacedSizes) {
1306             rsize = fi->replacedSizes[ix];
1307         }
1308     }
1309     return rsize;
1310 }
1311
1312 void rpmfiFpLookup(rpmfi fi, fingerPrintCache fpc)
1313 {
1314     /* This can get called twice (eg yum), scratch former results and redo */
1315     if (fi->fc > 0) {
1316         if (fi->fps)
1317             free(fi->fps);
1318         fi->fps = fpLookupList(fpc, fi->pool,
1319                                fi->dnid, fi->bnid, fi->dil, fi->fc);
1320     }
1321 }
1322
1323 /* 
1324  * Generate iterator accessors function wrappers, these do nothing but
1325  * call the corresponding rpmfiFooIndex(fi, fi->[ij])
1326  */
1327
1328 #define RPMFI_ITERFUNC(TYPE, NAME, IXV) \
1329     TYPE rpmfi ## NAME(rpmfi fi) { return rpmfi ## NAME ## Index(fi, fi ? fi->IXV : -1); }
1330
1331 RPMFI_ITERFUNC(rpmsid, BNId, i)
1332 RPMFI_ITERFUNC(rpmsid, DNId, j)
1333 RPMFI_ITERFUNC(const char *, BN, i)
1334 RPMFI_ITERFUNC(const char *, DN, j)
1335 RPMFI_ITERFUNC(const char *, FLink, i)
1336 RPMFI_ITERFUNC(const char *, FUser, i)
1337 RPMFI_ITERFUNC(const char *, FGroup, i)
1338 RPMFI_ITERFUNC(const char *, FCaps, i)
1339 RPMFI_ITERFUNC(const char *, FLangs, i)
1340 RPMFI_ITERFUNC(const char *, FClass, i)
1341 RPMFI_ITERFUNC(rpmfileState, FState, i)
1342 RPMFI_ITERFUNC(rpmfileAttrs, FFlags, i)
1343 RPMFI_ITERFUNC(rpmVerifyAttrs, VFlags, i)
1344 RPMFI_ITERFUNC(rpm_mode_t, FMode, i)
1345 RPMFI_ITERFUNC(rpm_rdev_t, FRdev, i)
1346 RPMFI_ITERFUNC(rpm_time_t, FMtime, i)
1347 RPMFI_ITERFUNC(rpm_ino_t, FInode, i)
1348 RPMFI_ITERFUNC(rpm_loff_t, FSize, i)
1349 RPMFI_ITERFUNC(rpm_color_t, FColor, i)
1350 RPMFI_ITERFUNC(uint32_t, FNlink, i)
1351
1352 const char * rpmfiFN(rpmfi fi)
1353 {
1354     const char *fn = ""; /* preserve behavior on errors */
1355     if (fi != NULL) {
1356         free(fi->fn);
1357         fi->fn = rpmfiFNIndex(fi, fi->i);
1358         if (fi->fn != NULL)
1359             fn = fi->fn;
1360     }
1361     return fn;
1362 }
1363
1364 const unsigned char * rpmfiFDigest(rpmfi fi, int *algo, size_t *len)
1365 {
1366     return rpmfiFDigestIndex(fi, fi ? fi->i : -1, algo, len);
1367 }
1368
1369 uint32_t rpmfiFDepends(rpmfi fi, const uint32_t ** fddictp)
1370 {
1371     return rpmfiFDependsIndex(fi,  fi ? fi->i : -1, fddictp);
1372 }
1373
1374 int rpmfiCompare(const rpmfi afi, const rpmfi bfi)
1375 {
1376     return rpmfiCompareIndex(afi, afi ? afi->i : -1, bfi, bfi ? bfi->i : -1);
1377 }
1378
1379 rpmFileAction rpmfiDecideFate(const rpmfi ofi, rpmfi nfi, int skipMissing)
1380 {
1381     return rpmfiDecideFateIndex(ofi, ofi ? ofi->i : -1,
1382                                 nfi, nfi ? nfi->i : -1,
1383                                 skipMissing);
1384 }
1385
1386 int rpmfiConfigConflict(const rpmfi fi)
1387 {
1388     return rpmfiConfigConflictIndex(fi, fi ? fi->i : -1);
1389 }
1390
1391 rpmstrPool rpmfiPool(rpmfi fi)
1392 {
1393     return (fi != NULL) ? fi->pool : NULL;
1394 }