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