- resurrect --rollback.
[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 <rpmio_internal.h>
9 #include <rpmlib.h>
10
11 #include "cpio.h"       /* XXX CPIO_FOO */
12 #include "fsm.h"        /* XXX newFSM() */
13
14 #define _RPMFI_INTERNAL
15 #include "rpmfi.h"
16
17 #define _RPMTE_INTERNAL /* relocations */
18 #include "rpmte.h"
19 #include "rpmts.h"
20
21 #include "misc.h"       /* XXX stripTrailingChar */
22
23 #include "debug.h"
24
25 /*@access rpmte @*/
26
27 /*@unchecked@*/
28 int _rpmfi_debug = 0;
29
30 rpmfi XrpmfiUnlink(rpmfi fi, const char * msg, const char * fn, unsigned ln)
31 {
32     if (fi == NULL) return NULL;
33 /*@-modfilesystem@*/
34 if (_rpmfi_debug && msg != NULL)
35 fprintf(stderr, "--> fi %p -- %d %s at %s:%u\n", fi, fi->nrefs, msg, fn, ln);
36 /*@=modfilesystem@*/
37     fi->nrefs--;
38     return NULL;
39 }
40
41 rpmfi XrpmfiLink(rpmfi fi, const char * msg, const char * fn, unsigned ln)
42 {
43     if (fi == NULL) return NULL;
44     fi->nrefs++;
45 /*@-modfilesystem@*/
46 if (_rpmfi_debug && msg != NULL)
47 fprintf(stderr, "--> fi %p ++ %d %s at %s:%u\n", fi, fi->nrefs, msg, fn, ln);
48 /*@=modfilesystem@*/
49     /*@-refcounttrans@*/ return fi; /*@=refcounttrans@*/
50 }
51
52 int rpmfiFC(rpmfi fi)
53 {
54     return (fi != NULL ? fi->fc : 0);
55 }
56
57 int rpmfiDC(rpmfi fi)
58 {
59     return (fi != NULL ? fi->dc : 0);
60 }
61
62 #ifdef  NOTYET
63 int rpmfiDI(rpmfi fi)
64 {
65 }
66 #endif
67
68 int rpmfiFX(rpmfi fi)
69 {
70     return (fi != NULL ? fi->i : -1);
71 }
72
73 int rpmfiSetFX(rpmfi fi, int fx)
74 {
75     int i = -1;
76
77     if (fi != NULL && fx >= 0 && fx < fi->fc) {
78         i = fi->i;
79         fi->i = fx;
80 /*@-boundsread@*/
81         fi->j = fi->dil[fi->i];
82 /*@=boundsread@*/
83     }
84     return i;
85 }
86
87 int rpmfiDX(rpmfi fi)
88 {
89     return (fi != NULL ? fi->j : -1);
90 }
91
92 int rpmfiSetDX(rpmfi fi, int dx)
93 {
94     int j = -1;
95
96     if (fi != NULL && dx >= 0 && dx < fi->dc) {
97         j = fi->j;
98         fi->j = dx;
99     }
100     return j;
101 }
102
103 const char * rpmfiBN(rpmfi fi)
104 {
105     const char * BN = NULL;
106
107     if (fi != NULL && fi->i >= 0 && fi->i < fi->fc) {
108 /*@-boundsread@*/
109         if (fi->bnl != NULL)
110             BN = fi->bnl[fi->i];
111 /*@=boundsread@*/
112     }
113     return BN;
114 }
115
116 const char * rpmfiDN(rpmfi fi)
117 {
118     const char * DN = NULL;
119
120     if (fi != NULL && fi->j >= 0 && fi->j < fi->dc) {
121 /*@-boundsread@*/
122         if (fi->dnl != NULL)
123             DN = fi->dnl[fi->j];
124 /*@=boundsread@*/
125     }
126     return DN;
127 }
128
129 const char * rpmfiFN(rpmfi fi)
130 {
131     const char * FN = "";
132
133     /*@-branchstate@*/
134     if (fi != NULL && fi->i >= 0 && fi->i < fi->fc) {
135         char * t;
136         if (fi->fn == NULL)
137             fi->fn = xmalloc(fi->fnlen);
138         FN = t = fi->fn;
139 /*@-boundswrite@*/
140         *t = '\0';
141         t = stpcpy(t, fi->dnl[fi->dil[fi->i]]);
142         t = stpcpy(t, fi->bnl[fi->i]);
143 /*@=boundswrite@*/
144     }
145     /*@=branchstate@*/
146     return FN;
147 }
148
149 int_32 rpmfiFFlags(rpmfi fi)
150 {
151     int_32 FFlags = 0;
152
153     if (fi != NULL && fi->i >= 0 && fi->i < fi->fc) {
154 /*@-boundsread@*/
155         if (fi->fflags != NULL)
156             FFlags = fi->fflags[fi->i];
157 /*@=boundsread@*/
158     }
159     return FFlags;
160 }
161
162 int_32 rpmfiVFlags(rpmfi fi)
163 {
164     int_32 VFlags = 0;
165
166     if (fi != NULL && fi->i >= 0 && fi->i < fi->fc) {
167 /*@-boundsread@*/
168         if (fi->vflags != NULL)
169             VFlags = fi->vflags[fi->i];
170 /*@=boundsread@*/
171     }
172     return VFlags;
173 }
174
175 int_16 rpmfiFMode(rpmfi fi)
176 {
177     int_16 fmode = 0;
178
179     if (fi != NULL && fi->i >= 0 && fi->i < fi->fc) {
180 /*@-boundsread@*/
181         if (fi->fmodes != NULL)
182             fmode = fi->fmodes[fi->i];
183 /*@=boundsread@*/
184     }
185     return fmode;
186 }
187
188 rpmfileState rpmfiFState(rpmfi fi)
189 {
190     rpmfileState fstate = RPMFILE_STATE_MISSING;
191
192     if (fi != NULL && fi->i >= 0 && fi->i < fi->fc) {
193 /*@-boundsread@*/
194         if (fi->fstates != NULL)
195             fstate = fi->fstates[fi->i];
196 /*@=boundsread@*/
197     }
198     return fstate;
199 }
200
201 const unsigned char * rpmfiMD5(rpmfi fi)
202 {
203     unsigned char * MD5 = NULL;
204
205     if (fi != NULL && fi->i >= 0 && fi->i < fi->fc) {
206 /*@-boundsread@*/
207         if (fi->md5s != NULL)
208             MD5 = fi->md5s + (16 * fi->i);
209 /*@=boundsread@*/
210     }
211     return MD5;
212 }
213
214 const char * rpmfiFLink(rpmfi fi)
215 {
216     const char * flink = NULL;
217
218     if (fi != NULL && fi->i >= 0 && fi->i < fi->fc) {
219 /*@-boundsread@*/
220         if (fi->flinks != NULL)
221             flink = fi->flinks[fi->i];
222 /*@=boundsread@*/
223     }
224     return flink;
225 }
226
227 int_32 rpmfiFSize(rpmfi fi)
228 {
229     int_32 fsize = 0;
230
231     if (fi != NULL && fi->i >= 0 && fi->i < fi->fc) {
232 /*@-boundsread@*/
233         if (fi->fsizes != NULL)
234             fsize = fi->fsizes[fi->i];
235 /*@=boundsread@*/
236     }
237     return fsize;
238 }
239
240 int_16 rpmfiFRdev(rpmfi fi)
241 {
242     int_16 frdev = 0;
243
244     if (fi != NULL && fi->i >= 0 && fi->i < fi->fc) {
245 /*@-boundsread@*/
246         if (fi->frdevs != NULL)
247             frdev = fi->frdevs[fi->i];
248 /*@=boundsread@*/
249     }
250     return frdev;
251 }
252
253 int_32 rpmfiFInode(rpmfi fi)
254 {
255     int_32 finode = 0;
256
257     if (fi != NULL && fi->i >= 0 && fi->i < fi->fc) {
258 /*@-boundsread@*/
259         if (fi->finodes != NULL)
260             finode = fi->finodes[fi->i];
261 /*@=boundsread@*/
262     }
263     return finode;
264 }
265
266 int_32 rpmfiFNlink(rpmfi fi)
267 {
268     int_32 nlink = 0;
269
270     if (fi != NULL && fi->i >= 0 && fi->i < fi->fc) {
271         /* XXX rpm-2.3.12 has not RPMTAG_FILEINODES */
272 /*@-boundsread@*/
273         if (fi->finodes && fi->frdevs) {
274             int_32 finode = fi->finodes[fi->i];
275             int_16 frdev = fi->frdevs[fi->i];
276             int j;
277
278             for (j = 0; j < fi->fc; j++) {
279                 if (fi->frdevs[j] == frdev && fi->finodes[j] == finode)
280                     nlink++;
281             }
282         }
283 /*@=boundsread@*/
284     }
285     return nlink;
286 }
287
288 int_32 rpmfiFMtime(rpmfi fi)
289 {
290     int_32 fmtime = 0;
291
292     if (fi != NULL && fi->i >= 0 && fi->i < fi->fc) {
293 /*@-boundsread@*/
294         if (fi->fmtimes != NULL)
295             fmtime = fi->fmtimes[fi->i];
296 /*@=boundsread@*/
297     }
298     return fmtime;
299 }
300
301 const char * rpmfiFUser(rpmfi fi)
302 {
303     const char * fuser = NULL;
304
305     /* XXX add support for ancient RPMTAG_FILEUIDS? */
306     if (fi != NULL && fi->i >= 0 && fi->i < fi->fc) {
307 /*@-boundsread@*/
308         if (fi->fuser != NULL)
309             fuser = fi->fuser[fi->i];
310 /*@=boundsread@*/
311     }
312     return fuser;
313 }
314
315 const char * rpmfiFGroup(rpmfi fi)
316 {
317     const char * fgroup = NULL;
318
319     /* XXX add support for ancient RPMTAG_FILEGIDS? */
320     if (fi != NULL && fi->i >= 0 && fi->i < fi->fc) {
321 /*@-boundsread@*/
322         if (fi->fgroup != NULL)
323             fgroup = fi->fgroup[fi->i];
324 /*@=boundsread@*/
325     }
326     return fgroup;
327 }
328
329 int rpmfiNext(rpmfi fi)
330 {
331     int i = -1;
332
333     if (fi != NULL && ++fi->i >= 0) {
334         if (fi->i < fi->fc) {
335             i = fi->i;
336 /*@-boundsread@*/
337             if (fi->dil != NULL)
338                 fi->j = fi->dil[fi->i];
339 /*@=boundsread@*/
340         } else
341             fi->i = -1;
342
343 /*@-modfilesystem @*/
344 if (_rpmfi_debug  < 0 && i != -1)
345 fprintf(stderr, "*** fi %p\t%s[%d] %s%s\n", fi, (fi->Type ? fi->Type : "?Type?"), i, (i >= 0 ? fi->dnl[fi->j] : ""), (i >= 0 ? fi->bnl[fi->i] : ""));
346 /*@=modfilesystem @*/
347
348     }
349
350     return i;
351 }
352
353 rpmfi rpmfiInit(rpmfi fi, int fx)
354 {
355     if (fi != NULL) {
356         if (fx >= 0 && fx < fi->fc) {
357             fi->i = fx - 1;
358             fi->j = -1;
359         }
360     }
361
362     /*@-refcounttrans@*/
363     return fi;
364     /*@=refcounttrans@*/
365 }
366
367 int rpmfiNextD(rpmfi fi)
368 {
369     int j = -1;
370
371     if (fi != NULL && ++fi->j >= 0) {
372         if (fi->j < fi->dc)
373             j = fi->j;
374         else
375             fi->j = -1;
376
377 /*@-modfilesystem @*/
378 if (_rpmfi_debug  < 0 && j != -1)
379 fprintf(stderr, "*** fi %p\t%s[%d]\n", fi, (fi->Type ? fi->Type : "?Type?"), j);
380 /*@=modfilesystem @*/
381
382     }
383
384     return j;
385 }
386
387 rpmfi rpmfiInitD(rpmfi fi, int dx)
388 {
389     if (fi != NULL) {
390         if (dx >= 0 && dx < fi->fc)
391             fi->j = dx - 1;
392         else
393             fi = NULL;
394     }
395
396     /*@-refcounttrans@*/
397     return fi;
398     /*@=refcounttrans@*/
399 }
400
401 /**
402  * Identify a file type.
403  * @param ft            file type
404  * @return              string to identify a file type
405  */
406 static /*@observer@*/
407 const char *const ftstring (fileTypes ft)
408         /*@*/
409 {
410     switch (ft) {
411     case XDIR:  return "directory";
412     case CDEV:  return "char dev";
413     case BDEV:  return "block dev";
414     case LINK:  return "link";
415     case SOCK:  return "sock";
416     case PIPE:  return "fifo/pipe";
417     case REG:   return "file";
418     default:    return "unknown file type";
419     }
420     /*@notreached@*/
421 }
422
423 fileTypes whatis(uint_16 mode)
424 {
425     if (S_ISDIR(mode))  return XDIR;
426     if (S_ISCHR(mode))  return CDEV;
427     if (S_ISBLK(mode))  return BDEV;
428     if (S_ISLNK(mode))  return LINK;
429 /*@-unrecog@*/
430     if (S_ISSOCK(mode)) return SOCK;
431 /*@=unrecog@*/
432     if (S_ISFIFO(mode)) return PIPE;
433     return REG;
434 }
435
436 /*@observer@*/
437 const char *const rpmfiTypeString(rpmfi fi)
438 {
439     switch(rpmteType(fi->te)) {
440     case TR_ADDED:      return " install";
441     case TR_REMOVED:    return "   erase";
442     default:            return "???";
443     }
444     /*@noteached@*/
445 }
446
447 #define alloca_strdup(_s)       strcpy(alloca(strlen(_s)+1), (_s))
448
449 /**
450  * Relocate files in header.
451  * @todo multilib file dispositions need to be checked.
452  * @param ts            transaction set
453  * @param fi            transaction element file info
454  * @param origH         package header
455  * @param actions       file dispositions
456  * @return              header with relocated files
457  */
458 /*@-bounds@*/
459 static
460 Header relocateFileList(const rpmts ts, rpmfi fi,
461                 Header origH, fileAction * actions)
462         /*@modifies ts, fi, origH, actions @*/
463 {
464     rpmte p = fi->te;
465     HGE_t hge = fi->hge;
466     HAE_t hae = fi->hae;
467     HME_t hme = fi->hme;
468     HFD_t hfd = (fi->hfd ? fi->hfd : headerFreeData);
469     static int _printed = 0;
470     int allowBadRelocate = (rpmtsFilterFlags(ts) & RPMPROB_FILTER_FORCERELOCATE);
471     rpmRelocation * relocations = NULL;
472     int numRelocations;
473     const char ** validRelocations;
474     rpmTagType validType;
475     int numValid;
476     const char ** baseNames;
477     const char ** dirNames;
478     int_32 * dirIndexes;
479     int_32 * newDirIndexes;
480     int_32 fileCount;
481     int_32 dirCount;
482     uint_32 * fFlags = NULL;
483     uint_16 * fModes = NULL;
484     char * skipDirList;
485     Header h;
486     int nrelocated = 0;
487     int fileAlloced = 0;
488     char * fn = NULL;
489     int haveRelocatedFile = 0;
490     int reldel = 0;
491     int len;
492     int i, j, xx;
493
494     if (!hge(origH, RPMTAG_PREFIXES, &validType,
495                         (void **) &validRelocations, &numValid))
496         numValid = 0;
497
498     numRelocations = 0;
499     if (p->relocs)
500         while (p->relocs[numRelocations].newPath ||
501                p->relocs[numRelocations].oldPath)
502             numRelocations++;
503
504     /*
505      * If no relocations are specified (usually the case), then return the
506      * original header. If there are prefixes, however, then INSTPREFIXES
507      * should be added, but, since relocateFileList() can be called more
508      * than once for the same header, don't bother if already present.
509      */
510     if (p->relocs == NULL || numRelocations == 0) {
511         if (numValid) {
512             if (!headerIsEntry(origH, RPMTAG_INSTPREFIXES))
513                 xx = hae(origH, RPMTAG_INSTPREFIXES,
514                         validType, validRelocations, numValid);
515             validRelocations = hfd(validRelocations, validType);
516         }
517         /* XXX FIXME multilib file actions need to be checked. */
518         return headerLink(origH);
519     }
520
521     h = headerLink(origH);
522
523     relocations = alloca(sizeof(*relocations) * numRelocations);
524
525     /* Build sorted relocation list from raw relocations. */
526     for (i = 0; i < numRelocations; i++) {
527         char * t;
528
529         /*
530          * Default relocations (oldPath == NULL) are handled in the UI,
531          * not rpmlib.
532          */
533         if (p->relocs[i].oldPath == NULL) continue; /* XXX can't happen */
534
535         /* FIXME: Trailing /'s will confuse us greatly. Internal ones will 
536            too, but those are more trouble to fix up. :-( */
537         t = alloca_strdup(p->relocs[i].oldPath);
538         /*@-branchstate@*/
539         relocations[i].oldPath = (t[0] == '/' && t[1] == '\0')
540             ? t
541             : stripTrailingChar(t, '/');
542         /*@=branchstate@*/
543
544         /* An old path w/o a new path is valid, and indicates exclusion */
545         if (p->relocs[i].newPath) {
546             int del;
547
548             t = alloca_strdup(p->relocs[i].newPath);
549             /*@-branchstate@*/
550             relocations[i].newPath = (t[0] == '/' && t[1] == '\0')
551                 ? t
552                 : stripTrailingChar(t, '/');
553             /*@=branchstate@*/
554
555             /*@-nullpass@*/     /* FIX:  relocations[i].oldPath == NULL */
556             /* Verify that the relocation's old path is in the header. */
557             for (j = 0; j < numValid; j++) {
558                 if (!strcmp(validRelocations[j], relocations[i].oldPath))
559                     /*@innerbreak@*/ break;
560             }
561
562             /* XXX actions check prevents problem from being appended twice. */
563             if (j == numValid && !allowBadRelocate && actions) {
564                 rpmps ps = rpmtsProblems(ts);
565                 rpmpsAppend(ps, RPMPROB_BADRELOCATE,
566                         rpmteNEVR(p), rpmteKey(p),
567                         relocations[i].oldPath, NULL, NULL, 0);
568                 ps = rpmpsFree(ps);
569             }
570             del =
571                 strlen(relocations[i].newPath) - strlen(relocations[i].oldPath);
572             /*@=nullpass@*/
573
574             if (del > reldel)
575                 reldel = del;
576         } else {
577             relocations[i].newPath = NULL;
578         }
579     }
580
581     /* stupid bubble sort, but it's probably faster here */
582     for (i = 0; i < numRelocations; i++) {
583         int madeSwap;
584         madeSwap = 0;
585         for (j = 1; j < numRelocations; j++) {
586             rpmRelocation tmpReloc;
587             if (relocations[j - 1].oldPath == NULL || /* XXX can't happen */
588                 relocations[j    ].oldPath == NULL || /* XXX can't happen */
589         strcmp(relocations[j - 1].oldPath, relocations[j].oldPath) <= 0)
590                 /*@innercontinue@*/ continue;
591             /*@-usereleased@*/ /* LCL: ??? */
592             tmpReloc = relocations[j - 1];
593             relocations[j - 1] = relocations[j];
594             relocations[j] = tmpReloc;
595             /*@=usereleased@*/
596             madeSwap = 1;
597         }
598         if (!madeSwap) break;
599     }
600
601     if (!_printed) {
602         _printed = 1;
603         rpmMessage(RPMMESS_DEBUG, _("========== relocations\n"));
604         for (i = 0; i < numRelocations; i++) {
605             if (relocations[i].oldPath == NULL) continue; /* XXX can't happen */
606             if (relocations[i].newPath == NULL)
607                 rpmMessage(RPMMESS_DEBUG, _("%5d exclude  %s\n"),
608                         i, relocations[i].oldPath);
609             else
610                 rpmMessage(RPMMESS_DEBUG, _("%5d relocate %s -> %s\n"),
611                         i, relocations[i].oldPath, relocations[i].newPath);
612         }
613     }
614
615     /* Add relocation values to the header */
616     if (numValid) {
617         const char ** actualRelocations;
618         int numActual;
619
620         actualRelocations = xmalloc(numValid * sizeof(*actualRelocations));
621         numActual = 0;
622         for (i = 0; i < numValid; i++) {
623             for (j = 0; j < numRelocations; j++) {
624                 if (relocations[j].oldPath == NULL || /* XXX can't happen */
625                     strcmp(validRelocations[i], relocations[j].oldPath))
626                     /*@innercontinue@*/ continue;
627                 /* On install, a relocate to NULL means skip the path. */
628                 if (relocations[j].newPath) {
629                     actualRelocations[numActual] = relocations[j].newPath;
630                     numActual++;
631                 }
632                 /*@innerbreak@*/ break;
633             }
634             if (j == numRelocations) {
635                 actualRelocations[numActual] = validRelocations[i];
636                 numActual++;
637             }
638         }
639
640         if (numActual)
641             xx = hae(h, RPMTAG_INSTPREFIXES, RPM_STRING_ARRAY_TYPE,
642                        (void **) actualRelocations, numActual);
643
644         actualRelocations = _free(actualRelocations);
645         validRelocations = hfd(validRelocations, validType);
646     }
647
648     xx = hge(h, RPMTAG_BASENAMES, NULL, (void **) &baseNames, &fileCount);
649     xx = hge(h, RPMTAG_DIRINDEXES, NULL, (void **) &dirIndexes, NULL);
650     xx = hge(h, RPMTAG_DIRNAMES, NULL, (void **) &dirNames, &dirCount);
651     xx = hge(h, RPMTAG_FILEFLAGS, NULL, (void **) &fFlags, NULL);
652     xx = hge(h, RPMTAG_FILEMODES, NULL, (void **) &fModes, NULL);
653
654     skipDirList = alloca(dirCount * sizeof(*skipDirList));
655     memset(skipDirList, 0, dirCount * sizeof(*skipDirList));
656
657     newDirIndexes = alloca(sizeof(*newDirIndexes) * fileCount);
658     memcpy(newDirIndexes, dirIndexes, sizeof(*newDirIndexes) * fileCount);
659     dirIndexes = newDirIndexes;
660
661     /*
662      * For all relocations, we go through sorted file/relocation lists 
663      * backwards so that /usr/local relocations take precedence over /usr 
664      * ones.
665      */
666
667     /* Relocate individual paths. */
668
669     for (i = fileCount - 1; i >= 0; i--) {
670         fileTypes ft;
671         int fnlen;
672
673         /*
674          * If only adding libraries of different arch into an already
675          * installed package, skip all other files.
676          */
677         if (rpmteMultiLib(p) && !isFileMULTILIB((fFlags[i]))) {
678             if (actions) {
679                 actions[i] = FA_SKIPMULTILIB;
680                 rpmMessage(RPMMESS_DEBUG, _("excluding multilib path %s%s\n"), 
681                         dirNames[dirIndexes[i]], baseNames[i]);
682             }
683             continue;
684         }
685
686         len = reldel +
687                 strlen(dirNames[dirIndexes[i]]) + strlen(baseNames[i]) + 1;
688         /*@-branchstate@*/
689         if (len >= fileAlloced) {
690             fileAlloced = len * 2;
691             fn = xrealloc(fn, fileAlloced);
692         }
693         /*@=branchstate@*/
694         *fn = '\0';
695         fnlen = stpcpy( stpcpy(fn, dirNames[dirIndexes[i]]), baseNames[i]) - fn;
696
697         /*
698          * See if this file path needs relocating.
699          */
700         /*
701          * XXX FIXME: Would a bsearch of the (already sorted) 
702          * relocation list be a good idea?
703          */
704         for (j = numRelocations - 1; j >= 0; j--) {
705             if (relocations[j].oldPath == NULL) /* XXX can't happen */
706                 /*@innercontinue@*/ continue;
707             len = strcmp(relocations[j].oldPath, "/")
708                 ? strlen(relocations[j].oldPath)
709                 : 0;
710
711             if (fnlen < len)
712                 /*@innercontinue@*/ continue;
713             /*
714              * Only subdirectories or complete file paths may be relocated. We
715              * don't check for '\0' as our directory names all end in '/'.
716              */
717             if (!(fn[len] == '/' || fnlen == len))
718                 /*@innercontinue@*/ continue;
719
720             if (strncmp(relocations[j].oldPath, fn, len))
721                 /*@innercontinue@*/ continue;
722             /*@innerbreak@*/ break;
723         }
724         if (j < 0) continue;
725
726         ft = whatis(fModes[i]);
727
728         /* On install, a relocate to NULL means skip the path. */
729         if (relocations[j].newPath == NULL) {
730             if (ft == XDIR) {
731                 /* Start with the parent, looking for directory to exclude. */
732                 for (j = dirIndexes[i]; j < dirCount; j++) {
733                     len = strlen(dirNames[j]) - 1;
734                     while (len > 0 && dirNames[j][len-1] == '/') len--;
735                     if (fnlen != len)
736                         /*@innercontinue@*/ continue;
737                     if (strncmp(fn, dirNames[j], fnlen))
738                         /*@innercontinue@*/ continue;
739                     /*@innerbreak@*/ break;
740                 }
741                 if (j < dirCount)
742                     skipDirList[j] = 1;
743             }
744             if (actions) {
745                 actions[i] = FA_SKIPNSTATE;
746                 rpmMessage(RPMMESS_DEBUG, _("excluding %s %s\n"),
747                         ftstring(ft), fn);
748             }
749             continue;
750         }
751
752         /* Relocation on full paths only, please. */
753         if (fnlen != len) continue;
754
755         if (actions)
756             rpmMessage(RPMMESS_DEBUG, _("relocating %s to %s\n"),
757                     fn, relocations[j].newPath);
758         nrelocated++;
759
760         strcpy(fn, relocations[j].newPath);
761         {   char * te = strrchr(fn, '/');
762             if (te) {
763                 if (te > fn) te++;      /* root is special */
764                 fnlen = te - fn;
765             } else
766                 te = fn + strlen(fn);
767             /*@-nullpass -nullderef@*/  /* LCL: te != NULL here. */
768             if (strcmp(baseNames[i], te)) /* basename changed too? */
769                 baseNames[i] = alloca_strdup(te);
770             *te = '\0';                 /* terminate new directory name */
771             /*@=nullpass =nullderef@*/
772         }
773
774         /* Does this directory already exist in the directory list? */
775         for (j = 0; j < dirCount; j++) {
776             if (fnlen != strlen(dirNames[j]))
777                 /*@innercontinue@*/ continue;
778             if (strncmp(fn, dirNames[j], fnlen))
779                 /*@innercontinue@*/ continue;
780             /*@innerbreak@*/ break;
781         }
782         
783         if (j < dirCount) {
784             dirIndexes[i] = j;
785             continue;
786         }
787
788         /* Creating new paths is a pita */
789         if (!haveRelocatedFile) {
790             const char ** newDirList;
791
792             haveRelocatedFile = 1;
793             newDirList = xmalloc((dirCount + 1) * sizeof(*newDirList));
794             for (j = 0; j < dirCount; j++)
795                 newDirList[j] = alloca_strdup(dirNames[j]);
796             dirNames = hfd(dirNames, RPM_STRING_ARRAY_TYPE);
797             dirNames = newDirList;
798         } else {
799             dirNames = xrealloc(dirNames, 
800                                sizeof(*dirNames) * (dirCount + 1));
801         }
802
803         dirNames[dirCount] = alloca_strdup(fn);
804         dirIndexes[i] = dirCount;
805         dirCount++;
806     }
807
808     /* Finish off by relocating directories. */
809     for (i = dirCount - 1; i >= 0; i--) {
810         for (j = numRelocations - 1; j >= 0; j--) {
811
812             if (relocations[j].oldPath == NULL) /* XXX can't happen */
813                 /*@innercontinue@*/ continue;
814             len = strcmp(relocations[j].oldPath, "/")
815                 ? strlen(relocations[j].oldPath)
816                 : 0;
817
818             if (len && strncmp(relocations[j].oldPath, dirNames[i], len))
819                 /*@innercontinue@*/ continue;
820
821             /*
822              * Only subdirectories or complete file paths may be relocated. We
823              * don't check for '\0' as our directory names all end in '/'.
824              */
825             if (dirNames[i][len] != '/')
826                 /*@innercontinue@*/ continue;
827
828             if (relocations[j].newPath) { /* Relocate the path */
829                 const char * s = relocations[j].newPath;
830                 char * t = alloca(strlen(s) + strlen(dirNames[i]) - len + 1);
831
832                 (void) stpcpy( stpcpy(t, s) , dirNames[i] + len);
833                 if (actions)
834                     rpmMessage(RPMMESS_DEBUG,
835                         _("relocating directory %s to %s\n"), dirNames[i], t);
836                 dirNames[i] = t;
837                 nrelocated++;
838             }
839         }
840     }
841
842     /* Save original filenames in header and replace (relocated) filenames. */
843     if (nrelocated) {
844         int c;
845         void * d;
846         rpmTagType t;
847
848         d = NULL;
849         xx = hge(h, RPMTAG_BASENAMES, &t, &d, &c);
850         xx = hae(h, RPMTAG_ORIGBASENAMES, t, d, c);
851         d = hfd(d, t);
852
853         d = NULL;
854         xx = hge(h, RPMTAG_DIRNAMES, &t, &d, &c);
855         xx = hae(h, RPMTAG_ORIGDIRNAMES, t, d, c);
856         d = hfd(d, t);
857
858         d = NULL;
859         xx = hge(h, RPMTAG_DIRINDEXES, &t, &d, &c);
860         xx = hae(h, RPMTAG_ORIGDIRINDEXES, t, d, c);
861         d = hfd(d, t);
862
863         xx = hme(h, RPMTAG_BASENAMES, RPM_STRING_ARRAY_TYPE,
864                           baseNames, fileCount);
865         fi->bnl = hfd(fi->bnl, RPM_STRING_ARRAY_TYPE);
866         xx = hge(h, RPMTAG_BASENAMES, NULL, (void **) &fi->bnl, &fi->fc);
867
868         xx = hme(h, RPMTAG_DIRNAMES, RPM_STRING_ARRAY_TYPE,
869                           dirNames, dirCount);
870         fi->dnl = hfd(fi->dnl, RPM_STRING_ARRAY_TYPE);
871         xx = hge(h, RPMTAG_DIRNAMES, NULL, (void **) &fi->dnl, &fi->dc);
872
873         xx = hme(h, RPMTAG_DIRINDEXES, RPM_INT32_TYPE,
874                           dirIndexes, fileCount);
875         xx = hge(h, RPMTAG_DIRINDEXES, NULL, (void **) &fi->dil, NULL);
876     }
877
878     baseNames = hfd(baseNames, RPM_STRING_ARRAY_TYPE);
879     dirNames = hfd(dirNames, RPM_STRING_ARRAY_TYPE);
880     fn = _free(fn);
881
882     return h;
883 }
884 /*@=bounds@*/
885
886 rpmfi rpmfiFree(rpmfi fi, int freefimem)
887 {
888     HFD_t hfd = headerFreeData;
889
890     if (fi == NULL) return NULL;
891
892     if (fi->nrefs > 1)
893         return rpmfiUnlink(fi, fi->Type);
894
895 /*@-modfilesystem@*/
896 if (_rpmfi_debug < 0)
897 fprintf(stderr, "*** fi %p\t%s[%d]\n", fi, fi->Type, fi->fc);
898 /*@=modfilesystem@*/
899
900     /*@-branchstate@*/
901     if (fi->fc > 0) {
902         fi->bnl = hfd(fi->bnl, -1);
903         fi->dnl = hfd(fi->dnl, -1);
904
905         fi->flinks = hfd(fi->flinks, -1);
906         fi->flangs = hfd(fi->flangs, -1);
907         fi->fmd5s = hfd(fi->fmd5s, -1);
908         fi->md5s = _free(fi->md5s);
909
910         fi->fuser = hfd(fi->fuser, -1);
911         fi->fuids = _free(fi->fuids);
912         fi->fgroup = hfd(fi->fgroup, -1);
913         fi->fgids = _free(fi->fgids);
914
915         fi->fstates = _free(fi->fstates);
916
917         /*@-evalorder@*/
918         if (!fi->keep_header && fi->h == NULL) {
919             fi->fmtimes = _free(fi->fmtimes);
920             fi->fmodes = _free(fi->fmodes);
921             fi->fflags = _free(fi->fflags);
922             fi->vflags = _free(fi->vflags);
923             fi->fsizes = _free(fi->fsizes);
924             fi->frdevs = _free(fi->frdevs);
925             fi->finodes = _free(fi->finodes);
926             fi->dil = _free(fi->dil);
927         }
928         /*@=evalorder@*/
929     }
930     /*@=branchstate@*/
931
932     fi->fsm = freeFSM(fi->fsm);
933
934     fi->fn = _free(fi->fn);
935     fi->apath = _free(fi->apath);
936     fi->fmapflags = _free(fi->fmapflags);
937
938     fi->obnl = hfd(fi->obnl, -1);
939     fi->odnl = hfd(fi->odnl, -1);
940
941     fi->actions = _free(fi->actions);
942     fi->replacedSizes = _free(fi->replacedSizes);
943     fi->replaced = _free(fi->replaced);
944
945     fi->h = headerFree(fi->h);
946
947     /*@-nullstate -refcounttrans -usereleased@*/
948     (void) rpmfiUnlink(fi, fi->Type);
949     /*@-branchstate@*/
950     if (freefimem) {
951         memset(fi, 0, sizeof(*fi));             /* XXX trash and burn */
952         fi = _free(fi);
953     }
954     /*@=branchstate@*/
955     /*@=nullstate =refcounttrans =usereleased@*/
956
957     return NULL;
958 }
959
960 /**
961  * Convert hex to binary nibble.
962  * @param c             hex character
963  * @return              binary nibble
964  */
965 static inline unsigned char nibble(char c)
966         /*@*/
967 {
968     if (c >= '0' && c <= '9')
969         return (c - '0');
970     if (c >= 'A' && c <= 'F')
971         return (c - 'A') + 10;
972     if (c >= 'a' && c <= 'f')
973         return (c - 'a') + 10;
974     return 0;
975 }
976
977 #define _fdupe(_fi, _data)      \
978     if ((_fi)->_data != NULL)   \
979         (_fi)->_data = memcpy(xmalloc((_fi)->fc * sizeof(*(_fi)->_data)), \
980                         (_fi)->_data, (_fi)->fc * sizeof(*(_fi)->_data))
981
982 rpmfi rpmfiNew(rpmts ts, rpmfi fi,
983                 Header h, rpmTag tagN, int scareMem)
984 {
985     HGE_t hge =
986         (scareMem ? (HGE_t) headerGetEntryMinMemory : (HGE_t) headerGetEntry);
987     HFD_t hfd = headerFreeData;
988     const char * Type;
989     uint_32 * uip;
990     int malloced = 0;
991     int dnlmax, bnlmax;
992     unsigned char * t;
993     int len;
994     int xx;
995     int i;
996
997     if (tagN == RPMTAG_BASENAMES) {
998         Type = "Files";
999     } else {
1000         Type = "?Type?";
1001         goto exit;
1002     }
1003
1004     /*@-branchstate@*/
1005     if (fi == NULL) {
1006         fi = xcalloc(1, sizeof(*fi));
1007         malloced = 0;   /* XXX always return with memory alloced. */
1008     }
1009     /*@=branchstate@*/
1010
1011     fi->magic = RPMFIMAGIC;
1012     fi->Type = Type;
1013     fi->i = -1;
1014     fi->tagN = tagN;
1015
1016     fi->hge = hge;
1017     fi->hae = (HAE_t) headerAddEntry;
1018     fi->hme = (HME_t) headerModifyEntry;
1019     fi->hre = (HRE_t) headerRemoveEntry;
1020     fi->hfd = headerFreeData;
1021
1022     fi->h = (scareMem ? headerLink(h) : NULL);
1023
1024     if (fi->fsm == NULL)
1025         fi->fsm = newFSM();
1026
1027     /* 0 means unknown */
1028     xx = hge(h, RPMTAG_ARCHIVESIZE, NULL, (void **) &uip, NULL);
1029     fi->archivePos = 0;
1030     fi->archiveSize = (xx ? *uip : 0);
1031
1032     if (!hge(h, RPMTAG_BASENAMES, NULL, (void **) &fi->bnl, &fi->fc)) {
1033         /*@-branchstate@*/
1034         if (malloced) {
1035             if (scareMem && fi->h)
1036                 fi->h = headerFree(fi->h);
1037             fi->fsm = freeFSM(fi->fsm);
1038             /*@-refcounttrans@*/
1039             fi = _free(fi);
1040             /*@=refcounttrans@*/
1041         } else {
1042             fi->fc = 0;
1043             fi->dc = 0;
1044         }
1045         /*@=branchstate@*/
1046         goto exit;
1047     }
1048     xx = hge(h, RPMTAG_DIRNAMES, NULL, (void **) &fi->dnl, &fi->dc);
1049     xx = hge(h, RPMTAG_DIRINDEXES, NULL, (void **) &fi->dil, NULL);
1050     xx = hge(h, RPMTAG_FILEMODES, NULL, (void **) &fi->fmodes, NULL);
1051     xx = hge(h, RPMTAG_FILEFLAGS, NULL, (void **) &fi->fflags, NULL);
1052     xx = hge(h, RPMTAG_FILEVERIFYFLAGS, NULL, (void **) &fi->vflags, NULL);
1053     xx = hge(h, RPMTAG_FILESIZES, NULL, (void **) &fi->fsizes, NULL);
1054     xx = hge(h, RPMTAG_FILESTATES, NULL, (void **) &fi->fstates, NULL);
1055     if (xx == 0 || fi->fstates == NULL)
1056         fi->fstates = xcalloc(fi->fc, sizeof(*fi->fstates));
1057     else
1058         _fdupe(fi, fstates);
1059
1060     fi->action = FA_UNKNOWN;
1061     fi->flags = 0;
1062
1063 if (fi->actions == NULL)
1064         fi->actions = xcalloc(fi->fc, sizeof(*fi->actions));
1065
1066     fi->keep_header = (scareMem ? 1 : 0);
1067
1068     /* XXX TR_REMOVED needs CPIO_MAP_{ABSOLUTE,ADDDOT} CPIO_ALL_HARDLINKS */
1069     fi->mapflags =
1070                 CPIO_MAP_PATH | CPIO_MAP_MODE | CPIO_MAP_UID | CPIO_MAP_GID;
1071
1072     xx = hge(h, RPMTAG_FILELINKTOS, NULL, (void **) &fi->flinks, NULL);
1073     xx = hge(h, RPMTAG_FILELANGS, NULL, (void **) &fi->flangs, NULL);
1074
1075     xx = hge(h, RPMTAG_FILEMD5S, NULL, (void **) &fi->fmd5s, NULL);
1076
1077     t = xmalloc(fi->fc * 16);
1078     fi->md5s = t;
1079     for (i = 0; i < fi->fc; i++) {
1080         const char * fmd5;
1081         int j;
1082
1083         fmd5 = fi->fmd5s[i];
1084         if (!(fmd5 && *fmd5 != '\0')) {
1085             memset(t, 0, 16);
1086             t += 16;
1087             continue;
1088         }
1089         for (j = 0; j < 16; j++, t++, fmd5 += 2)
1090             *t = (nibble(fmd5[0]) << 4) | nibble(fmd5[1]);
1091     }
1092     fi->fmd5s = hfd(fi->fmd5s, -1);
1093
1094     /* XXX TR_REMOVED doesn;t need fmtimes, frdevs or finodes */
1095     xx = hge(h, RPMTAG_FILEMTIMES, NULL, (void **) &fi->fmtimes, NULL);
1096     xx = hge(h, RPMTAG_FILERDEVS, NULL, (void **) &fi->frdevs, NULL);
1097     xx = hge(h, RPMTAG_FILEINODES, NULL, (void **) &fi->finodes, NULL);
1098
1099     fi->replacedSizes = xcalloc(fi->fc, sizeof(*fi->replacedSizes));
1100
1101     xx = hge(h, RPMTAG_FILEUSERNAME, NULL, (void **) &fi->fuser, NULL);
1102     fi->fuids = NULL;
1103     xx = hge(h, RPMTAG_FILEGROUPNAME, NULL, (void **) &fi->fgroup, NULL);
1104     fi->fgids = NULL;
1105
1106     if (ts != NULL)
1107     if (fi != NULL)
1108     if (fi->te != NULL && rpmteType(fi->te) == TR_ADDED) {
1109         Header foo;
1110 /* XXX DYING */
1111 if (fi->actions == NULL)
1112         fi->actions = xcalloc(fi->fc, sizeof(*fi->actions));
1113         /*@-compdef@*/ /* FIX: fi-md5s undefined */
1114         foo = relocateFileList(ts, fi, h, fi->actions);
1115         /*@=compdef@*/
1116         fi->h = headerFree(fi->h);
1117         fi->h = headerLink(foo);
1118         foo = headerFree(foo);
1119     }
1120
1121     if (!scareMem) {
1122         _fdupe(fi, fmtimes);
1123         _fdupe(fi, frdevs);
1124         _fdupe(fi, finodes);
1125         _fdupe(fi, fsizes);
1126         _fdupe(fi, fflags);
1127         _fdupe(fi, vflags);
1128         _fdupe(fi, fmodes);
1129         _fdupe(fi, dil);
1130         fi->h = headerFree(fi->h);
1131     }
1132
1133     dnlmax = -1;
1134     for (i = 0; i < fi->dc; i++) {
1135         if ((len = strlen(fi->dnl[i])) > dnlmax)
1136             dnlmax = len;
1137     }
1138     bnlmax = -1;
1139     for (i = 0; i < fi->fc; i++) {
1140         if ((len = strlen(fi->bnl[i])) > bnlmax)
1141             bnlmax = len;
1142     }
1143     fi->fnlen = dnlmax + bnlmax + 1;
1144     fi->fn = NULL;
1145
1146     fi->dperms = 0755;
1147     fi->fperms = 0644;
1148
1149 exit:
1150 /*@-modfilesystem@*/
1151 if (_rpmfi_debug < 0)
1152 fprintf(stderr, "*** fi %p\t%s[%d]\n", fi, Type, (fi ? fi->fc : 0));
1153 /*@=modfilesystem@*/
1154
1155     /*@-compdef -nullstate@*/ /* FIX: rpmfi null annotations */
1156     return rpmfiLink(fi, (fi ? fi->Type : NULL));
1157     /*@=compdef =nullstate@*/
1158 }