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>
22 #include <rpm/rpmbase64.h>
24 #include "rpmio/rpmio_internal.h" /* XXX rpmioSlurp */
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"
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)++;}
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),
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)
55 typedef rpmFlags specfFlags;
59 typedef struct FileListRec_s {
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
71 char *diskPath; /* get file from here */
72 char *cpioPath; /* filename in cpio archive */
76 specfFlags specdFlags; /* which attributes have been explicitly specified. */
77 rpmVerifyFlags verifyFlags;
78 char *langs; /* XXX locales separated with | */
84 typedef struct AttrRec_s {
93 static struct AttrRec_s root_ar = { NULL, NULL, "root", "root", 0, 0 };
96 static StringBuf check_fileList = NULL;
99 * Package file tree walk data.
101 typedef struct FileList_s {
104 int processingFailed;
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;
124 FileListRec fileList;
125 int fileListRecsAlloced;
126 int fileListRecsUsed;
132 static void nullAttrRec(AttrRec ar)
134 ar->ar_fmodestr = NULL;
135 ar->ar_dmodestr = NULL;
144 static void freeAttrRec(AttrRec ar)
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) */
156 static void dupAttrRec(const AttrRec oar, AttrRec 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;
172 static void dumpAttrRec(const char * msg, AttrRec ar)
175 fprintf(stderr, "%s:\t", msg);
176 fprintf(stderr, "(%s, %s, %s, %s)\n",
189 static char *strtokWithQuotes(char *s, const char *delim)
191 static char *olds = NULL;
199 /* Skip leading delimiters */
200 s += strspn(s, delim);
204 /* Find the end of the token. */
208 /* Find next " char */
209 s = strchr(token, '"');
211 s = strpbrk(token, delim);
216 /* This token finishes the string */
217 olds = strchr(token, '\0');
219 /* Terminate the token and make olds point past it */
229 typedef const struct VFA {
230 const char * attribute;
231 int neg; /* XXX unused */
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 },
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
257 static rpmRC parseForVerify(char * buf, FileList fl)
259 char *p, *pe, *q = NULL;
261 rpmVerifyFlags *resultVerify;
263 rpmVerifyFlags verifyFlags;
264 specfFlags * specdFlags;
265 rpmRC rc = RPMRC_FAIL;
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;
276 for (pe = p; (pe-p) < strlen(name); pe++)
282 rpmlog(RPMLOG_ERR, _("Missing '(' in %s %s\n"), name, pe);
286 /* Bracket %*verify args */
288 for (p = pe; *pe && *pe != ')'; pe++)
292 rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
296 /* Localize. Erase parsed string */
297 q = xmalloc((pe-p) + 1);
298 rstrlcpy(q, p, (pe-p) + 1);
303 verifyFlags = RPMVERIFY_NONE;
305 for (p = q; *p != '\0'; p = pe) {
315 for (vfa = verifyAttrs; vfa->attribute != NULL; vfa++) {
316 if (!rstreq(p, vfa->attribute))
318 verifyFlags |= vfa->flag;
325 if (rstreq(p, "not")) {
328 rpmlog(RPMLOG_ERR, _("Invalid %s token: %s\n"), name, p);
333 *resultVerify = negated ? ~(verifyFlags) : verifyFlags;
334 *specdFlags |= SPECD_VERIFY;
339 if (rc != RPMRC_OK) {
340 fl->processingFailed = 1;
346 #define isAttrDefault(_ars) ((_ars)[0] == '-' && (_ars)[1] == '\0')
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
354 static rpmRC parseForDev(char * buf, FileList fl)
357 const char * errstr = NULL;
358 char *p, *pe, *q = NULL;
359 rpmRC rc = RPMRC_FAIL; /* assume error */
361 if ((p = strstr(buf, (name = "%dev"))) == NULL)
364 for (pe = p; (pe-p) < strlen(name); pe++)
373 /* Bracket %dev args */
375 for (p = pe; *pe && *pe != ')'; pe++)
382 /* Localize. Erase parsed string */
383 q = xmalloc((pe-p) + 1);
384 rstrlcpy(q, p, (pe-p) + 1);
389 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
399 p = pe; SKIPWHITE(p);
400 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe = '\0';
401 for (pe = p; *pe && risdigit(*pe); pe++)
404 fl->devmajor = atoi(p);
405 if (!(fl->devmajor >= 0 && fl->devmajor < 256)) {
415 p = pe; SKIPWHITE(p);
416 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe = '\0';
417 for (pe = p; *pe && risdigit(*pe); pe++)
420 fl->devminor = atoi(p);
421 if (!(fl->devminor >= 0 && fl->devminor < 256)) {
434 rpmlog(RPMLOG_ERR, _("Missing %s in %s %s\n"), errstr, name, p);
435 fl->processingFailed = 1;
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
447 static rpmRC parseForAttr(char * buf, FileList fl)
450 char *p, *pe, *q = NULL;
452 struct AttrRec_s arbuf;
453 AttrRec ar = &arbuf, ret_ar;
454 specfFlags * specdFlags;
455 rpmRC rc = RPMRC_FAIL;
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;
466 for (pe = p; (pe-p) < strlen(name); pe++)
472 rpmlog(RPMLOG_ERR, _("Missing '(' in %s %s\n"), name, pe);
476 /* Bracket %*attr args */
478 for (p = pe; *pe && *pe != ')'; pe++)
481 if (ret_ar == &(fl->def_ar)) { /* %defattr */
487 _("Non-white space follows %s(): %s\n"), name, r);
492 /* Localize. Erase parsed string */
493 q = xmalloc((pe-p) + 1);
494 rstrlcpy(q, p, (pe-p) + 1);
502 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
504 p = pe; SKIPWHITE(p);
507 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
509 p = pe; SKIPWHITE(p);
512 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
514 p = pe; SKIPWHITE(p);
516 if (*p != '\0' && ret_ar == &(fl->def_ar)) { /* %defattr */
517 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
519 p = pe; SKIPWHITE(p);
522 if (!(ar->ar_fmodestr && ar->ar_user && ar->ar_group) || *p != '\0') {
523 rpmlog(RPMLOG_ERR, _("Bad syntax: %s(%s)\n"), name, q);
527 /* Do a quick test on the mode argument and adjust for "-" */
528 if (ar->ar_fmodestr && !isAttrDefault(ar->ar_fmodestr)) {
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);
537 ar->ar_fmodestr = NULL;
540 if (ar->ar_dmodestr && !isAttrDefault(ar->ar_dmodestr)) {
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);
549 ar->ar_dmodestr = NULL;
552 if (!(ar->ar_user && !isAttrDefault(ar->ar_user))) {
556 if (!(ar->ar_group && !isAttrDefault(ar->ar_group))) {
560 dupAttrRec(ar, ret_ar);
562 /* XXX fix all this */
563 *specdFlags |= SPECD_UID | SPECD_GID | SPECD_FILEMODE | SPECD_DIRMODE;
568 if (rc != RPMRC_OK) {
569 fl->processingFailed = 1;
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
581 static rpmRC parseForConfig(char * buf, FileList fl)
583 char *p, *pe, *q = NULL;
585 rpmRC rc = RPMRC_FAIL;
587 if ((p = strstr(buf, (name = "%config"))) == NULL)
590 fl->currentFlags |= RPMFILE_CONFIG;
592 /* Erase "%config" token. */
593 for (pe = p; (pe-p) < strlen(name); pe++)
599 /* Bracket %config args */
601 for (p = pe; *pe && *pe != ')'; pe++)
605 rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
609 /* Localize. Erase parsed string. */
610 q = xmalloc((pe-p) + 1);
611 rstrlcpy(q, p, (pe-p) + 1);
615 for (p = q; *p != '\0'; p = pe) {
623 if (rstreq(p, "missingok")) {
624 fl->currentFlags |= RPMFILE_MISSINGOK;
625 } else if (rstreq(p, "noreplace")) {
626 fl->currentFlags |= RPMFILE_NOREPLACE;
628 rpmlog(RPMLOG_ERR, _("Invalid %s token: %s\n"), name, p);
636 if (rc != RPMRC_OK) {
637 fl->processingFailed = 1;
643 static rpmRC addLang(ARGV_t *av, const char *lang, size_t n, const char *ent)
645 rpmRC rc = RPMRC_FAIL;
647 rstrlcpy(lbuf, lang, sizeof(lbuf));
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"),
657 /* Check for duplicate locales */
658 if (argvSearch(*av, lbuf, NULL)) {
659 rpmlog(RPMLOG_WARNING, _("Duplicate locale %s in %%lang(%s)\n"),
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
677 static rpmRC parseForLang(char * buf, FileList fl)
679 char *p, *pe, *q = NULL;
681 rpmRC rc = RPMRC_FAIL;
683 while ((p = strstr(buf, (name = "%lang"))) != NULL) {
685 for (pe = p; (pe-p) < strlen(name); pe++)
690 rpmlog(RPMLOG_ERR, _("Missing '(' in %s %s\n"), name, pe);
694 /* Bracket %lang args */
696 for (pe = p; *pe && *pe != ')'; pe++)
700 rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
704 /* Localize. Erase parsed string. */
705 q = xmalloc((pe-p) + 1);
706 rstrlcpy(q, p, (pe-p) + 1);
710 /* Parse multiple arguments from %lang */
711 for (p = q; *p != '\0'; p = pe) {
716 if (addLang(&(fl->currentLangs), p, (pe-p), q))
719 if (*pe == ',') pe++; /* skip , if present */
727 if (rc != RPMRC_OK) {
728 fl->processingFailed = 1;
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
740 static rpmRC parseForCaps(char * buf, FileList fl)
742 char *p, *pe, *q = NULL;
744 rpmRC rc = RPMRC_FAIL;
746 if ((p = strstr(buf, (name = "%caps"))) == NULL)
749 /* Erase "%caps" token. */
750 for (pe = p; (pe-p) < strlen(name); pe++)
756 /* Bracket %caps args */
758 for (p = pe; *pe && *pe != ')'; pe++)
762 rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
766 /* Localize. Erase parsed string. */
767 q = xmalloc((pe-p) + 1);
768 rstrlcpy(q, p, (pe-p) + 1);
775 cap_t fcaps = cap_from_text(q);
777 rpmlog(RPMLOG_ERR, _("Invalid capability: %s\n"), q);
780 /* run our string through cap_to_text() to get libcap presentation */
781 captxt = cap_to_text(fcaps, NULL);
782 fl->currentCaps = xstrdup(captxt);
788 rpmlog(RPMLOG_ERR, _("File capability support not built in\n"));
796 if (rc != RPMRC_OK) {
797 fl->processingFailed = 1;
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 },
816 * Parse simple attributes (e.g. %dir) from file manifest.
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
824 static rpmRC parseForSimple(rpmSpec spec, Package pkg, char * buf,
825 FileList fl, const char ** fileName)
829 char *specialDocBuf = NULL;
835 while ((s = strtokWithQuotes(t, " \t\n")) != NULL) {
838 if (rstreq(s, "%docdir")) {
839 s = strtokWithQuotes(NULL, " \t\n");
841 if (s == NULL || strtokWithQuotes(NULL, " \t\n")) {
842 rpmlog(RPMLOG_ERR, _("Only one arg for %%docdir\n"));
845 argvAdd(&(fl->docDirs), s);
850 /* Set flags for virtual file attributes */
851 for (vfa = virtualFileAttributes; vfa->attribute != NULL; vfa++) {
852 if (!rstreq(s, vfa->attribute))
855 if (rstreq(s, "%dir"))
856 fl->isDir = 1; /* XXX why not RPMFILE_DIR? */
859 fl->currentFlags &= ~vfa->flag;
861 fl->currentFlags |= vfa->flag;
865 /* if we got an attribute, continue with next token */
866 if (vfa->attribute != NULL)
870 /* We already got a file -- error */
871 rpmlog(RPMLOG_ERR, _("Two files on one line: %s\n"), *fileName);
876 if (fl->currentFlags & RPMFILE_DOC) {
877 rstrscat(&specialDocBuf, " ", s, NULL);
879 if (fl->currentFlags & RPMFILE_PUBKEY)
883 /* not in %doc, does not begin with / -- error */
884 rpmlog(RPMLOG_ERR, _("File must begin with \"/\": %s\n"), s);
893 if (*fileName || (fl->currentFlags & ~(RPMFILE_DOC))) {
895 _("Can't mix special %%doc with other forms: %s\n"),
896 (*fileName ? *fileName : ""));
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);
909 *fileName = pkg->specialDocDir;
912 appendStringBuf(pkg->specialDoc, "cp -pr ");
913 appendStringBuf(pkg->specialDoc, specialDocBuf);
914 appendLineStringBuf(pkg->specialDoc, " $DOCDIR");
919 if (res != RPMRC_OK) {
920 fl->processingFailed = 1;
928 static int compareFileListRecs(const void * ap, const void * bp)
930 const char *a = ((FileListRec)ap)->cpioPath;
931 const char *b = ((FileListRec)bp)->cpioPath;
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
941 static int isDoc(FileList fl, const char * fileName)
946 k = strlen(fileName);
947 for (dd = fl->docDirs; *dd; dd++) {
949 if (l < k && rstreqn(fileName, *dd, l) && fileName[l] == '/')
955 static int isHardLink(FileListRec flp, FileListRec tlp)
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));
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.
969 static int checkHardLinks(FileList fl)
971 FileListRec ilp, jlp;
974 for (i = 0; i < fl->fileListRecsUsed; i++) {
975 ilp = fl->fileList + i;
976 if (!(S_ISREG(ilp->fl_mode) && ilp->fl_nlink > 1))
979 for (j = i + 1; j < fl->fileListRecsUsed; j++) {
980 jlp = fl->fileList + j;
981 if (isHardLink(ilp, jlp)) {
989 static int seenHardLink(FileList fl, FileListRec flp)
991 for (FileListRec ilp = fl->fileList; ilp < flp; ilp++) {
992 if (isHardLink(flp, ilp)) {
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
1008 static void genCpioListAndHeader(FileList fl,
1009 rpmfi * fip, Header h, int isSrc)
1011 int _addDotSlash = !(isSrc || rpmExpandNumeric("%{_noPayloadPrefix}"));
1012 size_t apathlen = 0;
1013 size_t dpathlen = 0;
1018 uint32_t defaultalgo = PGPHASHALGO_MD5, digestalgo;
1019 rpm_loff_t totalFileSize = 0;
1022 * See if non-md5 file digest algorithm is requested. If not
1023 * specified, quietly assume md5. Otherwise check if supported type.
1025 digestalgo = rpmExpandNumeric(isSrc ? "%{_source_filedigest_algorithm}" :
1026 "%{_binary_filedigest_algorithm}");
1027 if (digestalgo == 0) {
1028 digestalgo = defaultalgo;
1031 if (rpmDigestLength(digestalgo) == 0) {
1032 rpmlog(RPMLOG_WARNING,
1033 _("Unknown file digest algorithm %u, falling back to MD5\n"),
1035 digestalgo = defaultalgo;
1038 /* Sort the big list */
1039 qsort(fl->fileList, fl->fileListRecsUsed,
1040 sizeof(*(fl->fileList)), compareFileListRecs);
1042 /* Generate the header. */
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)) {
1052 /* Two entries for the same file found, merge the entries. */
1053 /* Note that an %exclude is a duplication of a file reference */
1056 flp[1].flags |= flp->flags;
1058 if (!(flp[1].flags & RPMFILE_EXCLUDE))
1059 rpmlog(RPMLOG_WARNING, _("File listed twice: %s\n"),
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;
1068 if ((flp[1].specdFlags & (SPECD_FILEMODE | SPECD_DEFFILEMODE)) <
1069 (flp->specdFlags & (SPECD_FILEMODE | SPECD_DEFFILEMODE)))
1070 flp[1].fl_mode = flp->fl_mode;
1074 if ((flp[1].specdFlags & (SPECD_UID | SPECD_DEFUID)) <
1075 (flp->specdFlags & (SPECD_UID | SPECD_DEFUID)))
1077 flp[1].fl_uid = flp->fl_uid;
1078 flp[1].uname = flp->uname;
1082 if ((flp[1].specdFlags & (SPECD_GID | SPECD_DEFGID)) <
1083 (flp->specdFlags & (SPECD_GID | SPECD_DEFGID)))
1085 flp[1].fl_gid = flp->fl_gid;
1086 flp[1].gname = flp->gname;
1090 if ((flp[1].specdFlags & (SPECD_VERIFY | SPECD_DEFVERIFY)) <
1091 (flp->specdFlags & (SPECD_VERIFY | SPECD_DEFVERIFY)))
1092 flp[1].verifyFlags = flp->verifyFlags;
1094 /* XXX to-do: language */
1099 /* Skip files that were marked with %exclude. */
1100 if (flp->flags & RPMFILE_EXCLUDE) continue;
1102 /* Omit '/' and/or URL prefix, leave room for "./" prefix */
1103 apathlen += (strlen(flp->cpioPath) - skipLen + (_addDotSlash ? 3 : 1));
1105 /* Leave room for both dirname and basename NUL's */
1106 dpathlen += (strlen(flp->diskPath) + 2);
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.
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);
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.
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 */
1128 rpm_off_t rsize32 = (rpm_off_t)flp->fl_size;
1129 headerPutUint32(h, RPMTAG_FILESIZES, &rsize32, 1);
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;
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.
1143 { rpm_time_t rtime = (rpm_time_t) flp->fl_mtime;
1144 headerPutUint32(h, RPMTAG_FILEMTIMES, &rtime, 1);
1147 { rpm_mode_t rmode = (rpm_mode_t) flp->fl_mode;
1148 headerPutUint16(h, RPMTAG_FILEMODES, &rmode, 1);
1151 { rpm_rdev_t rrdev = (rpm_rdev_t) flp->fl_rdev;
1152 headerPutUint16(h, RPMTAG_FILERDEVS, &rrdev, 1);
1155 { rpm_dev_t rdev = (rpm_dev_t) flp->fl_dev;
1156 headerPutUint32(h, RPMTAG_FILEDEVICES, &rdev, 1);
1159 { rpm_ino_t rino = (rpm_ino_t) flp->fl_ino;
1160 headerPutUint32(h, RPMTAG_FILEINODES, &rino, 1);
1163 headerPutString(h, RPMTAG_FILELANGS, flp->langs);
1166 headerPutString(h, RPMTAG_FILECAPS, flp->caps);
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);
1176 if (S_ISLNK(flp->fl_mode)) {
1177 ssize_t llen = readlink(flp->diskPath, buf, BUFSIZ-1);
1179 rpmlog(RPMLOG_ERR, _("reading symlink %s failed: %s\n"),
1180 flp->diskPath, strerror(errno));
1181 fl->processingFailed = 1;
1184 if (buf[0] == '/' && !rstreq(fl->buildRoot, "/") &&
1185 rstreqn(buf, fl->buildRoot, strlen(fl->buildRoot))) {
1187 _("Symlink points to BuildRoot: %s -> %s\n"),
1188 flp->cpioPath, buf);
1189 fl->processingFailed = 1;
1193 headerPutString(h, RPMTAG_FILELINKTOS, buf);
1195 if (flp->flags & RPMFILE_GHOST) {
1196 flp->verifyFlags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE |
1197 RPMVERIFY_LINKTO | RPMVERIFY_MTIME);
1199 headerPutUint32(h, RPMTAG_FILEVERIFYFLAGS, &(flp->verifyFlags),1);
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);
1207 headerPutUint32(h, RPMTAG_FILEFLAGS, &(flp->flags) ,1);
1210 if (totalFileSize < UINT32_MAX) {
1211 rpm_off_t totalsize = totalFileSize;
1212 headerPutUint32(h, RPMTAG_SIZE, &totalsize, 1);
1214 rpm_loff_t totalsize = totalFileSize;
1215 headerPutUint64(h, RPMTAG_LONGSIZE, &totalsize, 1);
1218 if (digestalgo != defaultalgo) {
1219 headerPutUint32(h, RPMTAG_FILEDIGESTALGO, &digestalgo, 1);
1220 rpmlibNeedsFeature(h, "FileDigests", "4.6.0-1");
1224 rpmlibNeedsFeature(h, "FileCaps", "4.6.1-1");
1228 (void) rpmlibNeedsFeature(h, "PayloadFilesHavePrefix", "4.0-1");
1231 struct rpmtd_s filenames;
1232 rpmfiFlags flags = RPMFI_NOHEADER|RPMFI_NOFILEUSER|RPMFI_NOFILEGROUP;
1238 /* rpmfiNew() only groks compressed filelists */
1239 headerConvert(h, HEADERCONV_COMPRESSFILELIST);
1240 fi = rpmfiNew(NULL, h, RPMTAG_BASENAMES, flags);
1243 * Grab the real filenames from ORIGFILENAMES and put into OLDFILENAMES,
1244 * remove temporary cruft and side-effects from filelist compression
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);
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);
1260 rpmtdInit(&filenames);
1261 for (int i = 0; (fn = rpmtdNextString(&filenames)); i++) {
1264 a = stpcpy(a, "./");
1265 a = stpcpy(a, (fn + skipLen));
1266 a++; /* skip apath NUL */
1270 rpmtdFreeData(&filenames);
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");
1283 static FileListRec freeFileList(FileListRec fileList,
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);
1297 static rpmRC recurseDir(FileList fl, const char * diskPath);
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
1306 static rpmRC addFile(FileList fl, const char * diskPath,
1307 struct stat * statp)
1309 size_t plen = strlen(diskPath);
1311 const char *cpioPath;
1312 struct stat statbuf;
1316 const char *fileUname;
1317 const char *fileGname;
1318 rpmRC rc = RPMRC_FAIL; /* assume failure */
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';
1325 cpioPath = diskPath;
1328 /* Path may have prepended buildRoot, so locate the original filename. */
1330 * XXX There are 3 types of entry into addFile:
1332 * From diskUrl statp
1333 * =====================================================
1334 * processBinaryFile path NULL
1335 * processBinaryFile glob result path NULL
1339 if (fl->buildRoot && !rstreq(fl->buildRoot, "/"))
1340 cpioPath += strlen(fl->buildRoot);
1342 /* XXX make sure '/' can be packaged also */
1343 if (*cpioPath == '\0')
1346 if (statp == NULL) {
1347 time_t now = time(NULL);
1349 memset(statp, 0, sizeof(*statp));
1351 /* XXX hack up a stat structure for a %dev(...) directive. */
1352 statp->st_nlink = 1;
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;
1362 int is_ghost = fl->currentFlags & RPMFILE_GHOST;
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);
1369 rpmlog(RPMLOG_ERR, _("Explicit file attributes required in spec for: %s\n"), diskPath);
1372 statp->st_atime = now;
1373 statp->st_mtime = now;
1374 statp->st_ctime = now;
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;
1384 rpmlog(lvl, msg, diskPath);
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);
1396 fileMode = statp->st_mode;
1397 fileUid = statp->st_uid;
1398 fileGid = statp->st_gid;
1400 /* Explicit %attr() always wins */
1401 if (fl->cur_ar.ar_fmodestr != NULL) {
1403 fileMode |= fl->cur_ar.ar_fmode;
1405 /* ...but %defattr() for directories and files is different */
1406 if (S_ISDIR(fileMode)) {
1407 if (fl->def_ar.ar_dmodestr) {
1409 fileMode |= fl->def_ar.ar_dmode;
1411 } else if (fl->def_ar.ar_fmodestr) {
1413 fileMode |= fl->def_ar.ar_fmode;
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;
1421 fileUname = rpmugUname(fileUid);
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;
1428 fileGname = rpmugGname(fileGid);
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());
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");
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)));
1450 { FileListRec flp = &fl->fileList[fl->fileListRecsUsed];
1452 flp->fl_st = *statp; /* structure assignment */
1453 flp->fl_mode = fileMode;
1454 flp->fl_uid = fileUid;
1455 flp->fl_gid = fileGid;
1457 flp->cpioPath = xstrdup(cpioPath);
1458 flp->diskPath = xstrdup(diskPath);
1459 flp->uname = rpmugStashStr(fileUname);
1460 flp->gname = rpmugStashStr(fileGname);
1462 if (fl->currentLangs) {
1463 flp->langs = argvJoin(fl->currentLangs, "|");
1465 flp->langs = xstrdup("");
1468 if (fl->currentCaps) {
1469 flp->caps = fl->currentCaps;
1471 flp->caps = xstrdup("");
1474 flp->flags = fl->currentFlags;
1475 flp->specdFlags = fl->currentSpecdFlags;
1476 flp->verifyFlags = fl->currentVerifyFlags;
1478 if (!(flp->flags & RPMFILE_EXCLUDE) && S_ISREG(flp->fl_mode)) {
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.
1483 if ((rpm_loff_t) flp->fl_size >= CPIO_FILESIZE_MAX) {
1485 rpmlog(RPMLOG_ERR, _("File %s too large for payload\n"),
1493 fl->fileListRecsUsed++;
1497 fl->processingFailed = 1;
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
1508 static rpmRC recurseDir(FileList fl, const char * diskPath)
1513 int myFtsOpts = (FTS_COMFOLLOW | FTS_NOCHDIR | FTS_PHYSICAL);
1514 rpmRC rc = RPMRC_FAIL;
1516 ftsSet[0] = (char *) diskPath;
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);
1528 case FTS_DOT: /* dot or dot-dot */
1529 case FTS_DP: /* postorder directory */
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 */
1546 (void) Fts_close(ftsp);
1552 * Add a pubkey/icon to a binary package.
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
1559 static rpmRC processMetadataFile(Package pkg, FileList fl,
1560 const char * fileName, rpmTagVal tag)
1562 const char * buildDir = "%{_builddir}/%{?buildsubdir}/";
1565 uint8_t * pkt = NULL;
1568 rpmRC rc = RPMRC_FAIL;
1571 if (*fileName == '/') {
1572 fn = rpmGenPath(fl->buildRoot, NULL, fileName);
1575 fn = rpmGenPath(buildDir, NULL, fileName);
1579 rpmlog(RPMLOG_ERR, _("%s: can't load unknown tag (%d).\n"),
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);
1588 if (xx != PGPARMOR_PUBKEY) {
1589 rpmlog(RPMLOG_ERR, _("%s: not an armored public key.\n"), fn);
1592 apkt = pgpArmorWrap(PGPARMOR_PUBKEY, pkt, pktlen);
1598 rpmlog(RPMLOG_ERR, _("%s: failed to encode\n"), fn);
1602 headerPutString(pkg->header, tag, apkt);
1606 rc = addFile(fl, fn, NULL);
1613 fl->processingFailed = 1;
1620 * Add a file to a binary package.
1622 * @param fl package file tree walk data
1623 * @param fileName file to add
1624 * @return RPMRC_OK on success
1626 static rpmRC processBinaryFile(Package pkg, FileList fl, const char * fileName)
1628 int quote = 1; /* XXX permit quoted glob characters. */
1630 char *diskPath = NULL;
1631 rpmRC rc = RPMRC_OK;
1632 size_t fnlen = strlen(fileName);
1633 int trailing_slash = (fnlen > 0 && fileName[fnlen-1] == '/');
1635 /* XXX differentiate other directories from explicit %dir */
1636 if (trailing_slash && !fl->isDir)
1639 doGlob = glob_pattern_p(fileName, quote);
1641 /* Check that file starts with leading "/" */
1642 if (*fileName != '/') {
1643 rpmlog(RPMLOG_ERR, _("File needs leading \"/\": %s\n"), fileName);
1648 /* Copy file name or glob pattern removing multiple "/" chars. */
1650 * Note: rpmGetPath should guarantee a "canonical" path. That means
1651 * that the following pathologies should be weeded out:
1654 * /.././../usr/../bin//./sh
1656 diskPath = rpmGenPath(fl->buildRoot, NULL, fileName);
1657 /* Arrange trailing slash on directories */
1659 diskPath = rstrcat(&diskPath, "/");
1667 rpmlog(RPMLOG_ERR, _("%%dev glob not permitted: %s\n"), diskPath);
1672 if (rpmGlob(diskPath, &argc, &argv) == 0 && argc >= 1) {
1673 for (i = 0; i < argc; i++) {
1674 rc = addFile(fl, argv[i], NULL);
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)) {
1686 rpmlog(lvl, msg, diskPath);
1690 rc = addFile(fl, diskPath, NULL);
1696 fl->processingFailed = 1;
1702 static rpmRC readFilesManifest(rpmSpec spec, Package pkg, const char *path)
1704 char *fn, buf[BUFSIZ];
1706 rpmRC rc = RPMRC_FAIL;
1709 fn = rpmGetPath(path, NULL);
1711 fn = rpmGetPath("%{_builddir}/",
1712 (spec->buildSubdir ? spec->buildSubdir : "") , "/", path, NULL);
1714 fd = fopen(fn, "r");
1717 rpmlog(RPMLOG_ERR, _("Could not open %%files file %s: %m\n"), fn);
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);
1727 argvAdd(&(pkg->fileList), buf);
1731 rpmlog(RPMLOG_ERR, _("Error reading %%files file %s: %m\n"), fn);
1741 static rpmRC processPackageFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags,
1742 Package pkg, int installSpecialDoc, int test)
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;
1751 nullAttrRec(specialDocAttrRec);
1752 nullAttrRec(def_specialDocAttrRec);
1753 pkg->cpioList = NULL;
1755 for (ARGV_const_t fp = pkg->fileFile; fp && *fp != NULL; fp++) {
1756 if (readFilesManifest(spec, pkg, *fp))
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);
1765 fl.processingFailed = 0;
1768 fl.currentFlags = 0;
1769 fl.currentVerifyFlags = 0;
1775 nullAttrRec(&fl.cur_ar);
1776 nullAttrRec(&fl.def_ar);
1777 dupAttrRec(&root_ar, &fl.def_ar); /* XXX assume %defattr(-,root,root) */
1779 fl.defVerifyFlags = RPMVERIFY_ALL;
1780 fl.currentLangs = NULL;
1782 fl.currentCaps = NULL;
1784 fl.currentSpecdFlags = 0;
1785 fl.defSpecdFlags = 0;
1788 fl.pkgFlags = pkgFlags;
1791 { char *docs = rpmGetPath("%{?__docdir_path}", NULL);
1792 argvSplit(&fl.docDirs, docs, ":");
1797 fl.fileListRecsAlloced = 0;
1798 fl.fileListRecsUsed = 0;
1800 for (ARGV_const_t fp = pkg->fileList; *fp != NULL; fp++) {
1801 char buf[strlen(*fp) + 1];
1802 const char *s = *fp;
1807 rstrlcpy(buf, s, sizeof(buf));
1809 /* Reset for a new line in %files */
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;
1820 /* XXX should reset to %deflang value */
1821 fl.currentLangs = argvFree(fl.currentLangs);
1822 fl.currentCaps = NULL;
1824 freeAttrRec(&fl.cur_ar);
1826 if (parseForVerify(buf, &fl))
1828 if (parseForAttr(buf, &fl))
1830 if (parseForDev(buf, &fl))
1832 if (parseForConfig(buf, &fl))
1834 if (parseForLang(buf, &fl))
1836 if (parseForCaps(buf, &fl))
1838 if (parseForSimple(spec, pkg, buf, &fl, &fileName))
1840 if (fileName == NULL)
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);
1851 (void) processBinaryFile(pkg, &fl, fileName);
1855 /* Now process special doc, if there is one */
1857 if (installSpecialDoc) {
1858 int _missing_doc_files_terminate_build =
1859 rpmExpandNumeric("%{?_missing_doc_files_terminate_build}");
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;
1868 /* Reset for %doc */
1870 fl.currentFlags = 0;
1871 fl.currentVerifyFlags = fl.defVerifyFlags;
1877 /* XXX should reset to %deflang value */
1878 fl.currentLangs = argvFree(fl.currentLangs);
1880 dupAttrRec(specialDocAttrRec, &fl.cur_ar);
1881 dupAttrRec(def_specialDocAttrRec, &fl.def_ar);
1882 freeAttrRec(specialDocAttrRec);
1883 freeAttrRec(def_specialDocAttrRec);
1885 (void) processBinaryFile(pkg, &fl, specialDoc);
1887 specialDoc = _free(specialDoc);
1890 if (fl.processingFailed)
1893 /* Verify that file attributes scope over hardlinks correctly. */
1894 if (checkHardLinks(&fl))
1895 (void) rpmlibNeedsFeature(pkg->header,
1896 "PartialHardlinkSets", "4.0.4-1");
1898 genCpioListAndHeader(&fl, &pkg->cpioList, pkg->header, 0);
1901 fl.buildRoot = _free(fl.buildRoot);
1903 freeAttrRec(&fl.cur_ar);
1904 freeAttrRec(&fl.def_ar);
1906 fl.fileList = freeFileList(fl.fileList, fl.fileListRecsUsed);
1907 argvFree(fl.currentLangs);
1908 argvFree(fl.docDirs);
1909 return fl.processingFailed ? RPMRC_FAIL : RPMRC_OK;
1912 static void genSourceRpmName(rpmSpec spec)
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" : "");
1922 rpmRC processSourceFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags)
1924 struct Source *srcPtr;
1926 struct FileList_s fl;
1927 ARGV_t files = NULL;
1929 static char *_srcdefattr;
1933 _srcdefattr = rpmExpand("%{?_srcdefattr}", NULL);
1934 if (_srcdefattr && !*_srcdefattr)
1935 _srcdefattr = _free(_srcdefattr);
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);
1949 for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
1950 for (srcPtr = pkg->icon; srcPtr != NULL; srcPtr = srcPtr->next) {
1952 sfn = rpmGetPath( ((srcPtr->flags & RPMBUILD_ISNO) ? "!" : ""),
1953 "%{_sourcedir}/", srcPtr->source, NULL);
1954 argvAdd(&files, sfn);
1959 spec->sourceCpioList = NULL;
1961 /* Init the file list structure */
1962 memset(&fl, 0, sizeof(fl));
1964 char *a = rstrscat(NULL, "%defattr ", _srcdefattr, NULL);
1965 parseForAttr(a, &fl);
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;
1974 /* The first source file is the spec file */
1976 for (ARGV_const_t fp = files; *fp != NULL; fp++) {
1977 const char *diskPath = *fp;
1981 SKIPSPACE(diskPath);
1985 flp = &fl.fileList[x];
1987 flp->flags = isSpec ? RPMFILE_SPECFILE : 0;
1988 /* files with leading ! are no source files */
1989 if (*diskPath == '!') {
1990 flp->flags |= RPMFILE_GHOST;
1994 tmp = xstrdup(diskPath); /* basename() might modify */
1995 flp->diskPath = xstrdup(diskPath);
1996 flp->cpioPath = xstrdup(basename(tmp));
1997 flp->verifyFlags = RPMVERIFY_ALL;
2000 if (stat(diskPath, &flp->fl_st)) {
2001 rpmlog(RPMLOG_ERR, _("Bad file: %s: %s\n"),
2002 diskPath, strerror(errno));
2003 fl.processingFailed = 1;
2006 if (fl.def_ar.ar_fmodestr) {
2007 flp->fl_mode &= S_IFMT;
2008 flp->fl_mode |= fl.def_ar.ar_fmode;
2010 if (fl.def_ar.ar_user) {
2011 flp->uname = rpmugStashStr(fl.def_ar.ar_user);
2013 flp->uname = rpmugStashStr(rpmugUname(flp->fl_uid));
2015 if (fl.def_ar.ar_group) {
2016 flp->gname = rpmugStashStr(fl.def_ar.ar_group);
2018 flp->gname = rpmugStashStr(rpmugGname(flp->fl_gid));
2020 flp->langs = xstrdup("");
2022 if (! (flp->uname && flp->gname)) {
2023 rpmlog(RPMLOG_ERR, _("Bad owner/group: %s\n"), diskPath);
2024 fl.processingFailed = 1;
2030 fl.fileListRecsUsed = x;
2033 if (! fl.processingFailed) {
2034 if (spec->sourceHeader != NULL)
2035 genCpioListAndHeader(&fl, &spec->sourceCpioList,
2036 spec->sourceHeader, 1);
2039 fl.fileList = freeFileList(fl.fileList, fl.fileListRecsUsed);
2040 freeAttrRec(&fl.def_ar);
2041 return fl.processingFailed ? RPMRC_FAIL : RPMRC_OK;
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
2049 static int checkFiles(const char *buildRoot, StringBuf fileList)
2051 static char * const av_ckfile[] = { "%{?__check_files}", NULL };
2052 StringBuf sb_stdout = NULL;
2054 char * s = rpmExpand(av_ckfile[0], NULL);
2059 rpmlog(RPMLOG_NOTICE, _("Checking for unpackaged file(s): %s\n"), s);
2061 rc = rpmfcExec(av_ckfile, fileList, &sb_stdout, 0, buildRoot);
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);
2077 freeStringBuf(sb_stdout);
2082 rpmRC processBinaryFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags,
2083 int installSpecialDoc, int test)
2086 rpmRC rc = RPMRC_OK;
2088 check_fileList = newStringBuf();
2089 genSourceRpmName(spec);
2091 for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
2095 if (pkg->fileList == NULL)
2098 headerPutString(pkg->header, RPMTAG_SOURCERPM, spec->sourceRpmName);
2100 nvr = headerGetAsString(pkg->header, RPMTAG_NVRA);
2101 rpmlog(RPMLOG_NOTICE, _("Processing files: %s\n"), nvr);
2104 if ((rc = processPackageFiles(spec, pkgFlags, pkg, installSpecialDoc, test)) != RPMRC_OK ||
2105 (rc = rpmfcGenerateDepends(spec, pkg)) != RPMRC_OK)
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"));
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.
2126 if (checkFiles(spec->buildRoot, check_fileList) > 0) {
2130 check_fileList = freeStringBuf(check_fileList);