apply some patch for pythons rpm from opensource
[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 #include <errno.h>
15
16 #include "lib/rpmfi_internal.h"
17 #include "lib/rpmte_internal.h" /* relocations */
18 #include "lib/cpio.h"   /* XXX CPIO_FOO */
19 #include "lib/fsm.h"    /* rpmpsm stuff for now */
20 #include "lib/rpmug.h"
21 #include "rpmio/rpmio_internal.h"       /* fdInit/FiniDigest */
22
23 #include "debug.h"
24
25 struct hardlinks_s {
26     int nlink;
27     int files[];
28 };
29
30 typedef struct hardlinks_s * hardlinks_t;
31
32 #undef HASHTYPE
33 #undef HTKEYTYPE
34 #undef HTDATATYPE
35 #define HASHTYPE nlinkHash
36 #define HTKEYTYPE int
37 #define HTDATATYPE struct hardlinks_s *
38 #include "lib/rpmhash.H"
39 #include "lib/rpmhash.C"
40 #undef HASHTYPE
41 #undef HTKEYTYPE
42 #undef HTDATATYPE
43
44 typedef int (*iterfunc)(rpmfi fi);
45
46 struct rpmfi_s {
47     int i;                      /*!< Current file index. */
48     int j;                      /*!< Current directory index. */
49     iterfunc next;              /*!< Iterator function. */
50     char * fn;                  /*!< File name buffer. */
51     char * ofn;                 /*!< Original file name buffer. */
52
53     int intervalStart;          /*!< Start of iterating interval. */
54     int intervalEnd;            /*!< End of iterating interval. */
55
56     rpmfiles files;             /*!< File info set */
57     rpmcpio_t archive;          /*!< Archive with payload */
58     unsigned char * found;      /*!< Bit field of files found in the archive */
59     int nrefs;                  /*!< Reference count */
60 };
61
62 struct rpmfn_s {
63     rpm_count_t dc;             /*!< No. of directories. */
64     rpm_count_t fc;             /*!< No. of files. */
65
66     rpmsid * bnid;              /*!< Index to base name(s) (pool) */
67     rpmsid * dnid;              /*!< Index to directory name(s) (pool) */
68     uint32_t * dil;             /*!< Directory indice(s) (from header) */
69 };
70
71 typedef struct rpmfn_s * rpmfn;
72
73 /**
74  * A package filename set.
75  */
76 struct rpmfiles_s {
77     Header h;                   /*!< Header for file info set (or NULL) */
78     rpmstrPool pool;            /*!< String pool of this file info set */
79
80     struct rpmfn_s fndata;      /*!< File name data */
81     struct rpmfn_s *ofndata;    /*!< Original file name data */
82
83     rpmsid * flinks;            /*!< Index to file link(s) (pool) */
84
85     rpm_flag_t * fflags;        /*!< File flag(s) (from header) */
86     rpm_off_t * fsizes;         /*!< File size(s) (from header) */
87     rpm_loff_t * lfsizes;       /*!< File size(s) (from header) */
88     rpm_time_t * fmtimes;       /*!< File modification time(s) (from header) */
89     rpm_mode_t * fmodes;        /*!< File mode(s) (from header) */
90     rpm_rdev_t * frdevs;        /*!< File rdev(s) (from header) */
91     rpm_ino_t * finodes;        /*!< File inodes(s) (from header) */
92
93     rpmsid * fuser;             /*!< Index to file owner(s) (misc pool) */
94     rpmsid * fgroup;            /*!< Index to file group(s) (misc pool) */
95     rpmsid * flangs;            /*!< Index to file lang(s) (misc pool) */
96
97     char * fstates;             /*!< File state(s) (from header) */
98
99     rpm_color_t * fcolors;      /*!< File color bits (header) */
100     char ** fcaps;              /*!< File capability strings (header) */
101
102     char ** cdict;              /*!< File class dictionary (header) */
103     rpm_count_t ncdict;         /*!< No. of class entries. */
104     uint32_t * fcdictx;         /*!< File class dictionary index (header) */
105
106     uint32_t * ddict;           /*!< File depends dictionary (header) */
107     rpm_count_t nddict;         /*!< No. of depends entries. */
108     uint32_t * fddictx;         /*!< File depends dictionary start (header) */
109     uint32_t * fddictn;         /*!< File depends dictionary count (header) */
110     rpm_flag_t * vflags;        /*!< File verify flag(s) (from header) */
111
112     rpmfiFlags fiflags;         /*!< file info set control flags */
113
114     struct fingerPrint_s * fps; /*!< File fingerprint(s). */
115
116     int digestalgo;             /*!< File digest algorithm */
117     int signaturelength;        /*!< File signature length */
118     unsigned char * digests;    /*!< File digests in binary. */
119     unsigned char * signatures; /*!< File signatures in binary. */
120
121     struct nlinkHash_s * nlinks;/*!< Files connected by hardlinks */
122     rpm_off_t * replacedSizes;  /*!< (TR_ADDED) */
123     rpm_loff_t * replacedLSizes;/*!< (TR_ADDED) */
124     int magic;
125     int nrefs;          /*!< Reference count. */
126 };
127
128 static int indexSane(rpmtd xd, rpmtd yd, rpmtd zd);
129 static int cmpPoolFn(rpmstrPool pool, rpmfn files, int ix, const char * fn);
130
131 static rpmfiles rpmfilesUnlink(rpmfiles fi)
132 {
133     if (fi)
134         fi->nrefs--;
135     return NULL;
136 }
137
138 rpmfiles rpmfilesLink(rpmfiles fi)
139 {
140     if (fi)
141         fi->nrefs++;
142     return fi;
143 }
144
145 static rpmfi rpmfiUnlink(rpmfi fi)
146 {
147     if (fi)
148         fi->nrefs--;
149     return NULL;
150 }
151
152 rpmfi rpmfiLink(rpmfi fi)
153 {
154     if (fi)
155         fi->nrefs++;
156     return fi;
157 }
158
159 /*
160  * Collect and validate file path data from header.
161  * Return the number of files found (could be none) or -1 on error.
162  */
163 static int rpmfnInit(rpmfn fndata, rpmTagVal bntag, Header h, rpmstrPool pool)
164 {
165     struct rpmtd_s bn, dn, dx;
166     rpmTagVal dntag, ditag;
167     int rc = 0;
168
169     if (bntag == RPMTAG_BASENAMES) {
170         dntag = RPMTAG_DIRNAMES;
171         ditag = RPMTAG_DIRINDEXES;
172     } else if (bntag == RPMTAG_ORIGBASENAMES) {
173         dntag = RPMTAG_ORIGDIRNAMES;
174         ditag = RPMTAG_ORIGDIRINDEXES;
175     } else {
176         return -1;
177     }
178
179     /* Grab and validate file triplet data (if there is any) */
180     if (headerGet(h, bntag, &bn, HEADERGET_MINMEM)) {
181         headerGet(h, dntag, &dn, HEADERGET_MINMEM);
182         headerGet(h, ditag, &dx, HEADERGET_ALLOC);
183
184         if (indexSane(&bn, &dn, &dx)) {
185             /* Init the file triplet data */
186             fndata->fc = rpmtdCount(&bn);
187             fndata->dc = rpmtdCount(&dn);
188             fndata->bnid = rpmtdToPool(&bn, pool);
189             fndata->dnid = rpmtdToPool(&dn, pool);
190             /* Steal index data from the td (pooh...) */
191             fndata->dil = dx.data;
192             dx.data = NULL;
193             rc = fndata->fc;
194         } else {
195             memset(fndata, 0, sizeof(*fndata));
196             rc = -1;
197         }
198         rpmtdFreeData(&bn);
199         rpmtdFreeData(&dn);
200         rpmtdFreeData(&dx);
201     }
202
203     return rc;
204 }
205
206 static void rpmfnClear(rpmfn fndata)
207 {
208     if (fndata) {
209         free(fndata->bnid);
210         free(fndata->dnid);
211         free(fndata->dil);
212         memset(fndata, 0, sizeof(*fndata));
213     }
214 }
215
216 static rpm_count_t rpmfnFC(rpmfn fndata)
217 {
218     return (fndata != NULL) ? fndata->fc :0;
219 }
220
221 static rpm_count_t rpmfnDC(rpmfn fndata)
222 {
223     return (fndata != NULL) ? fndata->dc : 0;
224 }
225
226 static int rpmfnDI(rpmfn fndata, int ix)
227 {
228     int j = -1;
229     if (ix >= 0 && ix < rpmfnFC(fndata)) {
230         if (fndata->dil != NULL)
231             j = fndata->dil[ix];
232     }
233     return j;
234 }
235
236 static rpmsid rpmfnBNId(rpmfn fndata, int ix)
237 {
238     rpmsid id = 0;
239     if (ix >= 0 && ix < rpmfnFC(fndata)) {
240         if (fndata->bnid != NULL)
241             id = fndata->bnid[ix];
242     }
243     return id;
244 }
245
246 static rpmsid rpmfnDNId(rpmfn fndata, int ix)
247 {
248     rpmsid id = 0;
249     if (ix >= 0 && ix < rpmfnDC(fndata)) {
250         if (fndata->dnid != NULL)
251             id = fndata->dnid[ix];
252     }
253     return id;
254 }
255
256 static const char * rpmfnBN(rpmstrPool pool, rpmfn fndata, int ix)
257 {
258     return rpmstrPoolStr(pool, rpmfnBNId(fndata, ix));
259 }
260
261 static const char * rpmfnDN(rpmstrPool pool, rpmfn fndata, int ix)
262 {
263     return rpmstrPoolStr(pool, rpmfnDNId(fndata, ix));
264 }
265
266 static char * rpmfnFN(rpmstrPool pool, rpmfn fndata, int ix)
267 {
268     char *fn = NULL;
269     if (ix >= 0 && ix < rpmfnFC(fndata)) {
270         fn = rstrscat(NULL, rpmfnDN(pool, fndata, rpmfnDI(fndata, ix)),
271                             rpmfnBN(pool, fndata, ix), NULL);
272     }
273     return fn;
274 }
275
276 rpm_count_t rpmfilesFC(rpmfiles fi)
277 {
278     return (fi != NULL ? rpmfnFC(&fi->fndata) : 0);
279 }
280
281 rpm_count_t rpmfilesDC(rpmfiles fi)
282 {
283     return (fi != NULL ? rpmfnDC(&fi->fndata) : 0);
284 }
285
286 int rpmfilesDigestAlgo(rpmfiles fi)
287 {
288     return (fi != NULL) ? fi->digestalgo : 0;
289 }
290
291 rpm_count_t rpmfiFC(rpmfi fi)
292 {
293     return (fi != NULL ? rpmfilesFC(fi->files) : 0);
294 }
295
296 rpm_count_t rpmfiDC(rpmfi fi)
297 {
298     return (fi != NULL ? rpmfilesDC(fi->files) : 0);
299 }
300
301 #ifdef  NOTYET
302 int rpmfiDI(rpmfi fi)
303 {
304 }
305 #endif
306
307 int rpmfiFX(rpmfi fi)
308 {
309     return (fi != NULL ? fi->i : -1);
310 }
311
312 int rpmfiSetFX(rpmfi fi, int fx)
313 {
314     int i = -1;
315
316     if (fi != NULL && fx >= 0 && fx < rpmfilesFC(fi->files)) {
317         i = fi->i;
318         fi->i = fx;
319         fi->j = rpmfilesDI(fi->files, fi->i);
320     }
321     return i;
322 }
323
324 int rpmfiDX(rpmfi fi)
325 {
326     return (fi != NULL ? fi->j : -1);
327 }
328
329 int rpmfiSetDX(rpmfi fi, int dx)
330 {
331     int j = -1;
332
333     if (fi != NULL && dx >= 0 && dx < rpmfiDC(fi)) {
334         j = fi->j;
335         fi->j = dx;
336     }
337     return j;
338 }
339
340 int rpmfilesDI(rpmfiles fi, int ix)
341 {
342     return (fi != NULL) ? rpmfnDI(&fi->fndata, ix) : -1;
343 }
344
345 int rpmfilesODI(rpmfiles fi, int ix)
346 {
347     return (fi != NULL) ? rpmfnDI(fi->ofndata, ix) : -1;
348 }
349
350 rpmsid rpmfilesBNId(rpmfiles fi, int ix)
351 {
352     return (fi != NULL) ? rpmfnBNId(&fi->fndata, ix) : 0;
353 }
354
355 rpmsid rpmfilesOBNId(rpmfiles fi, int ix)
356 {
357     return (fi != NULL) ? rpmfnBNId(fi->ofndata, ix) : 0;
358 }
359
360 rpmsid rpmfilesDNId(rpmfiles fi, int jx)
361 {
362     return (fi != NULL) ? rpmfnDNId(&fi->fndata, jx) : 0;
363 }
364
365 rpmsid rpmfilesODNId(rpmfiles fi, int jx)
366 {
367     return (fi != NULL) ? rpmfnDNId(fi->ofndata, jx) : 0;
368 }
369
370 const char * rpmfilesBN(rpmfiles fi, int ix)
371 {
372     return (fi != NULL) ? rpmfnBN(fi->pool, &fi->fndata, ix) : NULL;
373 }
374
375 const char * rpmfilesOBN(rpmfiles fi, int ix)
376 {
377     return (fi != NULL) ? rpmstrPoolStr(fi->pool, rpmfilesOBNId(fi, ix)) : NULL;
378 }
379
380 const char * rpmfilesDN(rpmfiles fi, int jx)
381 {
382     return (fi != NULL) ? rpmfnDN(fi->pool, &fi->fndata, jx) : NULL;
383 }
384
385 const char * rpmfilesODN(rpmfiles fi, int jx)
386 {
387     return (fi != NULL) ? rpmstrPoolStr(fi->pool, rpmfilesODNId(fi, jx)) : NULL;
388 }
389
390 char * rpmfilesFN(rpmfiles fi, int ix)
391 {
392     return (fi != NULL) ? rpmfnFN(fi->pool, &fi->fndata, ix) : NULL;
393 }
394
395 char * rpmfilesOFN(rpmfiles fi, int ix)
396 {
397     return (fi != NULL) ? rpmfnFN(fi->pool, fi->ofndata, ix) : NULL;
398 }
399
400 /* Fn is expected to be relative path, convert directory to relative too */
401 static int cmpPoolFn(rpmstrPool pool, rpmfn files, int ix, const char * fn)
402 {
403     rpmsid dnid = rpmfnDNId(files, rpmfnDI(files, ix));
404     const char *dn = rpmstrPoolStr(pool, dnid);
405     const char *reldn = (dn[0] == '/') ? dn + 1 : dn;
406     size_t l = strlen(reldn);
407     int cmp = strncmp(reldn, fn, l);
408     if (cmp == 0)
409         cmp = strcmp(rpmfnBN(pool, files, ix), fn + l);
410     return cmp;
411 }
412
413 static int rpmfnFindFN(rpmstrPool pool, rpmfn files, const char * fn)
414 {
415     int fc = rpmfnFC(files);
416
417     /*
418      * Skip payload prefix, turn absolute paths into relative. This
419      * allows handling binary rpm payloads with and without ./ prefix and
420      * srpm payloads which only contain basenames.
421      */
422     if (fn[0] == '.' && fn[1] == '/')
423         fn += 2;
424     if (fn[0] == '/')
425         fn += 1;
426
427     /* try binary search */
428
429     int lo = 0;
430     int hi = fc;
431     int mid, cmp;
432
433     while (hi > lo) {
434         mid = (hi + lo) / 2 ;
435         cmp = cmpPoolFn(pool, files, mid, fn);
436         if (cmp < 0) {
437             lo = mid+1;
438         } else if (cmp > 0) {
439             hi = mid;
440         } else {
441             return mid;
442         }
443     }
444
445     /* not found: try linear search */
446     for (int i=0; i < fc; i++) {
447         if (cmpPoolFn(pool, files, i, fn) == 0)
448             return i;
449     }
450     return -1;
451 }
452
453 int rpmfilesFindFN(rpmfiles files, const char * fn)
454 {
455     return (files && fn) ? rpmfnFindFN(files->pool, &files->fndata, fn) : -1;
456 }
457
458 int rpmfilesFindOFN(rpmfiles files, const char * fn)
459 {
460     return (files && fn) ? rpmfnFindFN(files->pool, files->ofndata, fn) : -1;
461 }
462
463 int rpmfiFindFN(rpmfi fi, const char * fn)
464 {
465     int ix = -1;
466
467     if (fi != NULL) {
468         ix = rpmfilesFindFN(fi->files, fn);
469     }
470     return ix;
471 }
472
473 int rpmfiFindOFN(rpmfi fi, const char * fn)
474 {
475     int ix = -1;
476
477     if (fi != NULL) {
478         ix = rpmfilesFindOFN(fi->files, fn);
479     }
480     return ix;
481 }
482
483 /*
484  * Dirnames are not sorted when separated from basenames, we need to assemble
485  * the whole path for search (binary or otherwise) purposes.
486  */
487 static int cmpPfx(rpmfiles files, int ix, const char *pfx, size_t plen)
488 {
489     char *fn = rpmfilesFN(files, ix);
490     int rc = strncmp(pfx, fn, plen);
491     free(fn);
492     return rc;
493 }
494
495 rpmfileAttrs rpmfilesFFlags(rpmfiles fi, int ix)
496 {
497     rpmfileAttrs FFlags = 0;
498
499     if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
500         if (fi->fflags != NULL)
501             FFlags = fi->fflags[ix];
502     }
503     return FFlags;
504 }
505
506 rpmVerifyAttrs rpmfilesVFlags(rpmfiles fi, int ix)
507 {
508     rpmVerifyAttrs VFlags = 0;
509
510     if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
511         if (fi->vflags != NULL)
512             VFlags = fi->vflags[ix];
513     }
514     return VFlags;
515 }
516
517 rpm_mode_t rpmfilesFMode(rpmfiles fi, int ix)
518 {
519     rpm_mode_t fmode = 0;
520
521     if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
522         if (fi->fmodes != NULL)
523             fmode = fi->fmodes[ix];
524     }
525     return fmode;
526 }
527
528 rpmfileState rpmfilesFState(rpmfiles fi, int ix)
529 {
530     rpmfileState fstate = RPMFILE_STATE_MISSING;
531
532     if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
533         if (fi->fstates != NULL)
534             fstate = fi->fstates[ix];
535     }
536     return fstate;
537 }
538
539 int rpmfiDigestAlgo(rpmfi fi)
540 {
541     return fi ? rpmfilesDigestAlgo(fi->files) : 0;
542 }
543
544 const unsigned char * rpmfilesFDigest(rpmfiles fi, int ix, int *algo, size_t *len)
545 {
546     const unsigned char *digest = NULL;
547
548     if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
549         size_t diglen = rpmDigestLength(fi->digestalgo);
550         if (fi->digests != NULL)
551             digest = fi->digests + (diglen * ix);
552         if (len) 
553             *len = diglen;
554         if (algo) 
555             *algo = fi->digestalgo;
556     }
557     return digest;
558 }
559
560 char * rpmfiFDigestHex(rpmfi fi, int *algo)
561 {
562     size_t diglen = 0;
563     char *fdigest = NULL;
564     const unsigned char *digest = rpmfiFDigest(fi, algo, &diglen);
565     if (digest) {
566         fdigest = pgpHexStr(digest, diglen);
567     }
568     return fdigest;
569 }
570
571 const unsigned char * rpmfilesFSignature(rpmfiles fi, int ix, size_t *len)
572 {
573     const unsigned char *signature = NULL;
574
575     if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
576         if (fi->signatures != NULL)
577             signature = fi->signatures + (fi->signaturelength * ix);
578         if (len)
579             *len = fi->signaturelength;
580     }
581     return signature;
582 }
583
584 const char * rpmfilesFLink(rpmfiles fi, int ix)
585 {
586     const char * flink = NULL;
587
588     if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
589         if (fi->flinks != NULL)
590             flink = rpmstrPoolStr(fi->pool, fi->flinks[ix]);
591     }
592     return flink;
593 }
594
595 rpm_loff_t rpmfilesFSize(rpmfiles fi, int ix)
596 {
597     rpm_loff_t fsize = 0;
598
599     if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
600         if (fi->fsizes != NULL)
601             fsize = fi->fsizes[ix];
602         else if (fi->lfsizes != NULL)
603             fsize = fi->lfsizes[ix];
604     }
605     return fsize;
606 }
607
608 rpm_rdev_t rpmfilesFRdev(rpmfiles fi, int ix)
609 {
610     rpm_rdev_t frdev = 0;
611
612     if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
613         if (fi->frdevs != NULL)
614             frdev = fi->frdevs[ix];
615     }
616     return frdev;
617 }
618
619 rpm_ino_t rpmfilesFInode(rpmfiles fi, int ix)
620 {
621     rpm_ino_t finode = 0;
622
623     if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
624         if (fi->finodes != NULL)
625             finode = fi->finodes[ix];
626     }
627     return finode;
628 }
629
630 rpm_color_t rpmfilesColor(rpmfiles files)
631 {
632     rpm_color_t color = 0;
633
634     if (files != NULL && files->fcolors != NULL) {
635         int fc = rpmfilesFC(files);
636         for (int i = 0; i < fc; i++)
637             color |= files->fcolors[i];
638         /* XXX ignore all but lsnibble for now. */
639         color &= 0xf;
640     }
641     return color;
642 }
643
644 rpm_color_t rpmfiColor(rpmfi fi)
645 {
646     return (fi != NULL) ? rpmfilesColor(fi->files) : 0;
647 }
648
649 rpm_color_t rpmfilesFColor(rpmfiles fi, int ix)
650 {
651     rpm_color_t fcolor = 0;
652
653     if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
654         if (fi->fcolors != NULL)
655             /* XXX ignore all but lsnibble for now. */
656             fcolor = (fi->fcolors[ix] & 0x0f);
657     }
658     return fcolor;
659 }
660
661 const char * rpmfilesFClass(rpmfiles fi, int ix)
662 {
663     const char * fclass = NULL;
664     int cdictx;
665
666     if (fi != NULL && fi->fcdictx != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
667         cdictx = fi->fcdictx[ix];
668         if (fi->cdict != NULL && cdictx >= 0 && cdictx < fi->ncdict)
669             fclass = fi->cdict[cdictx];
670     }
671     return fclass;
672 }
673
674 uint32_t rpmfilesFDepends(rpmfiles fi, int ix, const uint32_t ** fddictp)
675 {
676     int fddictx = -1;
677     int fddictn = 0;
678     const uint32_t * fddict = NULL;
679
680     if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
681         if (fi->fddictn != NULL)
682             fddictn = fi->fddictn[ix];
683         if (fddictn > 0 && fi->fddictx != NULL)
684             fddictx = fi->fddictx[ix];
685         if (fi->ddict != NULL && fddictx >= 0 && (fddictx+fddictn) <= fi->nddict)
686             fddict = fi->ddict + fddictx;
687     }
688     if (fddictp)
689         *fddictp = fddict;
690     return fddictn;
691 }
692
693 uint32_t rpmfilesFLinks(rpmfiles fi, int ix, const int ** files)
694 {
695     uint32_t nlink = 0;
696
697     if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
698         nlink = 1;
699         if (fi->nlinks) {
700             struct hardlinks_s ** hardlinks = NULL;
701             nlinkHashGetEntry(fi->nlinks, ix, &hardlinks, NULL, NULL);
702             if (hardlinks) {
703                 nlink = hardlinks[0]->nlink;
704                 if (files) {
705                     *files = hardlinks[0]->files;
706                 }
707             } else if (files){
708                 *files = NULL;
709             }
710         }
711     }
712     return nlink;
713 }
714
715 uint32_t rpmfiFLinks(rpmfi fi, const int ** files)
716 {
717     return rpmfilesFLinks(fi->files, fi ? fi->i : -1, files);
718 }
719
720 uint32_t rpmfilesFNlink(rpmfiles fi, int ix)
721 {
722     return rpmfilesFLinks(fi, ix, NULL);
723 }
724
725 rpm_time_t rpmfilesFMtime(rpmfiles fi, int ix)
726 {
727     rpm_time_t fmtime = 0;
728
729     if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
730         if (fi->fmtimes != NULL)
731             fmtime = fi->fmtimes[ix];
732     }
733     return fmtime;
734 }
735
736 const char * rpmfilesFUser(rpmfiles fi, int ix)
737 {
738     const char * fuser = NULL;
739
740     if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
741         if (fi->fuser != NULL)
742             fuser = rpmstrPoolStr(fi->pool, fi->fuser[ix]);
743     }
744     return fuser;
745 }
746
747 const char * rpmfilesFGroup(rpmfiles fi, int ix)
748 {
749     const char * fgroup = NULL;
750
751     if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
752         if (fi->fgroup != NULL)
753             fgroup = rpmstrPoolStr(fi->pool, fi->fgroup[ix]);
754     }
755     return fgroup;
756 }
757
758 const char * rpmfilesFCaps(rpmfiles fi, int ix)
759 {
760     const char *fcaps = NULL;
761     if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
762         fcaps = fi->fcaps ? fi->fcaps[ix] : "";
763     }
764     return fcaps;
765 }
766
767 const char * rpmfilesFLangs(rpmfiles fi, int ix)
768 {
769     const char *flangs = NULL;
770     if (fi != NULL && fi->flangs != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
771         flangs = rpmstrPoolStr(fi->pool, fi->flangs[ix]);
772     }
773     return flangs;
774 }
775
776 int rpmfilesStat(rpmfiles fi, int ix, int flags, struct stat *sb)
777 {
778     int rc = -1;
779     if (fi && sb) {
780         /* XXX FIXME define proper flags with sane semantics... */
781         int warn = flags & 0x1;
782         const char *user = rpmfilesFUser(fi, ix);
783         const char *group = rpmfilesFGroup(fi, ix);
784         const int * hardlinks = NULL;
785         uint32_t nlinks = rpmfilesFLinks(fi, ix, &hardlinks);
786
787         memset(sb, 0, sizeof(*sb));
788         sb->st_nlink = nlinks;
789         sb->st_ino = rpmfilesFInode(fi, ix);
790         sb->st_rdev = rpmfilesFRdev(fi, ix);
791         sb->st_mode = rpmfilesFMode(fi, ix);
792         sb->st_mtime = rpmfilesFMtime(fi, ix);
793
794         /* Only regular files and symlinks have a size */
795         if (S_ISREG(sb->st_mode)) {
796             /* Content and thus size comes with last hardlink */
797             if (!(nlinks > 1 && hardlinks[nlinks-1] != ix))
798                 sb->st_size = rpmfilesFSize(fi, ix);
799         } else if (S_ISLNK(sb->st_mode)) {
800             /*
801              * Normally rpmfilesFSize() is correct for symlinks too, this is
802              * only needed for glob()'ed links from fakechroot environment.
803              */
804             sb->st_size = strlen(rpmfilesFLink(fi, ix));
805         }
806
807         if (user && rpmugUid(user, &sb->st_uid)) {
808             if (warn)
809                 rpmlog(RPMLOG_WARNING,
810                         _("user %s does not exist - using %s\n"), user, UID_0_USER);
811             sb->st_mode &= ~S_ISUID;      /* turn off suid bit */
812         }
813
814         if (group && rpmugGid(group, &sb->st_gid)) {
815             if (warn)
816                 rpmlog(RPMLOG_WARNING,
817                         _("group %s does not exist - using %s\n"), group, GID_0_GROUP);
818             sb->st_mode &= ~S_ISGID;    /* turn off sgid bit */
819         }
820
821         rc = 0;
822     }
823     return rc;
824 }
825
826 struct fingerPrint_s *rpmfilesFps(rpmfiles fi)
827 {
828     return (fi != NULL) ? fi->fps : NULL;
829 }
830
831 static int iterFwd(rpmfi fi)
832 {
833     return fi->i + 1;
834 }
835
836 static int iterBack(rpmfi fi)
837 {
838     return fi->i - 1;
839 }
840
841 static int iterInterval(rpmfi fi)
842 {
843     if (fi->i == -1)
844         return fi->intervalStart;
845     else if (fi->i + 1 < fi->intervalEnd)
846         return fi->i + 1;
847     else
848         return RPMERR_ITER_END;
849 }
850
851 int rpmfiNext(rpmfi fi)
852 {
853     int next = -1;
854     if (fi != NULL) {
855         do {
856             next = fi->next(fi);
857         } while (next == RPMERR_ITER_SKIP);
858
859         if (next >= 0 && next < rpmfilesFC(fi->files)) {
860             fi->i = next;
861             fi->j = rpmfilesDI(fi->files, fi->i);
862         } else {
863             fi->i = -1;
864             if (next >= 0) {
865                 next = -1;
866             }
867         }
868     }
869     return next;
870 }
871
872 rpmfi rpmfiInit(rpmfi fi, int fx)
873 {
874     if (fi != NULL) {
875         if (fx >= 0 && fx < rpmfilesFC(fi->files)) {
876             fi->i = fx - 1;
877             fi->j = -1;
878         }
879     }
880
881     return fi;
882 }
883
884 int rpmfiNextD(rpmfi fi)
885 {
886     int j = -1;
887
888     if (fi != NULL && ++fi->j >= 0) {
889         if (fi->j < rpmfilesDC(fi->files))
890             j = fi->j;
891         else
892             fi->j = -1;
893     }
894
895     return j;
896 }
897
898 rpmfi rpmfiInitD(rpmfi fi, int dx)
899 {
900     if (fi != NULL) {
901         if (dx >= 0 && dx < rpmfilesFC(fi->files))
902             fi->j = dx - 1;
903         else
904             fi = NULL;
905     }
906
907     return fi;
908 }
909
910 rpmFileTypes rpmfiWhatis(rpm_mode_t mode)
911 {
912     if (S_ISDIR(mode))  return XDIR;
913     if (S_ISCHR(mode))  return CDEV;
914     if (S_ISBLK(mode))  return BDEV;
915     if (S_ISLNK(mode))  return LINK;
916     if (S_ISSOCK(mode)) return SOCK;
917     if (S_ISFIFO(mode)) return PIPE;
918     return REG;
919 }
920
921 int rpmfilesCompare(rpmfiles afi, int aix, rpmfiles bfi, int bix)
922 {
923     mode_t amode = rpmfilesFMode(afi, aix);
924     mode_t bmode = rpmfilesFMode(bfi, bix);
925     rpmFileTypes awhat = rpmfiWhatis(amode);
926
927     if ((rpmfilesFFlags(afi, aix) & RPMFILE_GHOST) ||
928         (rpmfilesFFlags(bfi, bix) & RPMFILE_GHOST)) return 0;
929
930     /* Mode difference is a conflict, except for symlinks */
931     if (!(awhat == LINK && rpmfiWhatis(bmode) == LINK) && amode != bmode)
932         return 1;
933
934     if (awhat == LINK || awhat == REG) {
935         if (rpmfilesFSize(afi, aix) != rpmfilesFSize(bfi, bix))
936             return 1;
937     }
938
939     if (!rstreq(rpmfilesFUser(afi, aix), rpmfilesFUser(bfi, bix)))
940         return 1;
941     if (!rstreq(rpmfilesFGroup(afi, aix), rpmfilesFGroup(bfi, bix)))
942         return 1;
943
944     if (awhat == LINK) {
945         const char * alink = rpmfilesFLink(afi, aix);
946         const char * blink = rpmfilesFLink(bfi, bix);
947         if (alink == blink) return 0;
948         if (alink == NULL) return 1;
949         if (blink == NULL) return -1;
950         return strcmp(alink, blink);
951     } else if (awhat == REG) {
952         size_t adiglen, bdiglen;
953         int aalgo, balgo;
954         const unsigned char * adigest, * bdigest;
955         adigest = rpmfilesFDigest(afi, aix, &aalgo, &adiglen);
956         bdigest = rpmfilesFDigest(bfi, bix, &balgo, &bdiglen);
957         if (adigest == bdigest) return 0;
958         if (adigest == NULL) return 1;
959         if (bdigest == NULL) return -1;
960         /* can't meaningfully compare different hash types */
961         if (aalgo != balgo || adiglen != bdiglen) return -1;
962         return memcmp(adigest, bdigest, adiglen);
963     } else if (awhat == CDEV || awhat == BDEV) {
964         if (rpmfilesFRdev(afi, aix) != rpmfilesFRdev(bfi, bix))
965             return 1;
966     }
967
968     return 0;
969 }
970
971 int rpmfileContentsEqual(rpmfiles ofi, int oix, rpmfiles nfi, int nix)
972 {
973     char * fn = rpmfilesFN(nfi, nix);
974     rpmFileTypes diskWhat, newWhat, oldWhat;
975     struct stat sb;
976     int equal = 0;
977
978     if (fn == NULL || (lstat(fn, &sb))) {
979         goto exit; /* The file doesn't exist on the disk */
980     }
981
982     if (rpmfilesFSize(nfi, nix) != sb.st_size) {
983         goto exit;
984     }
985
986     diskWhat = rpmfiWhatis((rpm_mode_t)sb.st_mode);
987     newWhat = rpmfiWhatis(rpmfilesFMode(nfi, nix));
988     oldWhat = rpmfiWhatis(rpmfilesFMode(ofi, oix));
989     if ((diskWhat != newWhat) || (diskWhat != oldWhat)) {
990         goto exit;
991     }
992
993     if (diskWhat == REG) {
994         int oalgo, nalgo;
995         size_t odiglen, ndiglen;
996         const unsigned char * odigest, * ndigest;
997         char buffer[1024];
998
999         odigest = rpmfilesFDigest(ofi, oix, &oalgo, &odiglen);
1000         ndigest = rpmfilesFDigest(nfi, nix, &nalgo, &ndiglen);
1001         /* See if the file in old pkg is identical to the one in new pkg */
1002         if ((oalgo != nalgo) || (odiglen != ndiglen) || (!ndigest) ||
1003             (memcmp(odigest, ndigest, ndiglen) != 0)) {
1004             goto exit;
1005         }
1006
1007         if (rpmDoDigest(nalgo, fn, 0, (unsigned char *)buffer, NULL) != 0) {
1008              goto exit;         /* assume file has been removed */
1009         }
1010
1011         /* See if the file on disk is identical to the one in new pkg */
1012         if (memcmp(ndigest, buffer, ndiglen) == 0) {
1013             equal = 1;
1014             goto exit;
1015         }
1016     }  else if (diskWhat == LINK) {
1017         const char * nFLink;
1018         char buffer[1024];
1019         ssize_t link_len;
1020
1021         nFLink = rpmfilesFLink(nfi, nix);
1022         link_len = readlink(fn, buffer, sizeof(buffer) - 1);
1023         if (link_len == -1) {
1024             goto exit;          /* assume file has been removed */
1025         }
1026         buffer[link_len] = '\0';
1027         /* See if the link on disk is identical to the one in new pkg */
1028         if (nFLink && rstreq(nFLink, buffer)) {
1029             equal = 1;
1030             goto exit;
1031         }
1032     }
1033
1034 exit:
1035     free(fn);
1036     return equal;
1037 }
1038
1039
1040 rpmFileAction rpmfilesDecideFate(rpmfiles ofi, int oix,
1041                                    rpmfiles nfi, int nix,
1042                                    int skipMissing)
1043 {
1044     char * fn = rpmfilesFN(nfi, nix);
1045     rpmfileAttrs newFlags = rpmfilesFFlags(nfi, nix);
1046     char buffer[1024];
1047     rpmFileTypes dbWhat, newWhat, diskWhat;
1048     struct stat sb;
1049     int save = (newFlags & RPMFILE_NOREPLACE) ? FA_ALTNAME : FA_SAVE;
1050     int action = FA_CREATE; /* assume we can create */
1051
1052     /* If the new file is a ghost, leave whatever might be on disk alone. */
1053     if (newFlags & RPMFILE_GHOST) {
1054         action = FA_SKIP;
1055         goto exit;
1056     }
1057
1058     if (lstat(fn, &sb)) {
1059         /*
1060          * The file doesn't exist on the disk. Create it unless the new
1061          * package has marked it as missingok, or allfiles is requested.
1062          */
1063         if (skipMissing && (newFlags & RPMFILE_MISSINGOK)) {
1064             rpmlog(RPMLOG_DEBUG, "%s skipped due to missingok flag\n",
1065                         fn);
1066             action = FA_SKIP;
1067             goto exit;
1068         } else {
1069             goto exit;
1070         }
1071     }
1072
1073     diskWhat = rpmfiWhatis((rpm_mode_t)sb.st_mode);
1074     dbWhat = rpmfiWhatis(rpmfilesFMode(ofi, oix));
1075     newWhat = rpmfiWhatis(rpmfilesFMode(nfi, nix));
1076
1077     /*
1078      * This order matters - we'd prefer to TOUCH the file if at all
1079      * possible in case something else (like the timestamp) has changed.
1080      * Only regular files and symlinks might need a backup, everything
1081      * else falls through here with FA_CREATE.
1082      */
1083     if (dbWhat == REG) {
1084         int oalgo, nalgo;
1085         size_t odiglen, ndiglen;
1086         const unsigned char * odigest, * ndigest;
1087
1088         /* See if the file on disk is identical to the one in new pkg */
1089         ndigest = rpmfilesFDigest(nfi, nix, &nalgo, &ndiglen);
1090         if (diskWhat == REG && newWhat == REG) {
1091             if (rpmDoDigest(nalgo, fn, 0, (unsigned char *)buffer, NULL))
1092                 goto exit;              /* assume file has been removed */
1093             if (ndigest && memcmp(ndigest, buffer, ndiglen) == 0) {
1094                 action = FA_TOUCH;
1095                 goto exit;              /* unmodified config file, touch it. */
1096             }
1097         }
1098
1099         /* See if the file on disk is identical to the one in old pkg */
1100         odigest = rpmfilesFDigest(ofi, oix, &oalgo, &odiglen);
1101         if (diskWhat == REG) {
1102             /* hash algo changed or digest was not computed, recalculate it */
1103             if ((oalgo != nalgo) || (newWhat != REG)) {
1104                 if (rpmDoDigest(oalgo, fn, 0, (unsigned char *)buffer, NULL))
1105                     goto exit;  /* assume file has been removed */
1106                 }
1107             if (odigest && memcmp(odigest, buffer, odiglen) == 0)
1108                 goto exit;      /* unmodified config file, replace. */
1109         }
1110
1111         /* if new file is no longer config, backup it and replace it */
1112         if (!(newFlags & RPMFILE_CONFIG)) {
1113             action = FA_SAVE;
1114             goto exit;
1115         }
1116
1117         /* If file can be determined identical in old and new pkg, let it be */
1118         if (newWhat == REG && oalgo == nalgo && odiglen == ndiglen) {
1119             if (odigest && ndigest && memcmp(odigest, ndigest, odiglen) == 0) {
1120                 action = FA_SKIP; /* identical file, dont bother */
1121                 goto exit;
1122             }
1123         }
1124         
1125         /* ...but otherwise a backup will be needed */
1126         action = save;
1127     } else if (dbWhat == LINK) {
1128         const char * oFLink, * nFLink;
1129
1130         if (diskWhat == LINK) {
1131             /* Read link from the disk */
1132             ssize_t link_len = readlink(fn, buffer, sizeof(buffer) - 1);
1133             if (link_len == -1)
1134                 goto exit;              /* assume file has been removed */
1135             buffer[link_len] = '\0';
1136         }
1137
1138         /* See if the link on disk is identical to the one in new pkg */
1139         nFLink = rpmfilesFLink(nfi, nix);
1140         if (diskWhat == LINK && newWhat == LINK) {
1141             if (nFLink && rstreq(nFLink, buffer)) {
1142                 action = FA_TOUCH;
1143                 goto exit;              /* unmodified config file, touch it. */
1144             }
1145         }
1146
1147         /* See if the link on disk is identical to the one in old pkg */
1148         oFLink = rpmfilesFLink(ofi, oix);
1149         if (diskWhat == LINK) {
1150             if (oFLink && rstreq(oFLink, buffer))
1151                 goto exit;              /* unmodified config file, replace. */
1152         }
1153
1154         /* if new file is no longer config, backup it and replace it */
1155         if (!(newFlags & RPMFILE_CONFIG)) {
1156             action = FA_SAVE;
1157             goto exit;
1158         }
1159
1160         /* If link is identical in old and new pkg, let it be */
1161         if (newWhat == LINK && oFLink && nFLink && rstreq(oFLink, nFLink)) {
1162             action = FA_SKIP;           /* identical file, don't bother. */
1163             goto exit;
1164         }
1165
1166         /* ...but otherwise a backup will be needed */
1167         action = save;
1168     }
1169
1170 exit:
1171     free(fn);
1172     return action;
1173 }
1174
1175 int rpmfilesConfigConflict(rpmfiles fi, int ix)
1176 {
1177     char * fn = NULL;
1178     rpmfileAttrs flags = rpmfilesFFlags(fi, ix);
1179     char buffer[1024];
1180     rpmFileTypes newWhat, diskWhat;
1181     struct stat sb;
1182     int rc = 0;
1183
1184     /* Non-configs are not config conflicts. */
1185     if (!(flags & RPMFILE_CONFIG))
1186         return 0;
1187
1188     /* Only links and regular files can be %config, this is kinda moot */
1189     /* XXX: Why are we returning 1 here? */
1190     newWhat = rpmfiWhatis(rpmfilesFMode(fi, ix));
1191     if (newWhat != LINK && newWhat != REG)
1192         return 1;
1193
1194     /* If it's not on disk, there's nothing to be saved */
1195     fn = rpmfilesFN(fi, ix);
1196     if (lstat(fn, &sb))
1197         goto exit;
1198
1199     /*
1200      * Preserve legacy behavior: an existing %ghost %config is considered
1201      * "modified" but unlike regular %config, its never removed and
1202      * never backed up. Whether this actually makes sense is a whole
1203      * another question, but this is very long-standing behavior that
1204      * people might be depending on. The resulting FA_ALTNAME etc action
1205      * is special-cased in FSM to avoid actually creating backups on ghosts.
1206      */
1207     if (flags & RPMFILE_GHOST) {
1208         rc = 1;
1209         goto exit;
1210     }
1211
1212     /* Files of different types obviously are not identical */
1213     diskWhat = rpmfiWhatis((rpm_mode_t)sb.st_mode);
1214     if (diskWhat != newWhat) {
1215         rc = 1;
1216         goto exit;
1217     }
1218
1219     /* Files of different sizes obviously are not identical */
1220     if (rpmfilesFSize(fi, ix) != sb.st_size) {
1221         rc = 1;
1222         goto exit;
1223     }
1224     
1225     memset(buffer, 0, sizeof(buffer));
1226     if (newWhat == REG) {
1227         int algo;
1228         size_t diglen;
1229         const unsigned char *ndigest = rpmfilesFDigest(fi,ix, &algo, &diglen);
1230         if (rpmDoDigest(algo, fn, 0, (unsigned char *)buffer, NULL))
1231             goto exit;  /* assume file has been removed */
1232         if (ndigest && memcmp(ndigest, buffer, diglen) == 0)
1233             goto exit;  /* unmodified config file */
1234     } else /* newWhat == LINK */ {
1235         const char * nFLink;
1236         ssize_t link_len = readlink(fn, buffer, sizeof(buffer) - 1);
1237         if (link_len == -1)
1238             goto exit;  /* assume file has been removed */
1239         buffer[link_len] = '\0';
1240         nFLink = rpmfilesFLink(fi, ix);
1241         if (nFLink && rstreq(nFLink, buffer))
1242             goto exit;  /* unmodified config file */
1243     }
1244
1245     rc = 1;
1246
1247 exit:
1248     free(fn);
1249     return rc;
1250 }
1251
1252 rpmfiles rpmfilesFree(rpmfiles fi)
1253 {
1254     if (fi == NULL) return NULL;
1255
1256     if (fi->nrefs > 1)
1257         return rpmfilesUnlink(fi);
1258
1259     if (rpmfilesFC(fi) > 0) {
1260         if (fi->ofndata != &fi->fndata) {
1261             rpmfnClear(fi->ofndata);
1262             free(fi->ofndata);
1263         }
1264         rpmfnClear(&fi->fndata);
1265
1266         fi->flinks = _free(fi->flinks);
1267         fi->flangs = _free(fi->flangs);
1268         fi->digests = _free(fi->digests);
1269         fi->signatures = _free(fi->signatures);
1270         fi->fcaps = _free(fi->fcaps);
1271
1272         fi->cdict = _free(fi->cdict);
1273
1274         fi->fuser = _free(fi->fuser);
1275         fi->fgroup = _free(fi->fgroup);
1276
1277         fi->fstates = _free(fi->fstates);
1278         fi->fps = _free(fi->fps);
1279
1280         /* these point to header memory if KEEPHEADER is used, dont free */
1281         if (!(fi->fiflags & RPMFI_KEEPHEADER) && fi->h == NULL) {
1282             fi->fmtimes = _free(fi->fmtimes);
1283             fi->fmodes = _free(fi->fmodes);
1284             fi->fflags = _free(fi->fflags);
1285             fi->vflags = _free(fi->vflags);
1286             fi->fsizes = _free(fi->fsizes);
1287             fi->lfsizes = _free(fi->lfsizes);
1288             fi->frdevs = _free(fi->frdevs);
1289             fi->finodes = _free(fi->finodes);
1290
1291             fi->fcolors = _free(fi->fcolors);
1292             fi->fcdictx = _free(fi->fcdictx);
1293             fi->ddict = _free(fi->ddict);
1294             fi->fddictx = _free(fi->fddictx);
1295             fi->fddictn = _free(fi->fddictn);
1296
1297         }
1298     }
1299
1300     fi->replacedSizes = _free(fi->replacedSizes);
1301     fi->replacedLSizes = _free(fi->replacedLSizes);
1302
1303     fi->h = headerFree(fi->h);
1304     fi->pool = rpmstrPoolFree(fi->pool);
1305
1306     fi->nlinks = nlinkHashFree(fi->nlinks);
1307
1308     (void) rpmfilesUnlink(fi);
1309     memset(fi, 0, sizeof(*fi));         /* XXX trash and burn */
1310     fi = _free(fi);
1311
1312     return NULL;
1313 }
1314
1315 rpmfi rpmfiFree(rpmfi fi)
1316 {
1317     if (fi == NULL) return NULL;
1318
1319     if (fi->nrefs > 1)
1320         return rpmfiUnlink(fi);
1321
1322     fi->files = rpmfilesFree(fi->files);
1323     fi->fn = _free(fi->fn);
1324     fi->ofn = _free(fi->ofn);
1325     fi->found = _free(fi->found);
1326     fi->archive = rpmcpioFree(fi->archive);
1327
1328     free(fi);
1329     return NULL;
1330 }
1331
1332 static rpmsid * tag2pool(rpmstrPool pool, Header h, rpmTag tag, rpm_count_t size)
1333 {
1334     rpmsid *sids = NULL;
1335     struct rpmtd_s td;
1336     if (headerGet(h, tag, &td, HEADERGET_MINMEM)) {
1337         if ((size >= 0) && (rpmtdCount(&td) == size)) { /* ensure right size */
1338             sids = rpmtdToPool(&td, pool);
1339         }
1340         rpmtdFreeData(&td);
1341     }
1342     return sids;
1343 }
1344
1345 /* validate a indexed tag data triplet (such as file bn/dn/dx) */
1346 static int indexSane(rpmtd xd, rpmtd yd, rpmtd zd)
1347 {
1348     int sane = 0;
1349     uint32_t xc = rpmtdCount(xd);
1350     uint32_t yc = rpmtdCount(yd);
1351     uint32_t zc = rpmtdCount(zd);
1352
1353     /* check that the amount of data in each is sane */
1354     /* normally yc <= xc but larger values are not fatal (RhBug:1001553) */
1355     if (xc > 0 && yc > 0 && zc == xc) {
1356         uint32_t * i, nvalid = 0;
1357         /* ...and that the indexes are within bounds */
1358         while ((i = rpmtdNextUint32(zd))) {
1359             if (*i >= yc)
1360                 break;
1361             nvalid++;
1362         }
1363         /* unless the loop runs to finish, the data is broken */
1364         sane = (nvalid == zc);
1365     }
1366     return sane;
1367 }
1368
1369 /* Get file data from header */
1370 /* Requires totalfc to be set and label err: to goto on error */
1371 #define _hgfi(_h, _tag, _td, _flags, _data) \
1372     if (headerGet((_h), (_tag), (_td), (_flags))) { \
1373         if (rpmtdCount(_td) != totalfc) { \
1374             rpmlog(RPMLOG_ERR, _("Wrong number of entries for tag %s: %u found but %u expected.\n"), rpmTagGetName(_tag), rpmtdCount(_td), totalfc); \
1375             goto err; \
1376         } \
1377         if (rpmTagGetTagType(_tag) != RPM_STRING_ARRAY_TYPE && rpmTagGetTagType(_tag) != RPM_I18NSTRING_TYPE && \
1378             (_td)->size < totalfc * sizeof(*(_data))) {         \
1379             rpmlog(RPMLOG_ERR, _("Malformed data for tag %s: %u bytes found but %u expected.\n"), rpmTagGetName(_tag), (_td)->size, totalfc * sizeof(*(_data))); \
1380             goto err;                           \
1381         } \
1382         _data = ((_td)->data); \
1383     }
1384 /* Get file data from header without checking number of entries */
1385 #define _hgfinc(_h, _tag, _td, _flags, _data) \
1386     if (headerGet((_h), (_tag), (_td), (_flags))) {\
1387         _data = ((_td)->data);     \
1388     }
1389
1390 /*** Hard link handling ***/
1391
1392 struct fileid_s {
1393     rpm_dev_t id_dev;
1394     rpm_ino_t id_ino;
1395 };
1396
1397 #undef HASHTYPE
1398 #undef HTKEYTYPE
1399 #undef HTDATATYPE
1400 #define HASHTYPE fileidHash
1401 #define HTKEYTYPE struct fileid_s
1402 #define HTDATATYPE int
1403 #include "lib/rpmhash.H"
1404 #include "lib/rpmhash.C"
1405 #undef HASHTYPE
1406 #undef HTKEYTYPE
1407 #undef HTDATATYPE
1408
1409 static unsigned int fidHashFunc(struct fileid_s a)
1410 {
1411     return  a.id_ino + (a.id_dev<<16) + (a.id_dev>>16);
1412 }
1413
1414 static int fidCmp(struct fileid_s a, struct fileid_s b)
1415 {
1416     return  !((a.id_dev == b.id_dev) && (a.id_ino == b.id_ino));
1417 }
1418
1419 static unsigned int intHash(int a)
1420 {
1421     return a < 0 ? UINT_MAX-a : a;
1422 }
1423
1424 static int intCmp(int a, int b)
1425 {
1426     return a != b;
1427 }
1428
1429 static struct hardlinks_s * freeNLinks(struct hardlinks_s * nlinks)
1430 {
1431     nlinks->nlink--;
1432     if (!nlinks->nlink) {
1433         nlinks = _free(nlinks);
1434     }
1435     return nlinks;
1436 }
1437
1438 static void rpmfilesBuildNLink(rpmfiles fi, Header h)
1439 {
1440     struct fileid_s f_id;
1441     fileidHash files;
1442     rpm_dev_t * fdevs = NULL;
1443     struct rpmtd_s td;
1444     int fc = 0;
1445     int totalfc = rpmfilesFC(fi);
1446
1447     if (!fi->finodes)
1448         return;
1449
1450     _hgfi(h, RPMTAG_FILEDEVICES, &td, HEADERGET_ALLOC, fdevs);
1451     if (!fdevs)
1452         return;
1453
1454     files = fileidHashCreate(totalfc, fidHashFunc, fidCmp, NULL, NULL);
1455     for (int i=0; i < totalfc; i++) {
1456         if (!S_ISREG(rpmfilesFMode(fi, i)) ||
1457                 (rpmfilesFFlags(fi, i) & RPMFILE_GHOST) ||
1458                 fi->finodes[i] <= 0) {
1459             continue;
1460         }
1461         fc++;
1462         f_id.id_dev = fdevs[i];
1463         f_id.id_ino = fi->finodes[i];
1464         fileidHashAddEntry(files, f_id, i);
1465     }
1466     if (fileidHashNumKeys(files) != fc) {
1467         /* Hard links */
1468         fi->nlinks = nlinkHashCreate(2*(totalfc - fileidHashNumKeys(files)),
1469                 intHash, intCmp, NULL, freeNLinks);
1470         for (int i=0; i < totalfc; i++) {
1471             int fcnt;
1472             int * data;
1473             if (!S_ISREG(rpmfilesFMode(fi, i)) ||
1474                     (rpmfilesFFlags(fi, i) & RPMFILE_GHOST)) {
1475                 continue;
1476             }
1477             f_id.id_dev = fdevs[i];
1478             f_id.id_ino = fi->finodes[i];
1479             fileidHashGetEntry(files, f_id, &data, &fcnt, NULL);
1480             if (fcnt > 1 && !nlinkHashHasEntry(fi->nlinks, i)) {
1481                 struct hardlinks_s * hlinks;
1482                 hlinks = xmalloc(sizeof(struct hardlinks_s)+
1483                         fcnt*sizeof(hlinks->files[0]));
1484                 hlinks->nlink = fcnt;
1485                 for (int j=0; j<fcnt; j++) {
1486                     hlinks->files[j] = data[j];
1487                     nlinkHashAddEntry(fi->nlinks, data[j], hlinks);
1488                 }
1489             }
1490         }
1491     }
1492     _free(fdevs);
1493     files = fileidHashFree(files);
1494 err:
1495     return;
1496 }
1497
1498 /* Convert a tag of hex strings to binary presentation */
1499 static uint8_t *hex2bin(Header h, rpmTagVal tag, rpm_count_t num, size_t len)
1500 {
1501     struct rpmtd_s td;
1502     uint8_t *bin = NULL;
1503
1504     if (headerGet(h, tag, &td, HEADERGET_MINMEM) && rpmtdCount(&td) == num) {
1505         uint8_t *t = bin = xmalloc(num * len);
1506         const char *s;
1507
1508         while ((s = rpmtdNextString(&td))) {
1509             if (*s == '\0') {
1510                 memset(t, 0, len);
1511                 t += len;
1512                 continue;
1513             }
1514             for (int j = 0; j < len; j++, t++, s += 2)
1515                 *t = (rnibble(s[0]) << 4) | rnibble(s[1]);
1516         }
1517     }
1518     rpmtdFreeData(&td);
1519
1520     return bin;
1521 }
1522
1523 static int rpmfilesPopulate(rpmfiles fi, Header h, rpmfiFlags flags)
1524 {
1525     headerGetFlags scareFlags = (flags & RPMFI_KEEPHEADER) ? 
1526                                 HEADERGET_MINMEM : HEADERGET_ALLOC;
1527     headerGetFlags defFlags = HEADERGET_ALLOC;
1528     struct rpmtd_s digalgo, td;
1529     rpm_count_t totalfc = rpmfilesFC(fi);
1530
1531     /* XXX TODO: all these should be sanity checked, ugh... */
1532     if (!(flags & RPMFI_NOFILEMODES))
1533         _hgfi(h, RPMTAG_FILEMODES, &td, scareFlags, fi->fmodes);
1534     if (!(flags & RPMFI_NOFILEFLAGS))
1535         _hgfi(h, RPMTAG_FILEFLAGS, &td, scareFlags, fi->fflags);
1536     if (!(flags & RPMFI_NOFILEVERIFYFLAGS))
1537         _hgfi(h, RPMTAG_FILEVERIFYFLAGS, &td, scareFlags, fi->vflags);
1538     if (!(flags & RPMFI_NOFILESIZES)) {
1539         _hgfi(h, RPMTAG_FILESIZES, &td, scareFlags, fi->fsizes);
1540         _hgfi(h, RPMTAG_LONGFILESIZES, &td, scareFlags, fi->lfsizes);
1541     }
1542     if (!(flags & RPMFI_NOFILECOLORS))
1543         _hgfi(h, RPMTAG_FILECOLORS, &td, scareFlags, fi->fcolors);
1544
1545     if (!(flags & RPMFI_NOFILECLASS)) {
1546         _hgfinc(h, RPMTAG_CLASSDICT, &td, scareFlags, fi->cdict);
1547         fi->ncdict = rpmtdCount(&td);
1548         _hgfi(h, RPMTAG_FILECLASS, &td, scareFlags, fi->fcdictx);
1549     }
1550     if (!(flags & RPMFI_NOFILEDEPS)) {
1551         _hgfinc(h, RPMTAG_DEPENDSDICT, &td, scareFlags, fi->ddict);
1552         fi->nddict = rpmtdCount(&td);
1553         _hgfinc(h, RPMTAG_FILEDEPENDSX, &td, scareFlags, fi->fddictx);
1554         _hgfinc(h, RPMTAG_FILEDEPENDSN, &td, scareFlags, fi->fddictn);
1555     }
1556
1557     if (!(flags & RPMFI_NOFILESTATES))
1558         _hgfi(h, RPMTAG_FILESTATES, &td, defFlags, fi->fstates);
1559
1560     if (!(flags & RPMFI_NOFILECAPS))
1561         _hgfi(h, RPMTAG_FILECAPS, &td, defFlags, fi->fcaps);
1562
1563     if (!(flags & RPMFI_NOFILELINKTOS))
1564         fi->flinks = tag2pool(fi->pool, h, RPMTAG_FILELINKTOS, totalfc);
1565     /* FILELANGS are only interesting when installing */
1566     if ((headerGetInstance(h) == 0) && !(flags & RPMFI_NOFILELANGS))
1567         fi->flangs = tag2pool(fi->pool, h, RPMTAG_FILELANGS, totalfc);
1568
1569     /* See if the package has non-md5 file digests */
1570     fi->digestalgo = PGPHASHALGO_MD5;
1571     if (headerGet(h, RPMTAG_FILEDIGESTALGO, &digalgo, HEADERGET_MINMEM)) {
1572         uint32_t *algo = rpmtdGetUint32(&digalgo);
1573         /* Hmm, what to do with unknown digest algorithms? */
1574         if (algo && rpmDigestLength(*algo) != 0) {
1575             fi->digestalgo = *algo;
1576         }
1577     }
1578
1579     fi->digests = NULL;
1580     /* grab hex digests from header and store in binary format */
1581     if (!(flags & RPMFI_NOFILEDIGESTS)) {
1582         size_t diglen = rpmDigestLength(fi->digestalgo);
1583         fi->digests = hex2bin(h, RPMTAG_FILEDIGESTS, totalfc, diglen);
1584     }
1585
1586     fi->signatures = NULL;
1587     /* grab hex signatures from header and store in binary format */
1588     if (!(flags & RPMFI_NOFILESIGNATURES)) {
1589         fi->signaturelength = headerGetNumber(h, RPMTAG_FILESIGNATURELENGTH);
1590         fi->signatures = hex2bin(h, RPMTAG_FILESIGNATURES,
1591                                  totalfc, fi->signaturelength);
1592     }
1593
1594     /* XXX TR_REMOVED doesn;t need fmtimes, frdevs, finodes */
1595     if (!(flags & RPMFI_NOFILEMTIMES))
1596         _hgfi(h, RPMTAG_FILEMTIMES, &td, scareFlags, fi->fmtimes);
1597     if (!(flags & RPMFI_NOFILERDEVS))
1598         _hgfi(h, RPMTAG_FILERDEVS, &td, scareFlags, fi->frdevs);
1599     if (!(flags & RPMFI_NOFILEINODES)) {
1600         _hgfi(h, RPMTAG_FILEINODES, &td, scareFlags, fi->finodes);
1601         rpmfilesBuildNLink(fi, h);
1602     }
1603     if (!(flags & RPMFI_NOFILEUSER)) {
1604         fi->fuser = tag2pool(fi->pool, h, RPMTAG_FILEUSERNAME, totalfc);
1605         if (!fi->fuser) goto err;
1606     }
1607     if (!(flags & RPMFI_NOFILEGROUP)) {
1608         fi->fgroup = tag2pool(fi->pool, h, RPMTAG_FILEGROUPNAME, totalfc);
1609         if (!fi->fgroup) goto err;
1610     }
1611     /* TODO: validate and return a real error */
1612     return 0;
1613  err:
1614     return -1;
1615 }
1616
1617 rpmfiles rpmfilesNew(rpmstrPool pool, Header h, rpmTagVal tagN, rpmfiFlags flags)
1618 {
1619     rpmfiles fi = xcalloc(1, sizeof(*fi)); 
1620     int fc;
1621
1622     fi->magic = RPMFIMAGIC;
1623     fi->fiflags = flags;
1624     /* private or shared pool? */
1625     fi->pool = (pool != NULL) ? rpmstrPoolLink(pool) : rpmstrPoolCreate();
1626
1627     /*
1628      * Grab and validate file triplet data. Headers with no files simply
1629      * fall through here and an empty file set is returned.
1630      */
1631     fc = rpmfnInit(&fi->fndata, RPMTAG_BASENAMES, h, fi->pool);
1632
1633     /* Broken data, bail out */
1634     if (fc < 0)
1635         goto err;
1636
1637     /* populate the rest of the stuff if we have files */
1638     if (fc > 0) {
1639         if (headerIsEntry(h, RPMTAG_ORIGBASENAMES)) {
1640             /* For relocated packages, grab the original paths too */
1641             int ofc;
1642             fi->ofndata = xmalloc(sizeof(*fi->ofndata));
1643             ofc = rpmfnInit(fi->ofndata, RPMTAG_ORIGBASENAMES, h, fi->pool);
1644             
1645             if (ofc != 0 && ofc != fc)
1646                 goto err;
1647         } else {
1648             /* In the normal case, orig is the same as actual path data */
1649             fi->ofndata = &fi->fndata;
1650         }
1651             
1652         if (rpmfilesPopulate(fi, h, flags))
1653             goto err;
1654     }
1655
1656     /* freeze the pool to save memory, but only if private pool */
1657     if (fi->pool != pool)
1658         rpmstrPoolFreeze(fi->pool, 0);
1659
1660     fi->h = (fi->fiflags & RPMFI_KEEPHEADER) ? headerLink(h) : NULL;
1661
1662     return rpmfilesLink(fi);
1663
1664 err:
1665     rpmfilesFree(fi);
1666     return NULL;
1667 }
1668
1669 static int iterWriteArchiveNext(rpmfi fi);
1670 static int iterReadArchiveNext(rpmfi fi);
1671 static int iterReadArchiveNextContentFirst(rpmfi fi);
1672 static int iterReadArchiveNextOmitHardlinks(rpmfi fi);
1673
1674 static int (*nextfuncs[])(rpmfi fi) = {
1675     iterFwd,
1676     iterBack,
1677     iterWriteArchiveNext,
1678     iterReadArchiveNext,
1679     iterReadArchiveNextContentFirst,
1680     iterReadArchiveNextOmitHardlinks,
1681     iterInterval,
1682 };
1683
1684
1685 static rpmfi initIter(rpmfiles files, int itype, int link)
1686 {
1687     rpmfi fi = NULL;
1688
1689     if (files && itype>=0 && itype<=RPMFILEITERMAX) {
1690         fi = xcalloc(1, sizeof(*fi)); 
1691         fi->i = -1;
1692         fi->files = link ? rpmfilesLink(files) : files;
1693         fi->next = nextfuncs[itype];
1694         fi->i = -1;
1695         if (itype == RPMFI_ITER_BACK) {
1696             fi->i = rpmfilesFC(fi->files);
1697         } else if (itype >=RPMFI_ITER_READ_ARCHIVE
1698             && itype <= RPMFI_ITER_READ_ARCHIVE_OMIT_HARDLINKS) {
1699
1700             fi->found = xcalloc(1, (rpmfiFC(fi)>>3) + 1);
1701         }
1702         rpmfiLink(fi);
1703     }
1704     return fi;
1705 }
1706
1707 rpmfi rpmfilesIter(rpmfiles files, int itype)
1708 {
1709     /* standalone iterators need to bump our refcount */
1710     return initIter(files, itype, 1);
1711 }
1712
1713 rpmfi rpmfilesFindPrefix(rpmfiles fi, const char *pfx)
1714 {
1715     int l, u, c, comparison;
1716     rpmfi iterator = NULL;
1717
1718     if (!fi || !pfx)
1719         return NULL;
1720
1721     size_t plen = strlen(pfx);
1722     l = 0;
1723     u = rpmfilesFC(fi);
1724     while (l < u) {
1725         c = (l + u) / 2;
1726
1727         comparison = cmpPfx(fi, c, pfx, plen);
1728
1729         if (comparison < 0)
1730             u = c;
1731         else if (comparison > 0)
1732             l = c + 1;
1733         else {
1734             if (cmpPfx(fi, l, pfx, plen))
1735                 l = c;
1736             while (l > 0 && !cmpPfx(fi, l - 1, pfx, plen))
1737                 l--;
1738             if ( u >= rpmfilesFC(fi) || cmpPfx(fi, u, pfx, plen))
1739                 u = c;
1740             while (++u < rpmfilesFC(fi)) {
1741                 if (cmpPfx(fi, u, pfx, plen))
1742                     break;
1743             }
1744             break;
1745         }
1746
1747     }
1748
1749     if (l < u) {
1750         iterator = initIter(fi, RPMFI_ITER_INTERVAL, 1);
1751         iterator->intervalStart = l;
1752         iterator->intervalEnd = u;
1753     }
1754
1755     return iterator;
1756 }
1757
1758 rpmfi rpmfiNewPool(rpmstrPool pool, Header h, rpmTagVal tagN, rpmfiFlags flags)
1759 {
1760     rpmfiles files = rpmfilesNew(pool, h, tagN, flags);
1761     /* we already own rpmfiles, avoid extra refcount on it */
1762     return initIter(files, RPMFI_ITER_FWD, 0);
1763 }
1764
1765 rpmfi rpmfiNew(const rpmts ts, Header h, rpmTagVal tagN, rpmfiFlags flags)
1766 {
1767     return rpmfiNewPool(NULL, h, tagN, flags);
1768 }
1769
1770 void rpmfilesSetFReplacedSize(rpmfiles fi, int ix, rpm_loff_t newsize)
1771 {
1772     if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
1773         /* Switch over to 64 bit variant */
1774         int fc = rpmfilesFC(fi);
1775         if (newsize > UINT32_MAX && fi->replacedLSizes == NULL) {
1776             fi->replacedLSizes = xcalloc(fc, sizeof(*fi->replacedLSizes));
1777             /* copy 32 bit data */
1778             if (fi->replacedSizes) {
1779                 for (int i=0; i < fc; i++)
1780                     fi->replacedLSizes[i] = fi->replacedSizes[i];
1781                 fi->replacedSizes = _free(fi->replacedSizes);
1782             }
1783         }
1784         if (fi->replacedLSizes != NULL) {
1785             fi->replacedLSizes[ix] = newsize;
1786         } else {
1787             if (fi->replacedSizes == NULL)
1788                 fi->replacedSizes = xcalloc(fc, sizeof(*fi->replacedSizes));
1789             fi->replacedSizes[ix] = (rpm_off_t) newsize;
1790         }
1791     }
1792 }
1793
1794 rpm_loff_t rpmfilesFReplacedSize(rpmfiles fi, int ix)
1795 {
1796     rpm_loff_t rsize = 0;
1797     if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
1798         if (fi->replacedSizes) {
1799             rsize = fi->replacedSizes[ix];
1800         } else if (fi->replacedLSizes) {
1801             rsize = fi->replacedLSizes[ix];
1802         }
1803     }
1804     return rsize;
1805 }
1806
1807 void rpmfilesFpLookup(rpmfiles fi, fingerPrintCache fpc)
1808 {
1809     /* This can get called twice (eg yum), scratch former results and redo */
1810     if (rpmfilesFC(fi) > 0) {
1811         rpmfn fn = &fi->fndata;
1812         if (fi->fps)
1813             free(fi->fps);
1814         fi->fps = fpLookupList(fpc, fi->pool,
1815                                fn->dnid, fn->bnid, fn->dil, fn->fc);
1816     }
1817 }
1818
1819 /* 
1820  * Generate iterator accessors function wrappers, these do nothing but
1821  * call the corresponding rpmfiFooIndex(fi, fi->[ij])
1822  */
1823
1824 #define RPMFI_ITERFUNC(TYPE, NAME, IXV) \
1825     TYPE rpmfi ## NAME(rpmfi fi) { return rpmfiles ## NAME(fi ? fi->files : NULL, fi ? fi->IXV : -1); }
1826
1827 RPMFI_ITERFUNC(rpmsid, BNId, i)
1828 RPMFI_ITERFUNC(rpmsid, DNId, j)
1829 RPMFI_ITERFUNC(const char *, BN, i)
1830 RPMFI_ITERFUNC(const char *, DN, j)
1831 RPMFI_ITERFUNC(const char *, OBN, i)
1832 RPMFI_ITERFUNC(const char *, ODN, j)
1833 RPMFI_ITERFUNC(const char *, FLink, i)
1834 RPMFI_ITERFUNC(const char *, FUser, i)
1835 RPMFI_ITERFUNC(const char *, FGroup, i)
1836 RPMFI_ITERFUNC(const char *, FCaps, i)
1837 RPMFI_ITERFUNC(const char *, FLangs, i)
1838 RPMFI_ITERFUNC(const char *, FClass, i)
1839 RPMFI_ITERFUNC(rpmfileState, FState, i)
1840 RPMFI_ITERFUNC(rpmfileAttrs, FFlags, i)
1841 RPMFI_ITERFUNC(rpmVerifyAttrs, VFlags, i)
1842 RPMFI_ITERFUNC(rpm_mode_t, FMode, i)
1843 RPMFI_ITERFUNC(rpm_rdev_t, FRdev, i)
1844 RPMFI_ITERFUNC(rpm_time_t, FMtime, i)
1845 RPMFI_ITERFUNC(rpm_ino_t, FInode, i)
1846 RPMFI_ITERFUNC(rpm_loff_t, FSize, i)
1847 RPMFI_ITERFUNC(rpm_color_t, FColor, i)
1848 RPMFI_ITERFUNC(uint32_t, FNlink, i)
1849
1850 const char * rpmfiFN(rpmfi fi)
1851 {
1852     const char *fn = ""; /* preserve behavior on errors */
1853     if (fi != NULL) {
1854         free(fi->fn);
1855         fi->fn = rpmfilesFN(fi->files, fi->i);
1856         if (fi->fn != NULL)
1857             fn = fi->fn;
1858     }
1859     return fn;
1860 }
1861
1862 const char * rpmfiOFN(rpmfi fi)
1863 {
1864     const char *fn = ""; /* preserve behavior on errors */
1865     if (fi != NULL) {
1866         free(fi->ofn);
1867         fi->ofn = rpmfilesOFN(fi->files, fi->i);
1868         if (fi->ofn != NULL)
1869             fn = fi->ofn;
1870     }
1871     return fn;
1872 }
1873
1874 const unsigned char * rpmfiFDigest(rpmfi fi, int *algo, size_t *len)
1875 {
1876     return rpmfilesFDigest(fi->files, fi ? fi->i : -1, algo, len);
1877 }
1878
1879 const unsigned char * rpmfiFSignature(rpmfi fi, size_t *len)
1880 {
1881     return rpmfilesFSignature(fi->files, fi ? fi->i : -1, len);
1882 }
1883
1884 uint32_t rpmfiFDepends(rpmfi fi, const uint32_t ** fddictp)
1885 {
1886     return rpmfilesFDepends(fi->files,  fi ? fi->i : -1, fddictp);
1887 }
1888
1889 int rpmfiStat(rpmfi fi, int flags, struct stat *sb)
1890 {
1891     return rpmfilesStat(fi->files, fi->i, flags, sb);
1892 }
1893
1894 int rpmfiCompare(const rpmfi afi, const rpmfi bfi)
1895 {
1896     return rpmfilesCompare(afi->files, afi ? afi->i : -1, bfi->files, bfi ? bfi->i : -1);
1897 }
1898
1899 rpmVerifyAttrs rpmfiVerify(rpmfi fi, rpmVerifyAttrs omitMask)
1900 {
1901     return rpmfilesVerify(fi->files, fi->i, omitMask);
1902 }
1903
1904 rpmstrPool rpmfilesPool(rpmfiles fi)
1905 {
1906     return (fi != NULL) ? fi->pool : NULL;
1907 }
1908
1909 rpmfiles rpmfiFiles(rpmfi fi)
1910 {
1911     return (fi != NULL) ? fi->files : NULL;
1912 }
1913
1914 /******************************************************/
1915 /*** Archive handling *********************************/
1916 /******************************************************/
1917
1918 rpmfi rpmfiNewArchiveReader(FD_t fd, rpmfiles files, int itype)
1919 {
1920     rpmcpio_t archive = rpmcpioOpen(fd, O_RDONLY);
1921     rpmfi fi = NULL;
1922     if (archive && itype >= RPMFI_ITER_READ_ARCHIVE) {
1923         fi = rpmfilesIter(files, itype);
1924     }
1925     if (fi) {
1926         fi->archive = archive;
1927     } else {
1928         rpmcpioFree(archive);
1929     }
1930     return fi;
1931 }
1932
1933 rpmfi rpmfiNewArchiveWriter(FD_t fd, rpmfiles files)
1934 {
1935     rpmcpio_t archive = rpmcpioOpen(fd, O_WRONLY);
1936     rpmfi fi = NULL;
1937     if (archive) {
1938         fi = rpmfilesIter(files, RPMFI_ITER_WRITE_ARCHIVE);
1939     }
1940     if (fi) {
1941         fi->archive = archive;
1942     } else {
1943         rpmcpioFree(archive);
1944     }
1945     return fi;
1946 }
1947
1948 int rpmfiArchiveClose(rpmfi fi)
1949 {
1950     if (fi == NULL)
1951         return -1;
1952     int rc = rpmcpioClose(fi->archive);
1953     return rc;
1954 }
1955
1956 rpm_loff_t rpmfiArchiveTell(rpmfi fi)
1957 {
1958     if (fi == NULL || fi->archive == NULL)
1959         return 0;
1960     return (rpm_loff_t) rpmcpioTell(fi->archive);
1961 }
1962
1963 static int rpmfiArchiveWriteHeader(rpmfi fi)
1964 {
1965     int rc;
1966     struct stat st;
1967
1968     if (rpmfiStat(fi, 0, &st))
1969         return -1;
1970
1971     rpmfiles files = fi->files;
1972
1973     if (files->lfsizes) {
1974         return rpmcpioStrippedHeaderWrite(fi->archive, rpmfiFX(fi), st.st_size);
1975     } else {
1976         const char * dn = rpmfiDN(fi);
1977         char * path = rstrscat(NULL, (dn[0] == '/' && !rpmExpandNumeric("%{_noPayloadPrefix}")) ? "." : "",
1978                                dn, rpmfiBN(fi), NULL);
1979         rc = rpmcpioHeaderWrite(fi->archive, path, &st);
1980         free(path);
1981     }
1982
1983     return rc;
1984 }
1985
1986 static int iterWriteArchiveNextFile(rpmfi fi)
1987 {
1988     rpmfiles files = rpmfiFiles(fi);
1989     int fx = rpmfiFX(fi);
1990     int fc = rpmfiFC(fi);
1991     const int * hardlinks;
1992     int numHardlinks = 0;
1993
1994     /* already processing hard linked files */
1995     if (rpmfiFNlink(fi) > 1) {
1996         /* search next hard linked file */
1997         fi->i = -1;
1998         for (int i=fx+1; i<fc; i++) {
1999             /* no ghosts */
2000             if (rpmfilesFFlags(files, i) & RPMFILE_GHOST)
2001                 continue;
2002             numHardlinks = rpmfilesFLinks(files, i, &hardlinks);
2003             if (numHardlinks > 1 && hardlinks[0] == i) {
2004                 rpmfiSetFX(fi, i);
2005                 break;
2006             }
2007         }
2008     } else {
2009         fi->i = -1;
2010         /* search next non hardlinked file */
2011         for (int i=fx+1; i<fc; i++) {
2012             /* no ghosts */
2013             if (rpmfilesFFlags(files, i) & RPMFILE_GHOST)
2014                 continue;
2015             if (rpmfilesFNlink(files, i) < 2) {
2016                 rpmfiSetFX(fi, i);
2017                 break;
2018             }
2019         }
2020         if (rpmfiFX(fi) == -1) {
2021             /* continue with first hard linked file */
2022             for (int i=0; i<fc; i++) {
2023                 /* no ghosts */
2024                 if (rpmfilesFFlags(files, i) & RPMFILE_GHOST)
2025                     continue;
2026                 numHardlinks = rpmfilesFLinks(files, i, &hardlinks);
2027                 if (numHardlinks > 1) {
2028                     rpmfiSetFX(fi, i);
2029                     break;
2030                 }
2031             }
2032         }
2033     }
2034     if (rpmfiFX(fi) == -1)
2035         return -1;
2036
2037     /* write header(s) */
2038     if (numHardlinks>1) {
2039         for (int i=0; i<numHardlinks; i++) {
2040             rpmfiSetFX(fi, hardlinks[i]);
2041             int rc = rpmfiArchiveWriteHeader(fi);
2042             if (rc) {
2043                 return rc;
2044             }
2045         }
2046         rpmfiSetFX(fi, hardlinks[0]);
2047     } else {
2048         int rc = rpmfiArchiveWriteHeader(fi);
2049         if (rc) {
2050             return rc;
2051         }
2052     }
2053     return rpmfiFX(fi);
2054 }
2055
2056 static int iterWriteArchiveNext(rpmfi fi)
2057 {
2058     int fx;
2059     /* loop over the files we can handle ourself */
2060     do {
2061         fx = iterWriteArchiveNextFile(fi);
2062         if (S_ISLNK(rpmfiFMode(fi))) {
2063             /* write symlink target */
2064             const char *lnk = rpmfiFLink(fi);
2065             size_t len = strlen(lnk);
2066             if (rpmfiArchiveWrite(fi, lnk, len) != len) {
2067                 return RPMERR_WRITE_FAILED;
2068             }
2069         } else if (S_ISREG(rpmfiFMode(fi)) && rpmfiFSize(fi)) {
2070             /* this file actually needs some content */
2071             return fx;
2072         }
2073         /* go on for special files, directories and empty files */
2074     } while (fx >= 0);
2075     return fx;
2076 }
2077
2078 size_t rpmfiArchiveWrite(rpmfi fi, const void * buf, size_t size)
2079 {
2080     if (fi == NULL || fi->archive == NULL)
2081         return -1;
2082     return rpmcpioWrite(fi->archive, buf, size);
2083 }
2084
2085 int rpmfiArchiveWriteFile(rpmfi fi, FD_t fd)
2086 {
2087     rpm_loff_t left;
2088     int rc = 0;
2089     size_t len;
2090     char buf[BUFSIZ*4];
2091
2092     if (fi == NULL || fi->archive == NULL || fd == NULL)
2093         return -1;
2094
2095     left = rpmfiFSize(fi);
2096
2097     while (left) {
2098         len = (left > sizeof(buf) ? sizeof(buf) : left);
2099         if (Fread(buf, sizeof(*buf), len, fd) != len || Ferror(fd)) {
2100             rc = RPMERR_READ_FAILED;
2101             break;
2102         }
2103
2104         if (rpmcpioWrite(fi->archive, buf, len) != len) {
2105             rc = RPMERR_WRITE_FAILED;
2106             break;
2107         }
2108         left -= len;
2109     }
2110     return rc;
2111 }
2112
2113 static void rpmfiSetFound(rpmfi fi, int ix)
2114 {
2115     fi->found[ix >> 3] |= (1 << (ix % 8));
2116 }
2117
2118 static int rpmfiFound(rpmfi fi, int ix)
2119 {
2120     return fi->found[ix >> 3] & (1 << (ix % 8));
2121 }
2122
2123 static int iterReadArchiveNext(rpmfi fi)
2124 {
2125     int rc;
2126     int fx = -1;
2127     int fc = rpmfilesFC(fi->files);
2128     char * path = NULL;
2129
2130     if (fi->archive == NULL)
2131         return -1;
2132
2133     /* Read next payload header. */
2134     rc = rpmcpioHeaderRead(fi->archive, &path, &fx);
2135
2136     /* if archive ended, check if we found all files */
2137     if (rc == RPMERR_ITER_END) {
2138         for (int i=0; i<fc; i++) {
2139             if (!rpmfiFound(fi, i) &&
2140                         !(rpmfilesFFlags(fi->files, i) & RPMFILE_GHOST)) {
2141                 rc = RPMERR_MISSING_FILE;
2142                 break;
2143             }
2144         }
2145     }
2146     if (rc) {
2147         return rc;
2148     }
2149
2150     if (path) {
2151         /* Regular cpio archive, identify mapping index. */
2152         fx = rpmfilesFindOFN(fi->files, path);
2153         free(path);
2154     }
2155
2156     if (fx >= 0 && fx < fc) {
2157         rpm_loff_t fsize = 0;
2158         rpm_mode_t mode = rpmfilesFMode(fi->files, fx);
2159
2160         /* %ghost in payload, should not be there but rpm < 4.11 sometimes did this */
2161         if (rpmfilesFFlags(fi->files, fx) & RPMFILE_GHOST)
2162             return RPMERR_ITER_SKIP;
2163
2164         if (S_ISREG(mode)) {
2165             const int * links;
2166             uint32_t numlinks = rpmfilesFLinks(fi->files, fx, &links);
2167             if (!(numlinks > 1 && links[numlinks-1] != fx))
2168                 fsize = rpmfilesFSize(fi->files, fx);
2169         } else if (S_ISLNK(mode)) {
2170             /* Skip over symlink target data in payload */
2171             rpm_loff_t lsize = rpmfilesFSize(fi->files, fx);
2172             char *buf = xmalloc(lsize + 1);
2173             if (rpmcpioRead(fi->archive, buf, lsize) != lsize)
2174                 rc = RPMERR_READ_FAILED;
2175             /* XXX should we validate the payload matches? */
2176             free(buf);
2177         }
2178         rpmcpioSetExpectedFileSize(fi->archive, fsize);
2179         rpmfiSetFound(fi, fx);
2180     } else {
2181         /* Mapping error */
2182         rc = RPMERR_UNMAPPED_FILE;
2183     }
2184     return (rc != 0) ? rc : fx;
2185 }
2186
2187
2188 static int iterReadArchiveNextOmitHardlinks(rpmfi fi)
2189 {
2190     int fx;
2191     const int * links;
2192     int nlink;
2193     do {
2194         fx = iterReadArchiveNext(fi);
2195         nlink = rpmfilesFLinks(fi->files, fx, &links);
2196     } while (fx>=0 && nlink>1 && links[nlink-1]!=fx);
2197     return fx;
2198 }
2199
2200 static int iterReadArchiveNextContentFirst(rpmfi fi)
2201 {
2202     int fx = rpmfiFX(fi);
2203     const int * links;
2204     int nlink;
2205     /* decide what to do on the basis of the last entry */
2206     nlink = rpmfilesFLinks(fi->files, fx, &links);
2207     if (nlink > 1) {
2208         /* currently reading through hard links */
2209         if (fx == links[nlink-1]) {
2210             /* arrived back at last entry, read on */
2211             fx = iterReadArchiveNext(fi);    
2212         } else {
2213             /* next hard link */
2214             /* scales poorly but shouldn't matter */
2215             for (int i=0; i<nlink; i++) {
2216                 if (links[i] == fx) {
2217                     fx = links[i+1];
2218                     return fx;
2219                 }
2220             }
2221             /* should never happen */
2222             return -1;
2223         }
2224     } else {
2225         fx = iterReadArchiveNext(fi);
2226     }
2227
2228     /* look at the new entry */
2229     nlink = rpmfilesFLinks(fi->files, fx, &links);
2230     /* arrived at new set of hardlinks? */
2231     if (nlink > 1) {
2232         /* read over all entries to the last one (containing the content) */
2233         do {
2234             fx = iterReadArchiveNext(fi);
2235         } while (fx >=0 && fx != links[nlink-1]);
2236         /* rewind to the first entry */
2237         if (fx >= 0) {
2238             fx = links[0];
2239         }
2240     }
2241     return fx;
2242 }
2243
2244 int rpmfiArchiveHasContent(rpmfi fi)
2245 {
2246     int res = 0;
2247     if (fi && S_ISREG(rpmfiFMode(fi))) {
2248         const int * links;
2249         int nlink = rpmfiFLinks(fi, &links);
2250         if (nlink > 1) {
2251             if (fi->next == iterReadArchiveNext ||
2252                 fi->next == iterReadArchiveNextOmitHardlinks) {
2253                 res = rpmfiFX(fi) == links[nlink-1];
2254             } else if (fi->next == iterReadArchiveNextContentFirst) {
2255                 res = rpmfiFX(fi) == links[0];
2256             }
2257         } else {
2258             res = 1;
2259         }
2260     }
2261     return res;
2262 }
2263
2264 size_t rpmfiArchiveRead(rpmfi fi, void * buf, size_t size)
2265 {
2266     if (fi == NULL || fi->archive == NULL)
2267         return -1;
2268     return rpmcpioRead(fi->archive, buf, size);
2269 }
2270
2271 int rpmfiArchiveReadToFilePsm(rpmfi fi, FD_t fd, int nodigest, rpmpsm psm)
2272 {
2273     if (fi == NULL || fi->archive == NULL || fd == NULL)
2274         return -1;
2275
2276     rpm_loff_t left = rpmfiFSize(fi);
2277     const unsigned char * fidigest = NULL;
2278     pgpHashAlgo digestalgo = 0;
2279     int rc = 0;
2280     char buf[BUFSIZ*4];
2281
2282     if (!nodigest) {
2283         digestalgo = rpmfiDigestAlgo(fi);
2284         fidigest = rpmfilesFDigest(fi->files, rpmfiFX(fi), NULL, NULL);
2285         fdInitDigest(fd, digestalgo, 0);
2286     }
2287
2288     while (left) {
2289         size_t len;
2290         len = (left > sizeof(buf) ? sizeof(buf) : left);
2291         if (rpmcpioRead(fi->archive, buf, len) != len) {
2292             rc = RPMERR_READ_FAILED;
2293             goto exit;
2294         }
2295         if ((Fwrite(buf, sizeof(*buf), len, fd) != len) || Ferror(fd)) {
2296             rc = RPMERR_WRITE_FAILED;
2297             goto exit;
2298         }
2299
2300         rpmpsmNotify(psm, RPMCALLBACK_INST_PROGRESS, rpmfiArchiveTell(fi));
2301         left -= len;
2302     }
2303
2304     if (!nodigest) {
2305         void * digest = NULL;
2306
2307         (void) Fflush(fd);
2308         fdFiniDigest(fd, digestalgo, &digest, NULL, 0);
2309
2310         if (digest != NULL && fidigest != NULL) {
2311             size_t diglen = rpmDigestLength(digestalgo);
2312             if (memcmp(digest, fidigest, diglen)) {
2313                 rc = RPMERR_DIGEST_MISMATCH;
2314
2315                 /* ...but in old packages, empty files have zeros for digest */
2316                 if (rpmfiFSize(fi) == 0 && digestalgo == PGPHASHALGO_MD5) {
2317                     uint8_t zeros[diglen];
2318                     memset(&zeros, 0, diglen);
2319                     if (memcmp(zeros, fidigest, diglen) == 0)
2320                         rc = 0;
2321                 }
2322             }
2323         } else {
2324             rc = RPMERR_DIGEST_MISMATCH;
2325         }
2326         free(digest);
2327     }
2328
2329 exit:
2330     return rc;
2331 }
2332
2333 int rpmfiArchiveReadToFile(rpmfi fi, FD_t fd, int nodigest)
2334 {
2335     return rpmfiArchiveReadToFilePsm(fi, fd, nodigest, NULL);
2336 }
2337
2338 char * rpmfileStrerror(int rc)
2339 {
2340     char *msg = NULL;
2341     const char *s = NULL;
2342     const char *prefix = "cpio";
2343     int myerrno = errno;
2344
2345     switch (rc) {
2346     default:
2347         break;
2348     case RPMERR_BAD_MAGIC:      s = _("Bad magic");             break;
2349     case RPMERR_BAD_HEADER:     s = _("Bad/unreadable  header");break;
2350
2351     case RPMERR_OPEN_FAILED:    s = "open";     break;
2352     case RPMERR_CHMOD_FAILED:   s = "chmod";    break;
2353     case RPMERR_CHOWN_FAILED:   s = "chown";    break;
2354     case RPMERR_WRITE_FAILED:   s = "write";    break;
2355     case RPMERR_UTIME_FAILED:   s = "utime";    break;
2356     case RPMERR_UNLINK_FAILED:  s = "unlink";   break;
2357     case RPMERR_RENAME_FAILED:  s = "rename";   break;
2358     case RPMERR_SYMLINK_FAILED: s = "symlink";  break;
2359     case RPMERR_STAT_FAILED:    s = "stat";     break;
2360     case RPMERR_LSTAT_FAILED:   s = "lstat";    break;
2361     case RPMERR_MKDIR_FAILED:   s = "mkdir";    break;
2362     case RPMERR_RMDIR_FAILED:   s = "rmdir";    break;
2363     case RPMERR_MKNOD_FAILED:   s = "mknod";    break;
2364     case RPMERR_MKFIFO_FAILED:  s = "mkfifo";   break;
2365     case RPMERR_LINK_FAILED:    s = "link";     break;
2366     case RPMERR_READLINK_FAILED: s = "readlink";        break;
2367     case RPMERR_READ_FAILED:    s = "read";     break;
2368     case RPMERR_COPY_FAILED:    s = "copy";     break;
2369     case RPMERR_LSETFCON_FAILED: s = "lsetfilecon";     break;
2370     case RPMERR_SETCAP_FAILED: s = "cap_set_file";      break;
2371
2372     case RPMERR_HDR_SIZE:       s = _("Header size too big");   break;
2373     case RPMERR_FILE_SIZE:      s = _("File too large for archive");    break;
2374     case RPMERR_UNKNOWN_FILETYPE: s = _("Unknown file type");   break;
2375     case RPMERR_MISSING_FILE: s = _("Missing file(s)"); break;
2376     case RPMERR_DIGEST_MISMATCH: s = _("Digest mismatch");      break;
2377     case RPMERR_INTERNAL:       s = _("Internal error");        break;
2378     case RPMERR_UNMAPPED_FILE:  s = _("Archive file not in header"); break;
2379     case RPMERR_ENOENT: s = strerror(ENOENT); break;
2380     case RPMERR_ENOTEMPTY:      s = strerror(ENOTEMPTY); break;
2381     case RPMERR_EXIST_AS_DIR:
2382         s = _("File from package already exists as a directory in system");
2383         break;
2384     }
2385
2386     if (s != NULL) {
2387         rasprintf(&msg, "%s: %s", prefix, s);
2388         if ((rc <= RPMERR_CHECK_ERRNO) && myerrno) {
2389             rstrscat(&msg, _(" failed - "), strerror(myerrno), NULL);
2390         }
2391     } else {
2392         rasprintf(&msg, _("%s: (error 0x%x)"), prefix, rc);
2393     }
2394
2395     return msg;
2396 }