1 /** \ingroup rpmdb
2  * \file lib/headerutil.c
3  */
4 
5 #include "system.h"
6 
7 #include <rpm/rpmtypes.h>
8 #include <rpm/rpmlib.h>
9 #include <rpm/header.h>
10 #include <rpm/rpmstring.h>
11 #include <rpm/rpmds.h>
12 
13 #include "debug.h"
14 
headerIsSource(Header h)15 int headerIsSource(Header h)
16 {
17     return (!headerIsEntry(h, RPMTAG_SOURCERPM));
18 }
19 
headerCopy(Header h)20 Header headerCopy(Header h)
21 {
22     Header nh = headerNew();
23     HeaderIterator hi;
24     struct rpmtd_s td;
25 
26     hi = headerInitIterator(h);
27     while (headerNext(hi, &td)) {
28 	if (rpmtdCount(&td) > 0) {
29 	    (void) headerPut(nh, &td, HEADERPUT_DEFAULT);
30 	}
31 	rpmtdFreeData(&td);
32     }
33     headerFreeIterator(hi);
34 
35     return nh;
36 }
37 
headerCopyTags(Header headerFrom,Header headerTo,const rpmTagVal * tagstocopy)38 void headerCopyTags(Header headerFrom, Header headerTo,
39 		    const rpmTagVal * tagstocopy)
40 {
41     const rpmTagVal * p;
42     struct rpmtd_s td;
43 
44     if (headerFrom == headerTo)
45 	return;
46 
47     for (p = tagstocopy; *p != 0; p++) {
48 	if (headerIsEntry(headerTo, *p))
49 	    continue;
50 	if (!headerGet(headerFrom, *p, &td, (HEADERGET_MINMEM|HEADERGET_RAW)))
51 	    continue;
52 	(void) headerPut(headerTo, &td, HEADERPUT_DEFAULT);
53 	rpmtdFreeData(&td);
54     }
55 }
56 
headerGetAsString(Header h,rpmTagVal tag)57 char * headerGetAsString(Header h, rpmTagVal tag)
58 {
59     char *res = NULL;
60     struct rpmtd_s td;
61 
62     if (headerGet(h, tag, &td, HEADERGET_EXT)) {
63 	if (rpmtdCount(&td) == 1) {
64 	    res = rpmtdFormat(&td, RPMTD_FORMAT_STRING, NULL);
65 	}
66 	rpmtdFreeData(&td);
67     }
68     return res;
69 }
70 
headerGetString(Header h,rpmTagVal tag)71 const char * headerGetString(Header h, rpmTagVal tag)
72 {
73     const char *res = NULL;
74     struct rpmtd_s td;
75 
76     if (headerGet(h, tag, &td, HEADERGET_MINMEM)) {
77 	if (rpmtdCount(&td) == 1) {
78 	    res = rpmtdGetString(&td);
79 	}
80 	rpmtdFreeData(&td);
81     }
82     return res;
83 }
84 
headerGetNumber(Header h,rpmTagVal tag)85 uint64_t headerGetNumber(Header h, rpmTagVal tag)
86 {
87     uint64_t res = 0;
88     struct rpmtd_s td;
89 
90     if (headerGet(h, tag, &td, HEADERGET_EXT)) {
91 	if (rpmtdCount(&td) == 1) {
92 	    res = rpmtdGetNumber(&td);
93 	}
94 	rpmtdFreeData(&td);
95     }
96     return res;
97 }
98 
99 /*
100  * Sanity check data types against tag table before putting. Assume
101  * append on all array-types.
102  */
headerPutType(Header h,rpmTagVal tag,rpmTagType reqtype,rpm_constdata_t data,rpm_count_t size)103 static int headerPutType(Header h, rpmTagVal tag, rpmTagType reqtype,
104 			rpm_constdata_t data, rpm_count_t size)
105 {
106     struct rpmtd_s td;
107     rpmTagType type = rpmTagGetTagType(tag);
108     rpmTagReturnType retype = rpmTagGetReturnType(tag);
109     headerPutFlags flags = HEADERPUT_APPEND;
110     int valid = 1;
111 
112     /* Basic sanity checks: type must match and there must be data to put */
113     if (type != reqtype
114 	|| size < 1 || data == NULL || h == NULL) {
115 	valid = 0;
116     }
117 
118     /*
119      * Non-array types can't be appended to. Binary types use size
120      * for data length, for other non-array types size must be 1.
121      */
122     if (retype != RPM_ARRAY_RETURN_TYPE) {
123 	flags = HEADERPUT_DEFAULT;
124 	if (type != RPM_BIN_TYPE && size != 1) {
125 	    valid = 0;
126 	}
127     }
128 
129     if (valid) {
130 	rpmtdReset(&td);
131 	td.tag = tag;
132 	td.type = type;
133 	td.data = (void *) data;
134 	td.count = size;
135 
136 	valid = headerPut(h, &td, flags);
137     }
138 
139     return valid;
140 }
141 
headerPutString(Header h,rpmTagVal tag,const char * val)142 int headerPutString(Header h, rpmTagVal tag, const char *val)
143 {
144     rpmTagType type = rpmTagGetTagType(tag);
145     const void *sptr = NULL;
146 
147     /* string arrays expect char **, arrange that */
148     if (type == RPM_STRING_ARRAY_TYPE || type == RPM_I18NSTRING_TYPE) {
149 	sptr = &val;
150     } else if (type == RPM_STRING_TYPE) {
151 	sptr = val;
152     } else {
153 	return 0;
154     }
155 
156     return headerPutType(h, tag, type, sptr, 1);
157 }
158 
headerPutStringArray(Header h,rpmTagVal tag,const char ** array,rpm_count_t size)159 int headerPutStringArray(Header h, rpmTagVal tag, const char **array, rpm_count_t size)
160 {
161     return headerPutType(h, tag, RPM_STRING_ARRAY_TYPE, array, size);
162 }
163 
headerPutChar(Header h,rpmTagVal tag,const char * val,rpm_count_t size)164 int headerPutChar(Header h, rpmTagVal tag, const char *val, rpm_count_t size)
165 {
166     return headerPutType(h, tag, RPM_CHAR_TYPE, val, size);
167 }
168 
headerPutUint8(Header h,rpmTagVal tag,const uint8_t * val,rpm_count_t size)169 int headerPutUint8(Header h, rpmTagVal tag, const uint8_t *val, rpm_count_t size)
170 {
171     return headerPutType(h, tag, RPM_INT8_TYPE, val, size);
172 }
173 
headerPutUint16(Header h,rpmTagVal tag,const uint16_t * val,rpm_count_t size)174 int headerPutUint16(Header h, rpmTagVal tag, const uint16_t *val, rpm_count_t size)
175 {
176     return headerPutType(h, tag, RPM_INT16_TYPE, val, size);
177 }
178 
headerPutUint32(Header h,rpmTagVal tag,const uint32_t * val,rpm_count_t size)179 int headerPutUint32(Header h, rpmTagVal tag, const uint32_t *val, rpm_count_t size)
180 {
181     return headerPutType(h, tag, RPM_INT32_TYPE, val, size);
182 }
183 
headerPutUint64(Header h,rpmTagVal tag,const uint64_t * val,rpm_count_t size)184 int headerPutUint64(Header h, rpmTagVal tag, const uint64_t *val, rpm_count_t size)
185 {
186     return headerPutType(h, tag, RPM_INT64_TYPE, val, size);
187 }
188 
headerPutBin(Header h,rpmTagVal tag,const uint8_t * val,rpm_count_t size)189 int headerPutBin(Header h, rpmTagVal tag, const uint8_t *val, rpm_count_t size)
190 {
191     return headerPutType(h, tag, RPM_BIN_TYPE, val, size);
192 }
193 
dncmp(const void * a,const void * b)194 static int dncmp(const void * a, const void * b)
195 {
196     const char *const * first = a;
197     const char *const * second = b;
198     return strcmp(*first, *second);
199 }
200 
compressFilelist(Header h)201 static void compressFilelist(Header h)
202 {
203     struct rpmtd_s fileNames;
204     char ** dirNames;
205     const char ** baseNames;
206     uint32_t * dirIndexes;
207     rpm_count_t count, realCount = 0;
208     int i;
209     int dirIndex = -1;
210 
211     /*
212      * This assumes the file list is already sorted, and begins with a
213      * single '/'. That assumption isn't critical, but it makes things go
214      * a bit faster.
215      */
216 
217     if (headerIsEntry(h, RPMTAG_DIRNAMES)) {
218 	headerDel(h, RPMTAG_OLDFILENAMES);
219 	return;		/* Already converted. */
220     }
221 
222     if (!headerGet(h, RPMTAG_OLDFILENAMES, &fileNames, HEADERGET_MINMEM))
223 	return;
224     count = rpmtdCount(&fileNames);
225     if (count < 1)
226 	return;
227 
228     dirNames = xmalloc(sizeof(*dirNames) * count);	/* worst case */
229     baseNames = xmalloc(sizeof(*dirNames) * count);
230     dirIndexes = xmalloc(sizeof(*dirIndexes) * count);
231 
232     /* HACK. Source RPM, so just do things differently */
233     {	const char *fn = rpmtdGetString(&fileNames);
234 	if (fn && *fn != '/') {
235 	    dirIndex = 0;
236 	    dirNames[dirIndex] = xstrdup("");
237 	    while ((i = rpmtdNext(&fileNames)) >= 0) {
238 		dirIndexes[i] = dirIndex;
239 		baseNames[i] = rpmtdGetString(&fileNames);
240 		realCount++;
241 	    }
242 	    goto exit;
243 	}
244     }
245 
246     /*
247      * XXX EVIL HACK, FIXME:
248      * This modifies (and then restores) a const string from rpmtd
249      * through basename retrieved from strrchr() which silently
250      * casts away const on return.
251      */
252     while ((i = rpmtdNext(&fileNames)) >= 0) {
253 	char ** needle;
254 	char savechar;
255 	char * baseName;
256 	size_t len;
257 	char *filename = (char *) rpmtdGetString(&fileNames); /* HACK HACK */
258 
259 	if (filename == NULL)	/* XXX can't happen */
260 	    continue;
261 	baseName = strrchr(filename, '/');
262 	if (baseName == NULL) {
263 	    baseName = filename;
264 	} else {
265 	    baseName += 1;
266 	}
267 	len = baseName - filename;
268 	needle = dirNames;
269 	savechar = *baseName;
270 	*baseName = '\0';
271 	if (dirIndex < 0 ||
272 	    (needle = bsearch(&filename, dirNames, dirIndex + 1, sizeof(dirNames[0]), dncmp)) == NULL) {
273 	    char *s = xmalloc(len + 1);
274 	    rstrlcpy(s, filename, len + 1);
275 	    dirIndexes[realCount] = ++dirIndex;
276 	    dirNames[dirIndex] = s;
277 	} else
278 	    dirIndexes[realCount] = needle - dirNames;
279 
280 	*baseName = savechar;
281 	baseNames[realCount] = baseName;
282 	realCount++;
283     }
284 
285 exit:
286     if (count > 0) {
287 	headerPutUint32(h, RPMTAG_DIRINDEXES, dirIndexes, realCount);
288 	headerPutStringArray(h, RPMTAG_BASENAMES, baseNames, realCount);
289 	headerPutStringArray(h, RPMTAG_DIRNAMES,
290 			     (const char **) dirNames, dirIndex + 1);
291     }
292 
293     rpmtdFreeData(&fileNames);
294     for (i = 0; i <= dirIndex; i++) {
295 	free(dirNames[i]);
296     }
297     free(dirNames);
298     free(baseNames);
299     free(dirIndexes);
300 
301     headerDel(h, RPMTAG_OLDFILENAMES);
302 }
303 
expandFilelist(Header h)304 static void expandFilelist(Header h)
305 {
306     struct rpmtd_s filenames;
307 
308     if (!headerIsEntry(h, RPMTAG_OLDFILENAMES)) {
309 	(void) headerGet(h, RPMTAG_FILENAMES, &filenames, HEADERGET_EXT);
310 	if (rpmtdCount(&filenames) < 1)
311 	    return;
312 	rpmtdSetTag(&filenames, RPMTAG_OLDFILENAMES);
313 	headerPut(h, &filenames, HEADERPUT_DEFAULT);
314 	rpmtdFreeData(&filenames);
315     }
316 
317     (void) headerDel(h, RPMTAG_DIRNAMES);
318     (void) headerDel(h, RPMTAG_BASENAMES);
319     (void) headerDel(h, RPMTAG_DIRINDEXES);
320 }
321 
322 /*
323  * Up to rpm 3.0.4, packages implicitly provided their own name-version-release.
324  * Retrofit an explicit "Provides: name = epoch:version-release.
325  */
providePackageNVR(Header h)326 static void providePackageNVR(Header h)
327 {
328     const char *name = headerGetString(h, RPMTAG_NAME);
329     char *pEVR = headerGetAsString(h, RPMTAG_EVR);
330     rpmsenseFlags pFlags = RPMSENSE_EQUAL;
331     int bingo = 1;
332     struct rpmtd_s pnames;
333     rpmds hds, nvrds;
334 
335     /* Generate provides for this package name-version-release. */
336     if (!(name && pEVR)) {
337 	free(pEVR);
338 	return;
339     }
340 
341     /*
342      * Rpm prior to 3.0.3 does not have versioned provides.
343      * If no provides at all are available, we can just add.
344      */
345     if (!headerGet(h, RPMTAG_PROVIDENAME, &pnames, HEADERGET_MINMEM)) {
346 	goto exit;
347     }
348 
349     /*
350      * Otherwise, fill in entries on legacy packages.
351      */
352     if (!headerIsEntry(h, RPMTAG_PROVIDEVERSION)) {
353 	while (rpmtdNext(&pnames) >= 0) {
354 	    rpmsenseFlags fdummy = RPMSENSE_ANY;
355 
356 	    headerPutString(h, RPMTAG_PROVIDEVERSION, "");
357 	    headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &fdummy, 1);
358 	}
359 	goto exit;
360     }
361 
362     /* see if we already have this provide */
363     hds = rpmdsNew(h, RPMTAG_PROVIDENAME, 0);
364     nvrds = rpmdsSingle(RPMTAG_PROVIDENAME, name, pEVR, pFlags);
365     if (rpmdsFind(hds, nvrds) >= 0) {
366 	bingo = 0;
367     }
368     rpmdsFree(hds);
369     rpmdsFree(nvrds);
370 
371 
372 exit:
373     if (bingo) {
374 	headerPutString(h, RPMTAG_PROVIDENAME, name);
375 	headerPutString(h, RPMTAG_PROVIDEVERSION, pEVR);
376 	headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &pFlags, 1);
377     }
378     rpmtdFreeData(&pnames);
379     free(pEVR);
380 }
381 
legacyRetrofit(Header h)382 static void legacyRetrofit(Header h)
383 {
384     /*
385      * The file list was moved to a more compressed format which not
386      * only saves memory (nice), but gives fingerprinting a nice, fat
387      * speed boost (very nice). Go ahead and convert old headers to
388      * the new style (this is a noop for new headers).
389      */
390      compressFilelist(h);
391 
392     /* Retrofit "Provide: name = EVR" for binary packages. */
393     if (!headerIsSource(h)) {
394 	providePackageNVR(h);
395     }
396 }
397 
headerConvert(Header h,int op)398 int headerConvert(Header h, int op)
399 {
400     int rc = 1;
401 
402     if (h == NULL)
403 	return 0;
404 
405     switch (op) {
406     case HEADERCONV_EXPANDFILELIST:
407 	expandFilelist(h);
408 	break;
409     case HEADERCONV_COMPRESSFILELIST:
410 	compressFilelist(h);
411 	break;
412     case HEADERCONV_RETROFIT_V3:
413 	legacyRetrofit(h);
414 	break;
415     default:
416 	rc = 0;
417 	break;
418     }
419     return rc;
420 };
421 
rpmVersionCompare(Header first,Header second)422 int rpmVersionCompare(Header first, Header second)
423 {
424     /* Missing epoch becomes zero here, which is what we want */
425     uint32_t epochOne = headerGetNumber(first, RPMTAG_EPOCH);
426     uint32_t epochTwo = headerGetNumber(second, RPMTAG_EPOCH);
427     int rc;
428 
429     if (epochOne < epochTwo)
430 	return -1;
431     else if (epochOne > epochTwo)
432 	return 1;
433 
434     rc = rpmvercmp(headerGetString(first, RPMTAG_VERSION),
435 		   headerGetString(second, RPMTAG_VERSION));
436     if (rc)
437 	return rc;
438 
439     return rpmvercmp(headerGetString(first, RPMTAG_RELEASE),
440 		     headerGetString(second, RPMTAG_RELEASE));
441 }
442