3 * The post-build, pre-packaging file tree walk to assemble the package
9 #define MYALLPERMS 07777
13 #include <rpm/rpmbuild.h>
14 #include <rpm/rpmpgp.h>
16 #include <rpm/rpmfc.h>
17 #include <rpm/rpmfileutil.h> /* rpmDoDigest() */
18 #include <rpm/rpmlog.h>
20 #include "rpmio/rpmio_internal.h" /* XXX rpmioSlurp */
21 #include "rpmio/base64.h"
22 #include "rpmio/fts.h"
24 #include "lib/rpmfi_internal.h" /* XXX fi->apath */
25 #include "build/buildio.h"
29 #define SKIPSPACE(s) { while (*(s) && risspace(*(s))) (s)++; }
30 #define SKIPWHITE(_x) {while(*(_x) && (risspace(*_x) || *(_x) == ',')) (_x)++;}
31 #define SKIPNONWHITE(_x){while(*(_x) &&!(risspace(*_x) || *(_x) == ',')) (_x)++;}
35 typedef enum specdFlags_e {
36 SPECD_DEFFILEMODE = (1 << 0),
37 SPECD_DEFDIRMODE = (1 << 1),
38 SPECD_DEFUID = (1 << 2),
39 SPECD_DEFGID = (1 << 3),
40 SPECD_DEFVERIFY = (1 << 4),
42 SPECD_FILEMODE = (1 << 8),
43 SPECD_DIRMODE = (1 << 9),
44 SPECD_UID = (1 << 10),
45 SPECD_GID = (1 << 11),
46 SPECD_VERIFY = (1 << 12)
51 typedef struct FileListRec_s {
53 #define fl_dev fl_st.st_dev
54 #define fl_ino fl_st.st_ino
55 #define fl_mode fl_st.st_mode
56 #define fl_nlink fl_st.st_nlink
57 #define fl_uid fl_st.st_uid
58 #define fl_gid fl_st.st_gid
59 #define fl_rdev fl_st.st_rdev
60 #define fl_size fl_st.st_size
61 #define fl_mtime fl_st.st_mtime
63 char *diskPath; /* get file from here */
64 char *cpioPath; /* filename in cpio archive */
68 specdFlags specdFlags; /* which attributes have been explicitly specified. */
69 rpmVerifyFlags verifyFlags;
70 char *langs; /* XXX locales separated with | */
76 typedef struct AttrRec_s {
85 static struct AttrRec_s root_ar = { NULL, NULL, "root", "root", 0, 0 };
88 static StringBuf check_fileList = NULL;
91 * Package file tree walk data.
93 typedef struct FileList_s {
100 int passedSpecialDoc;
110 rpmfileAttrs currentFlags;
111 specdFlags currentSpecdFlags;
112 rpmVerifyFlags currentVerifyFlags;
113 struct AttrRec_s cur_ar;
114 struct AttrRec_s def_ar;
115 specdFlags defSpecdFlags;
116 rpmVerifyFlags defVerifyFlags;
118 char ** currentLangs;
123 FileListRec fileList;
124 int fileListRecsAlloced;
125 int fileListRecsUsed;
131 static void nullAttrRec(AttrRec ar)
133 ar->ar_fmodestr = NULL;
134 ar->ar_dmodestr = NULL;
143 static void freeAttrRec(AttrRec ar)
145 ar->ar_fmodestr = _free(ar->ar_fmodestr);
146 ar->ar_dmodestr = _free(ar->ar_dmodestr);
147 ar->ar_user = _free(ar->ar_user);
148 ar->ar_group = _free(ar->ar_group);
149 /* XXX doesn't free ar (yet) */
155 static void dupAttrRec(const AttrRec oar, AttrRec nar)
160 nar->ar_fmodestr = (oar->ar_fmodestr ? xstrdup(oar->ar_fmodestr) : NULL);
161 nar->ar_dmodestr = (oar->ar_dmodestr ? xstrdup(oar->ar_dmodestr) : NULL);
162 nar->ar_user = (oar->ar_user ? xstrdup(oar->ar_user) : NULL);
163 nar->ar_group = (oar->ar_group ? xstrdup(oar->ar_group) : NULL);
164 nar->ar_fmode = oar->ar_fmode;
165 nar->ar_dmode = oar->ar_dmode;
171 static void dumpAttrRec(const char * msg, AttrRec ar)
174 fprintf(stderr, "%s:\t", msg);
175 fprintf(stderr, "(%s, %s, %s, %s)\n",
188 static char *strtokWithQuotes(char *s, const char *delim)
190 static char *olds = NULL;
198 /* Skip leading delimiters */
199 s += strspn(s, delim);
203 /* Find the end of the token. */
207 /* Find next " char */
208 s = strchr(token, '"');
210 s = strpbrk(token, delim);
215 /* This token finishes the string */
216 olds = strchr(token, '\0');
218 /* Terminate the token and make olds point past it */
228 static void timeCheck(int tc, Header h)
231 time_t currentTime = time(NULL);
232 struct rpmtd_s files, mtimes;
234 headerGet(h, RPMTAG_FILENAMES, &files, HEADERGET_EXT);
235 headerGet(h, RPMTAG_FILEMTIMES, &mtimes, HEADERGET_MINMEM);
237 while ((mtime = rpmtdNextUint32(&mtimes))) {
238 if ((currentTime - (time_t) *mtime) > tc) {
239 rpmlog(RPMLOG_WARNING, _("TIMECHECK failure: %s\n"),
240 rpmtdGetString(&files));
243 rpmtdFreeData(&files);
244 rpmtdFreeData(&mtimes);
249 typedef const struct VFA {
250 const char const * attribute;
257 static VFA_t const verifyAttrs[] = {
258 { "md5", 0, RPMVERIFY_FILEDIGEST },
259 { "filedigest", 0, RPMVERIFY_FILEDIGEST },
260 { "size", 0, RPMVERIFY_FILESIZE },
261 { "link", 0, RPMVERIFY_LINKTO },
262 { "user", 0, RPMVERIFY_USER },
263 { "group", 0, RPMVERIFY_GROUP },
264 { "mtime", 0, RPMVERIFY_MTIME },
265 { "mode", 0, RPMVERIFY_MODE },
266 { "rdev", 0, RPMVERIFY_RDEV },
267 { "caps", 0, RPMVERIFY_CAPS },
272 * Parse %verify and %defverify from file manifest.
273 * @param buf current spec file line
274 * @param fl package file tree walk data
275 * @return RPMRC_OK on success
277 static rpmRC parseForVerify(const char * buf, FileList fl)
279 char *p, *pe, *q = NULL;
281 rpmVerifyFlags *resultVerify;
283 rpmVerifyFlags verifyFlags;
284 specdFlags * specdFlags;
285 rpmRC rc = RPMRC_FAIL;
287 if ((p = strstr(buf, (name = "%verify"))) != NULL) {
288 resultVerify = &(fl->currentVerifyFlags);
289 specdFlags = &fl->currentSpecdFlags;
290 } else if ((p = strstr(buf, (name = "%defverify"))) != NULL) {
291 resultVerify = &(fl->defVerifyFlags);
292 specdFlags = &fl->defSpecdFlags;
296 for (pe = p; (pe-p) < strlen(name); pe++)
302 rpmlog(RPMLOG_ERR, _("Missing '(' in %s %s\n"), name, pe);
306 /* Bracket %*verify args */
308 for (p = pe; *pe && *pe != ')'; pe++)
312 rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
316 /* Localize. Erase parsed string */
317 q = xmalloc((pe-p) + 1);
318 rstrlcpy(q, p, (pe-p) + 1);
323 verifyFlags = RPMVERIFY_NONE;
325 for (p = q; *p != '\0'; p = pe) {
335 for (vfa = verifyAttrs; vfa->attribute != NULL; vfa++) {
336 if (!rstreq(p, vfa->attribute))
338 verifyFlags |= vfa->flag;
345 if (rstreq(p, "not")) {
348 rpmlog(RPMLOG_ERR, _("Invalid %s token: %s\n"), name, p);
353 *resultVerify = negated ? ~(verifyFlags) : verifyFlags;
354 *specdFlags |= SPECD_VERIFY;
359 if (rc != RPMRC_OK) {
360 fl->processingFailed = 1;
366 #define isAttrDefault(_ars) ((_ars)[0] == '-' && (_ars)[1] == '\0')
369 * Parse %dev from file manifest.
370 * @param buf current spec file line
371 * @param fl package file tree walk data
372 * @return RPMRC_OK on success
374 static rpmRC parseForDev(const char * buf, FileList fl)
377 const char * errstr = NULL;
378 char *p, *pe, *q = NULL;
379 int rc = RPMRC_FAIL; /* assume error */
381 if ((p = strstr(buf, (name = "%dev"))) == NULL)
384 for (pe = p; (pe-p) < strlen(name); pe++)
393 /* Bracket %dev args */
395 for (p = pe; *pe && *pe != ')'; pe++)
402 /* Localize. Erase parsed string */
403 q = xmalloc((pe-p) + 1);
404 rstrlcpy(q, p, (pe-p) + 1);
409 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
419 p = pe; SKIPWHITE(p);
420 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
421 for (pe = p; *pe && risdigit(*pe); pe++)
424 fl->devmajor = atoi(p);
425 if (!(fl->devmajor >= 0 && fl->devmajor < 256)) {
435 p = pe; SKIPWHITE(p);
436 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
437 for (pe = p; *pe && risdigit(*pe); pe++)
440 fl->devminor = atoi(p);
441 if (!(fl->devminor >= 0 && fl->devminor < 256)) {
457 rpmlog(RPMLOG_ERR, _("Missing %s in %s %s\n"), errstr, name, p);
458 fl->processingFailed = 1;
465 * Parse %attr and %defattr from file manifest.
466 * @param buf current spec file line
467 * @param fl package file tree walk data
468 * @return 0 on success
470 static rpmRC parseForAttr(const char * buf, FileList fl)
473 char *p, *pe, *q = NULL;
475 struct AttrRec_s arbuf;
476 AttrRec ar = &arbuf, ret_ar;
477 specdFlags * specdFlags;
478 rpmRC rc = RPMRC_FAIL;
480 if ((p = strstr(buf, (name = "%attr"))) != NULL) {
481 ret_ar = &(fl->cur_ar);
482 specdFlags = &fl->currentSpecdFlags;
483 } else if ((p = strstr(buf, (name = "%defattr"))) != NULL) {
484 ret_ar = &(fl->def_ar);
485 specdFlags = &fl->defSpecdFlags;
489 for (pe = p; (pe-p) < strlen(name); pe++)
495 rpmlog(RPMLOG_ERR, _("Missing '(' in %s %s\n"), name, pe);
499 /* Bracket %*attr args */
501 for (p = pe; *pe && *pe != ')'; pe++)
504 if (ret_ar == &(fl->def_ar)) { /* %defattr */
510 _("Non-white space follows %s(): %s\n"), name, r);
515 /* Localize. Erase parsed string */
516 q = xmalloc((pe-p) + 1);
517 rstrlcpy(q, p, (pe-p) + 1);
525 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
527 p = pe; SKIPWHITE(p);
530 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
532 p = pe; SKIPWHITE(p);
535 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
537 p = pe; SKIPWHITE(p);
539 if (*p != '\0' && ret_ar == &(fl->def_ar)) { /* %defattr */
540 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
542 p = pe; SKIPWHITE(p);
545 if (!(ar->ar_fmodestr && ar->ar_user && ar->ar_group) || *p != '\0') {
546 rpmlog(RPMLOG_ERR, _("Bad syntax: %s(%s)\n"), name, q);
550 /* Do a quick test on the mode argument and adjust for "-" */
551 if (ar->ar_fmodestr && !isAttrDefault(ar->ar_fmodestr)) {
553 x = sscanf(ar->ar_fmodestr, "%o", &ui);
554 if ((x == 0) || (ar->ar_fmode & ~MYALLPERMS)) {
555 rpmlog(RPMLOG_ERR, _("Bad mode spec: %s(%s)\n"), name, q);
560 ar->ar_fmodestr = fl->def_ar.ar_fmodestr;
561 ar->ar_fmode = fl->def_ar.ar_fmode;
564 if (ar->ar_dmodestr && !isAttrDefault(ar->ar_dmodestr)) {
566 x = sscanf(ar->ar_dmodestr, "%o", &ui);
567 if ((x == 0) || (ar->ar_dmode & ~MYALLPERMS)) {
568 rpmlog(RPMLOG_ERR, _("Bad dirmode spec: %s(%s)\n"), name, q);
573 ar->ar_dmodestr = fl->def_ar.ar_dmodestr;
574 ar->ar_dmode = fl->def_ar.ar_dmode;
577 if (!(ar->ar_user && !isAttrDefault(ar->ar_user)))
578 ar->ar_user = fl->def_ar.ar_user;
580 if (!(ar->ar_group && !isAttrDefault(ar->ar_group)))
581 ar->ar_group = fl->def_ar.ar_group;
583 dupAttrRec(ar, ret_ar);
585 /* XXX fix all this */
586 *specdFlags |= SPECD_UID | SPECD_GID | SPECD_FILEMODE | SPECD_DIRMODE;
591 if (rc != RPMRC_OK) {
592 fl->processingFailed = 1;
599 * Parse %config from file manifest.
600 * @param buf current spec file line
601 * @param fl package file tree walk data
602 * @return RPMRC_OK on success
604 static rpmRC parseForConfig(const char * buf, FileList fl)
606 char *p, *pe, *q = NULL;
608 rpmRC rc = RPMRC_FAIL;
610 if ((p = strstr(buf, (name = "%config"))) == NULL)
613 fl->currentFlags |= RPMFILE_CONFIG;
615 /* Erase "%config" token. */
616 for (pe = p; (pe-p) < strlen(name); pe++)
622 /* Bracket %config args */
624 for (p = pe; *pe && *pe != ')'; pe++)
628 rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
632 /* Localize. Erase parsed string. */
633 q = xmalloc((pe-p) + 1);
634 rstrlcpy(q, p, (pe-p) + 1);
638 for (p = q; *p != '\0'; p = pe) {
646 if (rstreq(p, "missingok")) {
647 fl->currentFlags |= RPMFILE_MISSINGOK;
648 } else if (rstreq(p, "noreplace")) {
649 fl->currentFlags |= RPMFILE_NOREPLACE;
651 rpmlog(RPMLOG_ERR, _("Invalid %s token: %s\n"), name, p);
659 if (rc != RPMRC_OK) {
660 fl->processingFailed = 1;
668 static int langCmp(const void * ap, const void * bp)
670 return strcmp(*(const char **)ap, *(const char **)bp);
674 * Parse %lang from file manifest.
675 * @param buf current spec file line
676 * @param fl package file tree walk data
677 * @return RPMRC_OK on success
679 static rpmRC parseForLang(const char * buf, FileList fl)
681 char *p, *pe, *q = NULL;
683 rpmRC rc = RPMRC_FAIL;
685 while ((p = strstr(buf, (name = "%lang"))) != NULL) {
687 for (pe = p; (pe-p) < strlen(name); pe++)
692 rpmlog(RPMLOG_ERR, _("Missing '(' in %s %s\n"), name, pe);
696 /* Bracket %lang args */
698 for (pe = p; *pe && *pe != ')'; pe++)
702 rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
706 /* Localize. Erase parsed string. */
707 q = xmalloc((pe-p) + 1);
708 rstrlcpy(q, p, (pe-p) + 1);
712 /* Parse multiple arguments from %lang */
713 for (p = q; *p != '\0'; p = pe) {
724 /* Sanity check on locale lengths */
725 if (np < 1 || (np == 1 && *p != 'C') || np >= 32) {
727 _("Unusual locale length: \"%.*s\" in %%lang(%s)\n"),
732 /* Check for duplicate locales */
733 if (fl->currentLangs != NULL)
734 for (i = 0; i < fl->nLangs; i++) {
735 if (!rstreqn(fl->currentLangs[i], p, np))
737 rpmlog(RPMLOG_ERR, _("Duplicate locale %.*s in %%lang(%s)\n"),
743 fl->currentLangs = xrealloc(fl->currentLangs,
744 (fl->nLangs + 1) * sizeof(*fl->currentLangs));
745 newp = xmalloc( np+1 );
746 rstrlcpy(newp, p, np + 1);
747 fl->currentLangs[fl->nLangs++] = newp;
748 if (*pe == ',') pe++; /* skip , if present */
752 /* Insure that locales are sorted. */
753 if (fl->currentLangs)
754 qsort(fl->currentLangs, fl->nLangs, sizeof(*fl->currentLangs), langCmp);
759 if (rc != RPMRC_OK) {
760 fl->processingFailed = 1;
767 * Parse %caps from file manifest.
768 * @param buf current spec file line
769 * @param fl package file tree walk data
770 * @return RPMRC_OK on success
772 static rpmRC parseForCaps(const char * buf, FileList fl)
774 char *p, *pe, *q = NULL;
776 rpmRC rc = RPMRC_FAIL;
778 if ((p = strstr(buf, (name = "%caps"))) == NULL)
781 /* Erase "%caps" token. */
782 for (pe = p; (pe-p) < strlen(name); pe++)
788 /* Bracket %caps args */
790 for (p = pe; *pe && *pe != ')'; pe++)
794 rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
798 /* Localize. Erase parsed string. */
799 q = xmalloc((pe-p) + 1);
800 rstrlcpy(q, p, (pe-p) + 1);
807 cap_t fcaps = cap_from_text(q);
809 rpmlog(RPMLOG_ERR, _("Invalid capability: %s\n"), q);
812 /* run our string through cap_to_text() to get libcap presentation */
813 captxt = cap_to_text(fcaps, NULL);
814 fl->currentCaps = xstrdup(captxt);
820 rpmlog(RPMLOG_ERR, _("File capability support not built in\n"));
828 if (rc != RPMRC_OK) {
829 fl->processingFailed = 1;
836 static VFA_t virtualFileAttributes[] = {
837 { "%dir", 0, 0 }, /* XXX why not RPMFILE_DIR? */
838 { "%doc", 0, RPMFILE_DOC },
839 { "%ghost", 0, RPMFILE_GHOST },
840 { "%exclude", 0, RPMFILE_EXCLUDE },
841 { "%readme", 0, RPMFILE_README },
842 { "%license", 0, RPMFILE_LICENSE },
843 { "%pubkey", 0, RPMFILE_PUBKEY },
844 { "%policy", 0, RPMFILE_POLICY },
849 * Parse simple attributes (e.g. %dir) from file manifest.
852 * @param buf current spec file line
853 * @param fl package file tree walk data
854 * @retval *fileName file name
855 * @return RPMRC_OK on success
857 static rpmRC parseForSimple(rpmSpec spec, Package pkg, char * buf,
858 FileList fl, const char ** fileName)
862 char *specialDocBuf = NULL;
868 while ((s = strtokWithQuotes(t, " \t\n")) != NULL) {
871 if (rstreq(s, "%docdir")) {
872 s = strtokWithQuotes(NULL, " \t\n");
874 if (s == NULL || strtokWithQuotes(NULL, " \t\n")) {
875 rpmlog(RPMLOG_ERR, _("Only one arg for %%docdir\n"));
878 argvAdd(&(fl->docDirs), s);
883 /* Set flags for virtual file attributes */
884 for (vfa = virtualFileAttributes; vfa->attribute != NULL; vfa++) {
885 if (!rstreq(s, vfa->attribute))
888 if (rstreq(s, "%dir"))
889 fl->isDir = 1; /* XXX why not RPMFILE_DIR? */
892 fl->currentFlags &= ~vfa->flag;
894 fl->currentFlags |= vfa->flag;
898 /* if we got an attribute, continue with next token */
899 if (vfa->attribute != NULL)
903 /* We already got a file -- error */
904 rpmlog(RPMLOG_ERR, _("Two files on one line: %s\n"), *fileName);
909 if (fl->currentFlags & RPMFILE_DOC) {
910 rstrscat(&specialDocBuf, " ", s, NULL);
912 if (fl->currentFlags & (RPMFILE_POLICY|RPMFILE_PUBKEY))
916 /* not in %doc, does not begin with / -- error */
917 rpmlog(RPMLOG_ERR, _("File must begin with \"/\": %s\n"), s);
926 if (*fileName || (fl->currentFlags & ~(RPMFILE_DOC))) {
928 _("Can't mix special %%doc with other forms: %s\n"),
929 (*fileName ? *fileName : ""));
932 /* XXX FIXME: this is easy to do as macro expansion */
933 if (! fl->passedSpecialDoc) {
934 pkg->specialDoc = newStringBuf();
935 appendStringBuf(pkg->specialDoc, "DOCDIR=$RPM_BUILD_ROOT");
936 appendLineStringBuf(pkg->specialDoc, pkg->specialDocDir);
937 appendLineStringBuf(pkg->specialDoc, "export DOCDIR");
938 appendLineStringBuf(pkg->specialDoc, "rm -rf $DOCDIR");
939 appendLineStringBuf(pkg->specialDoc, RPM_MKDIR_P " $DOCDIR");
941 *fileName = pkg->specialDocDir;
942 fl->passedSpecialDoc = 1;
943 fl->isSpecialDoc = 1;
946 appendStringBuf(pkg->specialDoc, "cp -pr ");
947 appendStringBuf(pkg->specialDoc, specialDocBuf);
948 appendLineStringBuf(pkg->specialDoc, " $DOCDIR");
953 if (res != RPMRC_OK) {
954 fl->processingFailed = 1;
962 static int compareFileListRecs(const void * ap, const void * bp)
964 const char *a = ((FileListRec)ap)->cpioPath;
965 const char *b = ((FileListRec)bp)->cpioPath;
970 * Test if file is located in a %docdir.
971 * @param fl package file tree walk data
972 * @param fileName file path
973 * @return 1 if doc file, 0 if not
975 static int isDoc(FileList fl, const char * fileName)
980 k = strlen(fileName);
981 for (dd = fl->docDirs; *dd; dd++) {
983 if (l < k && rstreqn(fileName, *dd, l) && fileName[l] == '/')
989 static int isHardLink(FileListRec flp, FileListRec tlp)
991 return ((S_ISREG(flp->fl_mode) && S_ISREG(tlp->fl_mode)) &&
992 ((flp->fl_nlink > 1) && (flp->fl_nlink == tlp->fl_nlink)) &&
993 (flp->fl_ino == tlp->fl_ino) &&
994 (flp->fl_dev == tlp->fl_dev));
998 * Verify that file attributes scope over hardlinks correctly.
999 * If partial hardlink sets are possible, then add tracking dependency.
1000 * @param fl package file tree walk data
1001 * @return 1 if partial hardlink sets can exist, 0 otherwise.
1003 static int checkHardLinks(FileList fl)
1005 FileListRec ilp, jlp;
1008 for (i = 0; i < fl->fileListRecsUsed; i++) {
1009 ilp = fl->fileList + i;
1010 if (!(S_ISREG(ilp->fl_mode) && ilp->fl_nlink > 1))
1013 for (j = i + 1; j < fl->fileListRecsUsed; j++) {
1014 jlp = fl->fileList + j;
1015 if (isHardLink(ilp, jlp)) {
1023 static int seenHardLink(FileList fl, FileListRec flp)
1025 for (FileListRec ilp = fl->fileList; ilp < flp; ilp++) {
1026 if (isHardLink(flp, ilp)) {
1034 * Add file entries to header.
1035 * @todo Should directories have %doc/%config attributes? (#14531)
1036 * @todo Remove RPMTAG_OLDFILENAMES, add dirname/basename instead.
1037 * @param fl package file tree walk data
1038 * @retval *fip file info for package
1042 static void genCpioListAndHeader(FileList fl,
1043 rpmfi * fip, Header h, int isSrc)
1045 int _addDotSlash = !(isSrc || rpmExpandNumeric("%{_noPayloadPrefix}"));
1046 size_t apathlen = 0;
1047 size_t dpathlen = 0;
1052 pgpHashAlgo defaultalgo = PGPHASHALGO_MD5, digestalgo;
1053 rpm_loff_t totalFileSize = 0;
1056 * See if non-md5 file digest algorithm is requested. If not
1057 * specified, quietly assume md5. Otherwise check if supported type.
1059 digestalgo = rpmExpandNumeric(isSrc ? "%{_source_filedigest_algorithm}" :
1060 "%{_binary_filedigest_algorithm}");
1061 if (digestalgo == 0) {
1062 digestalgo = defaultalgo;
1065 if (rpmDigestLength(digestalgo) == 0) {
1066 rpmlog(RPMLOG_WARNING,
1067 _("Unknown file digest algorithm %u, falling back to MD5\n"),
1069 digestalgo = defaultalgo;
1072 /* Sort the big list */
1073 qsort(fl->fileList, fl->fileListRecsUsed,
1074 sizeof(*(fl->fileList)), compareFileListRecs);
1076 /* Generate the header. */
1080 skipLen += strlen(fl->prefix);
1083 for (i = 0, flp = fl->fileList; i < fl->fileListRecsUsed; i++, flp++) {
1084 /* Merge duplicate entries. */
1085 while (i < (fl->fileListRecsUsed - 1) &&
1086 rstreq(flp->cpioPath, flp[1].cpioPath)) {
1088 /* Two entries for the same file found, merge the entries. */
1089 /* Note that an %exclude is a duplication of a file reference */
1092 flp[1].flags |= flp->flags;
1094 if (!(flp[1].flags & RPMFILE_EXCLUDE))
1095 rpmlog(RPMLOG_WARNING, _("File listed twice: %s\n"),
1099 if (S_ISDIR(flp->fl_mode)) {
1100 if ((flp[1].specdFlags & (SPECD_DIRMODE | SPECD_DEFDIRMODE)) <
1101 (flp->specdFlags & (SPECD_DIRMODE | SPECD_DEFDIRMODE)))
1102 flp[1].fl_mode = flp->fl_mode;
1104 if ((flp[1].specdFlags & (SPECD_FILEMODE | SPECD_DEFFILEMODE)) <
1105 (flp->specdFlags & (SPECD_FILEMODE | SPECD_DEFFILEMODE)))
1106 flp[1].fl_mode = flp->fl_mode;
1110 if ((flp[1].specdFlags & (SPECD_UID | SPECD_DEFUID)) <
1111 (flp->specdFlags & (SPECD_UID | SPECD_DEFUID)))
1113 flp[1].fl_uid = flp->fl_uid;
1114 flp[1].uname = flp->uname;
1118 if ((flp[1].specdFlags & (SPECD_GID | SPECD_DEFGID)) <
1119 (flp->specdFlags & (SPECD_GID | SPECD_DEFGID)))
1121 flp[1].fl_gid = flp->fl_gid;
1122 flp[1].gname = flp->gname;
1126 if ((flp[1].specdFlags & (SPECD_VERIFY | SPECD_DEFVERIFY)) <
1127 (flp->specdFlags & (SPECD_VERIFY | SPECD_DEFVERIFY)))
1128 flp[1].verifyFlags = flp->verifyFlags;
1130 /* XXX to-do: language */
1135 /* Skip files that were marked with %exclude. */
1136 if (flp->flags & RPMFILE_EXCLUDE) continue;
1138 /* Omit '/' and/or URL prefix, leave room for "./" prefix */
1139 apathlen += (strlen(flp->cpioPath) - skipLen + (_addDotSlash ? 3 : 1));
1141 /* Leave room for both dirname and basename NUL's */
1142 dpathlen += (strlen(flp->diskPath) + 2);
1145 * Make the header. Store the on-disk path to OLDFILENAMES for
1146 * cpio list generation purposes for now, final path temporarily
1147 * to ORIGFILENAMES, to be swapped later into OLDFILENAMES.
1149 headerPutString(h, RPMTAG_OLDFILENAMES, flp->diskPath);
1150 headerPutString(h, RPMTAG_ORIGFILENAMES, flp->cpioPath);
1151 headerPutString(h, RPMTAG_FILEUSERNAME, flp->uname);
1152 headerPutString(h, RPMTAG_FILEGROUPNAME, flp->gname);
1155 * Only use 64bit filesizes if file sizes require it.
1156 * This is basically no-op for now as we error out in addFile() if
1157 * any individual file size exceeds the cpio limit.
1159 if (fl->largeFiles) {
1160 rpm_loff_t rsize64 = (rpm_loff_t)flp->fl_size;
1161 headerPutUint64(h, RPMTAG_LONGFILESIZES, &rsize64, 1);
1162 /* XXX TODO: add rpmlib() dependency for large files */
1164 rpm_off_t rsize32 = (rpm_off_t)flp->fl_size;
1165 headerPutUint32(h, RPMTAG_FILESIZES, &rsize32, 1);
1167 /* Excludes and dupes have been filtered out by now. */
1168 if (S_ISREG(flp->fl_mode)) {
1169 if (flp->fl_nlink == 1 || !seenHardLink(fl, flp)) {
1170 totalFileSize += flp->fl_size;
1175 * For items whose size varies between systems, always explicitly
1176 * cast to the header type before inserting.
1177 * TODO: check and warn if header type overflows for each case.
1179 { rpm_time_t rtime = (rpm_time_t) flp->fl_mtime;
1180 headerPutUint32(h, RPMTAG_FILEMTIMES, &rtime, 1);
1183 { rpm_mode_t rmode = (rpm_mode_t) flp->fl_mode;
1184 headerPutUint16(h, RPMTAG_FILEMODES, &rmode, 1);
1187 { rpm_rdev_t rrdev = (rpm_rdev_t) flp->fl_rdev;
1188 headerPutUint16(h, RPMTAG_FILERDEVS, &rrdev, 1);
1191 { rpm_dev_t rdev = (rpm_dev_t) flp->fl_dev;
1192 headerPutUint32(h, RPMTAG_FILEDEVICES, &rdev, 1);
1195 { rpm_ino_t rino = (rpm_ino_t) flp->fl_ino;
1196 headerPutUint32(h, RPMTAG_FILEINODES, &rino, 1);
1199 headerPutString(h, RPMTAG_FILELANGS, flp->langs);
1202 headerPutString(h, RPMTAG_FILECAPS, flp->caps);
1206 if (S_ISREG(flp->fl_mode))
1207 (void) rpmDoDigest(digestalgo, flp->diskPath, 1,
1208 (unsigned char *)buf, NULL);
1209 headerPutString(h, RPMTAG_FILEDIGESTS, buf);
1212 if (S_ISLNK(flp->fl_mode)) {
1213 buf[readlink(flp->diskPath, buf, BUFSIZ)] = '\0';
1214 if (fl->buildRoot) {
1215 if (buf[0] == '/' && !rstreq(fl->buildRoot, "/") &&
1216 rstreqn(buf, fl->buildRoot, strlen(fl->buildRoot))) {
1218 _("Symlink points to BuildRoot: %s -> %s\n"),
1219 flp->cpioPath, buf);
1220 fl->processingFailed = 1;
1224 headerPutString(h, RPMTAG_FILELINKTOS, buf);
1226 if (flp->flags & RPMFILE_GHOST) {
1227 flp->verifyFlags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE |
1228 RPMVERIFY_LINKTO | RPMVERIFY_MTIME);
1230 headerPutUint32(h, RPMTAG_FILEVERIFYFLAGS, &(flp->verifyFlags),1);
1232 if (!isSrc && isDoc(fl, flp->cpioPath))
1233 flp->flags |= RPMFILE_DOC;
1234 /* XXX Should directories have %doc/%config attributes? (#14531) */
1235 if (S_ISDIR(flp->fl_mode))
1236 flp->flags &= ~(RPMFILE_CONFIG|RPMFILE_DOC);
1238 headerPutUint32(h, RPMTAG_FILEFLAGS, &(flp->flags) ,1);
1241 if (totalFileSize < UINT32_MAX) {
1242 rpm_off_t totalsize = totalFileSize;
1243 headerPutUint32(h, RPMTAG_SIZE, &totalsize, 1);
1245 rpm_loff_t totalsize = totalFileSize;
1246 headerPutUint64(h, RPMTAG_LONGSIZE, &totalsize, 1);
1249 if (digestalgo != defaultalgo) {
1250 headerPutUint32(h, RPMTAG_FILEDIGESTALGO, &digestalgo, 1);
1251 rpmlibNeedsFeature(h, "FileDigests", "4.6.0-1");
1255 rpmlibNeedsFeature(h, "FileCaps", "4.6.1-1");
1259 (void) rpmlibNeedsFeature(h, "PayloadFilesHavePrefix", "4.0-1");
1262 struct rpmtd_s filenames;
1263 rpmfiFlags flags = RPMFI_NOHEADER|RPMFI_NOFILEUSER|RPMFI_NOFILEGROUP;
1269 /* rpmfiNew() only groks compressed filelists */
1270 headerConvert(h, HEADERCONV_COMPRESSFILELIST);
1271 fi = rpmfiNew(NULL, h, RPMTAG_BASENAMES, flags);
1274 * Grab the real filenames from ORIGFILENAMES and put into OLDFILENAMES,
1275 * remove temporary cruft and side-effects from filelist compression
1278 headerGet(h, RPMTAG_ORIGFILENAMES, &filenames, HEADERGET_ALLOC);
1279 headerDel(h, RPMTAG_ORIGFILENAMES);
1280 headerDel(h, RPMTAG_BASENAMES);
1281 headerDel(h, RPMTAG_DIRNAMES);
1282 headerDel(h, RPMTAG_DIRINDEXES);
1283 rpmtdSetTag(&filenames, RPMTAG_OLDFILENAMES);
1284 headerPut(h, &filenames, HEADERPUT_DEFAULT);
1286 /* Create hge-style archive path array, normally adding "./" */
1287 fc = rpmtdCount(&filenames);
1288 apath = xmalloc(fc * sizeof(*apath) + apathlen + 1);
1289 a = (char *)(apath + fc);
1291 rpmtdInit(&filenames);
1292 for (int i = 0; (fn = rpmtdNextString(&filenames)); i++) {
1295 a = stpcpy(a, "./");
1296 a = stpcpy(a, (fn + skipLen));
1297 a++; /* skip apath NUL */
1301 rpmtdFreeData(&filenames);
1304 /* Compress filelist unless legacy format requested */
1305 if (!_noDirTokens) {
1306 headerConvert(h, HEADERCONV_COMPRESSFILELIST);
1307 /* Binary packages with dirNames cannot be installed by legacy rpm. */
1308 (void) rpmlibNeedsFeature(h, "CompressedFileNames", "3.0.4-1");
1314 static FileListRec freeFileList(FileListRec fileList,
1318 fileList[count].diskPath = _free(fileList[count].diskPath);
1319 fileList[count].cpioPath = _free(fileList[count].cpioPath);
1320 fileList[count].langs = _free(fileList[count].langs);
1321 fileList[count].caps = _free(fileList[count].caps);
1323 fileList = _free(fileList);
1328 static rpmRC recurseDir(FileList fl, const char * diskPath);
1331 * Add a file to the package manifest.
1332 * @param fl package file tree walk data
1333 * @param diskPath path to file
1334 * @param statp file stat (possibly NULL)
1335 * @return RPMRC_OK on success
1337 static rpmRC addFile(FileList fl, const char * diskPath,
1338 struct stat * statp)
1340 const char *cpioPath = diskPath;
1341 struct stat statbuf;
1345 const char *fileUname;
1346 const char *fileGname;
1348 /* Path may have prepended buildRoot, so locate the original filename. */
1350 * XXX There are 3 types of entry into addFile:
1352 * From diskUrl statp
1353 * =====================================================
1354 * processBinaryFile path NULL
1355 * processBinaryFile glob result path NULL
1359 if (fl->buildRoot && !rstreq(fl->buildRoot, "/"))
1360 cpioPath += strlen(fl->buildRoot);
1362 /* XXX make sure '/' can be packaged also */
1363 if (*cpioPath == '\0')
1366 /* If we are using a prefix, validate the file */
1367 if (!fl->inFtw && fl->prefix) {
1368 const char *prefixPtr = fl->prefix;
1370 while (*prefixPtr && *cpioPath && (*cpioPath == *prefixPtr)) {
1374 if (*prefixPtr || (*cpioPath && *cpioPath != '/')) {
1375 rpmlog(RPMLOG_ERR, _("File doesn't match prefix (%s): %s\n"),
1376 fl->prefix, cpioPath);
1377 fl->processingFailed = 1;
1382 if (statp == NULL) {
1383 time_t now = time(NULL);
1385 memset(statp, 0, sizeof(*statp));
1387 /* XXX hack up a stat structure for a %dev(...) directive. */
1388 statp->st_nlink = 1;
1390 ((fl->devmajor & 0xff) << 8) | (fl->devminor & 0xff);
1391 statp->st_dev = statp->st_rdev;
1392 statp->st_mode = (fl->devtype == 'b' ? S_IFBLK : S_IFCHR);
1393 statp->st_mode |= (fl->cur_ar.ar_fmode & 0777);
1394 statp->st_atime = now;
1395 statp->st_mtime = now;
1396 statp->st_ctime = now;
1398 int is_ghost = fl->currentFlags & RPMFILE_GHOST;
1400 if (lstat(diskPath, statp)) {
1401 if (is_ghost) { /* the file is %ghost missing from build root, assume regular file */
1402 if (fl->cur_ar.ar_fmodestr != NULL) {
1403 statp->st_mode = S_IFREG | (fl->cur_ar.ar_fmode & 0777);
1405 rpmlog(RPMLOG_ERR, _("Explicit file attributes required in spec for: %s\n"), diskPath);
1406 fl->processingFailed = 1;
1409 statp->st_atime = now;
1410 statp->st_mtime = now;
1411 statp->st_ctime = now;
1413 rpmlog(RPMLOG_ERR, _("File not found: %s\n"), diskPath);
1414 fl->processingFailed = 1;
1418 if (is_ghost && !S_ISREG(statp->st_mode)) {
1419 rpmlog(RPMLOG_ERR, _("Only regular file can be %%ghost: %s\n"), diskPath);
1420 fl->processingFailed = 1;
1427 if ((! fl->isDir) && S_ISDIR(statp->st_mode)) {
1428 /* FIX: fl->buildRoot may be NULL */
1429 return recurseDir(fl, diskPath);
1432 fileMode = statp->st_mode;
1433 fileUid = statp->st_uid;
1434 fileGid = statp->st_gid;
1436 if (S_ISDIR(fileMode) && fl->cur_ar.ar_dmodestr) {
1438 fileMode |= fl->cur_ar.ar_dmode;
1439 } else if (fl->cur_ar.ar_fmodestr != NULL) {
1441 fileMode |= fl->cur_ar.ar_fmode;
1443 if (fl->cur_ar.ar_user) {
1444 fileUname = getUnameS(fl->cur_ar.ar_user);
1446 fileUname = getUname(fileUid);
1448 if (fl->cur_ar.ar_group) {
1449 fileGname = getGnameS(fl->cur_ar.ar_group);
1451 fileGname = getGname(fileGid);
1454 /* Default user/group to builder's user/group */
1455 if (fileUname == NULL)
1456 fileUname = getUname(getuid());
1457 if (fileGname == NULL)
1458 fileGname = getGname(getgid());
1460 /* S_XXX macro must be consistent with type in find call at check-files script */
1461 if (check_fileList && (S_ISREG(fileMode) || S_ISLNK(fileMode))) {
1462 appendStringBuf(check_fileList, diskPath);
1463 appendStringBuf(check_fileList, "\n");
1466 /* Add to the file list */
1467 if (fl->fileListRecsUsed == fl->fileListRecsAlloced) {
1468 fl->fileListRecsAlloced += 128;
1469 fl->fileList = xrealloc(fl->fileList,
1470 fl->fileListRecsAlloced * sizeof(*(fl->fileList)));
1473 { FileListRec flp = &fl->fileList[fl->fileListRecsUsed];
1476 flp->fl_st = *statp; /* structure assignment */
1477 flp->fl_mode = fileMode;
1478 flp->fl_uid = fileUid;
1479 flp->fl_gid = fileGid;
1481 flp->cpioPath = xstrdup(cpioPath);
1482 flp->diskPath = xstrdup(diskPath);
1483 flp->uname = fileUname;
1484 flp->gname = fileGname;
1486 if (fl->currentLangs && fl->nLangs > 0) {
1490 for (i = 0; i < fl->nLangs; i++)
1491 nl += strlen(fl->currentLangs[i]) + 1;
1493 flp->langs = ncl = xmalloc(nl);
1494 for (i = 0; i < fl->nLangs; i++) {
1496 if (i) *ncl++ = '|';
1497 for (ocl = fl->currentLangs[i]; *ocl != '\0'; ocl++)
1502 flp->langs = xstrdup("");
1505 if (fl->currentCaps) {
1506 flp->caps = fl->currentCaps;
1508 flp->caps = xstrdup("");
1511 flp->flags = fl->currentFlags;
1512 flp->specdFlags = fl->currentSpecdFlags;
1513 flp->verifyFlags = fl->currentVerifyFlags;
1515 if (!(flp->flags & RPMFILE_EXCLUDE) && S_ISREG(flp->fl_mode)) {
1517 * XXX Simple and stupid check for now, this needs to be per-payload
1518 * format check once we have other payloads than good 'ole cpio.
1520 if ((rpm_loff_t) flp->fl_size >= CPIO_FILESIZE_MAX) {
1522 rpmlog(RPMLOG_ERR, _("File %s too large for payload\n"),
1529 fl->fileListRecsUsed++;
1536 * Add directory (and all of its files) to the package manifest.
1537 * @param fl package file tree walk data
1538 * @param diskPath path to file
1539 * @return RPMRC_OK on success
1541 static rpmRC recurseDir(FileList fl, const char * diskPath)
1546 int myFtsOpts = (FTS_COMFOLLOW | FTS_NOCHDIR | FTS_PHYSICAL);
1547 int rc = RPMRC_FAIL;
1549 fl->inFtw = 1; /* Flag to indicate file has buildRoot prefixed */
1550 fl->isDir = 1; /* Keep it from following myftw() again */
1552 ftsSet[0] = (char *) diskPath;
1554 ftsp = Fts_open(ftsSet, myFtsOpts, NULL);
1555 while ((fts = Fts_read(ftsp)) != NULL) {
1556 switch (fts->fts_info) {
1557 case FTS_D: /* preorder directory */
1558 case FTS_F: /* regular file */
1559 case FTS_SL: /* symbolic link */
1560 case FTS_SLNONE: /* symbolic link without target */
1561 case FTS_DEFAULT: /* none of the above */
1562 rc = addFile(fl, fts->fts_accpath, fts->fts_statp);
1564 case FTS_DOT: /* dot or dot-dot */
1565 case FTS_DP: /* postorder directory */
1568 case FTS_NS: /* stat(2) failed */
1569 case FTS_DNR: /* unreadable directory */
1570 case FTS_ERR: /* error; errno is set */
1571 case FTS_DC: /* directory that causes cycles */
1572 case FTS_NSOK: /* no stat(2) requested */
1573 case FTS_INIT: /* initialized only */
1574 case FTS_W: /* whiteout object */
1582 (void) Fts_close(ftsp);
1591 * Add a pubkey/policy/icon to a binary package.
1593 * @param fl package file tree walk data
1594 * @param fileName path to file, relative is builddir, absolute buildroot.
1595 * @param tag tag to add
1596 * @return RPMRC_OK on success
1598 static rpmRC processMetadataFile(Package pkg, FileList fl,
1599 const char * fileName, rpmTag tag)
1601 const char * buildDir = "%{_builddir}/%{?buildsubdir}/";
1604 uint8_t * pkt = NULL;
1607 int rc = RPMRC_FAIL;
1610 if (*fileName == '/') {
1611 fn = rpmGenPath(fl->buildRoot, NULL, fileName);
1614 fn = rpmGenPath(buildDir, NULL, fileName);
1618 rpmlog(RPMLOG_ERR, _("%s: can't load unknown tag (%d).\n"),
1622 case RPMTAG_PUBKEYS: {
1623 if ((xx = pgpReadPkts(fn, &pkt, (size_t *)&pktlen)) <= 0) {
1624 rpmlog(RPMLOG_ERR, _("%s: public key read failed.\n"), fn);
1627 if (xx != PGPARMOR_PUBKEY) {
1628 rpmlog(RPMLOG_ERR, _("%s: not an armored public key.\n"), fn);
1631 apkt = pgpArmorWrap(PGPARMOR_PUBKEY, pkt, pktlen);
1634 case RPMTAG_POLICIES:
1635 if ((xx = rpmioSlurp(fn, &pkt, &pktlen)) != 0 || pkt == NULL) {
1636 rpmlog(RPMLOG_ERR, _("%s: policy file read failed.\n"), fn);
1639 apkt = b64encode(pkt, pktlen, -1);
1644 rpmlog(RPMLOG_ERR, _("%s: failed to encode\n"), fn);
1648 headerPutString(pkg->header, tag, apkt);
1652 rc = addFile(fl, fn, NULL);
1659 fl->processingFailed = 1;
1666 * Add a file to a binary package.
1668 * @param fl package file tree walk data
1669 * @param fileName file to add
1670 * @return RPMRC_OK on success
1672 static rpmRC processBinaryFile(Package pkg, FileList fl, const char * fileName)
1674 int quote = 1; /* XXX permit quoted glob characters. */
1676 char *diskPath = NULL;
1679 doGlob = glob_pattern_p(fileName, quote);
1681 /* Check that file starts with leading "/" */
1682 if (*fileName != '/') {
1683 rpmlog(RPMLOG_ERR, _("File needs leading \"/\": %s\n"), fileName);
1688 /* Copy file name or glob pattern removing multiple "/" chars. */
1690 * Note: rpmGetPath should guarantee a "canonical" path. That means
1691 * that the following pathologies should be weeded out:
1694 * /.././../usr/../bin//./sh
1696 diskPath = rpmGenPath(fl->buildRoot, NULL, fileName);
1703 /* XXX for %dev marker in file manifest only */
1705 rpmlog(RPMLOG_ERR, _("Glob not permitted: %s\n"), diskPath);
1710 rc = rpmGlob(diskPath, &argc, &argv);
1711 if (rc == 0 && argc >= 1) {
1712 for (i = 0; i < argc; i++) {
1713 rc = addFile(fl, argv[i], NULL);
1717 rpmlog(RPMLOG_ERR, _("File not found by glob: %s\n"), diskPath);
1722 rc = addFile(fl, diskPath, NULL);
1728 fl->processingFailed = 1;
1736 static rpmRC processPackageFiles(rpmSpec spec, Package pkg,
1737 int installSpecialDoc, int test)
1739 struct FileList_s fl;
1741 ARGV_t files = NULL;
1742 const char *fileName;
1744 struct AttrRec_s arbuf;
1745 AttrRec specialDocAttrRec = &arbuf;
1746 char *specialDoc = NULL;
1748 nullAttrRec(specialDocAttrRec);
1749 pkg->cpioList = NULL;
1751 if (pkg->fileFile) {
1756 argvSplit(&filelists, getStringBuf(pkg->fileFile), "\n");
1757 for (fp = filelists; *fp != NULL; fp++) {
1759 ffn = rpmGetPath(*fp, NULL);
1761 ffn = rpmGetPath("%{_builddir}/",
1762 (spec->buildSubdir ? spec->buildSubdir : "") ,
1765 fd = fopen(ffn, "r");
1767 if (fd == NULL || ferror(fd)) {
1768 rpmlog(RPMLOG_ERR, _("Could not open %%files file %s: %m\n"), ffn);
1773 while (fgets(buf, sizeof(buf), fd)) {
1774 handleComments(buf);
1775 if (expandMacros(spec, spec->macros, buf, sizeof(buf))) {
1776 rpmlog(RPMLOG_ERR, _("line: %s\n"), buf);
1780 appendStringBuf(pkg->fileList, buf);
1786 /* Init the file list structure */
1787 memset(&fl, 0, sizeof(fl));
1789 /* XXX spec->buildRoot == NULL, then xstrdup("") is returned */
1790 fl.buildRoot = rpmGenPath(spec->rootDir, spec->buildRoot, NULL);
1792 fl.prefix = headerGetAsString(pkg->header, RPMTAG_DEFAULTPREFIX);
1795 fl.processingFailed = 0;
1797 fl.passedSpecialDoc = 0;
1798 fl.isSpecialDoc = 0;
1802 fl.currentFlags = 0;
1803 fl.currentVerifyFlags = 0;
1810 nullAttrRec(&fl.cur_ar);
1811 nullAttrRec(&fl.def_ar);
1812 dupAttrRec(&root_ar, &fl.def_ar); /* XXX assume %defattr(-,root,root) */
1814 fl.defVerifyFlags = RPMVERIFY_ALL;
1816 fl.currentLangs = NULL;
1818 fl.currentCaps = NULL;
1820 fl.currentSpecdFlags = 0;
1821 fl.defSpecdFlags = 0;
1826 { char *docs = rpmGetPath("%{?__docdir_path}", NULL);
1827 argvSplit(&fl.docDirs, docs, ":");
1832 fl.fileListRecsAlloced = 0;
1833 fl.fileListRecsUsed = 0;
1835 s = getStringBuf(pkg->fileList);
1836 argvSplit(&files, s, "\n");
1838 for (fp = files; *fp != NULL; fp++) {
1844 rstrlcpy(buf, s, sizeof(buf));
1846 /* Reset for a new line in %files */
1849 fl.currentFlags = 0;
1850 /* turn explicit flags into %def'd ones (gosh this is hacky...) */
1851 fl.currentSpecdFlags = ((unsigned)fl.defSpecdFlags) >> 8;
1852 fl.currentVerifyFlags = fl.defVerifyFlags;
1853 fl.isSpecialDoc = 0;
1860 /* XXX should reset to %deflang value */
1861 if (fl.currentLangs) {
1863 for (i = 0; i < fl.nLangs; i++)
1864 fl.currentLangs[i] = _free(fl.currentLangs[i]);
1865 fl.currentLangs = _free(fl.currentLangs);
1868 fl.currentCaps = NULL;
1870 dupAttrRec(&fl.def_ar, &fl.cur_ar);
1872 if (parseForVerify(buf, &fl))
1874 if (parseForAttr(buf, &fl))
1876 if (parseForDev(buf, &fl))
1878 if (parseForConfig(buf, &fl))
1880 if (parseForLang(buf, &fl))
1882 if (parseForCaps(buf, &fl))
1884 if (parseForSimple(spec, pkg, buf, &fl, &fileName))
1886 if (fileName == NULL)
1889 if (fl.isSpecialDoc) {
1890 /* Save this stuff for last */
1891 specialDoc = _free(specialDoc);
1892 specialDoc = xstrdup(fileName);
1893 dupAttrRec(&fl.cur_ar, specialDocAttrRec);
1894 } else if (fl.currentFlags & RPMFILE_PUBKEY) {
1895 (void) processMetadataFile(pkg, &fl, fileName, RPMTAG_PUBKEYS);
1896 } else if (fl.currentFlags & RPMFILE_POLICY) {
1897 (void) processMetadataFile(pkg, &fl, fileName, RPMTAG_POLICIES);
1899 (void) processBinaryFile(pkg, &fl, fileName);
1903 /* Now process special doc, if there is one */
1905 if (installSpecialDoc) {
1906 int _missing_doc_files_terminate_build =
1907 rpmExpandNumeric("%{?_missing_doc_files_terminate_build}");
1910 rc = doScript(spec, RPMBUILD_STRINGBUF, "%doc", pkg->specialDoc, test);
1911 if (rc != RPMRC_OK && _missing_doc_files_terminate_build)
1912 fl.processingFailed = 1;
1915 /* Reset for %doc */
1918 fl.currentFlags = 0;
1919 fl.currentVerifyFlags = fl.defVerifyFlags;
1926 /* XXX should reset to %deflang value */
1927 if (fl.currentLangs) {
1929 for (i = 0; i < fl.nLangs; i++)
1930 fl.currentLangs[i] = _free(fl.currentLangs[i]);
1931 fl.currentLangs = _free(fl.currentLangs);
1935 dupAttrRec(specialDocAttrRec, &fl.cur_ar);
1936 freeAttrRec(specialDocAttrRec);
1938 (void) processBinaryFile(pkg, &fl, specialDoc);
1940 specialDoc = _free(specialDoc);
1945 if (fl.processingFailed)
1948 /* Verify that file attributes scope over hardlinks correctly. */
1949 if (checkHardLinks(&fl))
1950 (void) rpmlibNeedsFeature(pkg->header,
1951 "PartialHardlinkSets", "4.0.4-1");
1953 genCpioListAndHeader(&fl, &pkg->cpioList, pkg->header, 0);
1955 if (spec->timeCheck)
1956 timeCheck(spec->timeCheck, pkg->header);
1959 fl.buildRoot = _free(fl.buildRoot);
1960 fl.prefix = _free(fl.prefix);
1962 freeAttrRec(&fl.cur_ar);
1963 freeAttrRec(&fl.def_ar);
1965 if (fl.currentLangs) {
1967 for (i = 0; i < fl.nLangs; i++)
1968 fl.currentLangs[i] = _free(fl.currentLangs[i]);
1969 fl.currentLangs = _free(fl.currentLangs);
1972 fl.fileList = freeFileList(fl.fileList, fl.fileListRecsUsed);
1973 argvFree(fl.docDirs);
1974 return fl.processingFailed ? RPMRC_FAIL : RPMRC_OK;
1977 static const rpmTag sourceTags[] = {
1985 RPMTAG_DISTRIBUTION,
1992 RPMTAG_CHANGELOGTIME,
1993 RPMTAG_CHANGELOGNAME,
1994 RPMTAG_CHANGELOGTEXT,
2001 static void genSourceRpmName(rpmSpec spec)
2003 if (spec->sourceRpmName == NULL) {
2004 char *nvr = headerGetAsString(spec->packages->header, RPMTAG_NVR);
2005 rasprintf(&spec->sourceRpmName, "%s.%ssrc.rpm", nvr,
2006 spec->noSource ? "no" : "");
2011 void initSourceHeader(rpmSpec spec)
2016 spec->sourceHeader = headerNew();
2017 /* Only specific tags are added to the source package header */
2018 headerCopyTags(spec->packages->header, spec->sourceHeader, sourceTags);
2020 /* Add the build restrictions */
2021 hi = headerInitIterator(spec->buildRestrictions);
2022 while (headerNext(hi, &td)) {
2023 if (rpmtdCount(&td) > 0) {
2024 (void) headerPut(spec->sourceHeader, &td, HEADERPUT_DEFAULT);
2028 hi = headerFreeIterator(hi);
2030 if (spec->BANames && spec->BACount > 0) {
2031 headerPutStringArray(spec->sourceHeader, RPMTAG_BUILDARCHS,
2032 spec->BANames, spec->BACount);
2036 int processSourceFiles(rpmSpec spec)
2038 struct Source *srcPtr;
2039 StringBuf sourceFiles;
2041 struct FileList_s fl;
2043 ARGV_t files = NULL;
2045 static char *_srcdefattr;
2049 _srcdefattr = rpmExpand("%{?_srcdefattr}", NULL);
2050 if (_srcdefattr && !*_srcdefattr)
2051 _srcdefattr = _free(_srcdefattr);
2054 sourceFiles = newStringBuf();
2057 * XXX This is where the source header for noarch packages needs
2058 * XXX to be initialized.
2060 if (spec->sourceHeader == NULL)
2061 initSourceHeader(spec);
2063 genSourceRpmName(spec);
2064 /* Construct the file list and source entries */
2065 appendLineStringBuf(sourceFiles, spec->specFile);
2066 if (spec->sourceHeader != NULL)
2067 for (srcPtr = spec->sources; srcPtr != NULL; srcPtr = srcPtr->next) {
2068 if (srcPtr->flags & RPMBUILD_ISSOURCE) {
2069 headerPutString(spec->sourceHeader, RPMTAG_SOURCE, srcPtr->source);
2070 if (srcPtr->flags & RPMBUILD_ISNO) {
2071 headerPutUint32(spec->sourceHeader, RPMTAG_NOSOURCE,
2075 if (srcPtr->flags & RPMBUILD_ISPATCH) {
2076 headerPutString(spec->sourceHeader, RPMTAG_PATCH, srcPtr->source);
2077 if (srcPtr->flags & RPMBUILD_ISNO) {
2078 headerPutUint32(spec->sourceHeader, RPMTAG_NOSOURCE,
2084 sfn = rpmGetPath( ((srcPtr->flags & RPMBUILD_ISNO) ? "!" : ""),
2085 "%{_sourcedir}/", srcPtr->source, NULL);
2086 appendLineStringBuf(sourceFiles, sfn);
2091 for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
2092 for (srcPtr = pkg->icon; srcPtr != NULL; srcPtr = srcPtr->next) {
2094 sfn = rpmGetPath( ((srcPtr->flags & RPMBUILD_ISNO) ? "!" : ""),
2095 "%{_sourcedir}/", srcPtr->source, NULL);
2096 appendLineStringBuf(sourceFiles, sfn);
2101 spec->sourceCpioList = NULL;
2103 /* Init the file list structure */
2104 memset(&fl, 0, sizeof(fl));
2106 char *a = rstrscat(NULL, "%defattr ", _srcdefattr, NULL);
2107 parseForAttr(a, &fl);
2110 fl.fileList = xcalloc((spec->numSources + 1), sizeof(*fl.fileList));
2111 fl.processingFailed = 0;
2112 fl.fileListRecsUsed = 0;
2114 fl.buildRoot = NULL;
2116 s = getStringBuf(sourceFiles);
2117 argvSplit(&files, s, "\n");
2119 /* The first source file is the spec file */
2121 for (fp = files; *fp != NULL; fp++) {
2122 const char *diskPath;
2127 SKIPSPACE(diskPath);
2131 flp = &fl.fileList[x];
2133 flp->flags = isSpec ? RPMFILE_SPECFILE : 0;
2134 /* files with leading ! are no source files */
2135 if (*diskPath == '!') {
2136 flp->flags |= RPMFILE_GHOST;
2140 tmp = xstrdup(diskPath); /* basename() might modify */
2141 flp->diskPath = xstrdup(diskPath);
2142 flp->cpioPath = xstrdup(basename(tmp));
2143 flp->verifyFlags = RPMVERIFY_ALL;
2146 if (stat(diskPath, &flp->fl_st)) {
2147 rpmlog(RPMLOG_ERR, _("Bad file: %s: %s\n"),
2148 diskPath, strerror(errno));
2149 fl.processingFailed = 1;
2152 if (fl.def_ar.ar_fmodestr) {
2153 flp->fl_mode &= S_IFMT;
2154 flp->fl_mode |= fl.def_ar.ar_fmode;
2156 if (fl.def_ar.ar_user) {
2157 flp->uname = getUnameS(fl.def_ar.ar_user);
2159 flp->uname = getUname(flp->fl_uid);
2161 if (fl.def_ar.ar_group) {
2162 flp->gname = getGnameS(fl.def_ar.ar_group);
2164 flp->gname = getGname(flp->fl_gid);
2166 flp->langs = xstrdup("");
2168 if (! (flp->uname && flp->gname)) {
2169 rpmlog(RPMLOG_ERR, _("Bad owner/group: %s\n"), diskPath);
2170 fl.processingFailed = 1;
2176 fl.fileListRecsUsed = x;
2179 if (! fl.processingFailed) {
2180 if (spec->sourceHeader != NULL)
2181 genCpioListAndHeader(&fl, &spec->sourceCpioList,
2182 spec->sourceHeader, 1);
2185 sourceFiles = freeStringBuf(sourceFiles);
2186 fl.fileList = freeFileList(fl.fileList, fl.fileListRecsUsed);
2187 freeAttrRec(&fl.def_ar);
2188 return fl.processingFailed;
2192 * Check packaged file list against what's in the build root.
2193 * @param fileList packaged file list
2194 * @return -1 if skipped, 0 on OK, 1 on error
2196 static int checkFiles(StringBuf fileList)
2198 static char * const av_ckfile[] = { "%{?__check_files}", NULL };
2199 StringBuf sb_stdout = NULL;
2203 s = rpmExpand(av_ckfile[0], NULL);
2210 rpmlog(RPMLOG_NOTICE, _("Checking for unpackaged file(s): %s\n"), s);
2212 rc = rpmfcExec(av_ckfile, fileList, &sb_stdout, 0);
2217 int _unpackaged_files_terminate_build =
2218 rpmExpandNumeric("%{?_unpackaged_files_terminate_build}");
2221 t = getStringBuf(sb_stdout);
2222 if ((*t != '\0') && (*t != '\n')) {
2223 rc = (_unpackaged_files_terminate_build) ? 1 : 0;
2224 rpmlog((rc ? RPMLOG_ERR : RPMLOG_WARNING),
2225 _("Installed (but unpackaged) file(s) found:\n%s"), t);
2230 sb_stdout = freeStringBuf(sb_stdout);
2235 int processBinaryFiles(rpmSpec spec, int installSpecialDoc, int test)
2240 check_fileList = newStringBuf();
2241 genSourceRpmName(spec);
2243 for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
2246 headerPutString(pkg->header, RPMTAG_SOURCERPM, spec->sourceRpmName);
2248 if (pkg->fileList == NULL)
2251 nvr = headerGetAsString(pkg->header, RPMTAG_NVRA);
2252 rpmlog(RPMLOG_NOTICE, _("Processing files: %s\n"), nvr);
2255 if ((rc = processPackageFiles(spec, pkg, installSpecialDoc, test)) != RPMRC_OK ||
2256 (rc = rpmfcGenerateDepends(spec, pkg)) != RPMRC_OK)
2259 a = headerGetString(pkg->header, RPMTAG_ARCH);
2260 if (rstreq(a, "noarch") && headerGetNumber(pkg->header, RPMTAG_HEADERCOLOR) != 0) {
2261 int terminate = rpmExpandNumeric("%{?_binaries_in_noarch_packages_terminate_build}");
2262 rpmlog(terminate ? RPMLOG_ERR : RPMLOG_WARNING,
2263 _("Arch dependent binaries in noarch package\n"));
2271 /* Now we have in fileList list of files from all packages.
2272 * We pass it to a script which does the work of finding missing
2273 * and duplicated files.
2277 if (checkFiles(check_fileList) > 0) {
2281 check_fileList = freeStringBuf(check_fileList);