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