3 * The post-build, pre-packaging file tree walk to assemble the package
9 #define MYALLPERMS 07777
14 #include <sys/capability.h>
17 #include <rpm/rpmpgp.h>
19 #include <rpm/rpmfc.h>
20 #include <rpm/rpmfileutil.h> /* rpmDoDigest() */
21 #include <rpm/rpmlog.h>
23 #include "rpmio/rpmio_internal.h" /* XXX rpmioSlurp */
24 #include "rpmio/base64.h"
25 #include "build/fts.h"
27 #include "lib/rpmfi_internal.h" /* XXX fi->apath */
28 #include "build/rpmbuild_internal.h"
29 #include "build/rpmbuild_misc.h"
34 #define SKIPSPACE(s) { while (*(s) && risspace(*(s))) (s)++; }
35 #define SKIPWHITE(_x) {while(*(_x) && (risspace(*_x) || *(_x) == ',')) (_x)++;}
36 #define SKIPNONWHITE(_x){while(*(_x) &&!(risspace(*_x) || *(_x) == ',')) (_x)++;}
41 SPECD_DEFFILEMODE = (1 << 0),
42 SPECD_DEFDIRMODE = (1 << 1),
43 SPECD_DEFUID = (1 << 2),
44 SPECD_DEFGID = (1 << 3),
45 SPECD_DEFVERIFY = (1 << 4),
47 SPECD_FILEMODE = (1 << 8),
48 SPECD_DIRMODE = (1 << 9),
49 SPECD_UID = (1 << 10),
50 SPECD_GID = (1 << 11),
51 SPECD_VERIFY = (1 << 12)
54 typedef rpmFlags specfFlags;
58 typedef struct FileListRec_s {
60 #define fl_dev fl_st.st_dev
61 #define fl_ino fl_st.st_ino
62 #define fl_mode fl_st.st_mode
63 #define fl_nlink fl_st.st_nlink
64 #define fl_uid fl_st.st_uid
65 #define fl_gid fl_st.st_gid
66 #define fl_rdev fl_st.st_rdev
67 #define fl_size fl_st.st_size
68 #define fl_mtime fl_st.st_mtime
70 char *diskPath; /* get file from here */
71 char *cpioPath; /* filename in cpio archive */
75 specfFlags specdFlags; /* which attributes have been explicitly specified. */
76 rpmVerifyFlags verifyFlags;
77 char *langs; /* XXX locales separated with | */
83 typedef struct AttrRec_s {
92 static struct AttrRec_s root_ar = { NULL, NULL, "root", "root", 0, 0 };
95 static StringBuf check_fileList = NULL;
98 * Package file tree walk data.
100 typedef struct FileList_s {
104 int processingFailed;
106 int passedSpecialDoc;
115 rpmBuildPkgFlags pkgFlags;
116 rpmfileAttrs currentFlags;
117 specfFlags currentSpecdFlags;
118 rpmVerifyFlags currentVerifyFlags;
119 struct AttrRec_s cur_ar;
120 struct AttrRec_s def_ar;
121 specfFlags defSpecdFlags;
122 rpmVerifyFlags defVerifyFlags;
124 char ** currentLangs;
129 FileListRec fileList;
130 int fileListRecsAlloced;
131 int fileListRecsUsed;
137 static void nullAttrRec(AttrRec ar)
139 ar->ar_fmodestr = NULL;
140 ar->ar_dmodestr = NULL;
149 static void freeAttrRec(AttrRec ar)
151 ar->ar_fmodestr = _free(ar->ar_fmodestr);
152 ar->ar_dmodestr = _free(ar->ar_dmodestr);
153 ar->ar_user = _free(ar->ar_user);
154 ar->ar_group = _free(ar->ar_group);
155 /* XXX doesn't free ar (yet) */
161 static void dupAttrRec(const AttrRec oar, AttrRec nar)
166 nar->ar_fmodestr = (oar->ar_fmodestr ? xstrdup(oar->ar_fmodestr) : NULL);
167 nar->ar_dmodestr = (oar->ar_dmodestr ? xstrdup(oar->ar_dmodestr) : NULL);
168 nar->ar_user = (oar->ar_user ? xstrdup(oar->ar_user) : NULL);
169 nar->ar_group = (oar->ar_group ? xstrdup(oar->ar_group) : NULL);
170 nar->ar_fmode = oar->ar_fmode;
171 nar->ar_dmode = oar->ar_dmode;
177 static void dumpAttrRec(const char * msg, AttrRec ar)
180 fprintf(stderr, "%s:\t", msg);
181 fprintf(stderr, "(%s, %s, %s, %s)\n",
194 static char *strtokWithQuotes(char *s, const char *delim)
196 static char *olds = NULL;
204 /* Skip leading delimiters */
205 s += strspn(s, delim);
209 /* Find the end of the token. */
213 /* Find next " char */
214 s = strchr(token, '"');
216 s = strpbrk(token, delim);
221 /* This token finishes the string */
222 olds = strchr(token, '\0');
224 /* Terminate the token and make olds point past it */
234 typedef const struct VFA {
235 const char const * attribute;
242 static VFA_t const verifyAttrs[] = {
243 { "md5", 0, RPMVERIFY_FILEDIGEST },
244 { "filedigest", 0, RPMVERIFY_FILEDIGEST },
245 { "size", 0, RPMVERIFY_FILESIZE },
246 { "link", 0, RPMVERIFY_LINKTO },
247 { "user", 0, RPMVERIFY_USER },
248 { "group", 0, RPMVERIFY_GROUP },
249 { "mtime", 0, RPMVERIFY_MTIME },
250 { "mode", 0, RPMVERIFY_MODE },
251 { "rdev", 0, RPMVERIFY_RDEV },
252 { "caps", 0, RPMVERIFY_CAPS },
257 * Parse %verify and %defverify from file manifest.
258 * @param buf current spec file line
259 * @param fl package file tree walk data
260 * @return RPMRC_OK on success
262 static rpmRC parseForVerify(const char * buf, FileList fl)
264 char *p, *pe, *q = NULL;
266 rpmVerifyFlags *resultVerify;
268 rpmVerifyFlags verifyFlags;
269 specfFlags * specdFlags;
270 rpmRC rc = RPMRC_FAIL;
272 if ((p = strstr(buf, (name = "%verify"))) != NULL) {
273 resultVerify = &(fl->currentVerifyFlags);
274 specdFlags = &fl->currentSpecdFlags;
275 } else if ((p = strstr(buf, (name = "%defverify"))) != NULL) {
276 resultVerify = &(fl->defVerifyFlags);
277 specdFlags = &fl->defSpecdFlags;
281 for (pe = p; (pe-p) < strlen(name); pe++)
287 rpmlog(RPMLOG_ERR, _("Missing '(' in %s %s\n"), name, pe);
291 /* Bracket %*verify args */
293 for (p = pe; *pe && *pe != ')'; pe++)
297 rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
301 /* Localize. Erase parsed string */
302 q = xmalloc((pe-p) + 1);
303 rstrlcpy(q, p, (pe-p) + 1);
308 verifyFlags = RPMVERIFY_NONE;
310 for (p = q; *p != '\0'; p = pe) {
320 for (vfa = verifyAttrs; vfa->attribute != NULL; vfa++) {
321 if (!rstreq(p, vfa->attribute))
323 verifyFlags |= vfa->flag;
330 if (rstreq(p, "not")) {
333 rpmlog(RPMLOG_ERR, _("Invalid %s token: %s\n"), name, p);
338 *resultVerify = negated ? ~(verifyFlags) : verifyFlags;
339 *specdFlags |= SPECD_VERIFY;
344 if (rc != RPMRC_OK) {
345 fl->processingFailed = 1;
351 #define isAttrDefault(_ars) ((_ars)[0] == '-' && (_ars)[1] == '\0')
354 * Parse %dev from file manifest.
355 * @param buf current spec file line
356 * @param fl package file tree walk data
357 * @return RPMRC_OK on success
359 static rpmRC parseForDev(const char * buf, FileList fl)
362 const char * errstr = NULL;
363 char *p, *pe, *q = NULL;
364 rpmRC rc = RPMRC_FAIL; /* assume error */
366 if ((p = strstr(buf, (name = "%dev"))) == NULL)
369 for (pe = p; (pe-p) < strlen(name); pe++)
378 /* Bracket %dev args */
380 for (p = pe; *pe && *pe != ')'; pe++)
387 /* Localize. Erase parsed string */
388 q = xmalloc((pe-p) + 1);
389 rstrlcpy(q, p, (pe-p) + 1);
394 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
404 p = pe; SKIPWHITE(p);
405 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
406 for (pe = p; *pe && risdigit(*pe); pe++)
409 fl->devmajor = atoi(p);
410 if (!(fl->devmajor >= 0 && fl->devmajor < 256)) {
420 p = pe; SKIPWHITE(p);
421 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
422 for (pe = p; *pe && risdigit(*pe); pe++)
425 fl->devminor = atoi(p);
426 if (!(fl->devminor >= 0 && fl->devminor < 256)) {
442 rpmlog(RPMLOG_ERR, _("Missing %s in %s %s\n"), errstr, name, p);
443 fl->processingFailed = 1;
450 * Parse %attr and %defattr from file manifest.
451 * @param buf current spec file line
452 * @param fl package file tree walk data
453 * @return 0 on success
455 static rpmRC parseForAttr(const char * buf, FileList fl)
458 char *p, *pe, *q = NULL;
460 struct AttrRec_s arbuf;
461 AttrRec ar = &arbuf, ret_ar;
462 specfFlags * specdFlags;
463 rpmRC rc = RPMRC_FAIL;
465 if ((p = strstr(buf, (name = "%attr"))) != NULL) {
466 ret_ar = &(fl->cur_ar);
467 specdFlags = &fl->currentSpecdFlags;
468 } else if ((p = strstr(buf, (name = "%defattr"))) != NULL) {
469 ret_ar = &(fl->def_ar);
470 specdFlags = &fl->defSpecdFlags;
474 for (pe = p; (pe-p) < strlen(name); pe++)
480 rpmlog(RPMLOG_ERR, _("Missing '(' in %s %s\n"), name, pe);
484 /* Bracket %*attr args */
486 for (p = pe; *pe && *pe != ')'; pe++)
489 if (ret_ar == &(fl->def_ar)) { /* %defattr */
495 _("Non-white space follows %s(): %s\n"), name, r);
500 /* Localize. Erase parsed string */
501 q = xmalloc((pe-p) + 1);
502 rstrlcpy(q, p, (pe-p) + 1);
510 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
512 p = pe; SKIPWHITE(p);
515 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
517 p = pe; SKIPWHITE(p);
520 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
522 p = pe; SKIPWHITE(p);
524 if (*p != '\0' && ret_ar == &(fl->def_ar)) { /* %defattr */
525 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
527 p = pe; SKIPWHITE(p);
530 if (!(ar->ar_fmodestr && ar->ar_user && ar->ar_group) || *p != '\0') {
531 rpmlog(RPMLOG_ERR, _("Bad syntax: %s(%s)\n"), name, q);
535 /* Do a quick test on the mode argument and adjust for "-" */
536 if (ar->ar_fmodestr && !isAttrDefault(ar->ar_fmodestr)) {
538 x = sscanf(ar->ar_fmodestr, "%o", &ui);
539 if ((x == 0) || (ar->ar_fmode & ~MYALLPERMS)) {
540 rpmlog(RPMLOG_ERR, _("Bad mode spec: %s(%s)\n"), name, q);
545 if (ret_ar == &(fl->def_ar)) {
546 ar->ar_fmodestr = NULL;
549 ar->ar_fmodestr = fl->def_ar.ar_fmodestr;
550 ar->ar_fmode = fl->def_ar.ar_fmode;
554 if (ar->ar_dmodestr && !isAttrDefault(ar->ar_dmodestr)) {
556 x = sscanf(ar->ar_dmodestr, "%o", &ui);
557 if ((x == 0) || (ar->ar_dmode & ~MYALLPERMS)) {
558 rpmlog(RPMLOG_ERR, _("Bad dirmode spec: %s(%s)\n"), name, q);
563 if (ret_ar == &(fl->def_ar)) {
564 ar->ar_dmodestr = NULL;
567 ar->ar_dmodestr = fl->def_ar.ar_dmodestr;
568 ar->ar_dmode = fl->def_ar.ar_dmode;
572 if (!(ar->ar_user && !isAttrDefault(ar->ar_user))) {
573 if (ret_ar == &(fl->def_ar)) {
576 ar->ar_user = fl->def_ar.ar_user;
580 if (!(ar->ar_group && !isAttrDefault(ar->ar_group))) {
581 if (ret_ar == &(fl->def_ar)) {
584 ar->ar_group = fl->def_ar.ar_group;
588 dupAttrRec(ar, ret_ar);
590 /* XXX fix all this */
591 *specdFlags |= SPECD_UID | SPECD_GID | SPECD_FILEMODE | SPECD_DIRMODE;
596 if (rc != RPMRC_OK) {
597 fl->processingFailed = 1;
604 * Parse %config from file manifest.
605 * @param buf current spec file line
606 * @param fl package file tree walk data
607 * @return RPMRC_OK on success
609 static rpmRC parseForConfig(const char * buf, FileList fl)
611 char *p, *pe, *q = NULL;
613 rpmRC rc = RPMRC_FAIL;
615 if ((p = strstr(buf, (name = "%config"))) == NULL)
618 fl->currentFlags |= RPMFILE_CONFIG;
620 /* Erase "%config" token. */
621 for (pe = p; (pe-p) < strlen(name); pe++)
627 /* Bracket %config args */
629 for (p = pe; *pe && *pe != ')'; pe++)
633 rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
637 /* Localize. Erase parsed string. */
638 q = xmalloc((pe-p) + 1);
639 rstrlcpy(q, p, (pe-p) + 1);
643 for (p = q; *p != '\0'; p = pe) {
651 if (rstreq(p, "missingok")) {
652 fl->currentFlags |= RPMFILE_MISSINGOK;
653 } else if (rstreq(p, "noreplace")) {
654 fl->currentFlags |= RPMFILE_NOREPLACE;
656 rpmlog(RPMLOG_ERR, _("Invalid %s token: %s\n"), name, p);
664 if (rc != RPMRC_OK) {
665 fl->processingFailed = 1;
673 static int langCmp(const void * ap, const void * bp)
675 return strcmp(*(const char **)ap, *(const char **)bp);
679 * Parse %lang from file manifest.
680 * @param buf current spec file line
681 * @param fl package file tree walk data
682 * @return RPMRC_OK on success
684 static rpmRC parseForLang(const char * buf, FileList fl)
686 char *p, *pe, *q = NULL;
688 rpmRC rc = RPMRC_FAIL;
690 while ((p = strstr(buf, (name = "%lang"))) != NULL) {
692 for (pe = p; (pe-p) < strlen(name); pe++)
697 rpmlog(RPMLOG_ERR, _("Missing '(' in %s %s\n"), name, pe);
701 /* Bracket %lang args */
703 for (pe = p; *pe && *pe != ')'; pe++)
707 rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
711 /* Localize. Erase parsed string. */
712 q = xmalloc((pe-p) + 1);
713 rstrlcpy(q, p, (pe-p) + 1);
717 /* Parse multiple arguments from %lang */
718 for (p = q; *p != '\0'; p = pe) {
729 /* Sanity check on locale lengths */
730 if (np < 1 || (np == 1 && *p != 'C') || np >= 32) {
732 _("Unusual locale length: \"%.*s\" in %%lang(%s)\n"),
737 /* Check for duplicate locales */
738 if (fl->currentLangs != NULL)
739 for (i = 0; i < fl->nLangs; i++) {
740 if (!rstreqn(fl->currentLangs[i], p, np))
742 rpmlog(RPMLOG_ERR, _("Duplicate locale %.*s in %%lang(%s)\n"),
748 fl->currentLangs = xrealloc(fl->currentLangs,
749 (fl->nLangs + 1) * sizeof(*fl->currentLangs));
750 newp = xmalloc( np+1 );
751 rstrlcpy(newp, p, np + 1);
752 fl->currentLangs[fl->nLangs++] = newp;
753 if (*pe == ',') pe++; /* skip , if present */
757 /* Insure that locales are sorted. */
758 if (fl->currentLangs)
759 qsort(fl->currentLangs, fl->nLangs, sizeof(*fl->currentLangs), langCmp);
764 if (rc != RPMRC_OK) {
765 fl->processingFailed = 1;
772 * Parse %caps from file manifest.
773 * @param buf current spec file line
774 * @param fl package file tree walk data
775 * @return RPMRC_OK on success
777 static rpmRC parseForCaps(const char * buf, FileList fl)
779 char *p, *pe, *q = NULL;
781 rpmRC rc = RPMRC_FAIL;
783 if ((p = strstr(buf, (name = "%caps"))) == NULL)
786 /* Erase "%caps" token. */
787 for (pe = p; (pe-p) < strlen(name); pe++)
793 /* Bracket %caps args */
795 for (p = pe; *pe && *pe != ')'; pe++)
799 rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
803 /* Localize. Erase parsed string. */
804 q = xmalloc((pe-p) + 1);
805 rstrlcpy(q, p, (pe-p) + 1);
812 cap_t fcaps = cap_from_text(q);
814 rpmlog(RPMLOG_ERR, _("Invalid capability: %s\n"), q);
817 /* run our string through cap_to_text() to get libcap presentation */
818 captxt = cap_to_text(fcaps, NULL);
819 fl->currentCaps = xstrdup(captxt);
825 rpmlog(RPMLOG_ERR, _("File capability support not built in\n"));
833 if (rc != RPMRC_OK) {
834 fl->processingFailed = 1;
841 static VFA_t virtualFileAttributes[] = {
842 { "%dir", 0, 0 }, /* XXX why not RPMFILE_DIR? */
843 { "%doc", 0, RPMFILE_DOC },
844 { "%ghost", 0, RPMFILE_GHOST },
845 { "%exclude", 0, RPMFILE_EXCLUDE },
846 { "%readme", 0, RPMFILE_README },
847 { "%license", 0, RPMFILE_LICENSE },
848 { "%pubkey", 0, RPMFILE_PUBKEY },
853 * Parse simple attributes (e.g. %dir) from file manifest.
856 * @param buf current spec file line
857 * @param fl package file tree walk data
858 * @retval *fileName file name
859 * @return RPMRC_OK on success
861 static rpmRC parseForSimple(rpmSpec spec, Package pkg, char * buf,
862 FileList fl, const char ** fileName)
866 char *specialDocBuf = NULL;
872 while ((s = strtokWithQuotes(t, " \t\n")) != NULL) {
875 if (rstreq(s, "%docdir")) {
876 s = strtokWithQuotes(NULL, " \t\n");
878 if (s == NULL || strtokWithQuotes(NULL, " \t\n")) {
879 rpmlog(RPMLOG_ERR, _("Only one arg for %%docdir\n"));
882 argvAdd(&(fl->docDirs), s);
887 /* Set flags for virtual file attributes */
888 for (vfa = virtualFileAttributes; vfa->attribute != NULL; vfa++) {
889 if (!rstreq(s, vfa->attribute))
892 if (rstreq(s, "%dir"))
893 fl->isDir = 1; /* XXX why not RPMFILE_DIR? */
896 fl->currentFlags &= ~vfa->flag;
898 fl->currentFlags |= vfa->flag;
902 /* if we got an attribute, continue with next token */
903 if (vfa->attribute != NULL)
907 /* We already got a file -- error */
908 rpmlog(RPMLOG_ERR, _("Two files on one line: %s\n"), *fileName);
913 if (fl->currentFlags & RPMFILE_DOC) {
914 rstrscat(&specialDocBuf, " ", s, NULL);
916 if (fl->currentFlags & RPMFILE_PUBKEY)
920 /* not in %doc, does not begin with / -- error */
921 rpmlog(RPMLOG_ERR, _("File must begin with \"/\": %s\n"), s);
930 if (*fileName || (fl->currentFlags & ~(RPMFILE_DOC))) {
932 _("Can't mix special %%doc with other forms: %s\n"),
933 (*fileName ? *fileName : ""));
936 /* XXX FIXME: this is easy to do as macro expansion */
937 if (! fl->passedSpecialDoc) {
938 char *mkdocdir = rpmExpand("%{__mkdir_p} $DOCDIR", NULL);
939 pkg->specialDoc = newStringBuf();
940 appendStringBuf(pkg->specialDoc, "DOCDIR=$RPM_BUILD_ROOT");
941 appendLineStringBuf(pkg->specialDoc, pkg->specialDocDir);
942 appendLineStringBuf(pkg->specialDoc, "export DOCDIR");
943 appendLineStringBuf(pkg->specialDoc, "rm -rf $DOCDIR");
944 appendLineStringBuf(pkg->specialDoc, mkdocdir);
947 *fileName = pkg->specialDocDir;
948 fl->passedSpecialDoc = 1;
949 fl->isSpecialDoc = 1;
952 appendStringBuf(pkg->specialDoc, "cp -pr ");
953 appendStringBuf(pkg->specialDoc, specialDocBuf);
954 appendLineStringBuf(pkg->specialDoc, " $DOCDIR");
959 if (res != RPMRC_OK) {
960 fl->processingFailed = 1;
968 static int compareFileListRecs(const void * ap, const void * bp)
970 const char *a = ((FileListRec)ap)->cpioPath;
971 const char *b = ((FileListRec)bp)->cpioPath;
976 * Test if file is located in a %docdir.
977 * @param fl package file tree walk data
978 * @param fileName file path
979 * @return 1 if doc file, 0 if not
981 static int isDoc(FileList fl, const char * fileName)
986 k = strlen(fileName);
987 for (dd = fl->docDirs; *dd; dd++) {
989 if (l < k && rstreqn(fileName, *dd, l) && fileName[l] == '/')
995 static int isHardLink(FileListRec flp, FileListRec tlp)
997 return ((S_ISREG(flp->fl_mode) && S_ISREG(tlp->fl_mode)) &&
998 ((flp->fl_nlink > 1) && (flp->fl_nlink == tlp->fl_nlink)) &&
999 (flp->fl_ino == tlp->fl_ino) &&
1000 (flp->fl_dev == tlp->fl_dev));
1004 * Verify that file attributes scope over hardlinks correctly.
1005 * If partial hardlink sets are possible, then add tracking dependency.
1006 * @param fl package file tree walk data
1007 * @return 1 if partial hardlink sets can exist, 0 otherwise.
1009 static int checkHardLinks(FileList fl)
1011 FileListRec ilp, jlp;
1014 for (i = 0; i < fl->fileListRecsUsed; i++) {
1015 ilp = fl->fileList + i;
1016 if (!(S_ISREG(ilp->fl_mode) && ilp->fl_nlink > 1))
1019 for (j = i + 1; j < fl->fileListRecsUsed; j++) {
1020 jlp = fl->fileList + j;
1021 if (isHardLink(ilp, jlp)) {
1029 static int seenHardLink(FileList fl, FileListRec flp)
1031 for (FileListRec ilp = fl->fileList; ilp < flp; ilp++) {
1032 if (isHardLink(flp, ilp)) {
1040 * Add file entries to header.
1041 * @todo Should directories have %doc/%config attributes? (#14531)
1042 * @todo Remove RPMTAG_OLDFILENAMES, add dirname/basename instead.
1043 * @param fl package file tree walk data
1044 * @retval *fip file info for package
1048 static void genCpioListAndHeader(FileList fl,
1049 rpmfi * fip, Header h, int isSrc)
1051 int _addDotSlash = !(isSrc || rpmExpandNumeric("%{_noPayloadPrefix}"));
1052 size_t apathlen = 0;
1053 size_t dpathlen = 0;
1058 pgpHashAlgo defaultalgo = PGPHASHALGO_MD5, digestalgo;
1059 rpm_loff_t totalFileSize = 0;
1062 * See if non-md5 file digest algorithm is requested. If not
1063 * specified, quietly assume md5. Otherwise check if supported type.
1065 digestalgo = rpmExpandNumeric(isSrc ? "%{_source_filedigest_algorithm}" :
1066 "%{_binary_filedigest_algorithm}");
1067 if (digestalgo == 0) {
1068 digestalgo = defaultalgo;
1071 if (rpmDigestLength(digestalgo) == 0) {
1072 rpmlog(RPMLOG_WARNING,
1073 _("Unknown file digest algorithm %u, falling back to MD5\n"),
1075 digestalgo = defaultalgo;
1078 /* Sort the big list */
1079 qsort(fl->fileList, fl->fileListRecsUsed,
1080 sizeof(*(fl->fileList)), compareFileListRecs);
1082 /* Generate the header. */
1087 for (i = 0, flp = fl->fileList; i < fl->fileListRecsUsed; i++, flp++) {
1088 /* Merge duplicate entries. */
1089 while (i < (fl->fileListRecsUsed - 1) &&
1090 rstreq(flp->cpioPath, flp[1].cpioPath)) {
1092 /* Two entries for the same file found, merge the entries. */
1093 /* Note that an %exclude is a duplication of a file reference */
1096 flp[1].flags |= flp->flags;
1098 if (!(flp[1].flags & RPMFILE_EXCLUDE))
1099 rpmlog(RPMLOG_WARNING, _("File listed twice: %s\n"),
1103 if (S_ISDIR(flp->fl_mode)) {
1104 if ((flp[1].specdFlags & (SPECD_DIRMODE | SPECD_DEFDIRMODE)) <
1105 (flp->specdFlags & (SPECD_DIRMODE | SPECD_DEFDIRMODE)))
1106 flp[1].fl_mode = flp->fl_mode;
1108 if ((flp[1].specdFlags & (SPECD_FILEMODE | SPECD_DEFFILEMODE)) <
1109 (flp->specdFlags & (SPECD_FILEMODE | SPECD_DEFFILEMODE)))
1110 flp[1].fl_mode = flp->fl_mode;
1114 if ((flp[1].specdFlags & (SPECD_UID | SPECD_DEFUID)) <
1115 (flp->specdFlags & (SPECD_UID | SPECD_DEFUID)))
1117 flp[1].fl_uid = flp->fl_uid;
1118 flp[1].uname = flp->uname;
1122 if ((flp[1].specdFlags & (SPECD_GID | SPECD_DEFGID)) <
1123 (flp->specdFlags & (SPECD_GID | SPECD_DEFGID)))
1125 flp[1].fl_gid = flp->fl_gid;
1126 flp[1].gname = flp->gname;
1130 if ((flp[1].specdFlags & (SPECD_VERIFY | SPECD_DEFVERIFY)) <
1131 (flp->specdFlags & (SPECD_VERIFY | SPECD_DEFVERIFY)))
1132 flp[1].verifyFlags = flp->verifyFlags;
1134 /* XXX to-do: language */
1139 /* Skip files that were marked with %exclude. */
1140 if (flp->flags & RPMFILE_EXCLUDE) continue;
1142 /* Omit '/' and/or URL prefix, leave room for "./" prefix */
1143 apathlen += (strlen(flp->cpioPath) - skipLen + (_addDotSlash ? 3 : 1));
1145 /* Leave room for both dirname and basename NUL's */
1146 dpathlen += (strlen(flp->diskPath) + 2);
1149 * Make the header. Store the on-disk path to OLDFILENAMES for
1150 * cpio list generation purposes for now, final path temporarily
1151 * to ORIGFILENAMES, to be swapped later into OLDFILENAMES.
1153 headerPutString(h, RPMTAG_OLDFILENAMES, flp->diskPath);
1154 headerPutString(h, RPMTAG_ORIGFILENAMES, flp->cpioPath);
1155 headerPutString(h, RPMTAG_FILEUSERNAME, flp->uname);
1156 headerPutString(h, RPMTAG_FILEGROUPNAME, flp->gname);
1159 * Only use 64bit filesizes if file sizes require it.
1160 * This is basically no-op for now as we error out in addFile() if
1161 * any individual file size exceeds the cpio limit.
1163 if (fl->largeFiles) {
1164 rpm_loff_t rsize64 = (rpm_loff_t)flp->fl_size;
1165 headerPutUint64(h, RPMTAG_LONGFILESIZES, &rsize64, 1);
1166 /* XXX TODO: add rpmlib() dependency for large files */
1168 rpm_off_t rsize32 = (rpm_off_t)flp->fl_size;
1169 headerPutUint32(h, RPMTAG_FILESIZES, &rsize32, 1);
1171 /* Excludes and dupes have been filtered out by now. */
1172 if (S_ISREG(flp->fl_mode)) {
1173 if (flp->fl_nlink == 1 || !seenHardLink(fl, flp)) {
1174 totalFileSize += flp->fl_size;
1179 * For items whose size varies between systems, always explicitly
1180 * cast to the header type before inserting.
1181 * TODO: check and warn if header type overflows for each case.
1183 { rpm_time_t rtime = (rpm_time_t) flp->fl_mtime;
1184 headerPutUint32(h, RPMTAG_FILEMTIMES, &rtime, 1);
1187 { rpm_mode_t rmode = (rpm_mode_t) flp->fl_mode;
1188 headerPutUint16(h, RPMTAG_FILEMODES, &rmode, 1);
1191 { rpm_rdev_t rrdev = (rpm_rdev_t) flp->fl_rdev;
1192 headerPutUint16(h, RPMTAG_FILERDEVS, &rrdev, 1);
1195 { rpm_dev_t rdev = (rpm_dev_t) flp->fl_dev;
1196 headerPutUint32(h, RPMTAG_FILEDEVICES, &rdev, 1);
1199 { rpm_ino_t rino = (rpm_ino_t) flp->fl_ino;
1200 headerPutUint32(h, RPMTAG_FILEINODES, &rino, 1);
1203 headerPutString(h, RPMTAG_FILELANGS, flp->langs);
1206 headerPutString(h, RPMTAG_FILECAPS, flp->caps);
1210 if (S_ISREG(flp->fl_mode))
1211 (void) rpmDoDigest(digestalgo, flp->diskPath, 1,
1212 (unsigned char *)buf, NULL);
1213 headerPutString(h, RPMTAG_FILEDIGESTS, buf);
1216 if (S_ISLNK(flp->fl_mode)) {
1217 buf[readlink(flp->diskPath, buf, BUFSIZ)] = '\0';
1218 if (fl->buildRoot) {
1219 if (buf[0] == '/' && !rstreq(fl->buildRoot, "/") &&
1220 rstreqn(buf, fl->buildRoot, strlen(fl->buildRoot))) {
1222 _("Symlink points to BuildRoot: %s -> %s\n"),
1223 flp->cpioPath, buf);
1224 fl->processingFailed = 1;
1228 headerPutString(h, RPMTAG_FILELINKTOS, buf);
1230 if (flp->flags & RPMFILE_GHOST) {
1231 flp->verifyFlags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE |
1232 RPMVERIFY_LINKTO | RPMVERIFY_MTIME);
1234 headerPutUint32(h, RPMTAG_FILEVERIFYFLAGS, &(flp->verifyFlags),1);
1236 if (!isSrc && isDoc(fl, flp->cpioPath))
1237 flp->flags |= RPMFILE_DOC;
1238 /* XXX Should directories have %doc/%config attributes? (#14531) */
1239 if (S_ISDIR(flp->fl_mode))
1240 flp->flags &= ~(RPMFILE_CONFIG|RPMFILE_DOC);
1242 headerPutUint32(h, RPMTAG_FILEFLAGS, &(flp->flags) ,1);
1245 if (totalFileSize < UINT32_MAX) {
1246 rpm_off_t totalsize = totalFileSize;
1247 headerPutUint32(h, RPMTAG_SIZE, &totalsize, 1);
1249 rpm_loff_t totalsize = totalFileSize;
1250 headerPutUint64(h, RPMTAG_LONGSIZE, &totalsize, 1);
1253 if (digestalgo != defaultalgo) {
1254 headerPutUint32(h, RPMTAG_FILEDIGESTALGO, &digestalgo, 1);
1255 rpmlibNeedsFeature(h, "FileDigests", "4.6.0-1");
1259 rpmlibNeedsFeature(h, "FileCaps", "4.6.1-1");
1263 (void) rpmlibNeedsFeature(h, "PayloadFilesHavePrefix", "4.0-1");
1266 struct rpmtd_s filenames;
1267 rpmfiFlags flags = RPMFI_NOHEADER|RPMFI_NOFILEUSER|RPMFI_NOFILEGROUP;
1273 /* rpmfiNew() only groks compressed filelists */
1274 headerConvert(h, HEADERCONV_COMPRESSFILELIST);
1275 fi = rpmfiNew(NULL, h, RPMTAG_BASENAMES, flags);
1278 * Grab the real filenames from ORIGFILENAMES and put into OLDFILENAMES,
1279 * remove temporary cruft and side-effects from filelist compression
1282 headerGet(h, RPMTAG_ORIGFILENAMES, &filenames, HEADERGET_ALLOC);
1283 headerDel(h, RPMTAG_ORIGFILENAMES);
1284 headerDel(h, RPMTAG_BASENAMES);
1285 headerDel(h, RPMTAG_DIRNAMES);
1286 headerDel(h, RPMTAG_DIRINDEXES);
1287 rpmtdSetTag(&filenames, RPMTAG_OLDFILENAMES);
1288 headerPut(h, &filenames, HEADERPUT_DEFAULT);
1290 /* Create hge-style archive path array, normally adding "./" */
1291 fc = rpmtdCount(&filenames);
1292 apath = xmalloc(fc * sizeof(*apath) + apathlen + 1);
1293 a = (char *)(apath + fc);
1295 rpmtdInit(&filenames);
1296 for (int i = 0; (fn = rpmtdNextString(&filenames)); i++) {
1299 a = stpcpy(a, "./");
1300 a = stpcpy(a, (fn + skipLen));
1301 a++; /* skip apath NUL */
1305 rpmtdFreeData(&filenames);
1308 /* Compress filelist unless legacy format requested */
1309 if (!(fl->pkgFlags & RPMBUILD_PKG_NODIRTOKENS)) {
1310 headerConvert(h, HEADERCONV_COMPRESSFILELIST);
1311 /* Binary packages with dirNames cannot be installed by legacy rpm. */
1312 (void) rpmlibNeedsFeature(h, "CompressedFileNames", "3.0.4-1");
1318 static FileListRec freeFileList(FileListRec fileList,
1322 fileList[count].diskPath = _free(fileList[count].diskPath);
1323 fileList[count].cpioPath = _free(fileList[count].cpioPath);
1324 fileList[count].langs = _free(fileList[count].langs);
1325 fileList[count].caps = _free(fileList[count].caps);
1327 fileList = _free(fileList);
1332 static rpmRC recurseDir(FileList fl, const char * diskPath);
1335 * Add a file to the package manifest.
1336 * @param fl package file tree walk data
1337 * @param diskPath path to file
1338 * @param statp file stat (possibly NULL)
1339 * @return RPMRC_OK on success
1341 static rpmRC addFile(FileList fl, const char * diskPath,
1342 struct stat * statp)
1344 const char *cpioPath = diskPath;
1345 struct stat statbuf;
1349 const char *fileUname;
1350 const char *fileGname;
1352 /* Path may have prepended buildRoot, so locate the original filename. */
1354 * XXX There are 3 types of entry into addFile:
1356 * From diskUrl statp
1357 * =====================================================
1358 * processBinaryFile path NULL
1359 * processBinaryFile glob result path NULL
1363 if (fl->buildRoot && !rstreq(fl->buildRoot, "/"))
1364 cpioPath += strlen(fl->buildRoot);
1366 /* XXX make sure '/' can be packaged also */
1367 if (*cpioPath == '\0')
1370 if (statp == NULL) {
1371 time_t now = time(NULL);
1373 memset(statp, 0, sizeof(*statp));
1375 /* XXX hack up a stat structure for a %dev(...) directive. */
1376 statp->st_nlink = 1;
1378 ((fl->devmajor & 0xff) << 8) | (fl->devminor & 0xff);
1379 statp->st_dev = statp->st_rdev;
1380 statp->st_mode = (fl->devtype == 'b' ? S_IFBLK : S_IFCHR);
1381 statp->st_mode |= (fl->cur_ar.ar_fmode & 0777);
1382 statp->st_atime = now;
1383 statp->st_mtime = now;
1384 statp->st_ctime = now;
1386 int is_ghost = fl->currentFlags & RPMFILE_GHOST;
1388 if (lstat(diskPath, statp)) {
1389 if (is_ghost) { /* the file is %ghost missing from build root, assume regular file */
1390 if (fl->cur_ar.ar_fmodestr != NULL) {
1391 statp->st_mode = S_IFREG | (fl->cur_ar.ar_fmode & 0777);
1393 rpmlog(RPMLOG_ERR, _("Explicit file attributes required in spec for: %s\n"), diskPath);
1394 fl->processingFailed = 1;
1397 statp->st_atime = now;
1398 statp->st_mtime = now;
1399 statp->st_ctime = now;
1401 rpmlog(RPMLOG_ERR, _("File not found: %s\n"), diskPath);
1402 fl->processingFailed = 1;
1409 if ((! fl->isDir) && S_ISDIR(statp->st_mode)) {
1410 /* FIX: fl->buildRoot may be NULL */
1411 return recurseDir(fl, diskPath);
1414 fileMode = statp->st_mode;
1415 fileUid = statp->st_uid;
1416 fileGid = statp->st_gid;
1418 if (S_ISDIR(fileMode) && fl->cur_ar.ar_dmodestr) {
1420 fileMode |= fl->cur_ar.ar_dmode;
1421 } else if (fl->cur_ar.ar_fmodestr != NULL) {
1423 fileMode |= fl->cur_ar.ar_fmode;
1425 if (fl->cur_ar.ar_user) {
1426 fileUname = getUnameS(fl->cur_ar.ar_user);
1428 fileUname = getUname(fileUid);
1430 if (fl->cur_ar.ar_group) {
1431 fileGname = getGnameS(fl->cur_ar.ar_group);
1433 fileGname = getGname(fileGid);
1436 /* Default user/group to builder's user/group */
1437 if (fileUname == NULL)
1438 fileUname = getUname(getuid());
1439 if (fileGname == NULL)
1440 fileGname = getGname(getgid());
1442 /* S_XXX macro must be consistent with type in find call at check-files script */
1443 if (check_fileList && (S_ISREG(fileMode) || S_ISLNK(fileMode))) {
1444 appendStringBuf(check_fileList, diskPath);
1445 appendStringBuf(check_fileList, "\n");
1448 /* Add to the file list */
1449 if (fl->fileListRecsUsed == fl->fileListRecsAlloced) {
1450 fl->fileListRecsAlloced += 128;
1451 fl->fileList = xrealloc(fl->fileList,
1452 fl->fileListRecsAlloced * sizeof(*(fl->fileList)));
1455 { FileListRec flp = &fl->fileList[fl->fileListRecsUsed];
1458 flp->fl_st = *statp; /* structure assignment */
1459 flp->fl_mode = fileMode;
1460 flp->fl_uid = fileUid;
1461 flp->fl_gid = fileGid;
1463 flp->cpioPath = xstrdup(cpioPath);
1464 flp->diskPath = xstrdup(diskPath);
1465 flp->uname = fileUname;
1466 flp->gname = fileGname;
1468 if (fl->currentLangs && fl->nLangs > 0) {
1472 for (i = 0; i < fl->nLangs; i++)
1473 nl += strlen(fl->currentLangs[i]) + 1;
1475 flp->langs = ncl = xmalloc(nl);
1476 for (i = 0; i < fl->nLangs; i++) {
1478 if (i) *ncl++ = '|';
1479 for (ocl = fl->currentLangs[i]; *ocl != '\0'; ocl++)
1484 flp->langs = xstrdup("");
1487 if (fl->currentCaps) {
1488 flp->caps = fl->currentCaps;
1490 flp->caps = xstrdup("");
1493 flp->flags = fl->currentFlags;
1494 flp->specdFlags = fl->currentSpecdFlags;
1495 flp->verifyFlags = fl->currentVerifyFlags;
1497 if (!(flp->flags & RPMFILE_EXCLUDE) && S_ISREG(flp->fl_mode)) {
1499 * XXX Simple and stupid check for now, this needs to be per-payload
1500 * format check once we have other payloads than good 'ole cpio.
1502 if ((rpm_loff_t) flp->fl_size >= CPIO_FILESIZE_MAX) {
1504 rpmlog(RPMLOG_ERR, _("File %s too large for payload\n"),
1511 fl->fileListRecsUsed++;
1518 * Add directory (and all of its files) to the package manifest.
1519 * @param fl package file tree walk data
1520 * @param diskPath path to file
1521 * @return RPMRC_OK on success
1523 static rpmRC recurseDir(FileList fl, const char * diskPath)
1528 int myFtsOpts = (FTS_COMFOLLOW | FTS_NOCHDIR | FTS_PHYSICAL);
1529 rpmRC rc = RPMRC_FAIL;
1531 fl->isDir = 1; /* Keep it from following myftw() again */
1533 ftsSet[0] = (char *) diskPath;
1535 ftsp = Fts_open(ftsSet, myFtsOpts, NULL);
1536 while ((fts = Fts_read(ftsp)) != NULL) {
1537 switch (fts->fts_info) {
1538 case FTS_D: /* preorder directory */
1539 case FTS_F: /* regular file */
1540 case FTS_SL: /* symbolic link */
1541 case FTS_SLNONE: /* symbolic link without target */
1542 case FTS_DEFAULT: /* none of the above */
1543 rc = addFile(fl, fts->fts_accpath, fts->fts_statp);
1545 case FTS_DOT: /* dot or dot-dot */
1546 case FTS_DP: /* postorder directory */
1549 case FTS_NS: /* stat(2) failed */
1550 case FTS_DNR: /* unreadable directory */
1551 case FTS_ERR: /* error; errno is set */
1552 case FTS_DC: /* directory that causes cycles */
1553 case FTS_NSOK: /* no stat(2) requested */
1554 case FTS_INIT: /* initialized only */
1555 case FTS_W: /* whiteout object */
1563 (void) Fts_close(ftsp);
1571 * Add a pubkey/icon to a binary package.
1573 * @param fl package file tree walk data
1574 * @param fileName path to file, relative is builddir, absolute buildroot.
1575 * @param tag tag to add
1576 * @return RPMRC_OK on success
1578 static rpmRC processMetadataFile(Package pkg, FileList fl,
1579 const char * fileName, rpmTag tag)
1581 const char * buildDir = "%{_builddir}/%{?buildsubdir}/";
1584 uint8_t * pkt = NULL;
1587 rpmRC rc = RPMRC_FAIL;
1590 if (*fileName == '/') {
1591 fn = rpmGenPath(fl->buildRoot, NULL, fileName);
1594 fn = rpmGenPath(buildDir, NULL, fileName);
1598 rpmlog(RPMLOG_ERR, _("%s: can't load unknown tag (%d).\n"),
1602 case RPMTAG_PUBKEYS: {
1603 if ((xx = pgpReadPkts(fn, &pkt, (size_t *)&pktlen)) <= 0) {
1604 rpmlog(RPMLOG_ERR, _("%s: public key read failed.\n"), fn);
1607 if (xx != PGPARMOR_PUBKEY) {
1608 rpmlog(RPMLOG_ERR, _("%s: not an armored public key.\n"), fn);
1611 apkt = pgpArmorWrap(PGPARMOR_PUBKEY, pkt, pktlen);
1617 rpmlog(RPMLOG_ERR, _("%s: failed to encode\n"), fn);
1621 headerPutString(pkg->header, tag, apkt);
1625 rc = addFile(fl, fn, NULL);
1632 fl->processingFailed = 1;
1639 * Add a file to a binary package.
1641 * @param fl package file tree walk data
1642 * @param fileName file to add
1643 * @return RPMRC_OK on success
1645 static rpmRC processBinaryFile(Package pkg, FileList fl, const char * fileName)
1647 int quote = 1; /* XXX permit quoted glob characters. */
1649 char *diskPath = NULL;
1650 rpmRC rc = RPMRC_OK;
1652 doGlob = glob_pattern_p(fileName, quote);
1654 /* Check that file starts with leading "/" */
1655 if (*fileName != '/') {
1656 rpmlog(RPMLOG_ERR, _("File needs leading \"/\": %s\n"), fileName);
1661 /* Copy file name or glob pattern removing multiple "/" chars. */
1663 * Note: rpmGetPath should guarantee a "canonical" path. That means
1664 * that the following pathologies should be weeded out:
1667 * /.././../usr/../bin//./sh
1669 diskPath = rpmGenPath(fl->buildRoot, NULL, fileName);
1676 /* XXX for %dev marker in file manifest only */
1678 rpmlog(RPMLOG_ERR, _("Glob not permitted: %s\n"), diskPath);
1683 if (rpmGlob(diskPath, &argc, &argv) == 0 && argc >= 1) {
1684 for (i = 0; i < argc; i++) {
1685 rc = addFile(fl, argv[i], NULL);
1689 rpmlog(RPMLOG_ERR, _("File not found by glob: %s\n"), diskPath);
1694 rc = addFile(fl, diskPath, NULL);
1700 fl->processingFailed = 1;
1708 static rpmRC processPackageFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags,
1709 Package pkg, int installSpecialDoc, int test)
1711 struct FileList_s fl;
1713 ARGV_t files = NULL;
1714 const char *fileName;
1716 struct AttrRec_s arbuf;
1717 AttrRec specialDocAttrRec = &arbuf;
1718 char *specialDoc = NULL;
1720 nullAttrRec(specialDocAttrRec);
1721 pkg->cpioList = NULL;
1723 if (pkg->fileFile) {
1725 ARGV_t filelists = NULL;
1728 argvSplit(&filelists, getStringBuf(pkg->fileFile), "\n");
1729 for (fp = filelists; *fp != NULL; fp++) {
1731 ffn = rpmGetPath(*fp, NULL);
1733 ffn = rpmGetPath("%{_builddir}/",
1734 (spec->buildSubdir ? spec->buildSubdir : "") ,
1737 fd = fopen(ffn, "r");
1739 if (fd == NULL || ferror(fd)) {
1740 rpmlog(RPMLOG_ERR, _("Could not open %%files file %s: %m\n"), ffn);
1745 while (fgets(buf, sizeof(buf), fd)) {
1746 handleComments(buf);
1747 if (expandMacros(spec, spec->macros, buf, sizeof(buf))) {
1748 rpmlog(RPMLOG_ERR, _("line: %s\n"), buf);
1752 appendStringBuf(pkg->fileList, buf);
1756 argvFree(filelists);
1759 /* Init the file list structure */
1760 memset(&fl, 0, sizeof(fl));
1762 /* XXX spec->buildRoot == NULL, then xstrdup("") is returned */
1763 fl.buildRoot = rpmGenPath(spec->rootDir, spec->buildRoot, NULL);
1766 fl.processingFailed = 0;
1768 fl.passedSpecialDoc = 0;
1769 fl.isSpecialDoc = 0;
1772 fl.currentFlags = 0;
1773 fl.currentVerifyFlags = 0;
1780 nullAttrRec(&fl.cur_ar);
1781 nullAttrRec(&fl.def_ar);
1782 dupAttrRec(&root_ar, &fl.def_ar); /* XXX assume %defattr(-,root,root) */
1784 fl.defVerifyFlags = RPMVERIFY_ALL;
1786 fl.currentLangs = NULL;
1788 fl.currentCaps = NULL;
1790 fl.currentSpecdFlags = 0;
1791 fl.defSpecdFlags = 0;
1794 fl.pkgFlags = pkgFlags;
1797 { char *docs = rpmGetPath("%{?__docdir_path}", NULL);
1798 argvSplit(&fl.docDirs, docs, ":");
1803 fl.fileListRecsAlloced = 0;
1804 fl.fileListRecsUsed = 0;
1806 s = getStringBuf(pkg->fileList);
1807 argvSplit(&files, s, "\n");
1809 for (fp = files; *fp != NULL; fp++) {
1815 rstrlcpy(buf, s, sizeof(buf));
1817 /* Reset for a new line in %files */
1819 fl.currentFlags = 0;
1820 /* turn explicit flags into %def'd ones (gosh this is hacky...) */
1821 fl.currentSpecdFlags = ((unsigned)fl.defSpecdFlags) >> 8;
1822 fl.currentVerifyFlags = fl.defVerifyFlags;
1823 fl.isSpecialDoc = 0;
1830 /* XXX should reset to %deflang value */
1831 if (fl.currentLangs) {
1833 for (i = 0; i < fl.nLangs; i++)
1834 fl.currentLangs[i] = _free(fl.currentLangs[i]);
1835 fl.currentLangs = _free(fl.currentLangs);
1838 fl.currentCaps = NULL;
1840 dupAttrRec(&fl.def_ar, &fl.cur_ar);
1842 if (parseForVerify(buf, &fl))
1844 if (parseForAttr(buf, &fl))
1846 if (parseForDev(buf, &fl))
1848 if (parseForConfig(buf, &fl))
1850 if (parseForLang(buf, &fl))
1852 if (parseForCaps(buf, &fl))
1854 if (parseForSimple(spec, pkg, buf, &fl, &fileName))
1856 if (fileName == NULL)
1859 if (fl.isSpecialDoc) {
1860 /* Save this stuff for last */
1861 specialDoc = _free(specialDoc);
1862 specialDoc = xstrdup(fileName);
1863 dupAttrRec(&fl.cur_ar, specialDocAttrRec);
1864 } else if (fl.currentFlags & RPMFILE_PUBKEY) {
1865 (void) processMetadataFile(pkg, &fl, fileName, RPMTAG_PUBKEYS);
1867 (void) processBinaryFile(pkg, &fl, fileName);
1871 /* Now process special doc, if there is one */
1873 if (installSpecialDoc) {
1874 int _missing_doc_files_terminate_build =
1875 rpmExpandNumeric("%{?_missing_doc_files_terminate_build}");
1878 rc = doScript(spec, RPMBUILD_STRINGBUF, "%doc", pkg->specialDoc, test);
1879 if (rc != RPMRC_OK && _missing_doc_files_terminate_build)
1880 fl.processingFailed = 1;
1883 /* Reset for %doc */
1885 fl.currentFlags = 0;
1886 fl.currentVerifyFlags = fl.defVerifyFlags;
1893 /* XXX should reset to %deflang value */
1894 if (fl.currentLangs) {
1896 for (i = 0; i < fl.nLangs; i++)
1897 fl.currentLangs[i] = _free(fl.currentLangs[i]);
1898 fl.currentLangs = _free(fl.currentLangs);
1902 dupAttrRec(specialDocAttrRec, &fl.cur_ar);
1903 freeAttrRec(specialDocAttrRec);
1905 (void) processBinaryFile(pkg, &fl, specialDoc);
1907 specialDoc = _free(specialDoc);
1912 if (fl.processingFailed)
1915 /* Verify that file attributes scope over hardlinks correctly. */
1916 if (checkHardLinks(&fl))
1917 (void) rpmlibNeedsFeature(pkg->header,
1918 "PartialHardlinkSets", "4.0.4-1");
1920 genCpioListAndHeader(&fl, &pkg->cpioList, pkg->header, 0);
1923 fl.buildRoot = _free(fl.buildRoot);
1925 freeAttrRec(&fl.cur_ar);
1926 freeAttrRec(&fl.def_ar);
1928 if (fl.currentLangs) {
1930 for (i = 0; i < fl.nLangs; i++)
1931 fl.currentLangs[i] = _free(fl.currentLangs[i]);
1932 fl.currentLangs = _free(fl.currentLangs);
1935 fl.fileList = freeFileList(fl.fileList, fl.fileListRecsUsed);
1936 argvFree(fl.docDirs);
1937 return fl.processingFailed ? RPMRC_FAIL : RPMRC_OK;
1940 static void genSourceRpmName(rpmSpec spec)
1942 if (spec->sourceRpmName == NULL) {
1943 char *nvr = headerGetAsString(spec->packages->header, RPMTAG_NVR);
1944 rasprintf(&spec->sourceRpmName, "%s.%ssrc.rpm", nvr,
1945 spec->noSource ? "no" : "");
1950 rpmRC processSourceFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags)
1952 struct Source *srcPtr;
1953 StringBuf sourceFiles;
1955 struct FileList_s fl;
1957 ARGV_t files = NULL;
1959 static char *_srcdefattr;
1963 _srcdefattr = rpmExpand("%{?_srcdefattr}", NULL);
1964 if (_srcdefattr && !*_srcdefattr)
1965 _srcdefattr = _free(_srcdefattr);
1968 sourceFiles = newStringBuf();
1970 genSourceRpmName(spec);
1971 /* Construct the file list and source entries */
1972 appendLineStringBuf(sourceFiles, spec->specFile);
1973 for (srcPtr = spec->sources; srcPtr != NULL; srcPtr = srcPtr->next) {
1974 char * sfn = rpmGetPath( ((srcPtr->flags & RPMBUILD_ISNO) ? "!" : ""),
1975 "%{_sourcedir}/", srcPtr->source, NULL);
1976 appendLineStringBuf(sourceFiles, sfn);
1980 for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
1981 for (srcPtr = pkg->icon; srcPtr != NULL; srcPtr = srcPtr->next) {
1983 sfn = rpmGetPath( ((srcPtr->flags & RPMBUILD_ISNO) ? "!" : ""),
1984 "%{_sourcedir}/", srcPtr->source, NULL);
1985 appendLineStringBuf(sourceFiles, sfn);
1990 spec->sourceCpioList = NULL;
1992 /* Init the file list structure */
1993 memset(&fl, 0, sizeof(fl));
1995 char *a = rstrscat(NULL, "%defattr ", _srcdefattr, NULL);
1996 parseForAttr(a, &fl);
1999 fl.fileList = xcalloc((spec->numSources + 1), sizeof(*fl.fileList));
2000 fl.processingFailed = 0;
2001 fl.fileListRecsUsed = 0;
2002 fl.pkgFlags = pkgFlags;
2003 fl.buildRoot = NULL;
2005 s = getStringBuf(sourceFiles);
2006 argvSplit(&files, s, "\n");
2008 /* The first source file is the spec file */
2010 for (fp = files; *fp != NULL; fp++) {
2011 const char *diskPath;
2016 SKIPSPACE(diskPath);
2020 flp = &fl.fileList[x];
2022 flp->flags = isSpec ? RPMFILE_SPECFILE : 0;
2023 /* files with leading ! are no source files */
2024 if (*diskPath == '!') {
2025 flp->flags |= RPMFILE_GHOST;
2029 tmp = xstrdup(diskPath); /* basename() might modify */
2030 flp->diskPath = xstrdup(diskPath);
2031 flp->cpioPath = xstrdup(basename(tmp));
2032 flp->verifyFlags = RPMVERIFY_ALL;
2035 if (stat(diskPath, &flp->fl_st)) {
2036 rpmlog(RPMLOG_ERR, _("Bad file: %s: %s\n"),
2037 diskPath, strerror(errno));
2038 fl.processingFailed = 1;
2041 if (fl.def_ar.ar_fmodestr) {
2042 flp->fl_mode &= S_IFMT;
2043 flp->fl_mode |= fl.def_ar.ar_fmode;
2045 if (fl.def_ar.ar_user) {
2046 flp->uname = getUnameS(fl.def_ar.ar_user);
2048 flp->uname = getUname(flp->fl_uid);
2050 if (fl.def_ar.ar_group) {
2051 flp->gname = getGnameS(fl.def_ar.ar_group);
2053 flp->gname = getGname(flp->fl_gid);
2055 flp->langs = xstrdup("");
2057 if (! (flp->uname && flp->gname)) {
2058 rpmlog(RPMLOG_ERR, _("Bad owner/group: %s\n"), diskPath);
2059 fl.processingFailed = 1;
2065 fl.fileListRecsUsed = x;
2068 if (! fl.processingFailed) {
2069 if (spec->sourceHeader != NULL)
2070 genCpioListAndHeader(&fl, &spec->sourceCpioList,
2071 spec->sourceHeader, 1);
2074 sourceFiles = freeStringBuf(sourceFiles);
2075 fl.fileList = freeFileList(fl.fileList, fl.fileListRecsUsed);
2076 freeAttrRec(&fl.def_ar);
2077 return fl.processingFailed ? RPMRC_FAIL : RPMRC_OK;
2081 * Check packaged file list against what's in the build root.
2082 * @param fileList packaged file list
2083 * @return -1 if skipped, 0 on OK, 1 on error
2085 static int checkFiles(StringBuf fileList)
2087 static char * const av_ckfile[] = { "%{?__check_files}", NULL };
2088 StringBuf sb_stdout = NULL;
2092 s = rpmExpand(av_ckfile[0], NULL);
2099 rpmlog(RPMLOG_NOTICE, _("Checking for unpackaged file(s): %s\n"), s);
2101 rc = rpmfcExec(av_ckfile, fileList, &sb_stdout, 0);
2106 int _unpackaged_files_terminate_build =
2107 rpmExpandNumeric("%{?_unpackaged_files_terminate_build}");
2110 t = getStringBuf(sb_stdout);
2111 if ((*t != '\0') && (*t != '\n')) {
2112 rc = (_unpackaged_files_terminate_build) ? 1 : 0;
2113 rpmlog((rc ? RPMLOG_ERR : RPMLOG_WARNING),
2114 _("Installed (but unpackaged) file(s) found:\n%s"), t);
2119 sb_stdout = freeStringBuf(sb_stdout);
2124 rpmRC processBinaryFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags,
2125 int installSpecialDoc, int test)
2128 rpmRC rc = RPMRC_OK;
2130 check_fileList = newStringBuf();
2131 genSourceRpmName(spec);
2133 for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
2136 headerPutString(pkg->header, RPMTAG_SOURCERPM, spec->sourceRpmName);
2138 nvr = headerGetAsString(pkg->header, RPMTAG_NVRA);
2139 rpmlog(RPMLOG_NOTICE, _("Processing files: %s\n"), nvr);
2142 if ((rc = processPackageFiles(spec, pkgFlags, pkg, installSpecialDoc, test)) != RPMRC_OK ||
2143 (rc = rpmfcGenerateDepends(spec, pkg)) != RPMRC_OK)
2146 a = headerGetString(pkg->header, RPMTAG_ARCH);
2147 if (rstreq(a, "noarch") && headerGetNumber(pkg->header, RPMTAG_HEADERCOLOR) != 0) {
2148 int terminate = rpmExpandNumeric("%{?_binaries_in_noarch_packages_terminate_build}");
2149 rpmlog(terminate ? RPMLOG_ERR : RPMLOG_WARNING,
2150 _("Arch dependent binaries in noarch package\n"));
2158 /* Now we have in fileList list of files from all packages.
2159 * We pass it to a script which does the work of finding missing
2160 * and duplicated files.
2164 if (checkFiles(check_fileList) > 0) {
2168 check_fileList = freeStringBuf(check_fileList);