Add macro %isu_package to generate ISU Package
[platform/upstream/rpm.git] / build / files.c
1 /** \ingroup rpmbuild
2  * \file build/files.c
3  *  The post-build, pre-packaging file tree walk to assemble the package
4  *  manifest.
5  */
6
7 #include "system.h"
8
9 #define MYALLPERMS      07777
10
11 #include <errno.h>
12 #include <regex.h>
13 #if WITH_CAP
14 #include <sys/capability.h>
15 #endif
16
17 #include <rpm/rpmpgp.h>
18 #include <rpm/argv.h>
19 #include <rpm/rpmfc.h>
20 #include <rpm/rpmfileutil.h>    /* rpmDoDigest() */
21 #include <rpm/rpmlog.h>
22 #include <rpm/rpmbase64.h>
23
24 #if HAVE_GELF_H
25 #include <gelf.h>
26 #endif
27
28 #include "rpmio/rpmio_internal.h"       /* XXX rpmioSlurp */
29 #include "misc/fts.h"
30 #include "lib/cpio.h"
31 #include "lib/rpmfi_internal.h" /* XXX fi->apath */
32 #include "lib/rpmug.h"
33 #include "build/rpmbuild_internal.h"
34 #include "build/rpmbuild_misc.h"
35
36 #include "debug.h"
37 #include <libgen.h>
38
39 #define SKIPSPACE(s) { while (*(s) && risspace(*(s))) (s)++; }
40 #define SKIPWHITE(_x)   {while(*(_x) && (risspace(*_x) || *(_x) == ',')) (_x)++;}
41 #define SKIPNONWHITE(_x){while(*(_x) &&!(risspace(*_x) || *(_x) == ',')) (_x)++;}
42
43 /**
44  */
45 enum specfFlags_e {
46     SPECD_DEFFILEMODE   = (1 << 0),
47     SPECD_DEFDIRMODE    = (1 << 1),
48     SPECD_DEFUID        = (1 << 2),
49     SPECD_DEFGID        = (1 << 3),
50     SPECD_DEFVERIFY     = (1 << 4),
51
52     SPECD_FILEMODE      = (1 << 8),
53     SPECD_DIRMODE       = (1 << 9),
54     SPECD_UID           = (1 << 10),
55     SPECD_GID           = (1 << 11),
56     SPECD_VERIFY        = (1 << 12)
57 };
58
59 typedef rpmFlags specfFlags;
60
61 /* internal %files parsing state attributes */
62 enum parseAttrs_e {
63     RPMFILE_EXCLUDE     = (1 << 16),    /*!< from %%exclude */
64     RPMFILE_DOCDIR      = (1 << 17),    /*!< from %%docdir */
65     RPMFILE_DIR         = (1 << 18),    /*!< from %%dir */
66     RPMFILE_SPECIALDIR  = (1 << 19),    /*!< from special %%doc */
67 };
68
69 /* bits up to 15 (for now) reserved for exported rpmfileAttrs */
70 #define PARSEATTR_MASK 0x0000ffff
71
72 /**
73  */
74 typedef struct FileListRec_s {
75     struct stat fl_st;
76 #define fl_dev  fl_st.st_dev
77 #define fl_ino  fl_st.st_ino
78 #define fl_mode fl_st.st_mode
79 #define fl_nlink fl_st.st_nlink
80 #define fl_uid  fl_st.st_uid
81 #define fl_gid  fl_st.st_gid
82 #define fl_rdev fl_st.st_rdev
83 #define fl_size fl_st.st_size
84 #define fl_mtime fl_st.st_mtime
85
86     char *diskPath;             /* get file from here       */
87     char *cpioPath;             /* filename in cpio archive */
88     const char *uname;
89     const char *gname;
90     unsigned    flags;
91     specfFlags  specdFlags;     /* which attributes have been explicitly specified. */
92     rpmVerifyFlags verifyFlags;
93     char *langs;                /* XXX locales separated with | */
94     char *caps;
95 } * FileListRec;
96
97 /**
98  */
99 typedef struct AttrRec_s {
100     char *ar_fmodestr;
101     char *ar_dmodestr;
102     char *ar_user;
103     char *ar_group;
104     mode_t      ar_fmode;
105     mode_t      ar_dmode;
106 } * AttrRec;
107
108 static struct AttrRec_s root_ar = { NULL, NULL, "root", "root", 0, 0 };
109
110 /* list of files */
111 static StringBuf check_fileList = NULL;
112
113 typedef struct specialDir_s {
114     char * dirname;
115     ARGV_t files;
116     struct AttrRec_s ar;
117     struct AttrRec_s def_ar;
118     rpmFlags sdtype;
119 } * specialDir;
120
121 typedef struct FileEntry_s {
122     rpmfileAttrs attrFlags;
123     specfFlags specdFlags;
124     rpmVerifyFlags verifyFlags;
125     struct AttrRec_s ar;
126
127     ARGV_t langs;
128     char *caps;
129
130     /* these are only ever relevant for current entry */
131     unsigned devtype;
132     unsigned devmajor;
133     int devminor;
134     int isDir;
135 } * FileEntry;
136
137 typedef struct FileRecords_s {
138     FileListRec recs;
139     int alloced;
140     int used;
141 } * FileRecords;
142
143 /**
144  * Package file tree walk data.
145  */
146 typedef struct FileList_s {
147     /* global filelist state */
148     char * buildRoot;
149     int processingFailed;
150     int haveCaps;
151     int largeFiles;
152     ARGV_t docDirs;
153     rpmBuildPkgFlags pkgFlags;
154
155     /* actual file records */
156     struct FileRecords_s files;
157
158     /* active defaults */
159     struct FileEntry_s def;
160
161     /* current file-entry state */
162     struct FileEntry_s cur;
163 } * FileList;
164
165 /**
166  */
167 static void nullAttrRec(AttrRec ar)
168 {
169     ar->ar_fmodestr = NULL;
170     ar->ar_dmodestr = NULL;
171     ar->ar_user = NULL;
172     ar->ar_group = NULL;
173     ar->ar_fmode = 0;
174     ar->ar_dmode = 0;
175 }
176
177 /**
178  */
179 static void freeAttrRec(AttrRec ar)
180 {
181     ar->ar_fmodestr = _free(ar->ar_fmodestr);
182     ar->ar_dmodestr = _free(ar->ar_dmodestr);
183     ar->ar_user = _free(ar->ar_user);
184     ar->ar_group = _free(ar->ar_group);
185     /* XXX doesn't free ar (yet) */
186     return;
187 }
188
189 /**
190  */
191 static void dupAttrRec(const AttrRec oar, AttrRec nar)
192 {
193     if (oar == nar)
194         return;
195     freeAttrRec(nar);
196     nar->ar_fmodestr = (oar->ar_fmodestr ? xstrdup(oar->ar_fmodestr) : NULL);
197     nar->ar_dmodestr = (oar->ar_dmodestr ? xstrdup(oar->ar_dmodestr) : NULL);
198     nar->ar_user = (oar->ar_user ? xstrdup(oar->ar_user) : NULL);
199     nar->ar_group = (oar->ar_group ? xstrdup(oar->ar_group) : NULL);
200     nar->ar_fmode = oar->ar_fmode;
201     nar->ar_dmode = oar->ar_dmode;
202 }
203
204 #if 0
205 /**
206  */
207 static void dumpAttrRec(const char * msg, AttrRec ar)
208 {
209     if (msg)
210         fprintf(stderr, "%s:\t", msg);
211     fprintf(stderr, "(%s, %s, %s, %s)\n",
212         ar->ar_fmodestr,
213         ar->ar_user,
214         ar->ar_group,
215         ar->ar_dmodestr);
216 }
217 #endif
218
219 static void FileEntryFree(FileEntry entry)
220 {
221     freeAttrRec(&(entry->ar));
222     argvFree(entry->langs);
223     memset(entry, 0, sizeof(*entry));
224 }
225
226 /**
227  * strtokWithQuotes.
228  * @param s
229  * @param delim
230  */
231 static char *strtokWithQuotes(char *s, const char *delim)
232 {
233     static char *olds = NULL;
234     char *token;
235
236     if (s == NULL)
237         s = olds;
238     if (s == NULL)
239         return NULL;
240
241     /* Skip leading delimiters */
242     s += strspn(s, delim);
243     if (*s == '\0')
244         return NULL;
245
246     /* Find the end of the token.  */
247     token = s;
248     if (*token == '"') {
249         token++;
250         /* Find next " char */
251         s = strchr(token, '"');
252     } else {
253         s = strpbrk(token, delim);
254     }
255
256     /* Terminate it */
257     if (s == NULL) {
258         /* This token finishes the string */
259         olds = strchr(token, '\0');
260     } else {
261         /* Terminate the token and make olds point past it */
262         *s = '\0';
263         olds = s+1;
264     }
265
266     return token;
267 }
268
269 /**
270  */
271 typedef const struct VFA {
272     const char * attribute;
273     int flag;
274 } VFA_t;
275
276 /**
277  */
278 static VFA_t const verifyAttrs[] = {
279     { "md5",            RPMVERIFY_FILEDIGEST },
280     { "filedigest",     RPMVERIFY_FILEDIGEST },
281     { "size",           RPMVERIFY_FILESIZE },
282     { "link",           RPMVERIFY_LINKTO },
283     { "user",           RPMVERIFY_USER },
284     { "owner",          RPMVERIFY_USER },
285     { "group",          RPMVERIFY_GROUP },
286     { "mtime",          RPMVERIFY_MTIME },
287     { "mode",           RPMVERIFY_MODE },
288     { "rdev",           RPMVERIFY_RDEV },
289     { "caps",           RPMVERIFY_CAPS },
290     { NULL, 0 }
291 };
292
293 /**
294  * Add 'provides' information to debuginfo package
295  * @param h             Header information from debuginfo package
296  * @return              nothing
297  */
298 static void addPackageProvides_for_debuginfo_pkg(Header h)
299 {
300     const char *arch, *name;
301     char *evr, *isaprov;
302     rpmsenseFlags pflags = RPMSENSE_EQUAL;
303
304     /* <name> = <evr> provide */
305     name = headerGetString(h, RPMTAG_NAME);
306     arch = headerGetString(h, RPMTAG_ARCH);
307     evr = headerGetAsString(h, RPMTAG_EVR);
308     headerPutString(h, RPMTAG_PROVIDENAME, name);
309     headerPutString(h, RPMTAG_PROVIDEVERSION, evr);
310     headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &pflags, 1);
311
312     /*
313      * <name>(<isa>) = <evr> provide
314      * FIXME: noarch needs special casing for now as BuildArch: noarch doesn't
315      * cause reading in the noarch macros :-/
316      */
317
318     isaprov = rpmExpand(name, "%{?_isa}", NULL);
319     if (!rstreq(arch, "noarch") && !rstreq(name, isaprov)) {
320         headerPutString(h, RPMTAG_PROVIDENAME, isaprov);
321         headerPutString(h, RPMTAG_PROVIDEVERSION, evr);
322         headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &pflags, 1);
323     }
324
325     free(isaprov);
326     free(evr);
327 }
328
329 static rpmFlags vfaMatch(VFA_t *attrs, const char *token, rpmFlags *flags)
330 {
331     VFA_t *vfa;
332
333     for (vfa = attrs; vfa->attribute != NULL; vfa++) {
334         if (rstreq(token, vfa->attribute)) {
335             *flags |= vfa->flag;
336             break;
337         }
338     }
339     return vfa->flag;
340 }
341
342 /**
343  * Parse %verify and %defverify from file manifest.
344  * @param buf           current spec file line
345  * @param def           parse for %defverify or %verify?
346  * @param entry         file entry data (current or default)
347  * @return              RPMRC_OK on success
348  */
349 static rpmRC parseForVerify(char * buf, int def, FileEntry entry)
350 {
351     char *p, *pe, *q = NULL;
352     const char *name = def ? "%defverify" : "%verify";
353     int negated = 0;
354     rpmVerifyFlags verifyFlags = RPMVERIFY_NONE;
355     rpmRC rc = RPMRC_FAIL;
356
357     if ((p = strstr(buf, name)) == NULL)
358         return RPMRC_OK;
359
360     for (pe = p; (pe-p) < strlen(name); pe++)
361         *pe = ' ';
362
363     SKIPSPACE(pe);
364
365     if (*pe != '(') {
366         rpmlog(RPMLOG_ERR, _("Missing '(' in %s %s\n"), name, pe);
367         goto exit;
368     }
369
370     /* Bracket %*verify args */
371     *pe++ = ' ';
372     for (p = pe; *pe && *pe != ')'; pe++)
373         {};
374
375     if (*pe == '\0') {
376         rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
377         goto exit;
378     }
379
380     /* Localize. Erase parsed string */
381     q = xmalloc((pe-p) + 1);
382     rstrlcpy(q, p, (pe-p) + 1);
383     while (p <= pe)
384         *p++ = ' ';
385
386     for (p = q; *p != '\0'; p = pe) {
387         SKIPWHITE(p);
388         if (*p == '\0')
389             break;
390         pe = p;
391         SKIPNONWHITE(pe);
392         if (*pe != '\0')
393             *pe++ = '\0';
394
395         if (vfaMatch(verifyAttrs, p, &verifyFlags))
396             continue;
397
398         if (rstreq(p, "not")) {
399             negated ^= 1;
400         } else {
401             rpmlog(RPMLOG_ERR, _("Invalid %s token: %s\n"), name, p);
402             goto exit;
403         }
404     }
405
406     entry->verifyFlags = negated ? ~(verifyFlags) : verifyFlags;
407     entry->specdFlags |= SPECD_VERIFY;
408     rc = RPMRC_OK;
409
410 exit:
411     free(q);
412
413     return rc;
414 }
415
416 #define isAttrDefault(_ars)     ((_ars)[0] == '-' && (_ars)[1] == '\0')
417
418 /**
419  * Parse %dev from file manifest.
420  * @param buf           current spec file line
421  * @param cur           current file entry data
422  * @return              RPMRC_OK on success
423  */
424 static rpmRC parseForDev(char * buf, FileEntry cur)
425 {
426     const char * name;
427     const char * errstr = NULL;
428     char *p, *pe, *q = NULL;
429     rpmRC rc = RPMRC_FAIL;      /* assume error */
430
431     if ((p = strstr(buf, (name = "%dev"))) == NULL)
432         return RPMRC_OK;
433
434     for (pe = p; (pe-p) < strlen(name); pe++)
435         *pe = ' ';
436     SKIPSPACE(pe);
437
438     if (*pe != '(') {
439         errstr = "'('";
440         goto exit;
441     }
442
443     /* Bracket %dev args */
444     *pe++ = ' ';
445     for (p = pe; *pe && *pe != ')'; pe++)
446         {};
447     if (*pe != ')') {
448         errstr = "')'";
449         goto exit;
450     }
451
452     /* Localize. Erase parsed string */
453     q = xmalloc((pe-p) + 1);
454     rstrlcpy(q, p, (pe-p) + 1);
455     while (p <= pe)
456         *p++ = ' ';
457
458     p = q; SKIPWHITE(p);
459     pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
460     if (*p == 'b')
461         cur->devtype = 'b';
462     else if (*p == 'c')
463         cur->devtype = 'c';
464     else {
465         errstr = "devtype";
466         goto exit;
467     }
468
469     p = pe; SKIPWHITE(p);
470     pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe = '\0';
471     for (pe = p; *pe && risdigit(*pe); pe++)
472         {} ;
473     if (*pe == '\0') {
474         cur->devmajor = atoi(p);
475         if (!(cur->devmajor >= 0 && cur->devmajor < 256)) {
476             errstr = "devmajor";
477             goto exit;
478         }
479         pe++;
480     } else {
481         errstr = "devmajor";
482         goto exit;
483     }
484
485     p = pe; SKIPWHITE(p);
486     pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe = '\0';
487     for (pe = p; *pe && risdigit(*pe); pe++)
488         {} ;
489     if (*pe == '\0') {
490         cur->devminor = atoi(p);
491         if (!(cur->devminor >= 0 && cur->devminor < 256)) {
492             errstr = "devminor";
493             goto exit;
494         }
495     } else {
496         errstr = "devminor";
497         goto exit;
498     }
499
500     rc = RPMRC_OK;
501
502 exit:
503     if (rc) {
504         rpmlog(RPMLOG_ERR, _("Missing %s in %s %s\n"), errstr, name, p);
505     }
506     free(q);
507     return rc;
508 }
509
510 /**
511  * Parse %attr and %defattr from file manifest.
512  * @param buf           current spec file line
513  * @param def           parse for %defattr or %attr?
514  * @param entry         file entry data (current / default)
515  * @return              0 on success
516  */
517 static rpmRC parseForAttr(char * buf, int def, FileEntry entry)
518 {
519     const char *name = def ? "%defattr" : "%attr";
520     char *p, *pe, *q = NULL;
521     int x;
522     struct AttrRec_s arbuf;
523     AttrRec ar = &arbuf;
524     rpmRC rc = RPMRC_FAIL;
525
526     if ((p = strstr(buf, name)) == NULL)
527         return RPMRC_OK;
528
529     for (pe = p; (pe-p) < strlen(name); pe++)
530         *pe = ' ';
531
532     SKIPSPACE(pe);
533
534     if (*pe != '(') {
535         rpmlog(RPMLOG_ERR, _("Missing '(' in %s %s\n"), name, pe);
536         goto exit;
537     }
538
539     /* Bracket %*attr args */
540     *pe++ = ' ';
541     for (p = pe; *pe && *pe != ')'; pe++)
542         {};
543
544     if (def) {  /* %defattr */
545         char *r = pe;
546         r++;
547         SKIPSPACE(r);
548         if (*r != '\0') {
549             rpmlog(RPMLOG_ERR,
550                      _("Non-white space follows %s(): %s\n"), name, r);
551             goto exit;
552         }
553     }
554
555     /* Localize. Erase parsed string */
556     q = xmalloc((pe-p) + 1);
557     rstrlcpy(q, p, (pe-p) + 1);
558     while (p <= pe)
559         *p++ = ' ';
560
561     nullAttrRec(ar);
562
563     p = q; SKIPWHITE(p);
564     if (*p != '\0') {
565         pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
566         ar->ar_fmodestr = p;
567         p = pe; SKIPWHITE(p);
568     }
569     if (*p != '\0') {
570         pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
571         ar->ar_user = p;
572         p = pe; SKIPWHITE(p);
573     }
574     if (*p != '\0') {
575         pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
576         ar->ar_group = p;
577         p = pe; SKIPWHITE(p);
578     }
579     if (*p != '\0' && def) {    /* %defattr */
580         pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
581         ar->ar_dmodestr = p;
582         p = pe; SKIPWHITE(p);
583     }
584
585     if (!(ar->ar_fmodestr && ar->ar_user && ar->ar_group) || *p != '\0') {
586         rpmlog(RPMLOG_ERR, _("Bad syntax: %s(%s)\n"), name, q);
587         goto exit;
588     }
589
590     /* Do a quick test on the mode argument and adjust for "-" */
591     if (ar->ar_fmodestr && !isAttrDefault(ar->ar_fmodestr)) {
592         unsigned int ui;
593         x = sscanf(ar->ar_fmodestr, "%o", &ui);
594         if ((x == 0) || (ar->ar_fmode & ~MYALLPERMS)) {
595             rpmlog(RPMLOG_ERR, _("Bad mode spec: %s(%s)\n"), name, q);
596             goto exit;
597         }
598         ar->ar_fmode = ui;
599     } else {
600         ar->ar_fmodestr = NULL;
601     }
602
603     if (ar->ar_dmodestr && !isAttrDefault(ar->ar_dmodestr)) {
604         unsigned int ui;
605         x = sscanf(ar->ar_dmodestr, "%o", &ui);
606         if ((x == 0) || (ar->ar_dmode & ~MYALLPERMS)) {
607             rpmlog(RPMLOG_ERR, _("Bad dirmode spec: %s(%s)\n"), name, q);
608             goto exit;
609         }
610         ar->ar_dmode = ui;
611     } else {
612         ar->ar_dmodestr = NULL;
613     }
614
615     if (!(ar->ar_user && !isAttrDefault(ar->ar_user))) {
616         ar->ar_user = NULL;
617     }
618
619     if (!(ar->ar_group && !isAttrDefault(ar->ar_group))) {
620         ar->ar_group = NULL;
621     }
622
623     dupAttrRec(ar, &(entry->ar));
624
625     /* XXX fix all this */
626     entry->specdFlags |= SPECD_UID | SPECD_GID | SPECD_FILEMODE | SPECD_DIRMODE;
627     rc = RPMRC_OK;
628
629 exit:
630     free(q);
631     
632     return rc;
633 }
634
635 static VFA_t const configAttrs[] = {
636     { "missingok",      RPMFILE_MISSINGOK },
637     { "noreplace",      RPMFILE_NOREPLACE },
638     { NULL, 0 }
639 };
640
641 /**
642  * Parse %config from file manifest.
643  * @param buf           current spec file line
644  * @param cur           current file entry data
645  * @return              RPMRC_OK on success
646  */
647 static rpmRC parseForConfig(char * buf, FileEntry cur)
648 {
649     char *p, *pe, *q = NULL;
650     const char *name;
651     rpmRC rc = RPMRC_FAIL;
652
653     if ((p = strstr(buf, (name = "%config"))) == NULL)
654         return RPMRC_OK;
655
656     cur->attrFlags |= RPMFILE_CONFIG;
657
658     /* Erase "%config" token. */
659     for (pe = p; (pe-p) < strlen(name); pe++)
660         *pe = ' ';
661     SKIPSPACE(pe);
662     if (*pe != '(')
663         return RPMRC_OK;
664
665     /* Bracket %config args */
666     *pe++ = ' ';
667     for (p = pe; *pe && *pe != ')'; pe++)
668         {};
669
670     if (*pe == '\0') {
671         rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
672         goto exit;
673     }
674
675     /* Localize. Erase parsed string. */
676     q = xmalloc((pe-p) + 1);
677     rstrlcpy(q, p, (pe-p) + 1);
678     while (p <= pe)
679         *p++ = ' ';
680
681     for (p = q; *p != '\0'; p = pe) {
682         SKIPWHITE(p);
683         if (*p == '\0')
684             break;
685         pe = p;
686         SKIPNONWHITE(pe);
687         if (*pe != '\0')
688             *pe++ = '\0';
689         if (!vfaMatch(configAttrs, p, &(cur->attrFlags))) {
690             rpmlog(RPMLOG_ERR, _("Invalid %s token: %s\n"), name, p);
691             goto exit;
692         }
693     }
694     rc = RPMRC_OK;
695     
696 exit:
697     free(q);
698
699     return rc;
700 }
701
702 static rpmRC addLang(ARGV_t *av, const char *lang, size_t n, const char *ent)
703 {
704     rpmRC rc = RPMRC_FAIL;
705     char lbuf[n + 1];
706     rstrlcpy(lbuf, lang, sizeof(lbuf));
707     SKIPWHITE(ent);
708
709     /* Sanity check locale length */
710     if (n < 1 || (n == 1 && *lang != 'C') || n >= 32) {
711         rpmlog(RPMLOG_ERR, _("Unusual locale length: \"%s\" in %%lang(%s)\n"),
712                 lbuf, ent);
713         goto exit;
714     }
715
716     /* Check for duplicate locales */
717     if (argvSearch(*av, lbuf, NULL)) {
718         rpmlog(RPMLOG_WARNING, _("Duplicate locale %s in %%lang(%s)\n"),
719                 lbuf, ent);
720     } else {
721         argvAdd(av, lbuf);
722         argvSort(*av, NULL);
723     }
724     rc = RPMRC_OK;
725
726 exit:
727     return rc;
728 }
729
730 /**
731  * Parse %lang from file manifest.
732  * @param buf           current spec file line
733  * @param cur           current file entry data
734  * @return              RPMRC_OK on success
735  */
736 static rpmRC parseForLang(char * buf, FileEntry cur)
737 {
738     char *p, *pe, *q = NULL;
739     const char *name;
740     rpmRC rc = RPMRC_FAIL;
741
742   while ((p = strstr(buf, (name = "%lang"))) != NULL) {
743
744     for (pe = p; (pe-p) < strlen(name); pe++)
745         *pe = ' ';
746     SKIPSPACE(pe);
747
748     if (*pe != '(') {
749         rpmlog(RPMLOG_ERR, _("Missing '(' in %s %s\n"), name, pe);
750         goto exit;
751     }
752
753     /* Bracket %lang args */
754     *pe = ' ';
755     for (pe = p; *pe && *pe != ')'; pe++)
756         {};
757
758     if (*pe == '\0') {
759         rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
760         goto exit;
761     }
762
763     /* Localize. Erase parsed string. */
764     q = xmalloc((pe-p) + 1);
765     rstrlcpy(q, p, (pe-p) + 1);
766     while (p <= pe)
767         *p++ = ' ';
768
769     /* Parse multiple arguments from %lang */
770     for (p = q; *p != '\0'; p = pe) {
771         SKIPWHITE(p);
772         pe = p;
773         SKIPNONWHITE(pe);
774
775         if (addLang(&(cur->langs), p, (pe-p), q))
776             goto exit;
777
778         if (*pe == ',') pe++;   /* skip , if present */
779     }
780   }
781
782     rc = RPMRC_OK;
783
784 exit:
785     free(q);
786
787     return rc;
788 }
789
790 /**
791  * Parse %caps from file manifest.
792  * @param buf           current spec file line
793  * @param cur           current file entry data
794  * @return              RPMRC_OK on success
795  */
796 static rpmRC parseForCaps(char * buf, FileEntry cur)
797 {
798     char *p, *pe, *q = NULL;
799     const char *name;
800     rpmRC rc = RPMRC_FAIL;
801
802     if ((p = strstr(buf, (name = "%caps"))) == NULL)
803         return RPMRC_OK;
804
805     /* Erase "%caps" token. */
806     for (pe = p; (pe-p) < strlen(name); pe++)
807         *pe = ' ';
808     SKIPSPACE(pe);
809     if (*pe != '(')
810         return RPMRC_OK;
811
812     /* Bracket %caps args */
813     *pe++ = ' ';
814     for (p = pe; *pe && *pe != ')'; pe++)
815         {};
816
817     if (*pe == '\0') {
818         rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
819         goto exit;
820     }
821
822     /* Localize. Erase parsed string. */
823     q = xmalloc((pe-p) + 1);
824     rstrlcpy(q, p, (pe-p) + 1);
825     while (p <= pe)
826         *p++ = ' ';
827
828 #if WITH_CAP
829     {
830         char *captxt = NULL;
831         cap_t fcaps = cap_from_text(q);
832         if (fcaps == NULL) {
833             rpmlog(RPMLOG_ERR, _("Invalid capability: %s\n"), q);
834             goto exit;
835         }
836         /* run our string through cap_to_text() to get libcap presentation */
837         captxt = cap_to_text(fcaps, NULL);
838         cur->caps = xstrdup(captxt);
839         cap_free(captxt);
840         cap_free(fcaps);
841     }
842 #else
843         rpmlog(RPMLOG_ERR, _("File capability support not built in\n"));
844         goto exit;
845 #endif
846
847     rc = RPMRC_OK;
848     
849 exit:
850     free(q);
851
852     return rc;
853 }
854 /**
855  */
856 static VFA_t const virtualAttrs[] = {
857     { "%dir",           RPMFILE_DIR },
858     { "%docdir",        RPMFILE_DOCDIR },
859     { "%doc",           RPMFILE_DOC },
860     { "%ghost",         RPMFILE_GHOST },
861     { "%exclude",       RPMFILE_EXCLUDE },
862     { "%readme",        RPMFILE_README },
863     { "%license",       RPMFILE_LICENSE },
864     { "%pubkey",        RPMFILE_PUBKEY },
865     { "%manifest",      RPMFILE_SECMANIFEST },
866     { NULL, 0 }
867 };
868
869 /**
870  * Parse simple attributes (e.g. %dir) from file manifest.
871  * @param buf           current spec file line
872  * @param cur           current file entry data
873  * @retval *fileNames   file names
874  * @return              RPMRC_OK on success
875  */
876 static rpmRC parseForSimple(char * buf, FileEntry cur, ARGV_t * fileNames)
877 {
878     char *s, *t;
879     rpmRC res = RPMRC_OK;
880     int allow_relative = (RPMFILE_PUBKEY|RPMFILE_DOC|RPMFILE_LICENSE|RPMFILE_SECMANIFEST);
881
882     t = buf;
883     while ((s = strtokWithQuotes(t, " \t\n")) != NULL) {
884         t = NULL;
885
886         /* Set flags for virtual file attributes */
887         if (vfaMatch(virtualAttrs, s, &(cur->attrFlags)))
888             continue;
889
890         /* normally paths need to be absolute */
891         if (*s != '/') {
892            if (!(cur->attrFlags & allow_relative)) {
893                 rpmlog(RPMLOG_ERR, _("File must begin with \"/\": %s\n"), s);
894                 res = RPMRC_FAIL;
895                 continue;
896             }
897             /* non-absolute %doc and %license paths are special */
898             if (cur->attrFlags & (RPMFILE_DOC | RPMFILE_LICENSE))
899                 cur->attrFlags |= RPMFILE_SPECIALDIR;
900         }
901         argvAdd(fileNames, s);
902     }
903
904     return res;
905 }
906
907 /**
908  */
909 static int compareFileListRecs(const void * ap, const void * bp)        
910 {
911     const char *a = ((FileListRec)ap)->cpioPath;
912     const char *b = ((FileListRec)bp)->cpioPath;
913     return strcmp(a, b);
914 }
915
916 /**
917  * Test if file is located in a %docdir.
918  * @param docDirs       doc dirs
919  * @param fileName      file path
920  * @return              1 if doc file, 0 if not
921  */
922 static int isDoc(ARGV_const_t docDirs, const char * fileName)   
923 {
924     size_t k, l;
925
926     k = strlen(fileName);
927     for (ARGV_const_t dd = docDirs; *dd; dd++) {
928         l = strlen(*dd);
929         if (l < k && rstreqn(fileName, *dd, l) && fileName[l] == '/')
930             return 1;
931     }
932     return 0;
933 }
934
935 static int isHardLink(FileListRec flp, FileListRec tlp)
936 {
937     return ((S_ISREG(flp->fl_mode) && S_ISREG(tlp->fl_mode)) &&
938             ((flp->fl_nlink > 1) && (flp->fl_nlink == tlp->fl_nlink)) &&
939             (flp->fl_ino == tlp->fl_ino) && 
940             (flp->fl_dev == tlp->fl_dev));
941 }
942
943 /**
944  * Verify that file attributes scope over hardlinks correctly.
945  * If partial hardlink sets are possible, then add tracking dependency.
946  * @param fl            package file records
947  * @return              1 if partial hardlink sets can exist, 0 otherwise.
948  */
949 static int checkHardLinks(FileRecords files)
950 {
951     FileListRec ilp, jlp;
952     int i, j;
953
954     for (i = 0;  i < files->used; i++) {
955         ilp = files->recs + i;
956         if (!(S_ISREG(ilp->fl_mode) && ilp->fl_nlink > 1))
957             continue;
958
959         for (j = i + 1; j < files->used; j++) {
960             jlp = files->recs + j;
961             if (isHardLink(ilp, jlp)) {
962                 return 1;
963             }
964         }
965     }
966     return 0;
967 }
968
969 static int seenHardLink(FileRecords files, FileListRec flp, rpm_ino_t *fileid)
970 {
971     for (FileListRec ilp = files->recs; ilp < flp; ilp++) {
972         if (isHardLink(flp, ilp)) {
973             *fileid = ilp - files->recs;
974             return 1;
975         }
976     }
977     return 0;
978 }
979
980 /**
981  * Add file entries to header.
982  * @todo Should directories have %doc/%config attributes? (#14531)
983  * @todo Remove RPMTAG_OLDFILENAMES, add dirname/basename instead.
984  * @param fl            package file tree walk data
985  * @retval *fip         file info for package
986  * @param h
987  * @param isSrc
988  */
989 static void genCpioListAndHeader(FileList fl,
990                 rpmfi * fip, Header h, int isSrc)
991 {
992     int _addDotSlash = !(isSrc || rpmExpandNumeric("%{_noPayloadPrefix}"));
993     size_t apathlen = 0;
994     size_t dpathlen = 0;
995     size_t skipLen = 0;
996     FileListRec flp;
997     char buf[BUFSIZ];
998     int i;
999     uint32_t defaultalgo = PGPHASHALGO_MD5, digestalgo;
1000     rpm_loff_t totalFileSize = 0;
1001
1002     /*
1003      * See if non-md5 file digest algorithm is requested. If not
1004      * specified, quietly assume md5. Otherwise check if supported type.
1005      */
1006     digestalgo = rpmExpandNumeric(isSrc ? "%{_source_filedigest_algorithm}" :
1007                                           "%{_binary_filedigest_algorithm}");
1008     if (digestalgo == 0) {
1009         digestalgo = defaultalgo;
1010     }
1011
1012     if (rpmDigestLength(digestalgo) == 0) {
1013         rpmlog(RPMLOG_WARNING,
1014                 _("Unknown file digest algorithm %u, falling back to MD5\n"), 
1015                 digestalgo);
1016         digestalgo = defaultalgo;
1017     }
1018     
1019     /* Sort the big list */
1020     qsort(fl->files.recs, fl->files.used,
1021           sizeof(*(fl->files.recs)), compareFileListRecs);
1022     
1023     /* Generate the header. */
1024     if (! isSrc) {
1025         skipLen = 1;
1026     }
1027
1028     for (i = 0, flp = fl->files.recs; i < fl->files.used; i++, flp++) {
1029         rpm_ino_t fileid = flp - fl->files.recs;
1030
1031         /* Merge duplicate entries. */
1032         while (i < (fl->files.used - 1) &&
1033             rstreq(flp->cpioPath, flp[1].cpioPath)) {
1034
1035             /* Two entries for the same file found, merge the entries. */
1036             /* Note that an %exclude is a duplication of a file reference */
1037
1038             /* file flags */
1039             flp[1].flags |= flp->flags; 
1040
1041             if (!(flp[1].flags & RPMFILE_EXCLUDE))
1042                 rpmlog(RPMLOG_WARNING, _("File listed twice: %s\n"),
1043                         flp->cpioPath);
1044    
1045             /* file mode */
1046             if (S_ISDIR(flp->fl_mode)) {
1047                 if ((flp[1].specdFlags & (SPECD_DIRMODE | SPECD_DEFDIRMODE)) <
1048                     (flp->specdFlags & (SPECD_DIRMODE | SPECD_DEFDIRMODE)))
1049                         flp[1].fl_mode = flp->fl_mode;
1050             } else {
1051                 if ((flp[1].specdFlags & (SPECD_FILEMODE | SPECD_DEFFILEMODE)) <
1052                     (flp->specdFlags & (SPECD_FILEMODE | SPECD_DEFFILEMODE)))
1053                         flp[1].fl_mode = flp->fl_mode;
1054             }
1055
1056             /* uid */
1057             if ((flp[1].specdFlags & (SPECD_UID | SPECD_DEFUID)) <
1058                 (flp->specdFlags & (SPECD_UID | SPECD_DEFUID)))
1059             {
1060                 flp[1].fl_uid = flp->fl_uid;
1061                 flp[1].uname = flp->uname;
1062             }
1063
1064             /* gid */
1065             if ((flp[1].specdFlags & (SPECD_GID | SPECD_DEFGID)) <
1066                 (flp->specdFlags & (SPECD_GID | SPECD_DEFGID)))
1067             {
1068                 flp[1].fl_gid = flp->fl_gid;
1069                 flp[1].gname = flp->gname;
1070             }
1071
1072             /* verify flags */
1073             if ((flp[1].specdFlags & (SPECD_VERIFY | SPECD_DEFVERIFY)) <
1074                 (flp->specdFlags & (SPECD_VERIFY | SPECD_DEFVERIFY)))
1075                     flp[1].verifyFlags = flp->verifyFlags;
1076
1077             /* XXX to-do: language */
1078
1079             flp++; i++;
1080         }
1081
1082         /* Skip files that were marked with %exclude. */
1083         if (flp->flags & RPMFILE_EXCLUDE) continue;
1084
1085         /* Omit '/' and/or URL prefix, leave room for "./" prefix */
1086         apathlen += (strlen(flp->cpioPath) - skipLen + (_addDotSlash ? 3 : 1));
1087
1088         /* Leave room for both dirname and basename NUL's */
1089         dpathlen += (strlen(flp->diskPath) + 2);
1090
1091         /*
1092          * Make the header. Store the on-disk path to OLDFILENAMES for
1093          * cpio list generation purposes for now, final path temporarily
1094          * to ORIGFILENAMES, to be swapped later into OLDFILENAMES.
1095          */
1096         headerPutString(h, RPMTAG_OLDFILENAMES, flp->diskPath);
1097         headerPutString(h, RPMTAG_ORIGFILENAMES, flp->cpioPath);
1098         headerPutString(h, RPMTAG_FILEUSERNAME, flp->uname);
1099         headerPutString(h, RPMTAG_FILEGROUPNAME, flp->gname);
1100
1101         /* Only use 64bit filesizes tag if required. */
1102         if (fl->largeFiles) {
1103             rpm_loff_t rsize64 = (rpm_loff_t)flp->fl_size;
1104             headerPutUint64(h, RPMTAG_LONGFILESIZES, &rsize64, 1);
1105             /* XXX TODO: add rpmlib() dependency for large files */
1106         } else {
1107             rpm_off_t rsize32 = (rpm_off_t)flp->fl_size;
1108             headerPutUint32(h, RPMTAG_FILESIZES, &rsize32, 1);
1109         }
1110         /* Excludes and dupes have been filtered out by now. */
1111         if (S_ISREG(flp->fl_mode)) {
1112             if (flp->fl_nlink == 1 || !seenHardLink(&fl->files, flp, &fileid)) {
1113                 totalFileSize += flp->fl_size;
1114             }
1115         }
1116         
1117         /*
1118          * For items whose size varies between systems, always explicitly 
1119          * cast to the header type before inserting.
1120          * TODO: check and warn if header type overflows for each case.
1121          */
1122         {   rpm_time_t rtime = (rpm_time_t) flp->fl_mtime;
1123             headerPutUint32(h, RPMTAG_FILEMTIMES, &rtime, 1);
1124         }
1125
1126         {   rpm_mode_t rmode = (rpm_mode_t) flp->fl_mode;
1127             headerPutUint16(h, RPMTAG_FILEMODES, &rmode, 1);
1128         }
1129
1130         {   rpm_rdev_t rrdev = (rpm_rdev_t) flp->fl_rdev;
1131             headerPutUint16(h, RPMTAG_FILERDEVS, &rrdev, 1);
1132         }
1133         
1134         /*
1135          * To allow rpmbuild to work on filesystems with 64bit inodes numbers,
1136          * remap them into 32bit integers based on filelist index, just
1137          * preserving semantics for determining hardlinks.
1138          * Start at 1 as inode zero as that could be considered as an error.
1139          * Since we flatten all the inodes to appear within a single fs,
1140          * we also need to flatten the devices.
1141          */
1142         {   rpm_ino_t rino = fileid + 1;
1143             rpm_dev_t rdev = flp->fl_dev ? 1 : 0;
1144             headerPutUint32(h, RPMTAG_FILEINODES, &rino, 1);
1145             headerPutUint32(h, RPMTAG_FILEDEVICES, &rdev, 1);
1146         }
1147         
1148         headerPutString(h, RPMTAG_FILELANGS, flp->langs);
1149
1150         if (fl->haveCaps) {
1151             headerPutString(h, RPMTAG_FILECAPS, flp->caps);
1152         }
1153         
1154         buf[0] = '\0';
1155         if (S_ISREG(flp->fl_mode))
1156             (void) rpmDoDigest(digestalgo, flp->diskPath, 1, 
1157                                (unsigned char *)buf, NULL);
1158         headerPutString(h, RPMTAG_FILEDIGESTS, buf);
1159         
1160         buf[0] = '\0';
1161         if (S_ISLNK(flp->fl_mode)) {
1162             ssize_t llen = readlink(flp->diskPath, buf, BUFSIZ-1);
1163             if (llen == -1) {
1164                 rpmlog(RPMLOG_ERR, _("reading symlink %s failed: %s\n"),
1165                         flp->diskPath, strerror(errno));
1166                 fl->processingFailed = 1;
1167             } else {
1168                 buf[llen] = '\0';
1169                 if (buf[0] == '/' && !rstreq(fl->buildRoot, "/") &&
1170                         rstreqn(buf, fl->buildRoot, strlen(fl->buildRoot))) {
1171                     rpmlog(RPMLOG_ERR,
1172                                 _("Symlink points to BuildRoot: %s -> %s\n"),
1173                                 flp->cpioPath, buf);
1174                     fl->processingFailed = 1;
1175                 }
1176             }
1177         }
1178         headerPutString(h, RPMTAG_FILELINKTOS, buf);
1179         
1180         if (flp->flags & RPMFILE_GHOST) {
1181             flp->verifyFlags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE |
1182                                 RPMVERIFY_LINKTO | RPMVERIFY_MTIME);
1183         }
1184         headerPutUint32(h, RPMTAG_FILEVERIFYFLAGS, &(flp->verifyFlags),1);
1185         
1186         if (!isSrc && isDoc(fl->docDirs, flp->cpioPath))
1187             flp->flags |= RPMFILE_DOC;
1188         /* XXX Should directories have %doc/%config attributes? (#14531) */
1189         if (S_ISDIR(flp->fl_mode))
1190             flp->flags &= ~(RPMFILE_CONFIG|RPMFILE_DOC|RPMFILE_LICENSE);
1191         /* Strip internal parse data */
1192         flp->flags &= PARSEATTR_MASK;
1193
1194         headerPutUint32(h, RPMTAG_FILEFLAGS, &(flp->flags) ,1);
1195     }
1196
1197     if (totalFileSize < UINT32_MAX) {
1198         rpm_off_t totalsize = totalFileSize;
1199         headerPutUint32(h, RPMTAG_SIZE, &totalsize, 1);
1200     } else {
1201         rpm_loff_t totalsize = totalFileSize;
1202         headerPutUint64(h, RPMTAG_LONGSIZE, &totalsize, 1);
1203     }
1204
1205     if (digestalgo != defaultalgo) {
1206         headerPutUint32(h, RPMTAG_FILEDIGESTALGO, &digestalgo, 1);
1207         rpmlibNeedsFeature(h, "FileDigests", "4.6.0-1");
1208     }
1209
1210     if (fl->haveCaps) {
1211         rpmlibNeedsFeature(h, "FileCaps", "4.6.1-1");
1212     }
1213
1214     if (_addDotSlash)
1215         (void) rpmlibNeedsFeature(h, "PayloadFilesHavePrefix", "4.0-1");
1216
1217   {
1218     struct rpmtd_s filenames;
1219     rpmfiFlags flags = RPMFI_NOHEADER|RPMFI_NOFILEUSER|RPMFI_NOFILEGROUP;
1220     rpmfi fi;
1221     int fc;
1222     const char *fn;
1223     char *a, **apath;
1224
1225     /* rpmfiNew() only groks compressed filelists */
1226     headerConvert(h, HEADERCONV_COMPRESSFILELIST);
1227     fi = rpmfiNew(NULL, h, RPMTAG_BASENAMES, flags);
1228
1229     if (fi == NULL) {
1230         fl->processingFailed = 1;
1231         return;
1232     }
1233
1234     /* 
1235      * Grab the real filenames from ORIGFILENAMES and put into OLDFILENAMES,
1236      * remove temporary cruft and side-effects from filelist compression 
1237      * for rpmfiNew().
1238      */
1239     headerGet(h, RPMTAG_ORIGFILENAMES, &filenames, HEADERGET_ALLOC);
1240     headerDel(h, RPMTAG_ORIGFILENAMES);
1241     headerDel(h, RPMTAG_BASENAMES);
1242     headerDel(h, RPMTAG_DIRNAMES);
1243     headerDel(h, RPMTAG_DIRINDEXES);
1244     rpmtdSetTag(&filenames, RPMTAG_OLDFILENAMES);
1245     headerPut(h, &filenames, HEADERPUT_DEFAULT);
1246
1247     /* Create hge-style archive path array, normally adding "./" */
1248     fc = rpmtdCount(&filenames);
1249     apath = xmalloc(fc * sizeof(*apath) + apathlen + 1);
1250     a = (char *)(apath + fc);
1251     *a = '\0';
1252     rpmtdInit(&filenames);
1253     for (int i = 0; (fn = rpmtdNextString(&filenames)); i++) {
1254         apath[i] = a;
1255         if (_addDotSlash)
1256             a = stpcpy(a, "./");
1257         a = stpcpy(a, (fn + skipLen));
1258         a++;            /* skip apath NUL */
1259     }
1260     fi->apath = apath;
1261     *fip = fi;
1262     rpmtdFreeData(&filenames);
1263   }
1264
1265     /* Compress filelist unless legacy format requested */
1266     if (!(fl->pkgFlags & RPMBUILD_PKG_NODIRTOKENS)) {
1267         headerConvert(h, HEADERCONV_COMPRESSFILELIST);
1268         /* Binary packages with dirNames cannot be installed by legacy rpm. */
1269         (void) rpmlibNeedsFeature(h, "CompressedFileNames", "3.0.4-1");
1270     }
1271 }
1272
1273 static FileRecords FileRecordsFree(FileRecords files)
1274 {
1275     for (int i = 0; i < files->used; i++) {
1276         free(files->recs[i].diskPath);
1277         free(files->recs[i].cpioPath);
1278         free(files->recs[i].langs);
1279         free(files->recs[i].caps);
1280     }
1281     free(files->recs);
1282     return NULL;
1283 }
1284
1285 static void FileListFree(FileList fl)
1286 {
1287     FileEntryFree(&(fl->cur));
1288     FileEntryFree(&(fl->def));
1289     FileRecordsFree(&(fl->files));
1290     free(fl->buildRoot);
1291     argvFree(fl->docDirs);
1292 }
1293
1294 /* forward ref */
1295 static rpmRC recurseDir(FileList fl, const char * diskPath);
1296
1297 /* Hack up a stat structure for a %dev or non-existing %ghost */
1298 static struct stat * fakeStat(FileEntry cur, struct stat * statp)
1299 {
1300     time_t now = time(NULL);
1301
1302     if (cur->devtype) {
1303         statp->st_rdev = ((cur->devmajor & 0xff) << 8) | (cur->devminor & 0xff);
1304         statp->st_dev = statp->st_rdev;
1305         statp->st_mode = (cur->devtype == 'b' ? S_IFBLK : S_IFCHR);
1306     } else {
1307         /* non-existing %ghost file or directory */
1308         statp->st_mode = cur->isDir ? S_IFDIR : S_IFREG;
1309         /* can't recurse into non-existing directory */
1310         if (cur->isDir)
1311             cur->isDir = 1;
1312     }
1313     statp->st_mode |= (cur->ar.ar_fmode & 0777);
1314     statp->st_atime = now;
1315     statp->st_mtime = now;
1316     statp->st_ctime = now;
1317     statp->st_nlink = 1;
1318     return statp;
1319 }
1320
1321 /**
1322  * Add a file to the package manifest.
1323  * @param fl            package file tree walk data
1324  * @param diskPath      path to file
1325  * @param statp         file stat (possibly NULL)
1326  * @return              RPMRC_OK on success
1327  */
1328 static rpmRC addFile(FileList fl, const char * diskPath,
1329                 struct stat * statp)
1330 {
1331     size_t plen = strlen(diskPath);
1332     char buf[plen + 1];
1333     const char *cpioPath;
1334     struct stat statbuf;
1335     mode_t fileMode;
1336     uid_t fileUid;
1337     gid_t fileGid;
1338     const char *fileUname;
1339     const char *fileGname;
1340     rpmRC rc = RPMRC_FAIL; /* assume failure */
1341
1342     /* Strip trailing slash. The special case of '/' path is handled below. */
1343     if (plen > 0 && diskPath[plen - 1] == '/') {
1344         diskPath = strcpy(buf, diskPath);
1345         buf[plen - 1] = '\0';
1346     }
1347     cpioPath = diskPath;
1348         
1349     
1350     /* Path may have prepended buildRoot, so locate the original filename. */
1351     /*
1352      * XXX There are 3 types of entry into addFile:
1353      *
1354      *  From                    diskUrl                 statp
1355      *  =====================================================
1356      *  processBinaryFile       path                    NULL
1357      *  processBinaryFile       glob result path        NULL
1358      *  myftw                   path                    stat
1359      *
1360      */
1361     if (fl->buildRoot && !rstreq(fl->buildRoot, "/"))
1362         cpioPath += strlen(fl->buildRoot);
1363
1364     /* XXX make sure '/' can be packaged also */
1365     if (*cpioPath == '\0')
1366         cpioPath = "/";
1367
1368     /*
1369      * Unless recursing, we dont have stat() info at hand. Handle the
1370      * various cases, preserving historical behavior wrt %dev():
1371      * - for %dev() entries we fake it up whether the file exists or not
1372      * - otherwise try to grab the data by lstat()
1373      * - %ghost entries might not exist, fake it up
1374      */
1375     if (statp == NULL) {
1376         memset(&statbuf, 0, sizeof(statbuf));
1377
1378         if (fl->cur.devtype) {
1379             statp = fakeStat(&(fl->cur), &statbuf);
1380         } else if (lstat(diskPath, &statbuf) == 0) {
1381             statp = &statbuf;
1382         } else if (fl->cur.attrFlags & RPMFILE_GHOST) {
1383             statp = fakeStat(&(fl->cur), &statbuf);
1384         } else {
1385             int lvl = RPMLOG_ERR;
1386             const char *msg = fl->cur.isDir ? _("Directory not found: %s\n") :
1387                                               _("File not found: %s\n");
1388             if (fl->cur.attrFlags & RPMFILE_EXCLUDE) {
1389                 lvl = RPMLOG_WARNING;
1390                 rc = RPMRC_OK;
1391             }
1392             rpmlog(lvl, msg, diskPath);
1393             goto exit;
1394         }
1395     }
1396
1397     /* Don't recurse into explicit %dir, don't double-recurse from fts */
1398     if ((fl->cur.isDir != 1) && (statp == &statbuf) && S_ISDIR(statp->st_mode)) {
1399         return recurseDir(fl, diskPath);
1400     }
1401
1402     fileMode = statp->st_mode;
1403     fileUid = statp->st_uid;
1404     fileGid = statp->st_gid;
1405
1406     /* Explicit %attr() always wins */
1407     if (fl->cur.ar.ar_fmodestr != NULL) {
1408         fileMode &= S_IFMT;
1409         fileMode |= fl->cur.ar.ar_fmode;
1410     } else {
1411         /* ...but %defattr() for directories and files is different */
1412         if (S_ISDIR(fileMode)) {
1413             if (fl->def.ar.ar_dmodestr) {
1414                 fileMode &= S_IFMT;
1415                 fileMode |= fl->def.ar.ar_dmode;
1416             }
1417         } else if (fl->def.ar.ar_fmodestr) {
1418             fileMode &= S_IFMT;
1419             fileMode |= fl->def.ar.ar_fmode;
1420         }
1421     }
1422     if (fl->cur.ar.ar_user) {
1423         fileUname = fl->cur.ar.ar_user;
1424     } else if (fl->def.ar.ar_user) {
1425         fileUname = fl->def.ar.ar_user;
1426     } else {
1427         fileUname = rpmugUname(fileUid);
1428     }
1429     if (fl->cur.ar.ar_group) {
1430         fileGname = fl->cur.ar.ar_group;
1431     } else if (fl->def.ar.ar_group) {
1432         fileGname = fl->def.ar.ar_group;
1433     } else {
1434         fileGname = rpmugGname(fileGid);
1435     }
1436         
1437     /* Default user/group to builder's user/group */
1438     if (fileUname == NULL)
1439         fileUname = rpmugUname(getuid());
1440     if (fileGname == NULL)
1441         fileGname = rpmugGname(getgid());
1442     
1443     /* S_XXX macro must be consistent with type in find call at check-files script */
1444     if (check_fileList && (S_ISREG(fileMode) || S_ISLNK(fileMode))) {
1445         appendStringBuf(check_fileList, diskPath);
1446         appendStringBuf(check_fileList, "\n");
1447     }
1448
1449     /* Add to the file list */
1450     if (fl->files.used == fl->files.alloced) {
1451         fl->files.alloced += 128;
1452         fl->files.recs = xrealloc(fl->files.recs,
1453                         fl->files.alloced * sizeof(*(fl->files.recs)));
1454     }
1455             
1456     {   FileListRec flp = &fl->files.recs[fl->files.used];
1457
1458         flp->fl_st = *statp;    /* structure assignment */
1459         flp->fl_mode = fileMode;
1460         flp->fl_uid = fileUid;
1461         flp->fl_gid = fileGid;
1462
1463         flp->cpioPath = xstrdup(cpioPath);
1464         flp->diskPath = xstrdup(diskPath);
1465         flp->uname = rpmugStashStr(fileUname);
1466         flp->gname = rpmugStashStr(fileGname);
1467
1468         if (fl->cur.langs) {
1469             flp->langs = argvJoin(fl->cur.langs, "|");
1470         } else {
1471             flp->langs = xstrdup("");
1472         }
1473
1474         if (fl->cur.caps) {
1475             flp->caps = fl->cur.caps;
1476         } else {
1477             flp->caps = xstrdup("");
1478         }
1479
1480         flp->flags = fl->cur.attrFlags;
1481         flp->specdFlags = fl->cur.specdFlags;
1482         flp->verifyFlags = fl->cur.verifyFlags;
1483
1484         if (!(flp->flags & RPMFILE_EXCLUDE) && S_ISREG(flp->fl_mode)) {
1485             if (flp->fl_size >= UINT32_MAX) {
1486                 fl->largeFiles = 1;
1487             }
1488         }
1489     }
1490
1491     rc = RPMRC_OK;
1492     fl->files.used++;
1493
1494 exit:
1495     if (rc != RPMRC_OK)
1496         fl->processingFailed = 1;
1497
1498     return rc;
1499 }
1500
1501 /**
1502  * Add directory (and all of its files) to the package manifest.
1503  * @param fl            package file tree walk data
1504  * @param diskPath      path to file
1505  * @return              RPMRC_OK on success
1506  */
1507 static rpmRC recurseDir(FileList fl, const char * diskPath)
1508 {
1509     char * ftsSet[2];
1510     FTS * ftsp;
1511     FTSENT * fts;
1512     int myFtsOpts = (FTS_COMFOLLOW | FTS_NOCHDIR | FTS_PHYSICAL);
1513     rpmRC rc = RPMRC_FAIL;
1514
1515     ftsSet[0] = (char *) diskPath;
1516     ftsSet[1] = NULL;
1517     ftsp = Fts_open(ftsSet, myFtsOpts, NULL);
1518     while ((fts = Fts_read(ftsp)) != NULL) {
1519         switch (fts->fts_info) {
1520         case FTS_D:             /* preorder directory */
1521         case FTS_F:             /* regular file */
1522         case FTS_SL:            /* symbolic link */
1523         case FTS_SLNONE:        /* symbolic link without target */
1524         case FTS_DEFAULT:       /* none of the above */
1525             rc = addFile(fl, fts->fts_accpath, fts->fts_statp);
1526             break;
1527         case FTS_DOT:           /* dot or dot-dot */
1528         case FTS_DP:            /* postorder directory */
1529             rc = RPMRC_OK;
1530             break;
1531         case FTS_NS:            /* stat(2) failed */
1532         case FTS_DNR:           /* unreadable directory */
1533         case FTS_ERR:           /* error; errno is set */
1534         case FTS_DC:            /* directory that causes cycles */
1535         case FTS_NSOK:          /* no stat(2) requested */
1536         case FTS_INIT:          /* initialized only */
1537         case FTS_W:             /* whiteout object */
1538         default:
1539             rc = RPMRC_FAIL;
1540             break;
1541         }
1542         if (rc)
1543             break;
1544     }
1545     (void) Fts_close(ftsp);
1546
1547     return rc;
1548 }
1549
1550 /**
1551  * Add a pubkey/icon to a binary package.
1552  * @param pkg
1553  * @param fl            package file tree walk data
1554  * @param fileName      path to file, relative is builddir, absolute buildroot.
1555  * @param tag           tag to add
1556  * @return              RPMRC_OK on success
1557  */
1558 static rpmRC processMetadataFile(Package pkg, FileList fl, 
1559                                  const char * fileName, rpmTagVal tag)
1560 {
1561     const char * buildDir = "%{_builddir}/%{?buildsubdir}/";
1562     char * fn = NULL;
1563     char * apkt = NULL;
1564     uint8_t * pkt = NULL;
1565     ssize_t pktlen = 0;
1566     int absolute = 0;
1567     rpmRC rc = RPMRC_FAIL;
1568     int xx;
1569
1570     if (*fileName == '/') {
1571         fn = rpmGenPath(fl->buildRoot, NULL, fileName);
1572         absolute = 1;
1573     } else
1574         fn = rpmGenPath(buildDir, NULL, fileName);
1575
1576     switch (tag) {
1577     default:
1578         rpmlog(RPMLOG_ERR, _("%s: can't load unknown tag (%d).\n"),
1579                 fn, tag);
1580         goto exit;
1581         break;
1582     case RPMTAG_PUBKEYS: {
1583         if ((xx = pgpReadPkts(fn, &pkt, (size_t *)&pktlen)) <= 0) {
1584             rpmlog(RPMLOG_ERR, _("%s: public key read failed.\n"), fn);
1585             goto exit;
1586         }
1587         if (xx != PGPARMOR_PUBKEY) {
1588             rpmlog(RPMLOG_ERR, _("%s: not an armored public key.\n"), fn);
1589             goto exit;
1590         }
1591         apkt = pgpArmorWrap(PGPARMOR_PUBKEY, pkt, pktlen);
1592         break;
1593     }
1594     case RPMTAG_SECMANIFEST: {
1595         if ((xx = rpmioSlurp(fn, &pkt, &pktlen)) != 0 || pkt == NULL) {
1596             rpmlog(RPMLOG_ERR, _("%s: Security manifest file read failed.\n"), fn);
1597             goto exit;
1598         }
1599         apkt = rpmBase64Encode(pkt, pktlen, -1);
1600         rpmlog(RPMLOG_INFO, _("Aptk: %s\n"), apkt);
1601         break;
1602     }
1603     }
1604
1605     if (!apkt) {
1606         rpmlog(RPMLOG_ERR, _("%s: failed to encode\n"), fn);
1607         goto exit;
1608     }
1609
1610     headerPutString(pkg->header, tag, apkt);
1611     rc = RPMRC_OK;
1612
1613     if (absolute)
1614         rc = addFile(fl, fn, NULL);
1615
1616 exit:
1617     free(apkt);
1618     free(pkt);
1619     free(fn);
1620     if (rc) {
1621         fl->processingFailed = 1;
1622         rc = RPMRC_FAIL;
1623     }
1624     return rc;
1625 }
1626
1627 /**
1628  * Add a file to a binary package.
1629  * @param pkg
1630  * @param fl            package file tree walk data
1631  * @param fileName      file to add
1632  * @return              RPMRC_OK on success
1633  */
1634 static rpmRC processBinaryFile(Package pkg, FileList fl, const char * fileName)
1635 {
1636     int quote = 1;      /* XXX permit quoted glob characters. */
1637     int doGlob;
1638     char *diskPath = NULL;
1639     rpmRC rc = RPMRC_OK;
1640     size_t fnlen = strlen(fileName);
1641     int trailing_slash = (fnlen > 0 && fileName[fnlen-1] == '/');
1642
1643     /* XXX differentiate other directories from explicit %dir */
1644     if (trailing_slash && !fl->cur.isDir)
1645         fl->cur.isDir = -1;
1646     
1647     doGlob = rpmIsGlob(fileName, quote);
1648
1649     /* Check that file starts with leading "/" */
1650     if (*fileName != '/') {
1651         rpmlog(RPMLOG_ERR, _("File needs leading \"/\": %s\n"), fileName);
1652         rc = RPMRC_FAIL;
1653         goto exit;
1654     }
1655     
1656     /* Copy file name or glob pattern removing multiple "/" chars. */
1657     /*
1658      * Note: rpmGetPath should guarantee a "canonical" path. That means
1659      * that the following pathologies should be weeded out:
1660      *          //bin//sh
1661      *          //usr//bin/
1662      *          /.././../usr/../bin//./sh
1663      */
1664     diskPath = rpmGenPath(fl->buildRoot, NULL, fileName);
1665     /* Arrange trailing slash on directories */
1666     if (fl->cur.isDir)
1667         diskPath = rstrcat(&diskPath, "/");
1668
1669     if (doGlob) {
1670         ARGV_t argv = NULL;
1671         int argc = 0;
1672         int i;
1673
1674         if (fl->cur.devtype) {
1675             rpmlog(RPMLOG_ERR, _("%%dev glob not permitted: %s\n"), diskPath);
1676             rc = RPMRC_FAIL;
1677             goto exit;
1678         }
1679
1680         if (rpmGlob(diskPath, &argc, &argv) == 0 && argc >= 1) {
1681             for (i = 0; i < argc; i++) {
1682                 rc = addFile(fl, argv[i], NULL);
1683             }
1684             argvFree(argv);
1685         } else {
1686             int lvl = RPMLOG_WARNING;
1687             const char *msg = (fl->cur.isDir) ?
1688                                 _("Directory not found by glob: %s\n") :
1689                                 _("File not found by glob: %s\n");
1690             if (!(fl->cur.attrFlags & RPMFILE_EXCLUDE)) {
1691                 lvl = RPMLOG_ERR;
1692                 rc = RPMRC_FAIL;
1693             }
1694             rpmlog(lvl, msg, diskPath);
1695             goto exit;
1696         }
1697     } else {
1698         rc = addFile(fl, diskPath, NULL);
1699     }
1700
1701 exit:
1702     free(diskPath);
1703     if (rc) {
1704         fl->processingFailed = 1;
1705         rc = RPMRC_FAIL;
1706     }
1707     return rc;
1708 }
1709
1710 static rpmRC readFilesManifest(rpmSpec spec, Package pkg, const char *path)
1711 {
1712     char *fn, buf[BUFSIZ];
1713     FILE *fd = NULL;
1714     rpmRC rc = RPMRC_FAIL;
1715
1716     if (*path == '/') {
1717         fn = rpmGetPath(path, NULL);
1718     } else {
1719         fn = rpmGetPath("%{_builddir}/",
1720             (spec->buildSubdir ? spec->buildSubdir : "") , "/", path, NULL);
1721     }
1722     fd = fopen(fn, "r");
1723
1724     if (fd == NULL) {
1725         rpmlog(RPMLOG_ERR, _("Could not open %%files file %s: %m\n"), fn);
1726         goto exit;
1727     }
1728
1729     while (fgets(buf, sizeof(buf), fd)) {
1730         handleComments(buf);
1731         if (expandMacros(spec, spec->macros, buf, sizeof(buf))) {
1732             rpmlog(RPMLOG_ERR, _("line: %s\n"), buf);
1733             goto exit;
1734         }
1735         argvAdd(&(pkg->fileList), buf);
1736     }
1737
1738     if (ferror(fd))
1739         rpmlog(RPMLOG_ERR, _("Error reading %%files file %s: %m\n"), fn);
1740     else
1741         rc = RPMRC_OK;
1742
1743 exit:
1744     if (fd) fclose(fd);
1745     free(fn);
1746     return rc;
1747 }
1748
1749 static char * getSpecialDocDir(Header h, rpmFlags sdtype)
1750 {
1751     const char *errstr = NULL;
1752     const char *dirtype = (sdtype == RPMFILE_DOC) ? "docdir" : "licensedir";
1753     const char *fmt_default = "%{NAME}-%{VERSION}";
1754     char *fmt_macro = rpmExpand("%{?_docdir_fmt}", NULL);
1755     char *fmt = NULL; 
1756     char *res = NULL;
1757
1758     if (fmt_macro && strlen(fmt_macro) > 0) {
1759         fmt = headerFormat(h, fmt_macro, &errstr);
1760         if (errstr) {
1761             rpmlog(RPMLOG_WARNING, _("illegal _docdir_fmt %s: %s\n"),
1762                    fmt_macro, errstr);
1763         }
1764     }
1765
1766     if (fmt == NULL)
1767         fmt = headerFormat(h, fmt_default, &errstr);
1768
1769     res = rpmGetPath("%{_", dirtype, "}/", fmt, NULL);
1770
1771     free(fmt);
1772     free(fmt_macro);
1773     return res;
1774 }
1775
1776 static specialDir specialDirNew(Header h, rpmFlags sdtype,
1777                                 AttrRec ar, AttrRec def_ar)
1778 {
1779     specialDir sd = xcalloc(1, sizeof(*sd));
1780     dupAttrRec(ar, &(sd->ar));
1781     dupAttrRec(def_ar, &(sd->def_ar));
1782     sd->dirname = getSpecialDocDir(h, sdtype);
1783     sd->sdtype = sdtype;
1784     return sd;
1785 }
1786
1787 static specialDir specialDirFree(specialDir sd)
1788 {
1789     if (sd) {
1790         argvFree(sd->files);
1791         freeAttrRec(&(sd->ar));
1792         freeAttrRec(&(sd->def_ar));
1793         free(sd->dirname);
1794         free(sd);
1795     }
1796     return NULL;
1797 }
1798
1799 static void processSpecialDir(rpmSpec spec, Package pkg, FileList fl,
1800                                 specialDir sd, int install, int test)
1801 {
1802     const char *sdenv = (sd->sdtype == RPMFILE_DOC) ? "DOCDIR" : "LICENSEDIR";
1803     const char *sdname = (sd->sdtype == RPMFILE_DOC) ? "%doc" : "%license";
1804     char *mkdocdir = rpmExpand("%{__mkdir_p} $", sdenv, NULL);
1805     StringBuf docScript = newStringBuf();
1806
1807     appendStringBuf(docScript, sdenv);
1808     appendStringBuf(docScript, "=$RPM_BUILD_ROOT");
1809     appendLineStringBuf(docScript, sd->dirname);
1810     appendStringBuf(docScript, "export ");
1811     appendLineStringBuf(docScript, sdenv);
1812     appendLineStringBuf(docScript, mkdocdir);
1813
1814     for (ARGV_const_t fn = sd->files; fn && *fn; fn++) {
1815         /* Quotes would break globs, escape spaces instead */
1816         char *efn = rpmEscapeSpaces(*fn);
1817         appendStringBuf(docScript, "cp -pr ");
1818         appendStringBuf(docScript, efn);
1819         appendStringBuf(docScript, " $");
1820         appendLineStringBuf(docScript, sdenv);
1821         free(efn);
1822     }
1823
1824     if (install) {
1825         rpmRC rc = doScript(spec, RPMBUILD_STRINGBUF, sdname,
1826                             getStringBuf(docScript), test);
1827
1828         if (rc && rpmExpandNumeric("%{?_missing_doc_files_terminate_build}"))
1829             fl->processingFailed = 1;
1830     }
1831
1832     /* Reset for %doc */
1833     FileEntryFree(&fl->cur);
1834
1835     fl->cur.attrFlags |= sd->sdtype;
1836     fl->cur.verifyFlags = fl->def.verifyFlags;
1837     dupAttrRec(&(sd->ar), &(fl->cur.ar));
1838     dupAttrRec(&(sd->def_ar), &(fl->def.ar));
1839
1840     (void) processBinaryFile(pkg, fl, sd->dirname);
1841
1842     freeStringBuf(docScript);
1843     free(mkdocdir);
1844 }
1845                                 
1846
1847 static rpmRC processPackageFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags,
1848                                  Package pkg, int installSpecialDoc, int test)
1849 {
1850     struct FileList_s fl;
1851     ARGV_t fileNames = NULL;
1852     specialDir specialDoc = NULL;
1853     specialDir specialLic = NULL;
1854
1855     pkg->cpioList = NULL;
1856
1857     for (ARGV_const_t fp = pkg->fileFile; fp && *fp != NULL; fp++) {
1858         if (readFilesManifest(spec, pkg, *fp))
1859             return RPMRC_FAIL;
1860     }
1861     /* Init the file list structure */
1862     memset(&fl, 0, sizeof(fl));
1863
1864     /* XXX spec->buildRoot == NULL, then xstrdup("") is returned */
1865     fl.buildRoot = rpmGenPath(spec->rootDir, spec->buildRoot, NULL);
1866
1867     dupAttrRec(&root_ar, &fl.def.ar);   /* XXX assume %defattr(-,root,root) */
1868     fl.def.verifyFlags = RPMVERIFY_ALL;
1869
1870     fl.pkgFlags = pkgFlags;
1871
1872     {   char *docs = rpmGetPath("%{?__docdir_path}", NULL);
1873         argvSplit(&fl.docDirs, docs, ":");
1874         free(docs);
1875     }
1876     
1877     for (ARGV_const_t fp = pkg->fileList; *fp != NULL; fp++) {
1878         char buf[strlen(*fp) + 1];
1879         const char *s = *fp;
1880         SKIPSPACE(s);
1881         if (*s == '\0')
1882             continue;
1883         fileNames = argvFree(fileNames);
1884         rstrlcpy(buf, s, sizeof(buf));
1885         
1886         /* Reset for a new line in %files */
1887         FileEntryFree(&fl.cur);
1888
1889         /* turn explicit flags into %def'd ones (gosh this is hacky...) */
1890         fl.cur.specdFlags = ((unsigned)fl.def.specdFlags) >> 8;
1891         fl.cur.verifyFlags = fl.def.verifyFlags;
1892
1893         if (parseForVerify(buf, 0, &fl.cur) ||
1894             parseForVerify(buf, 1, &fl.def) ||
1895             parseForAttr(buf, 0, &fl.cur) ||
1896             parseForAttr(buf, 1, &fl.def) ||
1897             parseForDev(buf, &fl.cur) ||
1898             parseForConfig(buf, &fl.cur) ||
1899             parseForLang(buf, &fl.cur) ||
1900             parseForCaps(buf, &fl.cur) ||
1901             parseForSimple(buf, &fl.cur, &fileNames))
1902         {
1903             fl.processingFailed = 1;
1904             continue;
1905         }
1906
1907         for (ARGV_const_t fn = fileNames; fn && *fn; fn++) {
1908             if (fl.cur.attrFlags & RPMFILE_SPECIALDIR) {
1909                 rpmFlags oattrs = (fl.cur.attrFlags & ~RPMFILE_SPECIALDIR);
1910                 specialDir *sdp = NULL;
1911                 if (oattrs == RPMFILE_DOC) {
1912                     sdp = &specialDoc;
1913                 } else if (oattrs == RPMFILE_LICENSE) {
1914                     sdp = &specialLic;
1915                 }
1916
1917                 if (sdp == NULL || **fn == '/') {
1918                     rpmlog(RPMLOG_ERR,
1919                            _("Can't mix special %s with other forms: %s\n"),
1920                            (oattrs & RPMFILE_DOC) ? "%doc" : "%license", *fn);
1921                     fl.processingFailed = 1;
1922                     continue;
1923                 }
1924
1925                 /* save attributes on first special doc/license for later use */
1926                 if (*sdp == NULL) {
1927                     *sdp = specialDirNew(pkg->header, oattrs,
1928                                          &fl.cur.ar, &fl.def.ar);
1929                 }
1930                 argvAdd(&(*sdp)->files, *fn);
1931                 continue;
1932             }
1933
1934             /* this is now an artificial limitation */
1935             if (fn != fileNames) {
1936                 rpmlog(RPMLOG_ERR, _("More than one file on a line: %s\n"),*fn);
1937                 fl.processingFailed = 1;
1938                 continue;
1939             }
1940
1941             if (fl.cur.attrFlags & RPMFILE_DOCDIR) {
1942                 argvAdd(&(fl.docDirs), *fn);
1943             } else if (fl.cur.attrFlags & RPMFILE_PUBKEY) {
1944                 (void) processMetadataFile(pkg, &fl, *fn, RPMTAG_PUBKEYS);
1945             } else if (fl.cur.attrFlags & RPMFILE_SECMANIFEST) {
1946                 (void) processMetadataFile(pkg, &fl, *fn, RPMTAG_SECMANIFEST);
1947             } else {
1948                 if (fl.cur.attrFlags & RPMFILE_DIR)
1949                     fl.cur.isDir = 1;
1950                 (void) processBinaryFile(pkg, &fl, *fn);
1951             }
1952         }
1953
1954         if (fl.cur.caps)
1955             fl.haveCaps = 1;
1956     }
1957
1958     /* Now process special docs and licenses if present */
1959     if (specialDoc)
1960         processSpecialDir(spec, pkg, &fl, specialDoc, installSpecialDoc, test);
1961     if (specialLic)
1962         processSpecialDir(spec, pkg, &fl, specialLic, installSpecialDoc, test);
1963     
1964     if (fl.processingFailed)
1965         goto exit;
1966
1967     /* Verify that file attributes scope over hardlinks correctly. */
1968     if (checkHardLinks(&fl.files))
1969         (void) rpmlibNeedsFeature(pkg->header,
1970                         "PartialHardlinkSets", "4.0.4-1");
1971
1972     genCpioListAndHeader(&fl, &pkg->cpioList, pkg->header, 0);
1973
1974 exit:
1975     FileListFree(&fl);
1976     specialDirFree(specialDoc);
1977     specialDirFree(specialLic);
1978     return fl.processingFailed ? RPMRC_FAIL : RPMRC_OK;
1979 }
1980
1981 static void genSourceRpmName(rpmSpec spec)
1982 {
1983     if (spec->sourceRpmName == NULL) {
1984         char *nvr = headerGetAsString(spec->packages->header, RPMTAG_NVR);
1985         rasprintf(&spec->sourceRpmName, "%s.%ssrc.rpm", nvr,
1986                   spec->noSource ? "no" : "");
1987         free(nvr);
1988     }
1989 }
1990
1991 rpmRC processSourceFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags)
1992 {
1993     struct Source *srcPtr;
1994     struct FileList_s fl;
1995     ARGV_t files = NULL;
1996     Package pkg;
1997     static char *_srcdefattr;
1998     static int oneshot;
1999
2000     if (!oneshot) {
2001         _srcdefattr = rpmExpand("%{?_srcdefattr}", NULL);
2002         if (_srcdefattr && !*_srcdefattr)
2003             _srcdefattr = _free(_srcdefattr);
2004         oneshot = 1;
2005     }
2006
2007     genSourceRpmName(spec);
2008     /* Construct the file list and source entries */
2009     argvAdd(&files, spec->specFile);
2010     for (srcPtr = spec->sources; srcPtr != NULL; srcPtr = srcPtr->next) {
2011         char * sfn = rpmGetPath( ((srcPtr->flags & RPMBUILD_ISNO) ? "!" : ""),
2012                 "%{_sourcedir}/", srcPtr->source, NULL);
2013         argvAdd(&files, sfn);
2014         free(sfn);
2015     }
2016
2017     for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
2018         for (srcPtr = pkg->icon; srcPtr != NULL; srcPtr = srcPtr->next) {
2019             char * sfn;
2020             sfn = rpmGetPath( ((srcPtr->flags & RPMBUILD_ISNO) ? "!" : ""),
2021                 "%{_sourcedir}/", srcPtr->source, NULL);
2022             argvAdd(&files, sfn);
2023             free(sfn);
2024         }
2025     }
2026
2027     spec->sourceCpioList = NULL;
2028
2029     /* Init the file list structure */
2030     memset(&fl, 0, sizeof(fl));
2031     if (_srcdefattr) {
2032         char *a = rstrscat(NULL, "%defattr ", _srcdefattr, NULL);
2033         parseForAttr(a, 1, &fl.def);
2034         free(a);
2035     }
2036     fl.files.alloced = spec->numSources + 1;
2037     fl.files.recs = xcalloc(fl.files.alloced, sizeof(*fl.files.recs));
2038     fl.pkgFlags = pkgFlags;
2039
2040     for (ARGV_const_t fp = files; *fp != NULL; fp++) {
2041         const char *diskPath = *fp;
2042         char *tmp;
2043         FileListRec flp;
2044
2045         SKIPSPACE(diskPath);
2046         if (! *diskPath)
2047             continue;
2048
2049         flp = &fl.files.recs[fl.files.used];
2050
2051         /* The first source file is the spec file */
2052         flp->flags = (fl.files.used == 0) ? RPMFILE_SPECFILE : 0;
2053         /* files with leading ! are no source files */
2054         if (*diskPath == '!') {
2055             flp->flags |= RPMFILE_GHOST;
2056             diskPath++;
2057         }
2058
2059         tmp = xstrdup(diskPath); /* basename() might modify */
2060         flp->diskPath = xstrdup(diskPath);
2061         flp->cpioPath = xstrdup(basename(tmp));
2062         flp->verifyFlags = RPMVERIFY_ALL;
2063         free(tmp);
2064
2065         if (stat(diskPath, &flp->fl_st)) {
2066             rpmlog(RPMLOG_ERR, _("Bad file: %s: %s\n"),
2067                 diskPath, strerror(errno));
2068             fl.processingFailed = 1;
2069         } else {
2070             if (S_ISREG(flp->fl_mode) && flp->fl_size >= UINT32_MAX)
2071                 fl.largeFiles = 1;
2072         }
2073
2074         if (fl.def.ar.ar_fmodestr) {
2075             flp->fl_mode &= S_IFMT;
2076             flp->fl_mode |= fl.def.ar.ar_fmode;
2077         }
2078         if (fl.def.ar.ar_user) {
2079             flp->uname = rpmugStashStr(fl.def.ar.ar_user);
2080         } else {
2081             flp->uname = rpmugStashStr(rpmugUname(flp->fl_uid));
2082         }
2083         if (fl.def.ar.ar_group) {
2084             flp->gname = rpmugStashStr(fl.def.ar.ar_group);
2085         } else {
2086             flp->gname = rpmugStashStr(rpmugGname(flp->fl_gid));
2087         }
2088         flp->langs = xstrdup("");
2089         
2090         if (! (flp->uname && flp->gname)) {
2091             rpmlog(RPMLOG_ERR, _("Bad owner/group: %s\n"), diskPath);
2092             fl.processingFailed = 1;
2093         }
2094
2095         fl.files.used++;
2096     }
2097     argvFree(files);
2098
2099     if (! fl.processingFailed) {
2100         if (spec->sourceHeader != NULL) {
2101             genCpioListAndHeader(&fl, &spec->sourceCpioList,
2102                         spec->sourceHeader, 1);
2103         }
2104     }
2105
2106     FileListFree(&fl);
2107     return fl.processingFailed ? RPMRC_FAIL : RPMRC_OK;
2108 }
2109
2110 /**
2111  * Check packaged file list against what's in the build root.
2112  * @param fileList      packaged file list
2113  * @return              -1 if skipped, 0 on OK, 1 on error
2114  */
2115 static int checkFiles(const char *buildRoot, StringBuf fileList)
2116 {
2117     static char * const av_ckfile[] = { "%{?__check_files}", NULL };
2118     StringBuf sb_stdout = NULL;
2119     int rc = -1;
2120     char * s = rpmExpand(av_ckfile[0], NULL);
2121     
2122     if (!(s && *s))
2123         goto exit;
2124
2125     rpmlog(RPMLOG_NOTICE, _("Checking for unpackaged file(s): %s\n"), s);
2126
2127     rc = rpmfcExec(av_ckfile, fileList, &sb_stdout, 0, buildRoot);
2128     if (rc < 0)
2129         goto exit;
2130     
2131     if (sb_stdout) {
2132         int _unpackaged_files_terminate_build =
2133                 rpmExpandNumeric("%{?_unpackaged_files_terminate_build}");
2134         const char * t = getStringBuf(sb_stdout);
2135         if ((*t != '\0') && (*t != '\n')) {
2136             rc = (_unpackaged_files_terminate_build) ? 1 : 0;
2137             rpmlog((rc ? RPMLOG_ERR : RPMLOG_WARNING),
2138                 _("Installed (but unpackaged) file(s) found:\n%s"), t);
2139         }
2140     }
2141     
2142 exit:
2143     freeStringBuf(sb_stdout);
2144     free(s);
2145     return rc;
2146 }
2147
2148 #if HAVE_GELF_H && HAVE_LIBELF
2149 /* Query the build-id from the ELF file NAME and store it in the newly
2150    allocated *build_id array of size *build_id_size.  Returns -1 on
2151    error.  */
2152
2153 int
2154 getELFBuildId (const char *name,
2155                unsigned char **id, size_t *id_size)
2156 {
2157   int fd, i;
2158   Elf *elf;
2159   GElf_Ehdr ehdr;
2160   Elf_Data *build_id = NULL;
2161   size_t build_id_offset = 0, build_id_size = 0;
2162
2163   /* Now query the build-id of the file and add the
2164      corresponding links in the .build-id tree.
2165      The following code is based on tools/debugedit.c.  */
2166   fd = open (name, O_RDONLY);
2167   if (fd < 0)
2168     return -1;
2169   elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
2170   if (elf == NULL)
2171     {
2172       fprintf (stderr, "cannot open ELF file: %s",
2173                elf_errmsg (-1));
2174       close (fd);
2175       return -1;
2176     }
2177   if (elf_kind (elf) != ELF_K_ELF
2178       || gelf_getehdr (elf, &ehdr) == NULL
2179       || (ehdr.e_type != ET_DYN
2180           && ehdr.e_type != ET_EXEC
2181           && ehdr.e_type != ET_REL))
2182     {
2183       elf_end (elf);
2184       close (fd);
2185       return -1;
2186     }
2187   for (i = 0; i < ehdr.e_shnum; ++i)
2188     {
2189       Elf_Scn *s = elf_getscn (elf, i);
2190       GElf_Shdr shdr;
2191       Elf_Data *data;
2192       Elf32_Nhdr nh;
2193       Elf_Data dst =
2194         {
2195           .d_version = EV_CURRENT, .d_type = ELF_T_NHDR,
2196           .d_buf = &nh, .d_size = sizeof nh
2197         };
2198       Elf_Data src = dst;
2199
2200       gelf_getshdr (s, &shdr);
2201       if (shdr.sh_type != SHT_NOTE
2202           || !(shdr.sh_flags & SHF_ALLOC))
2203         continue;
2204
2205       /* Look for a build-ID note here.  */
2206       data = elf_rawdata (s, NULL);
2207       src.d_buf = data->d_buf;
2208       assert (sizeof (Elf32_Nhdr) == sizeof (Elf64_Nhdr));
2209       while (data->d_buf + data->d_size - src.d_buf > (int) sizeof nh
2210              && elf32_xlatetom (&dst, &src, ehdr.e_ident[EI_DATA]))
2211         {
2212           Elf32_Word len = sizeof nh + nh.n_namesz;
2213           len = (len + 3) & ~3;
2214
2215           if (nh.n_namesz == sizeof "GNU" && nh.n_type == 3
2216               && !memcmp (src.d_buf + sizeof nh, "GNU", sizeof "GNU"))
2217             {
2218               build_id = data;
2219               build_id_offset = src.d_buf + len - data->d_buf;
2220               build_id_size = nh.n_descsz;
2221               break;
2222             }
2223
2224           len += nh.n_descsz;
2225           len = (len + 3) & ~3;
2226           src.d_buf += len;
2227         }
2228
2229       if (build_id != NULL)
2230         break;
2231     }
2232
2233   if (build_id == NULL)
2234     return -1;
2235
2236   *id = malloc (build_id_size);
2237   *id_size = build_id_size;
2238   memcpy (*id, build_id->d_buf + build_id_offset, build_id_size);
2239
2240   elf_end (elf);
2241   close (fd);
2242
2243   return 0;
2244 }
2245
2246
2247 static rpmTag copyTagsForDebug[] = {
2248     RPMTAG_EPOCH,
2249     RPMTAG_VERSION,
2250     RPMTAG_RELEASE,
2251     RPMTAG_LICENSE,
2252     RPMTAG_PACKAGER,
2253     RPMTAG_DISTRIBUTION,
2254     RPMTAG_DISTURL,
2255     RPMTAG_VENDOR,
2256     RPMTAG_ICON,
2257     RPMTAG_URL,
2258     RPMTAG_CHANGELOGTIME,
2259     RPMTAG_CHANGELOGNAME,
2260     RPMTAG_CHANGELOGTEXT,
2261     RPMTAG_PREFIXES,
2262     RPMTAG_RHNPLATFORM,
2263     RPMTAG_OS,
2264     RPMTAG_DISTTAG,
2265     RPMTAG_CVSID,
2266     RPMTAG_ARCH,
2267     0
2268 };
2269
2270 static void addDebuginfoPackage(rpmSpec spec, Package pkg, char *buildroot)
2271 {
2272     const char *a;
2273     const char *ver, *rel;
2274
2275     elf_version(EV_CURRENT);
2276     a = headerGetString(pkg->header, RPMTAG_ARCH);
2277     ver = headerGetAsString(pkg->header, RPMTAG_VERSION);
2278     rel = headerGetAsString(pkg->header, RPMTAG_RELEASE);
2279
2280     if (strcmp(a, "noarch") != 0 && strcmp(a, "src") != 0 && strcmp(a, "nosrc") != 0)
2281       {
2282         Package dbg;
2283         rpmfi fi = pkg->cpioList;
2284         char tmp[1024];
2285         const char *name;
2286         ARGV_t files = NULL;
2287         int seen_build_id = 0;
2288
2289         /* Check if the current package has files with debug info
2290            and record them.  */
2291         fi = rpmfiInit (fi, 0);
2292         while (rpmfiNext (fi) >= 0)
2293           {
2294             const char *base;
2295             int i;
2296             unsigned char *build_id;
2297             size_t build_id_size = 0;
2298             struct stat sbuf;
2299
2300             name = rpmfiFN (fi);
2301             /* Skip leading buildroot.  */
2302             base = name + strlen (buildroot);
2303             /* Pre-pend %buildroot/usr/lib/debug and append .debug.  */
2304             snprintf (tmp, 1024, "%s/usr/lib/debug%s.debug",
2305                       buildroot, base);
2306             /* If that file exists we have debug information for it.  */
2307             if (access (tmp, F_OK) != 0)
2308               continue;
2309
2310             /* Append the file list preamble.  */
2311             if (!files)
2312               {
2313                 argvAdd(&files, "%defattr(-,root,root)");
2314                 argvAdd(&files, "%dir /usr/lib/debug");
2315               }
2316             /* Add the files main debug-info file.  */
2317             snprintf (tmp, 1024, "/usr/lib/debug/%s.debug", base);
2318             argvAdd(&files, tmp);
2319
2320             /* Do not bother to check build-ids for symbolic links.
2321                We'll handle them for the link target.  */
2322             if (lstat (name, &sbuf) == -1
2323                 || S_ISLNK (sbuf.st_mode))
2324               continue;
2325
2326             /* Try to gather the build-id from the binary.  */
2327             if (getELFBuildId (name, &build_id, &build_id_size) == -1)
2328               continue;
2329
2330             /* If we see build-id links for the first time add the
2331                directory.  */
2332             if (!seen_build_id)
2333               argvAdd(&files, "%dir /usr/lib/debug/.build-id");
2334
2335             /* From the build-id construct the two links pointing back
2336                to the debug information file and the binary.  */
2337             snprintf (tmp, 1024, "/usr/lib/debug/.build-id/%02x/",
2338                       build_id[0]);
2339             for (i = 1; i < build_id_size; ++i)
2340               sprintf (tmp + strlen (tmp), "%02x", build_id[i]);
2341             argvAdd(&files, tmp);
2342             sprintf (tmp + strlen (tmp), ".debug");
2343             argvAdd(&files, tmp);
2344
2345             free (build_id);
2346           }
2347
2348         /* If there are debuginfo files for this package add a
2349            new debuginfo package.  */
2350         if (files)
2351           {
2352             /* Add security manifest to set right SMACK labels */
2353             argvAdd(&files, "%manifest %{name}-debuginfo.manifest");
2354
2355             dbg = newPackage (spec);
2356             headerNVR (pkg->header, &name, NULL, NULL);
2357             /* Set name, summary and group.  */
2358             snprintf (tmp, 1024, "%s-debuginfo", name);
2359             headerPutString(dbg->header, RPMTAG_NAME, tmp);
2360             headerPutString(dbg->header, RPMTAG_VERSION, ver);
2361             headerPutString(dbg->header, RPMTAG_RELEASE, rel);
2362             headerPutString(dbg->header, RPMTAG_ARCH, a);
2363             snprintf (tmp, 1024, "Debug information for package %s", name);
2364             headerPutString(dbg->header, RPMTAG_SUMMARY, tmp);
2365             snprintf (tmp, 1024, "This package provides debug information for package %s.\n"
2366                       "Debug information is useful when developing applications that use this\n"
2367                       "package or when debugging this package.", name);
2368             headerPutString(dbg->header, RPMTAG_DESCRIPTION, tmp);
2369             headerPutString(dbg->header, RPMTAG_GROUP, "Development/Debug");
2370
2371             /* Add 'provides' information to debuginfo package. */
2372             addPackageProvides_for_debuginfo_pkg(dbg->header);
2373
2374             /* Inherit other tags from parent.  */
2375             headerCopyTags (pkg->header, dbg->header, copyTagsForDebug);
2376
2377             /* Build up the files list.  */
2378             dbg->fileList = files;
2379           }
2380       }
2381 }
2382 #endif
2383
2384 rpmRC processBinaryFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags,
2385                         int installSpecialDoc, int test)
2386 {
2387     Package pkg;
2388     rpmRC rc = RPMRC_OK;
2389     char *buildroot;
2390     
2391     check_fileList = newStringBuf();
2392     buildroot = rpmGenPath(spec->rootDir, spec->buildRoot, NULL);
2393     genSourceRpmName(spec);
2394     
2395     for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
2396         char *nvr;
2397         const char *a;
2398         int header_color;
2399         int arch_color;
2400
2401         if (pkg->fileList == NULL)
2402             continue;
2403
2404         headerPutString(pkg->header, RPMTAG_SOURCERPM, spec->sourceRpmName);
2405
2406         nvr = headerGetAsString(pkg->header, RPMTAG_NVRA);
2407         rpmlog(RPMLOG_NOTICE, _("Processing files: %s\n"), nvr);
2408         free(nvr);
2409                    
2410         if ((rc = processPackageFiles(spec, pkgFlags, pkg, installSpecialDoc, test)) != RPMRC_OK)
2411             goto exit;
2412 #if HAVE_GELF_H && HAVE_LIBELF
2413         addDebuginfoPackage(spec, pkg, buildroot);
2414 #endif
2415         if ((rc = rpmfcGenerateDepends(spec, pkg)) != RPMRC_OK)
2416             goto exit;
2417
2418         a = headerGetString(pkg->header, RPMTAG_ARCH);
2419         header_color = headerGetNumber(pkg->header, RPMTAG_HEADERCOLOR);
2420         if (!rstreq(a, "noarch")) {
2421             arch_color = rpmGetArchColor(a);
2422             if (arch_color > 0 && header_color > 0 &&
2423                                         !(arch_color & header_color)) {
2424                 rpmlog(RPMLOG_WARNING,
2425                        _("Binaries arch (%d) not matching the package arch (%d).\n"),
2426                        header_color, arch_color);
2427             }
2428         } else if (header_color != 0) {
2429             int terminate = rpmExpandNumeric("%{?_binaries_in_noarch_packages_terminate_build}");
2430             rpmlog(terminate ? RPMLOG_ERR : RPMLOG_WARNING, 
2431                    _("Arch dependent binaries in noarch package\n"));
2432             if (terminate) {
2433                 rc = RPMRC_FAIL;
2434                 goto exit;
2435             }
2436         }
2437     }
2438
2439     /* Now we have in fileList list of files from all packages.
2440      * We pass it to a script which does the work of finding missing
2441      * and duplicated files.
2442      */
2443     
2444     
2445     if (checkFiles(spec->buildRoot, check_fileList) > 0) {
2446         rc = RPMRC_FAIL;
2447     }
2448 exit:
2449     check_fileList = freeStringBuf(check_fileList);
2450     
2451     return rc;
2452 }