1 /** \ingroup rpmbuild
2 * \file build/files.c
3 * The post-build, pre-packaging file tree walk to assemble the package
4 * manifest.
5 */
6
7 #include "system.h"
8
9 #define MYALLPERMS 07777
10
11 #include <errno.h>
12 #include <stdlib.h>
13 #include <regex.h>
14 #include <fcntl.h>
15 #if WITH_CAP
16 #include <sys/capability.h>
17 #endif
18
19 #if HAVE_LIBDW
20 #include <libelf.h>
21 #include <elfutils/libdwelf.h>
22 #endif
23
24 #include <rpm/rpmpgp.h>
25 #include <rpm/argv.h>
26 #include <rpm/rpmfc.h>
27 #include <rpm/rpmfileutil.h> /* rpmDoDigest() */
28 #include <rpm/rpmlog.h>
29 #include <rpm/rpmbase64.h>
30
31 #include "rpmio/rpmio_internal.h" /* XXX rpmioSlurp */
32 #include "misc/rpmfts.h"
33 #include "lib/rpmfi_internal.h" /* XXX fi->apath */
34 #include "lib/rpmug.h"
35 #include "build/rpmbuild_internal.h"
36 #include "build/rpmbuild_misc.h"
37
38 #include "debug.h"
39 #include <libgen.h>
40
41 #define SKIPSPACE(s) { while (*(s) && risspace(*(s))) (s)++; }
42 #define SKIPWHITE(_x) {while (*(_x) && (risspace(*_x) || *(_x) == ',')) (_x)++;}
43 #define SKIPNONWHITE(_x){while (*(_x) &&!(risspace(*_x) || *(_x) == ',')) (_x)++;}
44
45 /* the following defines must be in sync with the equally hardcoded paths from
46 * scripts/find-debuginfo.sh
47 */
48 #define BUILD_ID_DIR "/usr/lib/.build-id"
49 #define DEBUG_SRC_DIR "/usr/src/debug"
50 #define DEBUG_LIB_DIR "/usr/lib/debug"
51 #define DEBUG_LIB_PREFIX "/usr/lib/debug/"
52 #define DEBUG_ID_DIR "/usr/lib/debug/.build-id"
53 #define DEBUG_DWZ_DIR "/usr/lib/debug/.dwz"
54
55 #undef HASHTYPE
56 #undef HTKEYTYPE
57 #undef HTDATATYPE
58 #define HASHTYPE fileRenameHash
59 #define HTKEYTYPE const char *
60 #define HTDATATYPE const char *
61 #include "lib/rpmhash.C"
62 #undef HASHTYPE
63 #undef HTKEYTYPE
64 #undef HTDATATYPE
65
66 /**
67 */
68 enum specfFlags_e {
69 SPECD_DEFFILEMODE = (1 << 0),
70 SPECD_DEFDIRMODE = (1 << 1),
71 SPECD_DEFUID = (1 << 2),
72 SPECD_DEFGID = (1 << 3),
73 SPECD_DEFVERIFY = (1 << 4),
74
75 SPECD_FILEMODE = (1 << 8),
76 SPECD_DIRMODE = (1 << 9),
77 SPECD_UID = (1 << 10),
78 SPECD_GID = (1 << 11),
79 SPECD_VERIFY = (1 << 12)
80 };
81
82 typedef rpmFlags specfFlags;
83
84 /* internal %files parsing state attributes */
85 enum parseAttrs_e {
86 RPMFILE_EXCLUDE = (1 << 16), /*!< from %%exclude */
87 RPMFILE_DOCDIR = (1 << 17), /*!< from %%docdir */
88 RPMFILE_DIR = (1 << 18), /*!< from %%dir */
89 RPMFILE_SPECIALDIR = (1 << 19), /*!< from special %%doc */
90 };
91
92 /* bits up to 15 (for now) reserved for exported rpmfileAttrs */
93 #define PARSEATTR_MASK 0x0000ffff
94
95 /**
96 */
97 typedef struct FileListRec_s {
98 struct stat fl_st;
99 #define fl_dev fl_st.st_dev
100 #define fl_ino fl_st.st_ino
101 #define fl_mode fl_st.st_mode
102 #define fl_nlink fl_st.st_nlink
103 #define fl_uid fl_st.st_uid
104 #define fl_gid fl_st.st_gid
105 #define fl_rdev fl_st.st_rdev
106 #define fl_size fl_st.st_size
107 #define fl_mtime fl_st.st_mtime
108
109 char *diskPath; /* get file from here */
110 char *cpioPath; /* filename in cpio archive */
111 rpmsid uname;
112 rpmsid gname;
113 unsigned flags;
114 specfFlags specdFlags; /* which attributes have been explicitly specified. */
115 rpmVerifyFlags verifyFlags;
116 char *langs; /* XXX locales separated with | */
117 char *caps;
118 } * FileListRec;
119
120 /**
121 */
122 typedef struct AttrRec_s {
123 rpmsid ar_fmodestr;
124 rpmsid ar_dmodestr;
125 rpmsid ar_user;
126 rpmsid ar_group;
127 mode_t ar_fmode;
128 mode_t ar_dmode;
129 } * AttrRec;
130
131 /* list of files */
132 static StringBuf check_fileList = NULL;
133
134 typedef struct FileEntry_s {
135 rpmfileAttrs attrFlags;
136 specfFlags specdFlags;
137 rpmVerifyFlags verifyFlags;
138 struct AttrRec_s ar;
139
140 ARGV_t langs;
141 char *caps;
142
143 /* these are only ever relevant for current entry */
144 unsigned devtype;
145 unsigned devmajor;
146 int devminor;
147 int isDir;
148 } * FileEntry;
149
150 typedef struct specialDir_s {
151 char * dirname;
152 ARGV_t files;
153 struct AttrRec_s ar;
154 struct AttrRec_s def_ar;
155 rpmFlags sdtype;
156
157 int entriesCount;
158 int entriesAlloced;
159
160 struct {
161 struct FileEntry_s defEntry;
162 struct FileEntry_s curEntry;
163 } *entries;
164
165 } * specialDir;
166
167 typedef struct FileRecords_s {
168 FileListRec recs;
169 int alloced;
170 int used;
171 } * FileRecords;
172
173 /**
174 * Package file tree walk data.
175 */
176 typedef struct FileList_s {
177 /* global filelist state */
178 char * buildRoot;
179 size_t buildRootLen;
180 int processingFailed;
181 int haveCaps;
182 int largeFiles;
183 ARGV_t docDirs;
184 rpmBuildPkgFlags pkgFlags;
185 rpmstrPool pool;
186
187 /* actual file records */
188 struct FileRecords_s files;
189
190 /* active defaults */
191 struct FileEntry_s def;
192
193 /* current file-entry state */
194 struct FileEntry_s cur;
195 } * FileList;
196
nullAttrRec(AttrRec ar)197 static void nullAttrRec(AttrRec ar)
198 {
199 memset(ar, 0, sizeof(*ar));
200 }
201
dupAttrRec(const AttrRec oar,AttrRec nar)202 static void dupAttrRec(const AttrRec oar, AttrRec nar)
203 {
204 if (oar == nar)
205 return;
206 *nar = *oar; /* struct assignment */
207 }
208
209 /* Creates a default $defattr string. Can be used with argvAdd().
210 Caller owns the new string which needs to be freed when done. */
mkattr(void)211 static char *mkattr(void)
212 {
213 char *s = NULL;
214 rasprintf(&s, "%s(644,%s,%s,755)", "%defattr", UID_0_USER, GID_0_GROUP);
215 return s;
216 }
217
copyFileEntry(FileEntry src,FileEntry dest)218 static void copyFileEntry(FileEntry src, FileEntry dest)
219 {
220 /* Copying struct makes just shallow copy */
221 *dest = *src;
222
223 /* Do also deep copying */
224 if (src->langs != NULL) {
225 dest->langs = argvNew();
226 argvAppend(&dest->langs, src->langs);
227 }
228
229 if (src->caps != NULL) {
230 dest->caps = xstrdup(src->caps);
231 }
232 }
233
FileEntryFree(FileEntry entry)234 static void FileEntryFree(FileEntry entry)
235 {
236 argvFree(entry->langs);
237 memset(entry, 0, sizeof(*entry));
238 }
239
240 /**
241 * strtokWithQuotes.
242 * @param s
243 * @param delim
244 */
strtokWithQuotes(char * s,const char * delim)245 static char *strtokWithQuotes(char *s, const char *delim)
246 {
247 static char *olds = NULL;
248 char *token;
249
250 if (s == NULL)
251 s = olds;
252 if (s == NULL)
253 return NULL;
254
255 /* Skip leading delimiters */
256 s += strspn(s, delim);
257 if (*s == '\0')
258 return NULL;
259
260 /* Find the end of the token. */
261 token = s;
262 if (*token == '"') {
263 token++;
264 /* Find next " char */
265 s = strchr(token, '"');
266 } else {
267 s = strpbrk(token, delim);
268 }
269
270 /* Terminate it */
271 if (s == NULL) {
272 /* This token finishes the string */
273 olds = strchr(token, '\0');
274 } else {
275 /* Terminate the token and make olds point past it */
276 *s = '\0';
277 olds = s+1;
278 }
279
280 return token;
281 }
282
283 /**
284 */
285 typedef const struct VFA {
286 const char * attribute;
287 int flag;
288 } VFA_t;
289
290 /**
291 */
292 static VFA_t const verifyAttrs[] = {
293 { "md5", RPMVERIFY_FILEDIGEST },
294 { "filedigest", RPMVERIFY_FILEDIGEST },
295 { "size", RPMVERIFY_FILESIZE },
296 { "link", RPMVERIFY_LINKTO },
297 { "user", RPMVERIFY_USER },
298 { "owner", RPMVERIFY_USER },
299 { "group", RPMVERIFY_GROUP },
300 { "mtime", RPMVERIFY_MTIME },
301 { "mode", RPMVERIFY_MODE },
302 { "rdev", RPMVERIFY_RDEV },
303 { "caps", RPMVERIFY_CAPS },
304 { NULL, 0 }
305 };
306
vfaMatch(VFA_t * attrs,const char * token,rpmFlags * flags)307 static rpmFlags vfaMatch(VFA_t *attrs, const char *token, rpmFlags *flags)
308 {
309 VFA_t *vfa;
310
311 for (vfa = attrs; vfa->attribute != NULL; vfa++) {
312 if (rstreq(token, vfa->attribute)) {
313 *flags |= vfa->flag;
314 break;
315 }
316 }
317 return vfa->flag;
318 }
319
320 /**
321 * Parse %verify and %defverify from file manifest.
322 * @param buf current spec file line
323 * @param def parse for %defverify or %verify?
324 * @param entry file entry data (current or default)
325 * @return RPMRC_OK on success
326 */
parseForVerify(char * buf,int def,FileEntry entry)327 static rpmRC parseForVerify(char * buf, int def, FileEntry entry)
328 {
329 char *p, *pe, *q = NULL;
330 const char *name = def ? "%defverify" : "%verify";
331 int negated = 0;
332 rpmVerifyFlags verifyFlags = RPMVERIFY_NONE;
333 rpmRC rc = RPMRC_FAIL;
334
335 if ((p = strstr(buf, name)) == NULL)
336 return RPMRC_OK;
337
338 for (pe = p; (pe-p) < strlen(name); pe++)
339 *pe = ' ';
340
341 SKIPSPACE(pe);
342
343 if (*pe != '(') {
344 rpmlog(RPMLOG_ERR, _("Missing '(' in %s %s\n"), name, pe);
345 goto exit;
346 }
347
348 /* Bracket %*verify args */
349 *pe++ = ' ';
350 for (p = pe; *pe && *pe != ')'; pe++)
351 {};
352
353 if (*pe == '\0') {
354 rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
355 goto exit;
356 }
357
358 /* Localize. Erase parsed string */
359 q = xmalloc((pe-p) + 1);
360 rstrlcpy(q, p, (pe-p) + 1);
361 while (p <= pe)
362 *p++ = ' ';
363
364 for (p = q; *p != '\0'; p = pe) {
365 SKIPWHITE(p);
366 if (*p == '\0')
367 break;
368 pe = p;
369 SKIPNONWHITE(pe);
370 if (*pe != '\0')
371 *pe++ = '\0';
372
373 if (vfaMatch(verifyAttrs, p, &verifyFlags))
374 continue;
375
376 if (rstreq(p, "not")) {
377 negated ^= 1;
378 } else {
379 rpmlog(RPMLOG_ERR, _("Invalid %s token: %s\n"), name, p);
380 goto exit;
381 }
382 }
383
384 entry->verifyFlags = negated ? ~(verifyFlags) : verifyFlags;
385 entry->specdFlags |= SPECD_VERIFY;
386 rc = RPMRC_OK;
387
388 exit:
389 free(q);
390
391 return rc;
392 }
393
isAttrDefault(rpmstrPool pool,rpmsid arsid)394 static int isAttrDefault(rpmstrPool pool, rpmsid arsid)
395 {
396 const char *ars = rpmstrPoolStr(pool, arsid);
397 return (ars && ars[0] == '-' && ars[1] == '\0');
398 }
399
400 /**
401 * Parse %dev from file manifest.
402 * @param buf current spec file line
403 * @param cur current file entry data
404 * @return RPMRC_OK on success
405 */
parseForDev(char * buf,FileEntry cur)406 static rpmRC parseForDev(char * buf, FileEntry cur)
407 {
408 const char * name;
409 const char * errstr = NULL;
410 char *p, *pe, *q = NULL;
411 rpmRC rc = RPMRC_FAIL; /* assume error */
412 char *attr_parameters = NULL;
413
414 if ((p = strstr(buf, (name = "%dev"))) == NULL)
415 return RPMRC_OK;
416
417 for (pe = p; (pe-p) < strlen(name); pe++)
418 *pe = ' ';
419 SKIPSPACE(pe);
420
421 if (*pe != '(') {
422 errstr = "'('";
423 goto exit;
424 }
425
426 /* Bracket %dev args */
427 *pe++ = ' ';
428 for (p = pe; *pe && *pe != ')'; pe++)
429 {};
430 if (*pe != ')') {
431 errstr = "')'";
432 goto exit;
433 }
434
435 /* Localize. Erase parsed string */
436 q = xmalloc((pe-p) + 1);
437 rstrlcpy(q, p, (pe-p) + 1);
438
439 attr_parameters = xmalloc((pe-p) + 1);
440 rstrlcpy(attr_parameters, p, (pe-p) + 1);
441
442 while (p <= pe)
443 *p++ = ' ';
444
445 p = q; SKIPWHITE(p);
446 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
447 if (*p == 'b')
448 cur->devtype = 'b';
449 else if (*p == 'c')
450 cur->devtype = 'c';
451 else {
452 errstr = "devtype";
453 goto exit;
454 }
455
456 p = pe; SKIPWHITE(p);
457 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe = '\0';
458 for (pe = p; *pe && risdigit(*pe); pe++)
459 {} ;
460 if (*pe == '\0') {
461 cur->devmajor = atoi(p);
462 if (!(cur->devmajor >= 0 && cur->devmajor < 256)) {
463 errstr = "devmajor";
464 goto exit;
465 }
466 pe++;
467 } else {
468 errstr = "devmajor";
469 goto exit;
470 }
471
472 p = pe; SKIPWHITE(p);
473 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe = '\0';
474 for (pe = p; *pe && risdigit(*pe); pe++)
475 {} ;
476 if (*pe == '\0') {
477 cur->devminor = atoi(p);
478 if (!(cur->devminor >= 0 && cur->devminor < 256)) {
479 errstr = "devminor";
480 goto exit;
481 }
482 } else {
483 errstr = "devminor";
484 goto exit;
485 }
486
487 rc = RPMRC_OK;
488
489 exit:
490 if (rc) {
491 rpmlog(RPMLOG_ERR, _("Missing %s in %s(%s)\n"), errstr, name, attr_parameters);
492 }
493 free(attr_parameters);
494 free(q);
495 return rc;
496 }
497
498 /**
499 * Parse %attr and %defattr from file manifest.
500 * @param pool string pool
501 * @param buf current spec file line
502 * @param def parse for %defattr or %attr?
503 * @param entry file entry data (current / default)
504 * @return 0 on success
505 */
parseForAttr(rpmstrPool pool,char * buf,int def,FileEntry entry)506 static rpmRC parseForAttr(rpmstrPool pool, char * buf, int def, FileEntry entry)
507 {
508 const char *name = def ? "%defattr" : "%attr";
509 char *p, *pe, *q = NULL;
510 char *attr_parameters = NULL;
511 int x;
512 struct AttrRec_s arbuf;
513 AttrRec ar = &arbuf;
514 rpmRC rc = RPMRC_FAIL;
515
516 if ((p = strstr(buf, name)) == NULL)
517 return RPMRC_OK;
518
519 for (pe = p; (pe-p) < strlen(name); pe++)
520 *pe = ' ';
521
522 SKIPSPACE(pe);
523
524 if (*pe != '(') {
525 rpmlog(RPMLOG_ERR, _("Missing '(' in %s %s\n"), name, pe);
526 goto exit;
527 }
528
529 /* Bracket %*attr args */
530 *pe++ = ' ';
531 for (p = pe; *pe && *pe != ')'; pe++)
532 {};
533
534 if (def) { /* %defattr */
535 char *r = pe;
536 r++;
537 SKIPSPACE(r);
538 if (*r != '\0') {
539 rpmlog(RPMLOG_ERR,
540 _("Non-white space follows %s(): %s\n"), name, r);
541 goto exit;
542 }
543 }
544
545 /* Localize. Erase parsed string */
546 q = xmalloc((pe-p) + 1);
547 rstrlcpy(q, p, (pe-p) + 1);
548
549 attr_parameters = xmalloc((pe-p) + 1);
550 rstrlcpy(attr_parameters, p, (pe-p) + 1);
551
552 while (p <= pe)
553 *p++ = ' ';
554
555 nullAttrRec(ar);
556
557 p = q; SKIPWHITE(p);
558 if (*p != '\0') {
559 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
560 ar->ar_fmodestr = rpmstrPoolId(pool, p, 1);
561 p = pe; SKIPWHITE(p);
562 }
563 if (*p != '\0') {
564 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
565 ar->ar_user = rpmstrPoolId(pool, p, 1);
566 p = pe; SKIPWHITE(p);
567 }
568 if (*p != '\0') {
569 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
570 ar->ar_group = rpmstrPoolId(pool, p, 1);
571 p = pe; SKIPWHITE(p);
572 }
573 if (*p != '\0' && def) { /* %defattr */
574 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
575 ar->ar_dmodestr = rpmstrPoolId(pool, p, 1);
576 p = pe; SKIPWHITE(p);
577 }
578
579 if (!(ar->ar_fmodestr && ar->ar_user && ar->ar_group) || *p != '\0') {
580 rpmlog(RPMLOG_ERR, _("Bad syntax: %s(%s)\n"), name, attr_parameters);
581 goto exit;
582 }
583
584 /* Do a quick test on the mode argument and adjust for "-" */
585 if (ar->ar_fmodestr && !isAttrDefault(pool, ar->ar_fmodestr)) {
586 unsigned int ui;
587 x = sscanf(rpmstrPoolStr(pool, ar->ar_fmodestr), "%o", &ui);
588 if ((x == 0) || (ar->ar_fmode & ~MYALLPERMS)) {
589 rpmlog(RPMLOG_ERR, _("Bad mode spec: %s(%s)\n"), name, attr_parameters);
590 goto exit;
591 }
592 ar->ar_fmode = ui;
593 } else {
594 ar->ar_fmodestr = 0;
595 }
596
597 if (ar->ar_dmodestr && !isAttrDefault(pool, ar->ar_dmodestr)) {
598 unsigned int ui;
599 x = sscanf(rpmstrPoolStr(pool, ar->ar_dmodestr), "%o", &ui);
600 if ((x == 0) || (ar->ar_dmode & ~MYALLPERMS)) {
601 rpmlog(RPMLOG_ERR, _("Bad dirmode spec: %s(%s)\n"), name, attr_parameters);
602 goto exit;
603 }
604 ar->ar_dmode = ui;
605 } else {
606 ar->ar_dmodestr = 0;
607 }
608
609 if (!(ar->ar_user && !isAttrDefault(pool, ar->ar_user))) {
610 ar->ar_user = 0;
611 }
612
613 if (!(ar->ar_group && !isAttrDefault(pool, ar->ar_group))) {
614 ar->ar_group = 0;
615 }
616
617 dupAttrRec(ar, &(entry->ar));
618
619 /* XXX fix all this */
620 entry->specdFlags |= SPECD_UID | SPECD_GID | SPECD_FILEMODE | SPECD_DIRMODE;
621 rc = RPMRC_OK;
622
623 exit:
624 free(q);
625 free(attr_parameters);
626
627 return rc;
628 }
629
630 static VFA_t const configAttrs[] = {
631 { "missingok", RPMFILE_MISSINGOK },
632 { "noreplace", RPMFILE_NOREPLACE },
633 { NULL, 0 }
634 };
635
636 /**
637 * Parse %config from file manifest.
638 * @param buf current spec file line
639 * @param cur current file entry data
640 * @return RPMRC_OK on success
641 */
parseForConfig(char * buf,FileEntry cur)642 static rpmRC parseForConfig(char * buf, FileEntry cur)
643 {
644 char *p, *pe, *q = NULL;
645 const char *name;
646 rpmRC rc = RPMRC_FAIL;
647
648 if ((p = strstr(buf, (name = "%config"))) == NULL)
649 return RPMRC_OK;
650
651 cur->attrFlags |= RPMFILE_CONFIG;
652
653 /* Erase "%config" token. */
654 for (pe = p; (pe-p) < strlen(name); pe++)
655 *pe = ' ';
656 SKIPSPACE(pe);
657 if (*pe != '(')
658 return RPMRC_OK;
659
660 /* Bracket %config args */
661 *pe++ = ' ';
662 for (p = pe; *pe && *pe != ')'; pe++)
663 {};
664
665 if (*pe == '\0') {
666 rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
667 goto exit;
668 }
669
670 /* Localize. Erase parsed string. */
671 q = xmalloc((pe-p) + 1);
672 rstrlcpy(q, p, (pe-p) + 1);
673 while (p <= pe)
674 *p++ = ' ';
675
676 for (p = q; *p != '\0'; p = pe) {
677 SKIPWHITE(p);
678 if (*p == '\0')
679 break;
680 pe = p;
681 SKIPNONWHITE(pe);
682 if (*pe != '\0')
683 *pe++ = '\0';
684 if (!vfaMatch(configAttrs, p, &(cur->attrFlags))) {
685 rpmlog(RPMLOG_ERR, _("Invalid %s token: %s\n"), name, p);
686 goto exit;
687 }
688 }
689 rc = RPMRC_OK;
690
691 exit:
692 free(q);
693
694 return rc;
695 }
696
addLang(ARGV_t * av,const char * lang,size_t n,const char * ent)697 static rpmRC addLang(ARGV_t *av, const char *lang, size_t n, const char *ent)
698 {
699 rpmRC rc = RPMRC_FAIL;
700 char lbuf[n + 1];
701 rstrlcpy(lbuf, lang, sizeof(lbuf));
702 SKIPWHITE(ent);
703
704 /* Sanity check locale length */
705 if (n < 1 || (n == 1 && *lang != 'C') || n >= 32) {
706 rpmlog(RPMLOG_ERR, _("Unusual locale length: \"%s\" in %%lang(%s)\n"),
707 lbuf, ent);
708 goto exit;
709 }
710
711 /* Check for duplicate locales */
712 if (argvSearch(*av, lbuf, NULL)) {
713 rpmlog(RPMLOG_WARNING, _("Duplicate locale %s in %%lang(%s)\n"),
714 lbuf, ent);
715 } else {
716 argvAdd(av, lbuf);
717 argvSort(*av, NULL);
718 }
719 rc = RPMRC_OK;
720
721 exit:
722 return rc;
723 }
724
725 /**
726 * Parse %lang from file manifest.
727 * @param buf current spec file line
728 * @param cur current file entry data
729 * @return RPMRC_OK on success
730 */
parseForLang(char * buf,FileEntry cur)731 static rpmRC parseForLang(char * buf, FileEntry cur)
732 {
733 char *p, *pe, *q = NULL;
734 const char *name;
735 rpmRC rc = RPMRC_FAIL;
736
737 while ((p = strstr(buf, (name = "%lang"))) != NULL) {
738
739 for (pe = p; (pe-p) < strlen(name); pe++)
740 *pe = ' ';
741 SKIPSPACE(pe);
742
743 if (*pe != '(') {
744 rpmlog(RPMLOG_ERR, _("Missing '(' in %s %s\n"), name, pe);
745 goto exit;
746 }
747
748 /* Bracket %lang args */
749 *pe = ' ';
750 for (pe = p; *pe && *pe != ')'; pe++)
751 {};
752
753 if (*pe == '\0') {
754 rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
755 goto exit;
756 }
757
758 /* Localize. Erase parsed string. */
759 q = xmalloc((pe-p) + 1);
760 rstrlcpy(q, p, (pe-p) + 1);
761 while (p <= pe)
762 *p++ = ' ';
763
764 /* Parse multiple arguments from %lang */
765 for (p = q; *p != '\0'; p = pe) {
766 SKIPWHITE(p);
767 pe = p;
768 SKIPNONWHITE(pe);
769
770 if (addLang(&(cur->langs), p, (pe-p), q))
771 goto exit;
772
773 if (*pe == ',') pe++; /* skip , if present */
774 }
775 }
776
777 rc = RPMRC_OK;
778
779 exit:
780 free(q);
781
782 return rc;
783 }
784
785 /**
786 * Parse %caps from file manifest.
787 * @param buf current spec file line
788 * @param cur current file entry data
789 * @return RPMRC_OK on success
790 */
parseForCaps(char * buf,FileEntry cur)791 static rpmRC parseForCaps(char * buf, FileEntry cur)
792 {
793 char *p, *pe, *q = NULL;
794 const char *name;
795 rpmRC rc = RPMRC_FAIL;
796
797 if ((p = strstr(buf, (name = "%caps"))) == NULL)
798 return RPMRC_OK;
799
800 /* Erase "%caps" token. */
801 for (pe = p; (pe-p) < strlen(name); pe++)
802 *pe = ' ';
803 SKIPSPACE(pe);
804 if (*pe != '(')
805 return RPMRC_OK;
806
807 /* Bracket %caps args */
808 *pe++ = ' ';
809 for (p = pe; *pe && *pe != ')'; pe++)
810 {};
811
812 if (*pe == '\0') {
813 rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
814 goto exit;
815 }
816
817 /* Localize. Erase parsed string. */
818 q = xmalloc((pe-p) + 1);
819 rstrlcpy(q, p, (pe-p) + 1);
820 while (p <= pe)
821 *p++ = ' ';
822
823 #if WITH_CAP
824 {
825 char *captxt = NULL;
826 cap_t fcaps = cap_from_text(q);
827 if (fcaps == NULL) {
828 rpmlog(RPMLOG_ERR, _("Invalid capability: %s\n"), q);
829 goto exit;
830 }
831 /* run our string through cap_to_text() to get libcap presentation */
832 captxt = cap_to_text(fcaps, NULL);
833 cur->caps = xstrdup(captxt);
834 cap_free(captxt);
835 cap_free(fcaps);
836 }
837 #else
838 rpmlog(RPMLOG_ERR, _("File capability support not built in\n"));
839 goto exit;
840 #endif
841
842 rc = RPMRC_OK;
843
844 exit:
845 free(q);
846
847 return rc;
848 }
849 /**
850 */
851 static VFA_t const virtualAttrs[] = {
852 { "%dir", RPMFILE_DIR },
853 { "%docdir", RPMFILE_DOCDIR },
854 { "%doc", RPMFILE_DOC },
855 { "%ghost", RPMFILE_GHOST },
856 { "%exclude", RPMFILE_EXCLUDE },
857 { "%readme", RPMFILE_README },
858 { "%license", RPMFILE_LICENSE },
859 { "%pubkey", RPMFILE_PUBKEY },
860 { "%missingok", RPMFILE_MISSINGOK },
861 { "%artifact", RPMFILE_ARTIFACT },
862 { NULL, 0 }
863 };
864
865 /**
866 * Parse simple attributes (e.g. %dir) from file manifest.
867 * @param buf current spec file line
868 * @param cur current file entry data
869 * @retval *fileNames file names
870 * @return RPMRC_OK on success
871 */
parseForSimple(char * buf,FileEntry cur,ARGV_t * fileNames)872 static rpmRC parseForSimple(char * buf, FileEntry cur, ARGV_t * fileNames)
873 {
874 char *s, *t;
875 rpmRC res = RPMRC_OK;
876 int allow_relative = (RPMFILE_PUBKEY|RPMFILE_DOC|RPMFILE_LICENSE);
877
878 t = buf;
879 while ((s = strtokWithQuotes(t, " \t\n")) != NULL) {
880 t = NULL;
881
882 /* Set flags for virtual file attributes */
883 if (vfaMatch(virtualAttrs, s, &(cur->attrFlags)))
884 continue;
885
886 /* normally paths need to be absolute */
887 if (*s != '/') {
888 if (!(cur->attrFlags & allow_relative)) {
889 rpmlog(RPMLOG_ERR, _("File must begin with \"/\": %s\n"), s);
890 res = RPMRC_FAIL;
891 continue;
892 }
893 /* non-absolute %doc and %license paths are special */
894 if (cur->attrFlags & (RPMFILE_DOC | RPMFILE_LICENSE))
895 cur->attrFlags |= RPMFILE_SPECIALDIR;
896 }
897 argvAdd(fileNames, s);
898 }
899
900 return res;
901 }
902
903 /**
904 */
compareFileListRecs(const void * ap,const void * bp)905 static int compareFileListRecs(const void * ap, const void * bp)
906 {
907 const char *a = ((FileListRec)ap)->cpioPath;
908 const char *b = ((FileListRec)bp)->cpioPath;
909 return strcmp(a, b);
910 }
911
912 /**
913 * Test if file is located in a %docdir.
914 * @param docDirs doc dirs
915 * @param fileName file path
916 * @return 1 if doc file, 0 if not
917 */
isDoc(ARGV_const_t docDirs,const char * fileName)918 static int isDoc(ARGV_const_t docDirs, const char * fileName)
919 {
920 size_t k, l;
921
922 k = strlen(fileName);
923 for (ARGV_const_t dd = docDirs; *dd; dd++) {
924 l = strlen(*dd);
925 if (l < k && rstreqn(fileName, *dd, l) && fileName[l] == '/')
926 return 1;
927 }
928 return 0;
929 }
930
isLinkable(mode_t mode)931 static int isLinkable(mode_t mode)
932 {
933 return (S_ISREG(mode) || S_ISLNK(mode));
934 }
935
isHardLink(FileListRec flp,FileListRec tlp)936 static int isHardLink(FileListRec flp, FileListRec tlp)
937 {
938 return ((isLinkable(flp->fl_mode) && isLinkable(tlp->fl_mode)) &&
939 ((flp->fl_nlink > 1) && (flp->fl_nlink == tlp->fl_nlink)) &&
940 (flp->fl_ino == tlp->fl_ino) &&
941 (flp->fl_dev == tlp->fl_dev));
942 }
943
944 /**
945 * Verify that file attributes scope over hardlinks correctly.
946 * If partial hardlink sets are possible, then add tracking dependency.
947 * @param files package file records
948 * @return 1 if partial hardlink sets can exist, 0 otherwise.
949 */
checkHardLinks(FileRecords files)950 static int checkHardLinks(FileRecords files)
951 {
952 FileListRec ilp, jlp;
953 int i, j;
954
955 for (i = 0; i < files->used; i++) {
956 ilp = files->recs + i;
957 if (!(isLinkable(ilp->fl_mode) && ilp->fl_nlink > 1))
958 continue;
959
960 for (j = i + 1; j < files->used; j++) {
961 jlp = files->recs + j;
962 if (isHardLink(ilp, jlp)) {
963 return 1;
964 }
965 }
966 }
967 return 0;
968 }
969
seenHardLink(FileRecords files,FileListRec flp,rpm_ino_t * fileid)970 static int seenHardLink(FileRecords files, FileListRec flp, rpm_ino_t *fileid)
971 {
972 for (FileListRec ilp = files->recs; ilp < flp; ilp++) {
973 if (isHardLink(flp, ilp)) {
974 *fileid = ilp - files->recs;
975 return 1;
976 }
977 }
978 return 0;
979 }
980
981 /**
982 * Add file entries to header.
983 * @todo Should directories have %doc/%config attributes? (#14531)
984 * @todo Remove RPMTAG_OLDFILENAMES, add dirname/basename instead.
985 * @param fl package file tree walk data
986 * @param pkg (sub) package
987 * @param isSrc pass 1 for source packages 0 otherwise
988 */
genCpioListAndHeader(FileList fl,Package pkg,int isSrc)989 static void genCpioListAndHeader(FileList fl, Package pkg, int isSrc)
990 {
991 FileListRec flp;
992 char buf[BUFSIZ];
993 int i, npaths = 0;
994 uint32_t defaultalgo = PGPHASHALGO_MD5, digestalgo;
995 rpm_loff_t totalFileSize = 0;
996 Header h = pkg->header; /* just a shortcut */
997 time_t source_date_epoch = 0;
998 char *srcdate = getenv("SOURCE_DATE_EPOCH");
999
1000 /* Limit the maximum date to SOURCE_DATE_EPOCH if defined
1001 * similar to the tar --clamp-mtime option
1002 * https://reproducible-builds.org/specs/source-date-epoch/
1003 */
1004 if (srcdate && rpmExpandNumeric("%{?clamp_mtime_to_source_date_epoch}")) {
1005 char *endptr;
1006 errno = 0;
1007 source_date_epoch = strtol(srcdate, &endptr, 10);
1008 if (srcdate == endptr || *endptr || errno != 0) {
1009 rpmlog(RPMLOG_ERR, _("unable to parse %s=%s\n"), "SOURCE_DATE_EPOCH", srcdate);
1010 fl->processingFailed = 1;
1011 }
1012 }
1013
1014 /*
1015 * See if non-md5 file digest algorithm is requested. If not
1016 * specified, quietly assume md5. Otherwise check if supported type.
1017 */
1018 digestalgo = rpmExpandNumeric(isSrc ? "%{_source_filedigest_algorithm}" :
1019 "%{_binary_filedigest_algorithm}");
1020 if (digestalgo == 0) {
1021 digestalgo = defaultalgo;
1022 }
1023
1024 if (rpmDigestLength(digestalgo) == 0) {
1025 rpmlog(RPMLOG_WARNING,
1026 _("Unknown file digest algorithm %u, falling back to MD5\n"),
1027 digestalgo);
1028 digestalgo = defaultalgo;
1029 }
1030
1031 /* Adjust paths if needed */
1032 if (!isSrc && pkg->removePostfixes) {
1033 pkg->fileRenameMap = fileRenameHashCreate(fl->files.used,
1034 rstrhash, strcmp,
1035 (fileRenameHashFreeKey)rfree, (fileRenameHashFreeData)rfree);
1036 for (i = 0, flp = fl->files.recs; i < fl->files.used; i++, flp++) {
1037 char * cpiopath = flp->cpioPath;
1038 char * cpiopath_orig = xstrdup(cpiopath);
1039
1040 for (ARGV_const_t postfix_p = pkg->removePostfixes; *postfix_p; postfix_p++) {
1041 int len = strlen(*postfix_p);
1042 int plen = strlen(cpiopath);
1043 if (len <= plen && !strncmp(cpiopath+plen-len, *postfix_p, len)) {
1044 cpiopath[plen-len] = '\0';
1045 if (plen-len > 0 && cpiopath[plen-len-1] == '/') {
1046 cpiopath[plen-len-1] = '\0';
1047 }
1048 }
1049 }
1050 if (strcmp(cpiopath_orig, cpiopath))
1051 fileRenameHashAddEntry(pkg->fileRenameMap, xstrdup(cpiopath), cpiopath_orig);
1052 else
1053 _free(cpiopath_orig);
1054 }
1055 }
1056
1057 /* Sort the big list */
1058 if (fl->files.recs) {
1059 qsort(fl->files.recs, fl->files.used,
1060 sizeof(*(fl->files.recs)), compareFileListRecs);
1061 }
1062
1063 pkg->dpaths = xmalloc((fl->files.used + 1) * sizeof(*pkg->dpaths));
1064
1065 /* Generate the header. */
1066 for (i = 0, flp = fl->files.recs; i < fl->files.used; i++, flp++) {
1067 rpm_ino_t fileid = flp - fl->files.recs;
1068
1069 /* Merge duplicate entries. */
1070 while (i < (fl->files.used - 1) &&
1071 rstreq(flp->cpioPath, flp[1].cpioPath)) {
1072
1073 /* Two entries for the same file found, merge the entries. */
1074 /* Note that an %exclude is a duplication of a file reference */
1075
1076 /* file flags */
1077 flp[1].flags |= flp->flags;
1078
1079 if (!(flp[1].flags & RPMFILE_EXCLUDE))
1080 rpmlog(RPMLOG_WARNING, _("File listed twice: %s\n"),
1081 flp->cpioPath);
1082
1083 /* file mode */
1084 if (S_ISDIR(flp->fl_mode)) {
1085 if ((flp[1].specdFlags & (SPECD_DIRMODE | SPECD_DEFDIRMODE)) <
1086 (flp->specdFlags & (SPECD_DIRMODE | SPECD_DEFDIRMODE)))
1087 flp[1].fl_mode = flp->fl_mode;
1088 } else {
1089 if ((flp[1].specdFlags & (SPECD_FILEMODE | SPECD_DEFFILEMODE)) <
1090 (flp->specdFlags & (SPECD_FILEMODE | SPECD_DEFFILEMODE)))
1091 flp[1].fl_mode = flp->fl_mode;
1092 }
1093
1094 /* uid */
1095 if ((flp[1].specdFlags & (SPECD_UID | SPECD_DEFUID)) <
1096 (flp->specdFlags & (SPECD_UID | SPECD_DEFUID)))
1097 {
1098 flp[1].fl_uid = flp->fl_uid;
1099 flp[1].uname = flp->uname;
1100 }
1101
1102 /* gid */
1103 if ((flp[1].specdFlags & (SPECD_GID | SPECD_DEFGID)) <
1104 (flp->specdFlags & (SPECD_GID | SPECD_DEFGID)))
1105 {
1106 flp[1].fl_gid = flp->fl_gid;
1107 flp[1].gname = flp->gname;
1108 }
1109
1110 /* verify flags */
1111 if ((flp[1].specdFlags & (SPECD_VERIFY | SPECD_DEFVERIFY)) <
1112 (flp->specdFlags & (SPECD_VERIFY | SPECD_DEFVERIFY)))
1113 flp[1].verifyFlags = flp->verifyFlags;
1114
1115 /* XXX to-do: language */
1116
1117 flp++; i++;
1118 }
1119
1120 /* Skip files that were marked with %exclude. */
1121 if (flp->flags & RPMFILE_EXCLUDE)
1122 {
1123 argvAdd(&pkg->fileExcludeList, flp->cpioPath);
1124 continue;
1125 }
1126
1127 /* Collect on-disk paths for archive creation */
1128 pkg->dpaths[npaths++] = xstrdup(flp->diskPath);
1129
1130 headerPutString(h, RPMTAG_OLDFILENAMES, flp->cpioPath);
1131 headerPutString(h, RPMTAG_FILEUSERNAME,
1132 rpmstrPoolStr(fl->pool, flp->uname));
1133 headerPutString(h, RPMTAG_FILEGROUPNAME,
1134 rpmstrPoolStr(fl->pool, flp->gname));
1135
1136 /* Only use 64bit filesizes tag if required. */
1137 if (fl->largeFiles) {
1138 rpm_loff_t rsize64 = (rpm_loff_t)flp->fl_size;
1139 headerPutUint64(h, RPMTAG_LONGFILESIZES, &rsize64, 1);
1140 (void) rpmlibNeedsFeature(pkg, "LargeFiles", "4.12.0-1");
1141 } else {
1142 rpm_off_t rsize32 = (rpm_off_t)flp->fl_size;
1143 headerPutUint32(h, RPMTAG_FILESIZES, &rsize32, 1);
1144 }
1145 /* Excludes and dupes have been filtered out by now. */
1146 if (isLinkable(flp->fl_mode)) {
1147 if (flp->fl_nlink == 1 || !seenHardLink(&fl->files, flp, &fileid)) {
1148 totalFileSize += flp->fl_size;
1149 }
1150 }
1151
1152 if (source_date_epoch && flp->fl_mtime > source_date_epoch) {
1153 flp->fl_mtime = source_date_epoch;
1154 }
1155 /*
1156 * For items whose size varies between systems, always explicitly
1157 * cast to the header type before inserting.
1158 * TODO: check and warn if header type overflows for each case.
1159 */
1160 { rpm_time_t rtime = (rpm_time_t) flp->fl_mtime;
1161 headerPutUint32(h, RPMTAG_FILEMTIMES, &rtime, 1);
1162 }
1163
1164 { rpm_mode_t rmode = (rpm_mode_t) flp->fl_mode;
1165 headerPutUint16(h, RPMTAG_FILEMODES, &rmode, 1);
1166 }
1167
1168 { rpm_rdev_t rrdev = (rpm_rdev_t) flp->fl_rdev;
1169 headerPutUint16(h, RPMTAG_FILERDEVS, &rrdev, 1);
1170 }
1171
1172 /*
1173 * To allow rpmbuild to work on filesystems with 64bit inodes numbers,
1174 * remap them into 32bit integers based on filelist index, just
1175 * preserving semantics for determining hardlinks.
1176 * Start at 1 as inode zero as that could be considered as an error.
1177 * Since we flatten all the inodes to appear within a single fs,
1178 * we also need to flatten the devices.
1179 */
1180 { rpm_ino_t rino = fileid + 1;
1181 rpm_dev_t rdev = flp->fl_dev ? 1 : 0;
1182 headerPutUint32(h, RPMTAG_FILEINODES, &rino, 1);
1183 headerPutUint32(h, RPMTAG_FILEDEVICES, &rdev, 1);
1184 }
1185
1186 headerPutString(h, RPMTAG_FILELANGS, flp->langs);
1187
1188 if (fl->haveCaps) {
1189 headerPutString(h, RPMTAG_FILECAPS, flp->caps);
1190 }
1191
1192 buf[0] = '\0';
1193 if (S_ISREG(flp->fl_mode) && !(flp->flags & RPMFILE_GHOST))
1194 (void) rpmDoDigest(digestalgo, flp->diskPath, 1,
1195 (unsigned char *)buf);
1196 headerPutString(h, RPMTAG_FILEDIGESTS, buf);
1197
1198 buf[0] = '\0';
1199 if (S_ISLNK(flp->fl_mode)) {
1200 ssize_t llen = readlink(flp->diskPath, buf, BUFSIZ-1);
1201 if (llen == -1) {
1202 rpmlog(RPMLOG_ERR, _("reading symlink %s failed: %s\n"),
1203 flp->diskPath, strerror(errno));
1204 fl->processingFailed = 1;
1205 } else {
1206 buf[llen] = '\0';
1207 if (buf[0] == '/') {
1208 rpmlog(RPMLOG_WARNING, _("absolute symlink: %s -> %s\n"),
1209 flp->cpioPath, buf);
1210 }
1211 if (buf[0] == '/' && !rstreq(fl->buildRoot, "/") &&
1212 rstreqn(buf, fl->buildRoot, fl->buildRootLen)) {
1213 rpmlog(RPMLOG_ERR,
1214 _("Symlink points to BuildRoot: %s -> %s\n"),
1215 flp->cpioPath, buf);
1216 fl->processingFailed = 1;
1217 }
1218 }
1219 }
1220 headerPutString(h, RPMTAG_FILELINKTOS, buf);
1221
1222 if (flp->flags & RPMFILE_GHOST) {
1223 flp->verifyFlags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE |
1224 RPMVERIFY_LINKTO | RPMVERIFY_MTIME);
1225 }
1226 headerPutUint32(h, RPMTAG_FILEVERIFYFLAGS, &(flp->verifyFlags),1);
1227
1228 if (!isSrc && isDoc(fl->docDirs, flp->cpioPath))
1229 flp->flags |= RPMFILE_DOC;
1230 /* XXX Should directories have %doc/%config attributes? (#14531) */
1231 if (S_ISDIR(flp->fl_mode))
1232 flp->flags &= ~(RPMFILE_CONFIG|RPMFILE_DOC|RPMFILE_LICENSE);
1233 /* Strip internal parse data */
1234 flp->flags &= PARSEATTR_MASK;
1235
1236 headerPutUint32(h, RPMTAG_FILEFLAGS, &(flp->flags) ,1);
1237 }
1238 pkg->dpaths[npaths] = NULL;
1239
1240 if (totalFileSize < UINT32_MAX) {
1241 rpm_off_t totalsize = totalFileSize;
1242 headerPutUint32(h, RPMTAG_SIZE, &totalsize, 1);
1243 } else {
1244 rpm_loff_t totalsize = totalFileSize;
1245 headerPutUint64(h, RPMTAG_LONGSIZE, &totalsize, 1);
1246 }
1247
1248 if (digestalgo != defaultalgo) {
1249 headerPutUint32(h, RPMTAG_FILEDIGESTALGO, &digestalgo, 1);
1250 rpmlibNeedsFeature(pkg, "FileDigests", "4.6.0-1");
1251 }
1252
1253 if (fl->haveCaps) {
1254 rpmlibNeedsFeature(pkg, "FileCaps", "4.6.1-1");
1255 }
1256
1257 if (!isSrc && !rpmExpandNumeric("%{_noPayloadPrefix}"))
1258 (void) rpmlibNeedsFeature(pkg, "PayloadFilesHavePrefix", "4.0-1");
1259
1260 /* rpmfiNew() only groks compressed filelists */
1261 headerConvert(h, HEADERCONV_COMPRESSFILELIST);
1262 pkg->cpioList = rpmfilesNew(NULL, h, RPMTAG_BASENAMES,
1263 (RPMFI_NOFILEUSER|RPMFI_NOFILEGROUP));
1264
1265 if (pkg->cpioList == NULL || rpmfilesFC(pkg->cpioList) != npaths) {
1266 fl->processingFailed = 1;
1267 }
1268
1269 if (fl->pkgFlags & RPMBUILD_PKG_NODIRTOKENS) {
1270 /* Uncompress filelist if legacy format requested */
1271 headerConvert(h, HEADERCONV_EXPANDFILELIST);
1272 } else {
1273 /* Binary packages with dirNames cannot be installed by legacy rpm. */
1274 (void) rpmlibNeedsFeature(pkg, "CompressedFileNames", "3.0.4-1");
1275 }
1276 }
1277
FileRecordsFree(FileRecords files)1278 static FileRecords FileRecordsFree(FileRecords files)
1279 {
1280 for (int i = 0; i < files->used; i++) {
1281 free(files->recs[i].diskPath);
1282 free(files->recs[i].cpioPath);
1283 free(files->recs[i].langs);
1284 free(files->recs[i].caps);
1285 }
1286 free(files->recs);
1287 return NULL;
1288 }
1289
FileListFree(FileList fl)1290 static void FileListFree(FileList fl)
1291 {
1292 FileEntryFree(&(fl->cur));
1293 FileEntryFree(&(fl->def));
1294 FileRecordsFree(&(fl->files));
1295 free(fl->buildRoot);
1296 argvFree(fl->docDirs);
1297 rpmstrPoolFree(fl->pool);
1298 }
1299
1300 /* forward ref */
1301 static rpmRC recurseDir(FileList fl, const char * diskPath);
1302
1303 /* Hack up a stat structure for a %dev or non-existing %ghost */
fakeStat(FileEntry cur,struct stat * statp)1304 static struct stat * fakeStat(FileEntry cur, struct stat * statp)
1305 {
1306 time_t now = time(NULL);
1307
1308 if (cur->devtype) {
1309 statp->st_rdev = ((cur->devmajor & 0xff) << 8) | (cur->devminor & 0xff);
1310 statp->st_dev = statp->st_rdev;
1311 statp->st_mode = (cur->devtype == 'b' ? S_IFBLK : S_IFCHR);
1312 } else {
1313 /* non-existing %ghost file or directory */
1314 statp->st_mode = cur->isDir ? S_IFDIR : S_IFREG;
1315 /* can't recurse into non-existing directory */
1316 if (cur->isDir)
1317 cur->isDir = 1;
1318 }
1319 statp->st_mode |= (cur->ar.ar_fmode & 0777);
1320 statp->st_atime = now;
1321 statp->st_mtime = now;
1322 statp->st_ctime = now;
1323 statp->st_nlink = 1;
1324 return statp;
1325 }
1326
validFilename(const char * fn)1327 static int validFilename(const char *fn)
1328 {
1329 int rc = 1;
1330 /* char is signed but we're dealing with unsigned values here! */
1331 for (const unsigned char *s = (const unsigned char *)fn; *s; s++) {
1332 /* Ban DEL and anything below space, UTF-8 is validated elsewhere */
1333 if (*s == 0x7f || *s < 0x20) {
1334 rpmlog(RPMLOG_ERR,
1335 _("Illegal character (0x%x) in filename: %s\n"), *s, fn);
1336 rc = 0;
1337 break;
1338 }
1339 }
1340 return rc;
1341 }
1342
1343 /**
1344 * Add a file to the package manifest.
1345 * @param fl package file tree walk data
1346 * @param diskPath path to file
1347 * @param statp file stat (possibly NULL)
1348 * @return RPMRC_OK on success
1349 */
addFile(FileList fl,const char * diskPath,struct stat * statp)1350 static rpmRC addFile(FileList fl, const char * diskPath,
1351 struct stat * statp)
1352 {
1353 size_t plen = strlen(diskPath);
1354 char buf[plen + 1];
1355 const char *cpioPath;
1356 struct stat statbuf;
1357 mode_t fileMode;
1358 uid_t fileUid;
1359 gid_t fileGid;
1360 const char *fileUname;
1361 const char *fileGname;
1362 rpmRC rc = RPMRC_FAIL; /* assume failure */
1363
1364 /* Strip trailing slash. The special case of '/' path is handled below. */
1365 if (plen > 0 && diskPath[plen - 1] == '/') {
1366 diskPath = strcpy(buf, diskPath);
1367 buf[plen - 1] = '\0';
1368 }
1369 cpioPath = diskPath;
1370
1371 if (strncmp(diskPath, fl->buildRoot, fl->buildRootLen)) {
1372 rpmlog(RPMLOG_ERR, _("Path is outside buildroot: %s\n"), diskPath);
1373 goto exit;
1374 }
1375
1376 if (!validFilename(diskPath))
1377 goto exit;
1378
1379 /* Path may have prepended buildRoot, so locate the original filename. */
1380 /*
1381 * XXX There are 3 types of entry into addFile:
1382 *
1383 * From diskUrl statp
1384 * =====================================================
1385 * processBinaryFile path NULL
1386 * processBinaryFile glob result path NULL
1387 * myftw path stat
1388 *
1389 */
1390 if (fl->buildRoot && !rstreq(fl->buildRoot, "/"))
1391 cpioPath += fl->buildRootLen;
1392
1393 /* XXX make sure '/' can be packaged also */
1394 if (*cpioPath == '\0')
1395 cpioPath = "/";
1396
1397 /*
1398 * Unless recursing, we dont have stat() info at hand. Handle the
1399 * various cases, preserving historical behavior wrt %dev():
1400 * - for %dev() entries we fake it up whether the file exists or not
1401 * - otherwise try to grab the data by lstat()
1402 * - %ghost entries might not exist, fake it up
1403 */
1404 if (statp == NULL) {
1405 memset(&statbuf, 0, sizeof(statbuf));
1406
1407 if (fl->cur.devtype) {
1408 statp = fakeStat(&(fl->cur), &statbuf);
1409 } else if (lstat(diskPath, &statbuf) == 0) {
1410 statp = &statbuf;
1411 } else if (fl->cur.attrFlags & RPMFILE_GHOST) {
1412 statp = fakeStat(&(fl->cur), &statbuf);
1413 } else {
1414 int lvl = RPMLOG_ERR;
1415 int ignore = 0;
1416 const char *msg = fl->cur.isDir ? _("Directory not found: %s\n") :
1417 _("File not found: %s\n");
1418 if (fl->cur.attrFlags & RPMFILE_EXCLUDE)
1419 ignore = 1;
1420 if (fl->cur.attrFlags & RPMFILE_DOC) {
1421 int strict_doc =
1422 rpmExpandNumeric("%{?_missing_doc_files_terminate_build}");
1423 if (!strict_doc)
1424 ignore = 1;
1425 }
1426
1427 if (ignore) {
1428 lvl = RPMLOG_WARNING;
1429 rc = RPMRC_OK;
1430 }
1431 rpmlog(lvl, msg, diskPath);
1432 goto exit;
1433 }
1434 }
1435
1436 /* Error out when a non-directory is specified as one in spec */
1437 if (fl->cur.isDir && (statp == &statbuf) && !S_ISDIR(statp->st_mode)) {
1438 rpmlog(RPMLOG_ERR, _("Not a directory: %s\n"), diskPath);
1439 goto exit;
1440 }
1441
1442 /* Don't recurse into explicit %dir, don't double-recurse from fts */
1443 if ((fl->cur.isDir != 1) && (statp == &statbuf) && S_ISDIR(statp->st_mode)) {
1444 return recurseDir(fl, diskPath);
1445 }
1446
1447 fileMode = statp->st_mode;
1448 fileUid = statp->st_uid;
1449 fileGid = statp->st_gid;
1450
1451 /* Explicit %attr() always wins */
1452 if (fl->cur.ar.ar_fmodestr) {
1453 if (S_ISLNK(fileMode)) {
1454 rpmlog(RPMLOG_WARNING,
1455 "Explicit %%attr() mode not applicable to symlink: %s\n",
1456 diskPath);
1457 } else {
1458 fileMode &= S_IFMT;
1459 fileMode |= fl->cur.ar.ar_fmode;
1460 }
1461 } else {
1462 /* ...but %defattr() for directories and files is different */
1463 if (S_ISDIR(fileMode)) {
1464 if (fl->def.ar.ar_dmodestr) {
1465 fileMode &= S_IFMT;
1466 fileMode |= fl->def.ar.ar_dmode;
1467 }
1468 } else if (!S_ISLNK(fileMode) && fl->def.ar.ar_fmodestr) {
1469 fileMode &= S_IFMT;
1470 fileMode |= fl->def.ar.ar_fmode;
1471 }
1472 }
1473 if (fl->cur.ar.ar_user) {
1474 fileUname = rpmstrPoolStr(fl->pool, fl->cur.ar.ar_user);
1475 } else if (fl->def.ar.ar_user) {
1476 fileUname = rpmstrPoolStr(fl->pool, fl->def.ar.ar_user);
1477 } else {
1478 fileUname = rpmugUname(fileUid);
1479 }
1480 if (fl->cur.ar.ar_group) {
1481 fileGname = rpmstrPoolStr(fl->pool, fl->cur.ar.ar_group);
1482 } else if (fl->def.ar.ar_group) {
1483 fileGname = rpmstrPoolStr(fl->pool, fl->def.ar.ar_group);
1484 } else {
1485 fileGname = rpmugGname(fileGid);
1486 }
1487
1488 /* Default user/group to builder's user/group */
1489 if (fileUname == NULL)
1490 fileUname = rpmugUname(getuid());
1491 if (fileGname == NULL)
1492 fileGname = rpmugGname(getgid());
1493
1494 /* S_XXX macro must be consistent with type in find call at check-files script */
1495 if (check_fileList && (S_ISREG(fileMode) || S_ISLNK(fileMode))) {
1496 appendStringBuf(check_fileList, diskPath);
1497 appendStringBuf(check_fileList, "\n");
1498 }
1499
1500 /* Add to the file list */
1501 if (fl->files.used == fl->files.alloced) {
1502 fl->files.alloced += 128;
1503 fl->files.recs = xrealloc(fl->files.recs,
1504 fl->files.alloced * sizeof(*(fl->files.recs)));
1505 }
1506
1507 { FileListRec flp = &fl->files.recs[fl->files.used];
1508
1509 flp->fl_st = *statp; /* structure assignment */
1510 flp->fl_mode = fileMode;
1511 flp->fl_uid = fileUid;
1512 flp->fl_gid = fileGid;
1513 if (S_ISDIR(fileMode))
1514 flp->fl_size = 0;
1515
1516 flp->cpioPath = xstrdup(cpioPath);
1517 flp->diskPath = xstrdup(diskPath);
1518 flp->uname = rpmstrPoolId(fl->pool, fileUname, 1);
1519 flp->gname = rpmstrPoolId(fl->pool, fileGname, 1);
1520
1521 if (fl->cur.langs) {
1522 flp->langs = argvJoin(fl->cur.langs, "|");
1523 } else {
1524 flp->langs = xstrdup("");
1525 }
1526
1527 if (fl->cur.caps) {
1528 flp->caps = xstrdup(fl->cur.caps);
1529 } else {
1530 flp->caps = xstrdup("");
1531 }
1532
1533 flp->flags = fl->cur.attrFlags;
1534 flp->specdFlags = fl->cur.specdFlags;
1535 flp->verifyFlags = fl->cur.verifyFlags;
1536
1537 if (!(flp->flags & RPMFILE_EXCLUDE) && S_ISREG(flp->fl_mode)) {
1538 if (flp->fl_size >= UINT32_MAX) {
1539 fl->largeFiles = 1;
1540 }
1541 }
1542 }
1543
1544 rc = RPMRC_OK;
1545 fl->files.used++;
1546
1547 exit:
1548 if (rc != RPMRC_OK)
1549 fl->processingFailed = 1;
1550
1551 return rc;
1552 }
1553
1554 /**
1555 * Add directory (and all of its files) to the package manifest.
1556 * @param fl package file tree walk data
1557 * @param diskPath path to file
1558 * @return RPMRC_OK on success
1559 */
recurseDir(FileList fl,const char * diskPath)1560 static rpmRC recurseDir(FileList fl, const char * diskPath)
1561 {
1562 char * ftsSet[2];
1563 FTS * ftsp;
1564 FTSENT * fts;
1565 int myFtsOpts = (FTS_COMFOLLOW | FTS_NOCHDIR | FTS_PHYSICAL);
1566 rpmRC rc = RPMRC_FAIL;
1567
1568 ftsSet[0] = (char *) diskPath;
1569 ftsSet[1] = NULL;
1570 ftsp = Fts_open(ftsSet, myFtsOpts, NULL);
1571 while ((fts = Fts_read(ftsp)) != NULL) {
1572 switch (fts->fts_info) {
1573 case FTS_D: /* preorder directory */
1574 case FTS_F: /* regular file */
1575 case FTS_SL: /* symbolic link */
1576 case FTS_SLNONE: /* symbolic link without target */
1577 case FTS_DEFAULT: /* none of the above */
1578 rc = addFile(fl, fts->fts_accpath, fts->fts_statp);
1579 break;
1580 case FTS_DOT: /* dot or dot-dot */
1581 case FTS_DP: /* postorder directory */
1582 rc = RPMRC_OK;
1583 break;
1584 case FTS_NS: /* stat(2) failed */
1585 case FTS_DNR: /* unreadable directory */
1586 case FTS_ERR: /* error; errno is set */
1587 case FTS_DC: /* directory that causes cycles */
1588 case FTS_NSOK: /* no stat(2) requested */
1589 case FTS_INIT: /* initialized only */
1590 case FTS_W: /* whiteout object */
1591 default:
1592 rpmlog(RPMLOG_ERR, _("Can't read content of file: %s\n"),
1593 fts->fts_path);
1594 rc = RPMRC_FAIL;
1595 break;
1596 }
1597 if (rc)
1598 break;
1599 }
1600 (void) Fts_close(ftsp);
1601
1602 return rc;
1603 }
1604
1605 /**
1606 * Add a pubkey/icon to a binary package.
1607 * @param pkg
1608 * @param fl package file tree walk data
1609 * @param fileName path to file, relative is builddir, absolute buildroot.
1610 * @param tag tag to add
1611 * @return RPMRC_OK on success
1612 */
processMetadataFile(Package pkg,FileList fl,const char * fileName,rpmTagVal tag)1613 static rpmRC processMetadataFile(Package pkg, FileList fl,
1614 const char * fileName, rpmTagVal tag)
1615 {
1616 const char * buildDir = "%{_builddir}/%{?buildsubdir}/";
1617 char * fn = NULL;
1618 char * apkt = NULL;
1619 uint8_t * pkt = NULL;
1620 ssize_t pktlen = 0;
1621 int absolute = 0;
1622 rpmRC rc = RPMRC_FAIL;
1623 int xx;
1624
1625 if (*fileName == '/') {
1626 fn = rpmGenPath(fl->buildRoot, NULL, fileName);
1627 absolute = 1;
1628 } else
1629 fn = rpmGenPath(buildDir, NULL, fileName);
1630
1631 switch (tag) {
1632 default:
1633 rpmlog(RPMLOG_ERR, _("%s: can't load unknown tag (%d).\n"),
1634 fn, tag);
1635 goto exit;
1636 break;
1637 case RPMTAG_PUBKEYS: {
1638 if ((xx = pgpReadPkts(fn, &pkt, (size_t *)&pktlen)) <= 0) {
1639 rpmlog(RPMLOG_ERR, _("%s: public key read failed.\n"), fn);
1640 goto exit;
1641 }
1642 if (xx != PGPARMOR_PUBKEY) {
1643 rpmlog(RPMLOG_ERR, _("%s: not an armored public key.\n"), fn);
1644 goto exit;
1645 }
1646 apkt = pgpArmorWrap(PGPARMOR_PUBKEY, pkt, pktlen);
1647 break;
1648 }
1649 }
1650
1651 if (!apkt) {
1652 rpmlog(RPMLOG_ERR, _("%s: failed to encode\n"), fn);
1653 goto exit;
1654 }
1655
1656 headerPutString(pkg->header, tag, apkt);
1657 rc = RPMRC_OK;
1658
1659 if (absolute)
1660 rc = addFile(fl, fn, NULL);
1661
1662 exit:
1663 free(apkt);
1664 free(pkt);
1665 free(fn);
1666 if (rc) {
1667 fl->processingFailed = 1;
1668 rc = RPMRC_FAIL;
1669 }
1670 return rc;
1671 }
1672
1673 /* add a file with possible virtual attributes to the file list */
argvAddAttr(ARGV_t * filesp,rpmfileAttrs attrs,const char * path)1674 static void argvAddAttr(ARGV_t *filesp, rpmfileAttrs attrs, const char *path)
1675 {
1676 char *line = NULL;
1677
1678 for (VFA_t *vfa = virtualAttrs; vfa->attribute != NULL; vfa++) {
1679 if (vfa->flag & attrs)
1680 line = rstrscat(&line, vfa->attribute, " ", NULL);
1681 }
1682 line = rstrscat(&line, path, NULL);
1683 argvAdd(filesp, line);
1684 free(line);
1685 }
1686
1687 #if HAVE_LIBDW
1688 /* How build id links are generated. See macros.in for description. */
1689 #define BUILD_IDS_NONE 0
1690 #define BUILD_IDS_ALLDEBUG 1
1691 #define BUILD_IDS_SEPARATE 2
1692 #define BUILD_IDS_COMPAT 3
1693
addNewIDSymlink(ARGV_t * files,char * targetpath,char * idlinkpath,int isDbg,int * dups)1694 static int addNewIDSymlink(ARGV_t *files,
1695 char *targetpath, char *idlinkpath,
1696 int isDbg, int *dups)
1697 {
1698 const char *linkerr = _("failed symlink");
1699 int rc = 0;
1700 int nr = 0;
1701 int exists = 0;
1702 char *origpath, *linkpath;
1703
1704 if (isDbg)
1705 rasprintf(&linkpath, "%s.debug", idlinkpath);
1706 else
1707 linkpath = idlinkpath;
1708 origpath = linkpath;
1709
1710 while (faccessat(AT_FDCWD, linkpath, F_OK, AT_SYMLINK_NOFOLLOW) == 0) {
1711 /* We don't care about finding dups for compat links, they are
1712 OK as is. Otherwise we will need to double check if
1713 existing link points to the correct target. */
1714 if (dups == NULL)
1715 {
1716 exists = 1;
1717 break;
1718 }
1719
1720 char ltarget[PATH_MAX];
1721 ssize_t llen;
1722 /* In short-circuited builds the link might already exist */
1723 if ((llen = readlink(linkpath, ltarget, sizeof(ltarget)-1)) != -1) {
1724 ltarget[llen] = '\0';
1725 if (rstreq(ltarget, targetpath)) {
1726 exists = 1;
1727 break;
1728 }
1729 }
1730
1731 if (nr > 0)
1732 free(linkpath);
1733 nr++;
1734 rasprintf(&linkpath, "%s.%d%s", idlinkpath, nr,
1735 isDbg ? ".debug" : "");
1736 }
1737
1738 if (!exists && symlink(targetpath, linkpath) < 0) {
1739 rc = 1;
1740 rpmlog(RPMLOG_ERR, "%s: %s -> %s: %m\n",
1741 linkerr, linkpath, targetpath);
1742 } else {
1743 argvAddAttr(files, RPMFILE_ARTIFACT, linkpath);
1744 }
1745
1746 if (nr > 0) {
1747 /* Lets see why there are multiple build-ids. If the original
1748 targets are hard linked, then it is OK, otherwise warn
1749 something fishy is going on. Would be nice to call
1750 something like eu-elfcmp to see if they are really the same
1751 ELF file or not. */
1752 struct stat st1, st2;
1753 if (stat (origpath, &st1) != 0) {
1754 rpmlog(RPMLOG_WARNING, _("Duplicate build-id, stat %s: %m\n"),
1755 origpath);
1756 } else if (stat (linkpath, &st2) != 0) {
1757 rpmlog(RPMLOG_WARNING, _("Duplicate build-id, stat %s: %m\n"),
1758 linkpath);
1759 } else if (!(S_ISREG(st1.st_mode) && S_ISREG(st2.st_mode)
1760 && st1.st_nlink > 1 && st2.st_nlink == st1.st_nlink
1761 && st1.st_ino == st2.st_ino && st1.st_dev == st2.st_dev)) {
1762 char *rpath1 = realpath(origpath, NULL);
1763 char *rpath2 = realpath(linkpath, NULL);
1764 rpmlog(RPMLOG_WARNING, _("Duplicate build-ids %s and %s\n"),
1765 rpath1, rpath2);
1766 free(rpath1);
1767 free(rpath2);
1768 }
1769 }
1770
1771 if (isDbg)
1772 free(origpath);
1773 if (nr > 0)
1774 free(linkpath);
1775 if (dups != NULL)
1776 *dups = nr;
1777
1778 return rc;
1779 }
1780
haveModinfo(Elf * elf)1781 static int haveModinfo(Elf *elf)
1782 {
1783 Elf_Scn * scn = NULL;
1784 size_t shstrndx;
1785 int have_modinfo = 0;
1786 const char *sname;
1787
1788 if (elf_getshdrstrndx(elf, &shstrndx) == 0) {
1789 while ((scn = elf_nextscn(elf, scn)) != NULL) {
1790 GElf_Shdr shdr_mem, *shdr = gelf_getshdr(scn, &shdr_mem);
1791 if (shdr == NULL)
1792 continue;
1793 sname = elf_strptr(elf, shstrndx, shdr->sh_name);
1794 if (sname && rstreq(sname, ".modinfo")) {
1795 have_modinfo = 1;
1796 break;
1797 }
1798 }
1799 }
1800 return have_modinfo;
1801 }
1802
generateBuildIDs(FileList fl,ARGV_t * files)1803 static int generateBuildIDs(FileList fl, ARGV_t *files)
1804 {
1805 int rc = 0;
1806 int i;
1807 FileListRec flp;
1808 char **ids = NULL;
1809 char **paths = NULL;
1810 size_t nr_ids, allocated;
1811 nr_ids = allocated = 0;
1812
1813 /* How are we supposed to create the build-id links? */
1814 char *build_id_links_macro = rpmExpand("%{?_build_id_links}", NULL);
1815 int build_id_links;
1816 if (*build_id_links_macro == '\0') {
1817 rpmlog(RPMLOG_WARNING,
1818 _("_build_id_links macro not set, assuming 'compat'\n"));
1819 build_id_links = BUILD_IDS_COMPAT;
1820 } else if (strcmp(build_id_links_macro, "none") == 0) {
1821 build_id_links = BUILD_IDS_NONE;
1822 } else if (strcmp(build_id_links_macro, "alldebug") == 0) {
1823 build_id_links = BUILD_IDS_ALLDEBUG;
1824 } else if (strcmp(build_id_links_macro, "separate") == 0) {
1825 build_id_links = BUILD_IDS_SEPARATE;
1826 } else if (strcmp(build_id_links_macro, "compat") == 0) {
1827 build_id_links = BUILD_IDS_COMPAT;
1828 } else {
1829 rc = 1;
1830 rpmlog(RPMLOG_ERR,
1831 _("_build_id_links macro set to unknown value '%s'\n"),
1832 build_id_links_macro);
1833 build_id_links = BUILD_IDS_NONE;
1834 }
1835 free(build_id_links_macro);
1836
1837 if (build_id_links == BUILD_IDS_NONE || rc != 0)
1838 return rc;
1839
1840 /* Historically we have only checked build_ids when __debug_package
1841 was defined. So don't terminate the build if __debug_package is
1842 unset, even when _missing_build_ids_terminate_build is. */
1843 int terminate = (rpmExpandNumeric("%{?_missing_build_ids_terminate_build}")
1844 && rpmExpandNumeric("%{?__debug_package}"));
1845
1846 /* Collect and check all build-ids for ELF files in this package. */
1847 int needMain = 0;
1848 int needDbg = 0;
1849 for (i = 0, flp = fl->files.recs; i < fl->files.used; i++, flp++) {
1850 struct stat sbuf;
1851 if (lstat(flp->diskPath, &sbuf) == 0 && S_ISREG (sbuf.st_mode)) {
1852 /* We determine whether this is a main or
1853 debug ELF based on path. */
1854 int isDbg = strncmp (flp->cpioPath,
1855 DEBUG_LIB_PREFIX, strlen (DEBUG_LIB_PREFIX)) == 0;
1856
1857 /* For the main package files mimic what find-debuginfo.sh does.
1858 Only check build-ids for executable files. Debug files are
1859 always non-executable. */
1860 if (!isDbg
1861 && (sbuf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0)
1862 continue;
1863
1864 int fd = open (flp->diskPath, O_RDONLY);
1865 if (fd >= 0) {
1866 /* Only real ELF files, that are ET_EXEC, ET_DYN or
1867 kernel modules (ET_REL files with .modinfo section)
1868 should have build-ids. */
1869 GElf_Ehdr ehdr;
1870 #if HAVE_DWELF_ELF_BEGIN
1871 Elf *elf = dwelf_elf_begin(fd);
1872 #else
1873 Elf *elf = elf_begin (fd, ELF_C_READ, NULL);
1874 #endif
1875 if (elf != NULL && elf_kind(elf) == ELF_K_ELF
1876 && gelf_getehdr(elf, &ehdr) != NULL
1877 && (ehdr.e_type == ET_EXEC || ehdr.e_type == ET_DYN
1878 || (ehdr.e_type == ET_REL && haveModinfo(elf)))) {
1879 const void *build_id;
1880 ssize_t len = dwelf_elf_gnu_build_id (elf, &build_id);
1881 /* len == -1 means error. Zero means no
1882 build-id. We want at least a length of 2 so we
1883 have at least a xx/yy (hex) dir/file. But
1884 reasonable build-ids are between 16 bytes (md5
1885 is 128 bits) and 64 bytes (largest sha3 is 512
1886 bits), common is 20 bytes (sha1 is 160 bits). */
1887 if (len >= 16 && len <= 64) {
1888 int addid = 0;
1889 if (isDbg) {
1890 needDbg = 1;
1891 addid = 1;
1892 }
1893 else if (build_id_links != BUILD_IDS_ALLDEBUG) {
1894 needMain = 1;
1895 addid = 1;
1896 }
1897 if (addid) {
1898 const unsigned char *p = build_id;
1899 const unsigned char *end = p + len;
1900 char *id_str;
1901 if (allocated <= nr_ids) {
1902 allocated += 16;
1903 paths = xrealloc (paths,
1904 allocated * sizeof(char *));
1905 ids = xrealloc (ids,
1906 allocated * sizeof(char *));
1907 }
1908
1909 paths[nr_ids] = xstrdup(flp->cpioPath);
1910 id_str = ids[nr_ids] = xmalloc(2 * len + 1);
1911 while (p < end)
1912 id_str += sprintf(id_str, "%02x",
1913 (unsigned)*p++);
1914 *id_str = '\0';
1915 nr_ids++;
1916 }
1917 } else {
1918 if (len < 0) {
1919 rpmlog(terminate ? RPMLOG_ERR : RPMLOG_WARNING,
1920 _("error reading build-id in %s: %s\n"),
1921 flp->diskPath, elf_errmsg (-1));
1922 } else if (len == 0) {
1923 rpmlog(terminate ? RPMLOG_ERR : RPMLOG_WARNING,
1924 _("Missing build-id in %s\n"),
1925 flp->diskPath);
1926 } else {
1927 rpmlog(terminate ? RPMLOG_ERR : RPMLOG_WARNING,
1928 (len < 16
1929 ? _("build-id found in %s too small\n")
1930 : _("build-id found in %s too large\n")),
1931 flp->diskPath);
1932 }
1933 if (terminate)
1934 rc = 1;
1935 }
1936 }
1937 elf_end (elf);
1938 close (fd);
1939 }
1940 }
1941 }
1942
1943 /* Process and clean up all build-ids. */
1944 if (nr_ids > 0) {
1945 const char *errdir = _("failed to create directory");
1946 char *mainiddir = NULL;
1947 char *debugiddir = NULL;
1948 if (rc == 0) {
1949 char *attrstr;
1950 /* Add .build-id directories to hold the subdirs/symlinks. */
1951
1952 mainiddir = rpmGetPath(fl->buildRoot, BUILD_ID_DIR, NULL);
1953 debugiddir = rpmGetPath(fl->buildRoot, DEBUG_ID_DIR, NULL);
1954
1955 /* Make sure to reset all file flags to defaults. */
1956 attrstr = mkattr();
1957 argvAdd(files, attrstr);
1958 free (attrstr);
1959
1960 /* Supported, but questionable. */
1961 if (needMain && needDbg)
1962 rpmlog(RPMLOG_WARNING,
1963 _("Mixing main ELF and debug files in package"));
1964
1965 if (needMain) {
1966 if ((rc = rpmioMkpath(mainiddir, 0755, -1, -1)) != 0) {
1967 rpmlog(RPMLOG_ERR, "%s %s: %m\n", errdir, mainiddir);
1968 } else {
1969 argvAddAttr(files, RPMFILE_DIR|RPMFILE_ARTIFACT, mainiddir);
1970 }
1971 }
1972
1973 if (rc == 0 && needDbg) {
1974 if ((rc = rpmioMkpath(debugiddir, 0755, -1, -1)) != 0) {
1975 rpmlog(RPMLOG_ERR, "%s %s: %m\n", errdir, debugiddir);
1976 } else {
1977 argvAddAttr(files, RPMFILE_DIR|RPMFILE_ARTIFACT, debugiddir);
1978 }
1979 }
1980 }
1981
1982 /* In case we need ALLDEBUG links we might need the vra as
1983 tagged onto the .debug file name. */
1984 char *vra = NULL;
1985 if (rc == 0 && needDbg && build_id_links == BUILD_IDS_ALLDEBUG) {
1986 int unique_debug_names =
1987 rpmExpandNumeric("%{?_unique_debug_names}");
1988 if (unique_debug_names == 1)
1989 vra = rpmExpand("-%{VERSION}-%{RELEASE}.%{_arch}", NULL);
1990 }
1991
1992 /* Now add a subdir and symlink for each buildid found. */
1993 for (i = 0; i < nr_ids; i++) {
1994 /* Don't add anything more when an error occurred. But do
1995 cleanup. */
1996 if (rc == 0) {
1997 int isDbg = strncmp (paths[i], DEBUG_LIB_PREFIX,
1998 strlen (DEBUG_LIB_PREFIX)) == 0;
1999
2000 char *buildidsubdir;
2001 char subdir[4];
2002 subdir[0] = '/';
2003 subdir[1] = ids[i][0];
2004 subdir[2] = ids[i][1];
2005 subdir[3] = '\0';
2006 if (isDbg)
2007 buildidsubdir = rpmGetPath(debugiddir, subdir, NULL);
2008 else
2009 buildidsubdir = rpmGetPath(mainiddir, subdir, NULL);
2010 /* We only need to create and add the subdir once. */
2011 int addsubdir = access (buildidsubdir, F_OK) == -1;
2012 if (addsubdir
2013 && (rc = rpmioMkpath(buildidsubdir, 0755, -1, -1)) != 0) {
2014 rpmlog(RPMLOG_ERR, "%s %s: %m\n", errdir, buildidsubdir);
2015 } else {
2016 if (addsubdir)
2017 argvAddAttr(files, RPMFILE_DIR|RPMFILE_ARTIFACT, buildidsubdir);
2018 if (rc == 0) {
2019 char *linkpattern, *targetpattern;
2020 char *linkpath, *targetpath;
2021 int dups = 0;
2022 if (isDbg) {
2023 linkpattern = "%s/%s";
2024 targetpattern = "../../../../..%s";
2025 } else {
2026 linkpattern = "%s/%s";
2027 targetpattern = "../../../..%s";
2028 }
2029 rasprintf(&linkpath, linkpattern,
2030 buildidsubdir, &ids[i][2]);
2031 rasprintf(&targetpath, targetpattern, paths[i]);
2032 rc = addNewIDSymlink(files, targetpath, linkpath,
2033 isDbg, &dups);
2034
2035 /* We might want to have a link from the debug
2036 build_ids dir to the main one. We create it
2037 when we are creating compat links or doing
2038 an old style alldebug build-ids package. In
2039 the first case things are simple since we
2040 just link to the main build-id symlink. The
2041 second case is a bit tricky, since we
2042 cannot be 100% sure the file names in the
2043 main and debug package match. Currently
2044 they do, but when creating parallel
2045 installable debuginfo packages they might
2046 not (in that case we might have to also
2047 strip the nvr from the debug name).
2048
2049 In general either method is discouraged
2050 since it might create dangling symlinks if
2051 the package versions get out of sync. */
2052 if (rc == 0 && isDbg
2053 && build_id_links == BUILD_IDS_COMPAT) {
2054 /* buildidsubdir already points to the
2055 debug buildid. We just need to setup
2056 the symlink to the main one. There
2057 might be duplicate IDs, those are found
2058 by the addNewIDSymlink above. Target
2059 the last found duplicate, if any. */
2060 free(linkpath);
2061 free(targetpath);
2062 if (dups == 0)
2063 {
2064 rasprintf(&linkpath, "%s/%s",
2065 buildidsubdir, &ids[i][2]);
2066 rasprintf(&targetpath,
2067 "../../../.build-id%s/%s",
2068 subdir, &ids[i][2]);
2069 }
2070 else
2071 {
2072 rasprintf(&linkpath, "%s/%s.%d",
2073 buildidsubdir, &ids[i][2], dups);
2074 rasprintf(&targetpath,
2075 "../../../.build-id%s/%s.%d",
2076 subdir, &ids[i][2], dups);
2077 }
2078 rc = addNewIDSymlink(files, targetpath, linkpath,
2079 0, NULL);
2080 }
2081
2082 if (rc == 0 && isDbg
2083 && build_id_links == BUILD_IDS_ALLDEBUG) {
2084 /* buildidsubdir already points to the
2085 debug buildid. We do have to figure out
2086 the main ELF file though (which is most
2087 likely not in this package). Guess we
2088 can find it by stripping the
2089 /usr/lib/debug path and .debug
2090 prefix. Which might not really be
2091 correct if there was a more involved
2092 transformation (for example for
2093 parallel installable debuginfo
2094 packages), but then we shouldn't be
2095 using ALLDEBUG in the first place.
2096 Also ignore things like .dwz multifiles
2097 which don't end in ".debug". */
2098 int pathlen = strlen(paths[i]);
2099 int debuglen = strlen(".debug");
2100 int prefixlen = strlen(DEBUG_LIB_DIR);
2101 int vralen = vra == NULL ? 0 : strlen(vra);
2102 if (pathlen > prefixlen + debuglen + vralen
2103 && strcmp ((paths[i] + pathlen - debuglen),
2104 ".debug") == 0) {
2105 free(linkpath);
2106 free(targetpath);
2107 char *targetstr = xstrdup (paths[i]
2108 + prefixlen);
2109 int targetlen = pathlen - prefixlen;
2110 int targetend = targetlen - debuglen - vralen;
2111 targetstr[targetend] = '\0';
2112 rasprintf(&linkpath, "%s/%s",
2113 buildidsubdir, &ids[i][2]);
2114 rasprintf(&targetpath, "../../../../..%s",
2115 targetstr);
2116 rc = addNewIDSymlink(files, targetpath,
2117 linkpath, 0, &dups);
2118 free(targetstr);
2119 }
2120 }
2121 free(linkpath);
2122 free(targetpath);
2123 }
2124 }
2125 free(buildidsubdir);
2126 }
2127 free(paths[i]);
2128 free(ids[i]);
2129 }
2130 free(mainiddir);
2131 free(debugiddir);
2132 free(vra);
2133 free(paths);
2134 free(ids);
2135 }
2136 return rc;
2137 }
2138 #endif
2139
2140 /**
2141 * Add a file to a binary package.
2142 * @param pkg
2143 * @param fl package file tree walk data
2144 * @param fileName file to add
2145 * @return RPMRC_OK on success
2146 */
processBinaryFile(Package pkg,FileList fl,const char * fileName)2147 static rpmRC processBinaryFile(Package pkg, FileList fl, const char * fileName)
2148 {
2149 int quote = 1; /* XXX permit quoted glob characters. */
2150 int doGlob;
2151 char *diskPath = NULL;
2152 rpmRC rc = RPMRC_OK;
2153 size_t fnlen = strlen(fileName);
2154 int trailing_slash = (fnlen > 0 && fileName[fnlen-1] == '/');
2155
2156 /* XXX differentiate other directories from explicit %dir */
2157 if (trailing_slash && !fl->cur.isDir)
2158 fl->cur.isDir = -1;
2159
2160 doGlob = rpmIsGlob(fileName, quote);
2161
2162 /* Check that file starts with leading "/" */
2163 if (*fileName != '/') {
2164 rpmlog(RPMLOG_ERR, _("File needs leading \"/\": %s\n"), fileName);
2165 rc = RPMRC_FAIL;
2166 goto exit;
2167 }
2168
2169 /* Copy file name or glob pattern removing multiple "/" chars. */
2170 /*
2171 * Note: rpmGetPath should guarantee a "canonical" path. That means
2172 * that the following pathologies should be weeded out:
2173 * //bin//sh
2174 * //usr//bin/
2175 * /.././../usr/../bin//./sh
2176 */
2177 diskPath = rpmGenPath(fl->buildRoot, NULL, fileName);
2178 /* Arrange trailing slash on directories */
2179 if (fl->cur.isDir)
2180 diskPath = rstrcat(&diskPath, "/");
2181
2182 if (doGlob) {
2183 ARGV_t argv = NULL;
2184 int argc = 0;
2185 int i;
2186
2187 if (fl->cur.devtype) {
2188 rpmlog(RPMLOG_ERR, _("%%dev glob not permitted: %s\n"), diskPath);
2189 rc = RPMRC_FAIL;
2190 goto exit;
2191 }
2192
2193 if (rpmGlob(diskPath, &argc, &argv) == 0) {
2194 for (i = 0; i < argc; i++) {
2195 rc = addFile(fl, argv[i], NULL);
2196 }
2197 argvFree(argv);
2198 } else {
2199 const char *msg = (fl->cur.isDir) ?
2200 _("Directory not found by glob: %s. "
2201 "Trying without globbing.\n") :
2202 _("File not found by glob: %s. "
2203 "Trying without globbing.\n");
2204 rpmlog(RPMLOG_DEBUG, msg, diskPath);
2205 rc = addFile(fl, diskPath, NULL);
2206 }
2207 } else {
2208 rc = addFile(fl, diskPath, NULL);
2209 }
2210
2211 exit:
2212 free(diskPath);
2213 if (rc) {
2214 fl->processingFailed = 1;
2215 rc = RPMRC_FAIL;
2216 }
2217 return rc;
2218 }
2219
readManifest(rpmSpec spec,const char * path,const char * descr,int flags,ARGV_t * avp,StringBuf * sbp)2220 int readManifest(rpmSpec spec, const char *path, const char *descr, int flags,
2221 ARGV_t *avp, StringBuf *sbp)
2222 {
2223 char *fn, buf[BUFSIZ];
2224 FILE *fd = NULL;
2225 int lineno = 0;
2226 int nlines = -1;
2227
2228 if (*path == '/') {
2229 fn = rpmGetPath(path, NULL);
2230 } else {
2231 fn = rpmGetPath("%{_builddir}/",
2232 (spec->buildSubdir ? spec->buildSubdir : "") , "/", path, NULL);
2233 }
2234 fd = fopen(fn, "r");
2235
2236 if (fd == NULL) {
2237 rpmlog(RPMLOG_ERR, _("Could not open %s file %s: %m\n"), descr, fn);
2238 goto exit;
2239 }
2240
2241 rpmPushMacroFlags(spec->macros, "__file_name", NULL, fn, RMIL_SPEC, RPMMACRO_LITERAL);
2242
2243 nlines = 0;
2244 while (fgets(buf, sizeof(buf), fd)) {
2245 char *expanded = NULL;
2246 lineno++;
2247 if ((flags & STRIP_COMMENTS) && handleComments(buf))
2248 continue;
2249 if (specExpand(spec, lineno, buf, &expanded))
2250 goto exit;
2251 if (avp)
2252 argvAdd(avp, expanded);
2253 if (sbp)
2254 appendStringBufAux(*sbp, expanded, (flags & STRIP_TRAILINGSPACE));
2255 free(expanded);
2256 nlines++;
2257 }
2258
2259 if (nlines == 0) {
2260 int emptyok = (flags & ALLOW_EMPTY);
2261 rpmlog(emptyok ? RPMLOG_WARNING : RPMLOG_ERR,
2262 _("Empty %s file %s\n"), descr, fn);
2263 if (!emptyok)
2264 nlines = -1;
2265 }
2266
2267 exit:
2268 if (fd) {
2269 fclose(fd);
2270 rpmPopMacro(spec->macros, "__file_name");
2271 }
2272 free(fn);
2273
2274 return nlines;
2275 }
2276
readFilesManifest(rpmSpec spec,Package pkg,const char * path)2277 static rpmRC readFilesManifest(rpmSpec spec, Package pkg, const char *path)
2278 {
2279 int nlines = 0;
2280 int flags = STRIP_COMMENTS | STRIP_TRAILINGSPACE;
2281
2282 if (!rpmExpandNumeric("%{?_empty_manifest_terminate_build}"))
2283 flags |= ALLOW_EMPTY;
2284
2285 /* XXX unmask %license while parsing files manifest*/
2286 rpmPushMacroFlags(spec->macros, "license", NULL, "%license", RMIL_SPEC, RPMMACRO_LITERAL);
2287
2288 nlines = readManifest(spec, path, "%files", flags, &(pkg->fileList), NULL);
2289
2290 rpmPopMacro(NULL, "license");
2291
2292 return (nlines >= 0) ? RPMRC_OK : RPMRC_FAIL;
2293 }
2294
getSpecialDocDir(Header h,rpmFlags sdtype)2295 static char * getSpecialDocDir(Header h, rpmFlags sdtype)
2296 {
2297 const char *errstr = NULL;
2298 const char *dirtype = (sdtype == RPMFILE_DOC) ? "docdir" : "licensedir";
2299 const char *fmt_default = "%{NAME}-%{VERSION}";
2300 char *fmt_macro = rpmExpand("%{?_docdir_fmt}", NULL);
2301 char *fmt = NULL;
2302 char *res = NULL;
2303
2304 if (fmt_macro && strlen(fmt_macro) > 0) {
2305 fmt = headerFormat(h, fmt_macro, &errstr);
2306 if (errstr) {
2307 rpmlog(RPMLOG_WARNING, _("illegal _docdir_fmt %s: %s\n"),
2308 fmt_macro, errstr);
2309 }
2310 }
2311
2312 if (fmt == NULL)
2313 fmt = headerFormat(h, fmt_default, &errstr);
2314
2315 res = rpmGetPath("%{_", dirtype, "}/", fmt, NULL);
2316
2317 free(fmt);
2318 free(fmt_macro);
2319 return res;
2320 }
2321
specialDirNew(Header h,rpmFlags sdtype)2322 static specialDir specialDirNew(Header h, rpmFlags sdtype)
2323 {
2324 specialDir sd = xcalloc(1, sizeof(*sd));
2325
2326 sd->entriesCount = 0;
2327 sd->entriesAlloced = 10;
2328 sd->entries = xcalloc(sd->entriesAlloced, sizeof(sd->entries[0]));
2329
2330 sd->dirname = getSpecialDocDir(h, sdtype);
2331 sd->sdtype = sdtype;
2332 return sd;
2333 }
2334
addSpecialFile(specialDir sd,const char * path,FileEntry cur,FileEntry def)2335 static void addSpecialFile(specialDir sd, const char *path, FileEntry cur,
2336 FileEntry def)
2337 {
2338 argvAdd(&sd->files, path);
2339
2340 if (sd->entriesCount >= sd->entriesAlloced) {
2341 sd->entriesAlloced <<= 1;
2342 sd->entries = xrealloc(sd->entries, sd->entriesAlloced *
2343 sizeof(sd->entries[0]));
2344 }
2345
2346 copyFileEntry(cur, &sd->entries[sd->entriesCount].curEntry);
2347 copyFileEntry(def, &sd->entries[sd->entriesCount].defEntry);
2348 sd->entriesCount++;
2349 }
2350
specialDirFree(specialDir sd)2351 static specialDir specialDirFree(specialDir sd)
2352 {
2353 int i = 0;
2354
2355 if (sd) {
2356 argvFree(sd->files);
2357 free(sd->dirname);
2358 for (i = 0; i < sd->entriesCount; i++) {
2359 FileEntryFree(&sd->entries[i].curEntry);
2360 FileEntryFree(&sd->entries[i].defEntry);
2361 }
2362 free(sd->entries);
2363 free(sd);
2364 }
2365 return NULL;
2366 }
2367
processSpecialDir(rpmSpec spec,Package pkg,FileList fl,specialDir sd,int install,int test)2368 static void processSpecialDir(rpmSpec spec, Package pkg, FileList fl,
2369 specialDir sd, int install, int test)
2370 {
2371 const char *sdenv = (sd->sdtype == RPMFILE_DOC) ? "DOCDIR" : "LICENSEDIR";
2372 const char *sdname = (sd->sdtype == RPMFILE_DOC) ? "%doc" : "%license";
2373 char *mkdocdir = rpmExpand("%{__mkdir_p} $", sdenv, NULL);
2374 StringBuf docScript = newStringBuf();
2375 char *basepath, **files;
2376 int fi;
2377
2378 appendStringBuf(docScript, sdenv);
2379 appendStringBuf(docScript, "=$RPM_BUILD_ROOT");
2380 appendLineStringBuf(docScript, sd->dirname);
2381 appendLineStringBuf(docScript, "export LC_ALL=C");
2382 appendStringBuf(docScript, "export ");
2383 appendLineStringBuf(docScript, sdenv);
2384 appendLineStringBuf(docScript, mkdocdir);
2385
2386 for (ARGV_const_t fn = sd->files; fn && *fn; fn++) {
2387 /* Quotes would break globs, escape spaces instead */
2388 char *efn = rpmEscapeSpaces(*fn);
2389 appendStringBuf(docScript, "cp -pr ");
2390 appendStringBuf(docScript, efn);
2391 appendStringBuf(docScript, " $");
2392 appendStringBuf(docScript, sdenv);
2393 appendLineStringBuf(docScript, " ||:");
2394 free(efn);
2395 }
2396
2397 if (install) {
2398 if (doScript(spec, RPMBUILD_STRINGBUF, sdname,
2399 getStringBuf(docScript), test, NULL)) {
2400 fl->processingFailed = 1;
2401 }
2402 }
2403
2404 basepath = rpmGenPath(spec->rootDir, "%{_builddir}", spec->buildSubdir);
2405 files = sd->files;
2406 fi = 0;
2407 while (*files != NULL) {
2408 char *origfile = rpmGenPath(basepath, *files, NULL);
2409 char *eorigfile = rpmEscapeSpaces(origfile);
2410 ARGV_t globFiles;
2411 int globFilesCount, i;
2412 char *newfile;
2413
2414 FileEntryFree(&fl->cur);
2415 FileEntryFree(&fl->def);
2416 copyFileEntry(&sd->entries[fi].curEntry, &fl->cur);
2417 copyFileEntry(&sd->entries[fi].defEntry, &fl->def);
2418 fi++;
2419
2420 if (rpmGlob(eorigfile, &globFilesCount, &globFiles) == 0) {
2421 for (i = 0; i < globFilesCount; i++) {
2422 rasprintf(&newfile, "%s/%s", sd->dirname, basename(globFiles[i]));
2423 processBinaryFile(pkg, fl, newfile);
2424 free(newfile);
2425 }
2426 argvFree(globFiles);
2427 } else {
2428 rpmlog(RPMLOG_ERR, _("File not found by glob: %s\n"), eorigfile);
2429 fl->processingFailed = 1;
2430 }
2431 free(eorigfile);
2432 free(origfile);
2433 files++;
2434 }
2435 free(basepath);
2436
2437 FileEntryFree(&fl->cur);
2438 FileEntryFree(&fl->def);
2439 copyFileEntry(&sd->entries[0].defEntry, &fl->def);
2440 copyFileEntry(&sd->entries[0].curEntry, &fl->cur);
2441 fl->cur.isDir = 1;
2442 (void) processBinaryFile(pkg, fl, sd->dirname);
2443
2444 freeStringBuf(docScript);
2445 free(mkdocdir);
2446 }
2447
2448
2449 /* Resets the default settings for files in the package list.
2450 Used in processPackageFiles whenever a new set of files is added. */
resetPackageFilesDefaults(struct FileList_s * fl,rpmBuildPkgFlags pkgFlags)2451 static void resetPackageFilesDefaults (struct FileList_s *fl,
2452 rpmBuildPkgFlags pkgFlags)
2453 {
2454 struct AttrRec_s root_ar = { 0, 0, 0, 0, 0, 0 };
2455
2456 root_ar.ar_user = rpmstrPoolId(fl->pool, UID_0_USER, 1);
2457 root_ar.ar_group = rpmstrPoolId(fl->pool, GID_0_GROUP, 1);
2458 dupAttrRec(&root_ar, &fl->def.ar); /* XXX assume %defattr(-,root,root) */
2459
2460 fl->def.verifyFlags = RPMVERIFY_ALL;
2461
2462 fl->pkgFlags = pkgFlags;
2463 }
2464
2465 /* Adds the given fileList to the package. If fromSpecFileList is not zero
2466 then the specialDirs are also filled in and the files are sanitized
2467 through processBinaryFile(). Otherwise no special files are processed
2468 and the files are added directly through addFile(). */
addPackageFileList(struct FileList_s * fl,Package pkg,ARGV_t * fileList,specialDir * specialDoc,specialDir * specialLic,int fromSpecFileList)2469 static void addPackageFileList (struct FileList_s *fl, Package pkg,
2470 ARGV_t *fileList,
2471 specialDir *specialDoc, specialDir *specialLic,
2472 int fromSpecFileList)
2473 {
2474 ARGV_t fileNames = NULL;
2475 for (ARGV_const_t fp = *fileList; *fp != NULL; fp++) {
2476 char buf[strlen(*fp) + 1];
2477 const char *s = *fp;
2478 SKIPSPACE(s);
2479 if (*s == '\0')
2480 continue;
2481 fileNames = argvFree(fileNames);
2482 rstrlcpy(buf, s, sizeof(buf));
2483
2484 /* Reset for a new line in %files */
2485 FileEntryFree(&fl->cur);
2486
2487 /* turn explicit flags into %def'd ones (gosh this is hacky...) */
2488 fl->cur.specdFlags = ((unsigned)fl->def.specdFlags) >> 8;
2489 fl->cur.verifyFlags = fl->def.verifyFlags;
2490
2491 if (parseForVerify(buf, 0, &fl->cur) ||
2492 parseForVerify(buf, 1, &fl->def) ||
2493 parseForAttr(fl->pool, buf, 0, &fl->cur) ||
2494 parseForAttr(fl->pool, buf, 1, &fl->def) ||
2495 parseForDev(buf, &fl->cur) ||
2496 parseForConfig(buf, &fl->cur) ||
2497 parseForLang(buf, &fl->cur) ||
2498 parseForCaps(buf, &fl->cur) ||
2499 parseForSimple(buf, &fl->cur, &fileNames))
2500 {
2501 fl->processingFailed = 1;
2502 continue;
2503 }
2504
2505 for (ARGV_const_t fn = fileNames; fn && *fn; fn++) {
2506
2507 /* For file lists that don't come from a spec file list
2508 processing is easy. There are no special files and the
2509 file names don't need to be adjusted. */
2510 if (!fromSpecFileList) {
2511 if (fl->cur.attrFlags & RPMFILE_SPECIALDIR
2512 || fl->cur.attrFlags & RPMFILE_DOCDIR
2513 || fl->cur.attrFlags & RPMFILE_PUBKEY) {
2514 rpmlog(RPMLOG_ERR,
2515 _("Special file in generated file list: %s\n"),
2516 *fn);
2517 fl->processingFailed = 1;
2518 continue;
2519 }
2520 if (fl->cur.attrFlags & RPMFILE_DIR)
2521 fl->cur.isDir = 1;
2522 addFile(fl, *fn, NULL);
2523 continue;
2524 }
2525
2526 /* File list does come from the spec, try to detect special
2527 files and adjust the actual file names. */
2528 if (fl->cur.attrFlags & RPMFILE_SPECIALDIR) {
2529 rpmFlags oattrs = (fl->cur.attrFlags & ~RPMFILE_SPECIALDIR);
2530 specialDir *sdp = NULL;
2531 if (oattrs == RPMFILE_DOC) {
2532 sdp = specialDoc;
2533 } else if (oattrs == RPMFILE_LICENSE) {
2534 sdp = specialLic;
2535 }
2536
2537 if (sdp == NULL || **fn == '/') {
2538 rpmlog(RPMLOG_ERR,
2539 _("Can't mix special %s with other forms: %s\n"),
2540 (oattrs & RPMFILE_DOC) ? "%doc" : "%license", *fn);
2541 fl->processingFailed = 1;
2542 continue;
2543 }
2544
2545 /* save attributes on first special doc/license for later use */
2546 if (*sdp == NULL) {
2547 *sdp = specialDirNew(pkg->header, oattrs);
2548 }
2549 addSpecialFile(*sdp, *fn, &fl->cur, &fl->def);
2550 continue;
2551 }
2552
2553 /* this is now an artificial limitation */
2554 if (fn != fileNames) {
2555 rpmlog(RPMLOG_ERR, _("More than one file on a line: %s\n"),*fn);
2556 fl->processingFailed = 1;
2557 continue;
2558 }
2559
2560 if (fl->cur.attrFlags & RPMFILE_DOCDIR) {
2561 argvAdd(&(fl->docDirs), *fn);
2562 } else if (fl->cur.attrFlags & RPMFILE_PUBKEY) {
2563 (void) processMetadataFile(pkg, fl, *fn, RPMTAG_PUBKEYS);
2564 } else {
2565 if (fl->cur.attrFlags & RPMFILE_DIR)
2566 fl->cur.isDir = 1;
2567 (void) processBinaryFile(pkg, fl, *fn);
2568 }
2569 }
2570
2571 if (fl->cur.caps)
2572 fl->haveCaps = 1;
2573 }
2574 argvFree(fileNames);
2575 }
2576
processPackageFiles(rpmSpec spec,rpmBuildPkgFlags pkgFlags,Package pkg,int didInstall,int test)2577 static rpmRC processPackageFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags,
2578 Package pkg, int didInstall, int test)
2579 {
2580 struct FileList_s fl;
2581 specialDir specialDoc = NULL;
2582 specialDir specialLic = NULL;
2583
2584 pkg->cpioList = NULL;
2585
2586 for (ARGV_const_t fp = pkg->fileFile; fp && *fp != NULL; fp++) {
2587 if (readFilesManifest(spec, pkg, *fp))
2588 return RPMRC_FAIL;
2589 }
2590 /* Init the file list structure */
2591 memset(&fl, 0, sizeof(fl));
2592
2593 fl.pool = rpmstrPoolLink(spec->pool);
2594 /* XXX spec->buildRoot == NULL, then xstrdup("") is returned */
2595 fl.buildRoot = rpmGenPath(spec->rootDir, spec->buildRoot, NULL);
2596 fl.buildRootLen = strlen(fl.buildRoot);
2597
2598 resetPackageFilesDefaults (&fl, pkgFlags);
2599
2600 { char *docs = rpmGetPath("%{?__docdir_path}", NULL);
2601 argvSplit(&fl.docDirs, docs, ":");
2602 free(docs);
2603 }
2604
2605 addPackageFileList (&fl, pkg, &pkg->fileList,
2606 &specialDoc, &specialLic, 1);
2607
2608 /* Now process special docs and licenses if present */
2609 if (specialDoc)
2610 processSpecialDir(spec, pkg, &fl, specialDoc, didInstall, test);
2611 if (specialLic)
2612 processSpecialDir(spec, pkg, &fl, specialLic, didInstall, test);
2613
2614 if (fl.processingFailed)
2615 goto exit;
2616
2617 #if HAVE_LIBDW
2618 /* Check build-ids and add build-ids links for files to package list. */
2619 const char *arch = headerGetString(pkg->header, RPMTAG_ARCH);
2620 if (!rstreq(arch, "noarch")) {
2621 /* Go through the current package list and generate a files list. */
2622 ARGV_t idFiles = NULL;
2623 if (generateBuildIDs (&fl, &idFiles) != 0) {
2624 rpmlog(RPMLOG_ERR, _("Generating build-id links failed\n"));
2625 fl.processingFailed = 1;
2626 argvFree(idFiles);
2627 goto exit;
2628 }
2629
2630 if (idFiles != NULL) {
2631 resetPackageFilesDefaults (&fl, pkgFlags);
2632 addPackageFileList (&fl, pkg, &idFiles, NULL, NULL, 0);
2633 }
2634 argvFree(idFiles);
2635
2636 if (fl.processingFailed)
2637 goto exit;
2638 }
2639 #endif
2640
2641 /* Verify that file attributes scope over hardlinks correctly. */
2642 if (checkHardLinks(&fl.files))
2643 (void) rpmlibNeedsFeature(pkg, "PartialHardlinkSets", "4.0.4-1");
2644
2645 genCpioListAndHeader(&fl, pkg, 0);
2646
2647 exit:
2648 FileListFree(&fl);
2649 specialDirFree(specialDoc);
2650 specialDirFree(specialLic);
2651 return fl.processingFailed ? RPMRC_FAIL : RPMRC_OK;
2652 }
2653
genSourceRpmName(rpmSpec spec)2654 static void genSourceRpmName(rpmSpec spec)
2655 {
2656 if (spec->sourceRpmName == NULL) {
2657 char *nvr = headerGetAsString(spec->packages->header, RPMTAG_NVR);
2658 rasprintf(&spec->sourceRpmName, "%s.%ssrc.rpm", nvr,
2659 spec->noSource ? "no" : "");
2660 free(nvr);
2661 }
2662 }
2663
processSourceFiles(rpmSpec spec,rpmBuildPkgFlags pkgFlags)2664 rpmRC processSourceFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags)
2665 {
2666 struct Source *srcPtr;
2667 struct FileList_s fl;
2668 ARGV_t files = NULL;
2669 Package pkg;
2670 Package sourcePkg = spec->sourcePackage;
2671 static char *_srcdefattr;
2672 static int oneshot;
2673
2674 if (!oneshot) {
2675 _srcdefattr = rpmExpand("%{?_srcdefattr}", NULL);
2676 if (_srcdefattr && !*_srcdefattr)
2677 _srcdefattr = _free(_srcdefattr);
2678 oneshot = 1;
2679 }
2680
2681 genSourceRpmName(spec);
2682 /* Construct the file list and source entries */
2683 argvAdd(&files, spec->specFile);
2684 for (srcPtr = spec->sources; srcPtr != NULL; srcPtr = srcPtr->next) {
2685 char * sfn = rpmGetPath( ((srcPtr->flags & RPMBUILD_ISNO) ? "!" : ""),
2686 srcPtr->path, NULL);
2687 argvAdd(&files, sfn);
2688 free(sfn);
2689 }
2690
2691 for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
2692 for (srcPtr = pkg->icon; srcPtr != NULL; srcPtr = srcPtr->next) {
2693 char * sfn;
2694 sfn = rpmGetPath( ((srcPtr->flags & RPMBUILD_ISNO) ? "!" : ""),
2695 srcPtr->path, NULL);
2696 argvAdd(&files, sfn);
2697 free(sfn);
2698 }
2699 }
2700
2701 sourcePkg->cpioList = NULL;
2702
2703 /* Init the file list structure */
2704 memset(&fl, 0, sizeof(fl));
2705 fl.pool = rpmstrPoolLink(spec->pool);
2706 if (_srcdefattr) {
2707 char *a = rstrscat(NULL, "%defattr ", _srcdefattr, NULL);
2708 parseForAttr(fl.pool, a, 1, &fl.def);
2709 free(a);
2710 }
2711 fl.files.alloced = spec->numSources + 1;
2712 fl.files.recs = xcalloc(fl.files.alloced, sizeof(*fl.files.recs));
2713 fl.pkgFlags = pkgFlags;
2714
2715 for (ARGV_const_t fp = files; *fp != NULL; fp++) {
2716 const char *diskPath = *fp;
2717 char *tmp;
2718 FileListRec flp;
2719
2720 SKIPSPACE(diskPath);
2721 if (! *diskPath)
2722 continue;
2723
2724 flp = &fl.files.recs[fl.files.used];
2725
2726 /* The first source file is the spec file */
2727 flp->flags = (fl.files.used == 0) ? RPMFILE_SPECFILE : 0;
2728 /* files with leading ! are no source files */
2729 if (*diskPath == '!') {
2730 flp->flags |= RPMFILE_GHOST;
2731 diskPath++;
2732 }
2733
2734 tmp = xstrdup(diskPath); /* basename() might modify */
2735 flp->diskPath = xstrdup(diskPath);
2736 flp->cpioPath = xstrdup(basename(tmp));
2737 flp->verifyFlags = RPMVERIFY_ALL;
2738 free(tmp);
2739
2740 if (stat(diskPath, &flp->fl_st)) {
2741 rpmlog(RPMLOG_ERR, _("Bad file: %s: %s\n"),
2742 diskPath, strerror(errno));
2743 fl.processingFailed = 1;
2744 } else {
2745 if (S_ISREG(flp->fl_mode) && flp->fl_size >= UINT32_MAX)
2746 fl.largeFiles = 1;
2747 }
2748
2749 if (fl.def.ar.ar_fmodestr) {
2750 flp->fl_mode &= S_IFMT;
2751 flp->fl_mode |= fl.def.ar.ar_fmode;
2752 }
2753
2754 if (fl.def.ar.ar_user) {
2755 flp->uname = fl.def.ar.ar_user;
2756 } else {
2757 flp->uname = rpmstrPoolId(fl.pool, rpmugUname(flp->fl_uid), 1);
2758 }
2759 if (! flp->uname) {
2760 flp->uname = rpmstrPoolId(fl.pool, rpmugUname(getuid()), 1);
2761 }
2762 if (! flp->uname) {
2763 flp->uname = rpmstrPoolId(fl.pool, UID_0_USER, 1);
2764 }
2765
2766 if (fl.def.ar.ar_group) {
2767 flp->gname = fl.def.ar.ar_group;
2768 } else {
2769 flp->gname = rpmstrPoolId(fl.pool, rpmugGname(flp->fl_gid), 1);
2770 }
2771 if (! flp->gname) {
2772 flp->gname = rpmstrPoolId(fl.pool, rpmugGname(getgid()), 1);
2773 }
2774 if (! flp->gname) {
2775 flp->gname = rpmstrPoolId(fl.pool, GID_0_GROUP, 1);
2776 }
2777
2778 flp->langs = xstrdup("");
2779 fl.files.used++;
2780 }
2781 argvFree(files);
2782
2783 if (! fl.processingFailed) {
2784 if (sourcePkg->header != NULL) {
2785 genCpioListAndHeader(&fl, sourcePkg, 1);
2786 }
2787 }
2788
2789 FileListFree(&fl);
2790 return fl.processingFailed ? RPMRC_FAIL : RPMRC_OK;
2791 }
2792
2793 /**
2794 * Check packaged file list against what's in the build root.
2795 * @param buildRoot path of build root
2796 * @param fileList packaged file list
2797 * @return -1 if skipped, 0 on OK, 1 on error
2798 */
checkFiles(const char * buildRoot,StringBuf fileList)2799 static int checkFiles(const char *buildRoot, StringBuf fileList)
2800 {
2801 static char * const av_ckfile[] = { "%{?__check_files}", NULL };
2802 StringBuf sb_stdout = NULL;
2803 int rc = -1;
2804 char * s = rpmExpand(av_ckfile[0], NULL);
2805
2806 if (!(s && *s))
2807 goto exit;
2808
2809 rpmlog(RPMLOG_NOTICE, _("Checking for unpackaged file(s): %s\n"), s);
2810
2811 rc = rpmfcExec(av_ckfile, fileList, &sb_stdout, 0, buildRoot);
2812 if (rc < 0)
2813 goto exit;
2814
2815 if (sb_stdout) {
2816 int _unpackaged_files_terminate_build =
2817 rpmExpandNumeric("%{?_unpackaged_files_terminate_build}");
2818 const char * t = getStringBuf(sb_stdout);
2819 if ((*t != '\0') && (*t != '\n')) {
2820 rc = (_unpackaged_files_terminate_build) ? 1 : 0;
2821 rpmlog((rc ? RPMLOG_ERR : RPMLOG_WARNING),
2822 _("Installed (but unpackaged) file(s) found:\n%s"), t);
2823 }
2824 }
2825
2826 exit:
2827 freeStringBuf(sb_stdout);
2828 free(s);
2829 return rc;
2830 }
2831
2832 static rpmTag copyTagsFromMainDebug[] = {
2833 RPMTAG_ARCH,
2834 RPMTAG_SUMMARY,
2835 RPMTAG_DESCRIPTION,
2836 RPMTAG_GROUP,
2837 /* see addTargets */
2838 RPMTAG_OS,
2839 RPMTAG_PLATFORM,
2840 RPMTAG_OPTFLAGS,
2841 0
2842 };
2843
2844 /* this is a hack: patch the summary and the description to include
2845 * the correct package name */
patchDebugPackageString(Package dbg,rpmTag tag,Package pkg,Package mainpkg)2846 static void patchDebugPackageString(Package dbg, rpmTag tag, Package pkg, Package mainpkg)
2847 {
2848 const char *oldname, *newname, *old;
2849 char *oldsubst = NULL, *newsubst = NULL, *p;
2850 oldname = headerGetString(mainpkg->header, RPMTAG_NAME);
2851 newname = headerGetString(pkg->header, RPMTAG_NAME);
2852 rasprintf(&oldsubst, "package %s", oldname);
2853 rasprintf(&newsubst, "package %s", newname);
2854 old = headerGetString(dbg->header, tag);
2855 p = old ? strstr(old, oldsubst) : NULL;
2856 if (p) {
2857 char *new = NULL;
2858 rasprintf(&new, "%.*s%s%s", (int)(p - old), old, newsubst, p + strlen(oldsubst));
2859 headerDel(dbg->header, tag);
2860 headerPutString(dbg->header, tag, new);
2861 _free(new);
2862 }
2863 _free(oldsubst);
2864 _free(newsubst);
2865 }
2866
2867 /* Early prototype for use in filterDebuginfoPackage. */
2868 static void addPackageDeps(Package from, Package to, enum rpmTag_e tag);
2869
2870 /* create a new debuginfo subpackage for package pkg from the
2871 * main debuginfo package */
cloneDebuginfoPackage(rpmSpec spec,Package pkg,Package maindbg)2872 static Package cloneDebuginfoPackage(rpmSpec spec, Package pkg, Package maindbg)
2873 {
2874 Package dbg = NULL;
2875 char *dbgname = headerFormat(pkg->header, "%{name}-debuginfo", NULL);
2876
2877 if (lookupPackage(spec, dbgname, PART_NAME|PART_QUIET, &dbg) == RPMRC_OK) {
2878 rpmlog(RPMLOG_WARNING, _("package %s already exists\n"), dbgname);
2879 goto exit;
2880 }
2881
2882 dbg = newPackage(dbgname, spec->pool, &spec->packages);
2883 headerPutString(dbg->header, RPMTAG_NAME, dbgname);
2884 copyInheritedTags(dbg->header, pkg->header);
2885 headerDel(dbg->header, RPMTAG_GROUP);
2886 headerCopyTags(maindbg->header, dbg->header, copyTagsFromMainDebug);
2887 dbg->autoReq = maindbg->autoReq;
2888 dbg->autoProv = maindbg->autoProv;
2889
2890 /* patch summary and description strings */
2891 patchDebugPackageString(dbg, RPMTAG_SUMMARY, pkg, spec->packages);
2892 patchDebugPackageString(dbg, RPMTAG_DESCRIPTION, pkg, spec->packages);
2893
2894 /* Add self-provides (normally done by addTargets) */
2895 addPackageProvides(dbg);
2896 dbg->ds = rpmdsThis(dbg->header, RPMTAG_REQUIRENAME, RPMSENSE_EQUAL);
2897
2898 exit:
2899 _free(dbgname);
2900 return dbg;
2901 }
2902
2903 /* collect the debug files for package pkg and put them into
2904 * a (possibly new) debuginfo subpackage */
filterDebuginfoPackage(rpmSpec spec,Package pkg,Package maindbg,Package dbgsrc,char * buildroot,char * uniquearch)2905 static void filterDebuginfoPackage(rpmSpec spec, Package pkg,
2906 Package maindbg, Package dbgsrc,
2907 char *buildroot, char *uniquearch)
2908 {
2909 rpmfi fi;
2910 ARGV_t files = NULL;
2911 ARGV_t dirs = NULL;
2912 int lastdiridx = -1, dirsadded;
2913 char *path = NULL, *p, *pmin;
2914 size_t buildrootlen = strlen(buildroot);
2915
2916 /* ignore noarch subpackages */
2917 if (rstreq(headerGetString(pkg->header, RPMTAG_ARCH), "noarch"))
2918 return;
2919
2920 if (!uniquearch)
2921 uniquearch = "";
2922
2923 fi = rpmfilesIter(pkg->cpioList, RPMFI_ITER_FWD);
2924 /* Check if the current package has files with debug info
2925 and add them to the file list */
2926 fi = rpmfiInit(fi, 0);
2927 while (rpmfiNext(fi) >= 0) {
2928 const char *name = rpmfiFN(fi);
2929 int namel = strlen(name);
2930
2931 /* strip trailing .debug like in find-debuginfo.sh */
2932 if (namel > 6 && !strcmp(name + namel - 6, ".debug"))
2933 namel -= 6;
2934
2935 /* fileRenameMap doesn't necessarily have to be initialized */
2936 if (pkg->fileRenameMap) {
2937 const char **names = NULL;
2938 int namec = 0;
2939 fileRenameHashGetEntry(pkg->fileRenameMap, name, &names, &namec, NULL);
2940 if (namec) {
2941 if (namec > 1)
2942 rpmlog(RPMLOG_WARNING, _("%s was mapped to multiple filenames"), name);
2943 name = *names;
2944 namel = strlen(name);
2945 }
2946 }
2947
2948 /* generate path */
2949 rasprintf(&path, "%s%s%.*s%s.debug", buildroot, DEBUG_LIB_DIR, namel, name, uniquearch);
2950
2951 /* If that file exists we have debug information for it */
2952 if (access(path, F_OK) == 0) {
2953 /* Append the file list preamble */
2954 if (!files) {
2955 char *attr = mkattr();
2956 argvAdd(&files, attr);
2957 argvAddAttr(&files, RPMFILE_DIR, DEBUG_LIB_DIR);
2958 free(attr);
2959 }
2960
2961 /* Add the files main debug-info file */
2962 argvAdd(&files, path + buildrootlen);
2963
2964 /* Add the dir(s) */
2965 dirsadded = 0;
2966 pmin = path + buildrootlen + strlen(DEBUG_LIB_DIR);
2967 while ((p = strrchr(path + buildrootlen, '/')) != NULL && p > pmin) {
2968 *p = 0;
2969 if (lastdiridx >= 0 && !strcmp(dirs[lastdiridx], path + buildrootlen))
2970 break; /* already added this one */
2971 argvAdd(&dirs, path + buildrootlen);
2972 dirsadded++;
2973 }
2974 if (dirsadded)
2975 lastdiridx = argvCount(dirs) - dirsadded; /* remember longest dir */
2976 }
2977 path = _free(path);
2978 }
2979 rpmfiFree(fi);
2980 /* Exclude debug files for files which were excluded in respective non-debug package */
2981 for (ARGV_const_t excl = pkg->fileExcludeList; excl && *excl; excl++) {
2982 const char *name = *excl;
2983
2984 /* generate path */
2985 rasprintf(&path, "%s%s%s%s.debug", buildroot, DEBUG_LIB_DIR, name, uniquearch);
2986 /* Exclude only debuginfo files which actually exist */
2987 if (access(path, F_OK) == 0) {
2988 char *line = NULL;
2989 rasprintf(&line, "%%exclude %s", path + buildrootlen);
2990 argvAdd(&files, line);
2991 _free(line);
2992 }
2993 path = _free(path);
2994 }
2995
2996 /* add collected directories to file list */
2997 if (dirs) {
2998 int i;
2999 argvSort(dirs, NULL);
3000 for (i = 0; dirs[i]; i++) {
3001 if (!i || strcmp(dirs[i], dirs[i - 1]) != 0)
3002 argvAddAttr(&files, RPMFILE_DIR, dirs[i]);
3003 }
3004 dirs = argvFree(dirs);
3005 }
3006
3007 if (files) {
3008 /* we have collected some files. Now put them in a debuginfo
3009 * package. If this is not the main package, clone the main
3010 * debuginfo package */
3011 if (pkg == spec->packages)
3012 maindbg->fileList = files;
3013 else {
3014 Package dbg = cloneDebuginfoPackage(spec, pkg, maindbg);
3015 dbg->fileList = files;
3016 /* Recommend the debugsource package (or the main debuginfo). */
3017 addPackageDeps(dbg, dbgsrc ? dbgsrc : maindbg,
3018 RPMTAG_RECOMMENDNAME);
3019 }
3020 }
3021 }
3022
3023 /* add the debug dwz files to package pkg.
3024 * return 1 if something was added, 0 otherwise. */
addDebugDwz(Package pkg,char * buildroot)3025 static int addDebugDwz(Package pkg, char *buildroot)
3026 {
3027 int ret = 0;
3028 char *path = NULL;
3029 struct stat sbuf;
3030
3031 rasprintf(&path, "%s%s", buildroot, DEBUG_DWZ_DIR);
3032 if (lstat(path, &sbuf) == 0 && S_ISDIR(sbuf.st_mode)) {
3033 if (!pkg->fileList) {
3034 char *attr = mkattr();
3035 argvAdd(&pkg->fileList, attr);
3036 argvAddAttr(&pkg->fileList, RPMFILE_DIR|RPMFILE_ARTIFACT, DEBUG_LIB_DIR);
3037 free(attr);
3038 }
3039 argvAddAttr(&pkg->fileList, RPMFILE_ARTIFACT, DEBUG_DWZ_DIR);
3040 ret = 1;
3041 }
3042 path = _free(path);
3043 return ret;
3044 }
3045
3046 /* add the debug source files to package pkg.
3047 * return 1 if something was added, 0 otherwise. */
addDebugSrc(Package pkg,char * buildroot)3048 static int addDebugSrc(Package pkg, char *buildroot)
3049 {
3050 int ret = 0;
3051 char *path = NULL;
3052 DIR *d;
3053 struct dirent *de;
3054
3055 /* not needed if we have an extra debugsource subpackage */
3056 if (rpmExpandNumeric("%{?_debugsource_packages}"))
3057 return 0;
3058
3059 rasprintf(&path, "%s%s", buildroot, DEBUG_SRC_DIR);
3060 d = opendir(path);
3061 path = _free(path);
3062 if (d) {
3063 while ((de = readdir(d)) != NULL) {
3064 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
3065 continue;
3066 rasprintf(&path, "%s/%s", DEBUG_SRC_DIR, de->d_name);
3067 if (!pkg->fileList) {
3068 char *attr = mkattr();
3069 argvAdd(&pkg->fileList, attr);
3070 free(attr);
3071 }
3072 argvAdd(&pkg->fileList, path);
3073 path = _free(path);
3074 ret = 1;
3075 }
3076 closedir(d);
3077 }
3078 return ret;
3079 }
3080
3081 /* find the debugsource package, if it has been created.
3082 * We do this simply by searching for a package with the right name. */
findDebugsourcePackage(rpmSpec spec)3083 static Package findDebugsourcePackage(rpmSpec spec)
3084 {
3085 Package pkg = NULL;
3086 if (lookupPackage(spec, "debugsource", PART_SUBNAME|PART_QUIET, &pkg))
3087 return NULL;
3088 return pkg && pkg->fileList ? pkg : NULL;
3089 }
3090
3091 /* find the main debuginfo package. We do this simply by
3092 * searching for a package with the right name. */
findDebuginfoPackage(rpmSpec spec)3093 static Package findDebuginfoPackage(rpmSpec spec)
3094 {
3095 Package pkg = NULL;
3096 if (lookupPackage(spec, "debuginfo", PART_SUBNAME|PART_QUIET, &pkg))
3097 return NULL;
3098 return pkg && pkg->fileList ? pkg : NULL;
3099 }
3100
3101 /* add a dependency (e.g. RPMTAG_REQUIRENAME or RPMTAG_RECOMMENDNAME)
3102 for package "to" into package "from". */
addPackageDeps(Package from,Package to,enum rpmTag_e tag)3103 static void addPackageDeps(Package from, Package to, enum rpmTag_e tag)
3104 {
3105 const char *name;
3106 char *evr, *isaprov;
3107 name = headerGetString(to->header, RPMTAG_NAME);
3108 evr = headerGetAsString(to->header, RPMTAG_EVR);
3109 isaprov = rpmExpand(name, "%{?_isa}", NULL);
3110 addReqProv(from, tag, isaprov, evr, RPMSENSE_EQUAL, 0);
3111 free(isaprov);
3112 free(evr);
3113 }
3114
processBinaryFiles(rpmSpec spec,rpmBuildPkgFlags pkgFlags,int didInstall,int test)3115 rpmRC processBinaryFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags,
3116 int didInstall, int test)
3117 {
3118 Package pkg;
3119 rpmRC rc = RPMRC_OK;
3120 char *buildroot;
3121 char *uniquearch = NULL;
3122 Package maindbg = NULL; /* the (existing) main debuginfo package */
3123 Package deplink = NULL; /* create requires to this package */
3124 /* The debugsource package, if it exists, that the debuginfo package(s)
3125 should Recommend. */
3126 Package dbgsrcpkg = findDebugsourcePackage(spec);
3127
3128 #if HAVE_LIBDW
3129 elf_version (EV_CURRENT);
3130 #endif
3131 check_fileList = newStringBuf();
3132 genSourceRpmName(spec);
3133 buildroot = rpmGenPath(spec->rootDir, spec->buildRoot, NULL);
3134
3135 if (rpmExpandNumeric("%{?_debuginfo_subpackages}")) {
3136 maindbg = findDebuginfoPackage(spec);
3137 if (maindbg) {
3138 /* move debuginfo package to back */
3139 if (maindbg->next) {
3140 Package *pp;
3141 /* dequeue */
3142 for (pp = &spec->packages; *pp != maindbg; pp = &(*pp)->next)
3143 ;
3144 *pp = maindbg->next;
3145 maindbg->next = 0;
3146 /* enqueue at tail */
3147 for (; *pp; pp = &(*pp)->next)
3148 ;
3149 *pp = maindbg;
3150 }
3151 /* delete unsplit file list, we will re-add files back later */
3152 maindbg->fileFile = argvFree(maindbg->fileFile);
3153 maindbg->fileList = argvFree(maindbg->fileList);
3154 if (rpmExpandNumeric("%{?_unique_debug_names}"))
3155 uniquearch = rpmExpand("-%{VERSION}-%{RELEASE}.%{_arch}", NULL);
3156 }
3157 } else if (dbgsrcpkg != NULL) {
3158 /* We have a debugsource package, but no debuginfo subpackages.
3159 The main debuginfo package should recommend the debugsource one. */
3160 Package dbgpkg = findDebuginfoPackage(spec);
3161 if (dbgpkg)
3162 addPackageDeps(dbgpkg, dbgsrcpkg, RPMTAG_RECOMMENDNAME);
3163 }
3164
3165 for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
3166 char *nvr;
3167 const char *a;
3168 int header_color;
3169 int arch_color;
3170
3171 if (pkg == maindbg) {
3172 /* if there is just one debuginfo package, we put our extra stuff
3173 * in it. Otherwise we put it in the main debug package */
3174 Package extradbg = !maindbg->fileList && maindbg->next && !maindbg->next->next ?
3175 maindbg->next : maindbg;
3176 if (addDebugDwz(extradbg, buildroot))
3177 deplink = extradbg;
3178 if (addDebugSrc(extradbg, buildroot))
3179 deplink = extradbg;
3180 if (dbgsrcpkg != NULL)
3181 addPackageDeps(extradbg, dbgsrcpkg, RPMTAG_RECOMMENDNAME);
3182 maindbg = NULL; /* all normal packages processed */
3183 }
3184
3185 if (pkg->fileList == NULL)
3186 continue;
3187
3188 headerPutString(pkg->header, RPMTAG_SOURCERPM, spec->sourceRpmName);
3189
3190 nvr = headerGetAsString(pkg->header, RPMTAG_NVRA);
3191 rpmlog(RPMLOG_NOTICE, _("Processing files: %s\n"), nvr);
3192 free(nvr);
3193
3194 if ((rc = processPackageFiles(spec, pkgFlags, pkg, didInstall, test)) != RPMRC_OK)
3195 goto exit;
3196
3197 if (maindbg)
3198 filterDebuginfoPackage(spec, pkg, maindbg, dbgsrcpkg,
3199 buildroot, uniquearch);
3200 else if (deplink && pkg != deplink)
3201 addPackageDeps(pkg, deplink, RPMTAG_REQUIRENAME);
3202
3203 if ((rc = rpmfcGenerateDepends(spec, pkg)) != RPMRC_OK)
3204 goto exit;
3205
3206 a = headerGetString(pkg->header, RPMTAG_ARCH);
3207 header_color = headerGetNumber(pkg->header, RPMTAG_HEADERCOLOR);
3208 if (!rstreq(a, "noarch")) {
3209 arch_color = rpmGetArchColor(a);
3210 if (arch_color > 0 && header_color > 0 &&
3211 !(arch_color & header_color)) {
3212 rpmlog(RPMLOG_WARNING,
3213 _("Binaries arch (%d) not matching the package arch (%d).\n"),
3214 header_color, arch_color);
3215 }
3216 } else if (header_color != 0) {
3217 int terminate = rpmExpandNumeric("%{?_binaries_in_noarch_packages_terminate_build}");
3218 rpmlog(terminate ? RPMLOG_ERR : RPMLOG_WARNING,
3219 _("Arch dependent binaries in noarch package\n"));
3220 if (terminate) {
3221 rc = RPMRC_FAIL;
3222 goto exit;
3223 }
3224 }
3225 }
3226
3227 /* Now we have in fileList list of files from all packages.
3228 * We pass it to a script which does the work of finding missing
3229 * and duplicated files.
3230 */
3231
3232
3233 if (checkFiles(spec->buildRoot, check_fileList) > 0) {
3234 rc = RPMRC_FAIL;
3235 }
3236 exit:
3237 check_fileList = freeStringBuf(check_fileList);
3238 _free(buildroot);
3239 _free(uniquearch);
3240
3241 return rc;
3242 }
3243