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