Avoid compressFilelist() stack overflow in pathological cases
[platform/upstream/rpm.git] / lib / legacy.c
1 /**
2  * \file lib/legacy.c
3  */
4
5 #include "system.h"
6
7 #include <rpm/rpmlib.h>
8 #include <rpm/rpmmacro.h>
9 #include <rpm/rpmstring.h>
10 #include <rpm/rpmfi.h>
11 #include <rpm/rpmds.h>
12 #include "lib/legacy.h"
13 #include "debug.h"
14
15 #define alloca_strdup(_s)       strcpy(alloca(strlen(_s)+1), (_s))
16
17 int _noDirTokens = 0;
18
19 static int dncmp(const void * a, const void * b)
20 {
21     const char *const * first = a;
22     const char *const * second = b;
23     return strcmp(*first, *second);
24 }
25
26 void compressFilelist(Header h)
27 {
28     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
29     HAE_t hae = (HAE_t)headerAddEntry;
30     HRE_t hre = (HRE_t)headerRemoveEntry;
31     HFD_t hfd = headerFreeData;
32     char ** fileNames;
33     const char ** dirNames;
34     const char ** baseNames;
35     uint32_t * dirIndexes;
36     rpm_tagtype_t fnt;
37     rpm_count_t count, i;
38     int xx;
39     int dirIndex = -1;
40
41     /*
42      * This assumes the file list is already sorted, and begins with a
43      * single '/'. That assumption isn't critical, but it makes things go
44      * a bit faster.
45      */
46
47     if (headerIsEntry(h, RPMTAG_DIRNAMES)) {
48         xx = hre(h, RPMTAG_OLDFILENAMES);
49         return;         /* Already converted. */
50     }
51
52     if (!hge(h, RPMTAG_OLDFILENAMES, &fnt, (rpm_data_t *) &fileNames, &count))
53         return;         /* no file list */
54     if (fileNames == NULL || count <= 0)
55         return;
56
57     dirNames = xmalloc(sizeof(*dirNames) * count);      /* worst case */
58     baseNames = xmalloc(sizeof(*dirNames) * count);
59     dirIndexes = xmalloc(sizeof(*dirIndexes) * count);
60
61     if (fileNames[0][0] != '/') {
62         /* HACK. Source RPM, so just do things differently */
63         dirIndex = 0;
64         dirNames[dirIndex] = "";
65         for (i = 0; i < count; i++) {
66             dirIndexes[i] = dirIndex;
67             baseNames[i] = fileNames[i];
68         }
69         goto exit;
70     }
71
72     for (i = 0; i < count; i++) {
73         const char ** needle;
74         char savechar;
75         char * baseName;
76         size_t len;
77
78         if (fileNames[i] == NULL)       /* XXX can't happen */
79             continue;
80         baseName = strrchr(fileNames[i], '/') + 1;
81         len = baseName - fileNames[i];
82         needle = dirNames;
83         savechar = *baseName;
84         *baseName = '\0';
85         if (dirIndex < 0 ||
86             (needle = bsearch(&fileNames[i], dirNames, dirIndex + 1, sizeof(dirNames[0]), dncmp)) == NULL) {
87             char *s = alloca(len + 1);
88             memcpy(s, fileNames[i], len + 1);
89             s[len] = '\0';
90             dirIndexes[i] = ++dirIndex;
91             dirNames[dirIndex] = s;
92         } else
93             dirIndexes[i] = needle - dirNames;
94
95         *baseName = savechar;
96         baseNames[i] = baseName;
97     }
98
99 exit:
100     if (count > 0) {
101         xx = hae(h, RPMTAG_DIRINDEXES, RPM_INT32_TYPE, dirIndexes, count);
102         xx = hae(h, RPMTAG_BASENAMES, RPM_STRING_ARRAY_TYPE,
103                         baseNames, count);
104         xx = hae(h, RPMTAG_DIRNAMES, RPM_STRING_ARRAY_TYPE,
105                         dirNames, (rpm_count_t) dirIndex + 1);
106     }
107
108     fileNames = hfd(fileNames, fnt);
109     free(dirNames);
110     free(baseNames);
111     free(dirIndexes);
112
113     xx = hre(h, RPMTAG_OLDFILENAMES);
114 }
115
116 void expandFilelist(Header h)
117 {
118     HAE_t hae = (HAE_t)headerAddEntry;
119     HRE_t hre = (HRE_t)headerRemoveEntry;
120     const char ** fileNames = NULL;
121     rpm_count_t count = 0;
122     int xx;
123
124     if (!headerIsEntry(h, RPMTAG_OLDFILENAMES)) {
125         rpmfiBuildFNames(h, RPMTAG_BASENAMES, &fileNames, &count);
126         if (fileNames == NULL || count <= 0)
127             return;
128         xx = hae(h, RPMTAG_OLDFILENAMES, RPM_STRING_ARRAY_TYPE,
129                         fileNames, count);
130         fileNames = _free(fileNames);
131     }
132
133     xx = hre(h, RPMTAG_DIRNAMES);
134     xx = hre(h, RPMTAG_BASENAMES);
135     xx = hre(h, RPMTAG_DIRINDEXES);
136 }
137
138 /*
139  * Up to rpm 3.0.4, packages implicitly provided their own name-version-release.
140  * Retrofit an explicit "Provides: name = epoch:version-release.
141  */
142 void providePackageNVR(Header h)
143 {
144     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
145     HFD_t hfd = headerFreeData;
146     const char *name, *version, *release;
147     int32_t * epoch;
148     const char *pEVR;
149     char *p;
150     int32_t pFlags = RPMSENSE_EQUAL;
151     const char ** provides = NULL;
152     const char ** providesEVR = NULL;
153     rpm_tagtype_t pnt, pvt;
154     int32_t * provideFlags = NULL;
155     rpm_count_t providesCount, i;
156     int xx;
157     int bingo = 1;
158
159     /* Generate provides for this package name-version-release. */
160     xx = headerNVR(h, &name, &version, &release);
161     if (!(name && version && release))
162         return;
163     pEVR = p = alloca(21 + strlen(version) + 1 + strlen(release) + 1);
164     *p = '\0';
165     if (hge(h, RPMTAG_EPOCH, NULL, (rpm_data_t *) &epoch, NULL)) {
166         sprintf(p, "%d:", *epoch);
167         while (*p != '\0')
168             p++;
169     }
170     (void) stpcpy( stpcpy( stpcpy(p, version) , "-") , release);
171
172     /*
173      * Rpm prior to 3.0.3 does not have versioned provides.
174      * If no provides at all are available, we can just add.
175      */
176     if (!hge(h, RPMTAG_PROVIDENAME, &pnt, (rpm_data_t *) &provides, &providesCount))
177         goto exit;
178
179     /*
180      * Otherwise, fill in entries on legacy packages.
181      */
182     if (!hge(h, RPMTAG_PROVIDEVERSION, &pvt, (rpm_data_t *) &providesEVR, NULL)) {
183         for (i = 0; i < providesCount; i++) {
184             const char * vdummy = "";
185             int32_t fdummy = RPMSENSE_ANY;
186             xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEVERSION, RPM_STRING_ARRAY_TYPE,
187                         &vdummy, 1);
188             xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEFLAGS, RPM_INT32_TYPE,
189                         &fdummy, 1);
190         }
191         goto exit;
192     }
193
194     xx = hge(h, RPMTAG_PROVIDEFLAGS, NULL, (rpm_data_t *) &provideFlags, NULL);
195
196         /* LCL: providesEVR is not NULL */
197     if (provides && providesEVR && provideFlags)
198     for (i = 0; i < providesCount; i++) {
199         if (!(provides[i] && providesEVR[i]))
200             continue;
201         if (!(provideFlags[i] == RPMSENSE_EQUAL &&
202             !strcmp(name, provides[i]) && !strcmp(pEVR, providesEVR[i])))
203             continue;
204         bingo = 0;
205         break;
206     }
207
208 exit:
209     provides = hfd(provides, pnt);
210     providesEVR = hfd(providesEVR, pvt);
211
212     if (bingo) {
213         xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDENAME, RPM_STRING_ARRAY_TYPE,
214                 &name, 1);
215         xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEFLAGS, RPM_INT32_TYPE,
216                 &pFlags, 1);
217         xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEVERSION, RPM_STRING_ARRAY_TYPE,
218                 &pEVR, 1);
219     }
220 }
221
222 void legacyRetrofit(Header h)
223 {
224     const char * prefix;
225
226     /*
227      * We don't use these entries (and rpm >= 2 never has) and they are
228      * pretty misleading. Let's just get rid of them so they don't confuse
229      * anyone.
230      */
231     if (headerIsEntry(h, RPMTAG_FILEUSERNAME))
232         (void) headerRemoveEntry(h, RPMTAG_FILEUIDS);
233     if (headerIsEntry(h, RPMTAG_FILEGROUPNAME))
234         (void) headerRemoveEntry(h, RPMTAG_FILEGIDS);
235
236     /*
237      * We switched the way we do relocatable packages. We fix some of
238      * it up here, though the install code still has to be a bit 
239      * careful. This fixup makes queries give the new values though,
240      * which is quite handy.
241      */
242     if (headerGetEntry(h, RPMTAG_DEFAULTPREFIX, NULL, (rpm_data_t *) &prefix, NULL))
243     {
244         const char * nprefix = stripTrailingChar(alloca_strdup(prefix), '/');
245         (void) headerAddEntry(h, RPMTAG_PREFIXES, RPM_STRING_ARRAY_TYPE,
246                 &nprefix, 1); 
247     }
248
249     /*
250      * The file list was moved to a more compressed format which not
251      * only saves memory (nice), but gives fingerprinting a nice, fat
252      * speed boost (very nice). Go ahead and convert old headers to
253      * the new style (this is a noop for new headers).
254      */
255      compressFilelist(h);
256
257     /* XXX binary rpms always have RPMTAG_SOURCERPM, source rpms do not */
258     if (headerIsSource(h)) {
259         int32_t one = 1;
260         if (!headerIsEntry(h, RPMTAG_SOURCEPACKAGE))
261             (void) headerAddEntry(h, RPMTAG_SOURCEPACKAGE, RPM_INT32_TYPE,
262                                 &one, 1);
263     } else {
264         /* Retrofit "Provide: name = EVR" for binary packages. */
265         providePackageNVR(h);
266     }
267 }