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