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/fts.h"
23 #include "lib/rpmfi_internal.h" /* XXX pretty much all rpmfi internals... */
24 #include "lib/rpmte_internal.h" /* XXX rpmte init */
25 #include "lib/legacy.h" /* XXX expandFileList, compressFileList */
26 #include "build/buildio.h"
30 #define SKIPSPACE(s) { while (*(s) && risspace(*(s))) (s)++; }
31 #define SKIPWHITE(_x) {while(*(_x) && (risspace(*_x) || *(_x) == ',')) (_x)++;}
32 #define SKIPNONWHITE(_x){while(*(_x) &&!(risspace(*_x) || *(_x) == ',')) (_x)++;}
34 #define MAXDOCDIR 1024
38 typedef enum specdFlags_e {
39 SPECD_DEFFILEMODE = (1 << 0),
40 SPECD_DEFDIRMODE = (1 << 1),
41 SPECD_DEFUID = (1 << 2),
42 SPECD_DEFGID = (1 << 3),
43 SPECD_DEFVERIFY = (1 << 4),
45 SPECD_FILEMODE = (1 << 8),
46 SPECD_DIRMODE = (1 << 9),
47 SPECD_UID = (1 << 10),
48 SPECD_GID = (1 << 11),
49 SPECD_VERIFY = (1 << 12)
54 typedef struct FileListRec_s {
56 #define fl_dev fl_st.st_dev
57 #define fl_ino fl_st.st_ino
58 #define fl_mode fl_st.st_mode
59 #define fl_nlink fl_st.st_nlink
60 #define fl_uid fl_st.st_uid
61 #define fl_gid fl_st.st_gid
62 #define fl_rdev fl_st.st_rdev
63 #define fl_size fl_st.st_size
64 #define fl_mtime fl_st.st_mtime
66 char *diskURL; /* get file from here */
67 char *fileURL; /* filename in cpio archive */
71 specdFlags specdFlags; /* which attributes have been explicitly specified. */
72 rpmVerifyFlags verifyFlags;
73 char *langs; /* XXX locales separated with | */
78 typedef struct AttrRec_s {
87 static struct AttrRec_s root_ar = { NULL, NULL, "root", "root", 0, 0 };
90 static StringBuf check_fileList = NULL;
93 * Package file tree walk data.
95 typedef struct FileList_s {
100 rpm_off_t totalFileSize;
101 int processingFailed;
103 int passedSpecialDoc;
113 rpmfileAttrs currentFlags;
114 specdFlags currentSpecdFlags;
115 rpmVerifyFlags currentVerifyFlags;
116 struct AttrRec_s cur_ar;
117 struct AttrRec_s def_ar;
118 specdFlags defSpecdFlags;
119 rpmVerifyFlags defVerifyFlags;
121 char ** currentLangs;
123 /* Hard coded limit of MAXDOCDIR docdirs. */
124 /* If you break it you are doing something wrong. */
125 char * docDirs[MAXDOCDIR];
128 FileListRec fileList;
129 int fileListRecsAlloced;
130 int fileListRecsUsed;
135 static void nullAttrRec(AttrRec ar)
137 ar->ar_fmodestr = NULL;
138 ar->ar_dmodestr = NULL;
147 static void freeAttrRec(AttrRec ar)
149 ar->ar_fmodestr = _free(ar->ar_fmodestr);
150 ar->ar_dmodestr = _free(ar->ar_dmodestr);
151 ar->ar_user = _free(ar->ar_user);
152 ar->ar_group = _free(ar->ar_group);
153 /* XXX doesn't free ar (yet) */
159 static void dupAttrRec(const AttrRec oar, AttrRec nar)
164 nar->ar_fmodestr = (oar->ar_fmodestr ? xstrdup(oar->ar_fmodestr) : NULL);
165 nar->ar_dmodestr = (oar->ar_dmodestr ? xstrdup(oar->ar_dmodestr) : NULL);
166 nar->ar_user = (oar->ar_user ? xstrdup(oar->ar_user) : NULL);
167 nar->ar_group = (oar->ar_group ? xstrdup(oar->ar_group) : NULL);
168 nar->ar_fmode = oar->ar_fmode;
169 nar->ar_dmode = oar->ar_dmode;
175 static void dumpAttrRec(const char * msg, AttrRec ar)
178 fprintf(stderr, "%s:\t", msg);
179 fprintf(stderr, "(%s, %s, %s, %s)\n",
192 static char *strtokWithQuotes(char *s, const char *delim)
194 static char *olds = NULL;
202 /* Skip leading delimiters */
203 s += strspn(s, delim);
207 /* Find the end of the token. */
211 /* Find next " char */
212 s = strchr(token, '"');
214 s = strpbrk(token, delim);
219 /* This token finishes the string */
220 olds = strchr(token, '\0');
222 /* Terminate the token and make olds point past it */
232 static void timeCheck(int tc, Header h)
234 HGE_t hge = (HGE_t)headerGetEntryMinMemory;
235 HFD_t hfd = headerFreeData;
239 rpm_count_t count, x;
240 time_t currentTime = time(NULL);
242 x = hge(h, RPMTAG_OLDFILENAMES, &fnt, (rpm_data_t *) &files, &count);
243 x = hge(h, RPMTAG_FILEMTIMES, NULL, (rpm_data_t *) &mtime, NULL);
245 for (x = 0; x < count; x++) {
246 if ((currentTime - mtime[x]) > tc)
247 rpmlog(RPMLOG_WARNING, _("TIMECHECK failure: %s\n"), files[x]);
249 files = hfd(files, fnt);
254 typedef const struct VFA {
255 const char const * attribute;
262 VFA_t const verifyAttrs[] = {
263 { "md5", 0, RPMVERIFY_MD5 },
264 { "size", 0, RPMVERIFY_FILESIZE },
265 { "link", 0, RPMVERIFY_LINKTO },
266 { "user", 0, RPMVERIFY_USER },
267 { "group", 0, RPMVERIFY_GROUP },
268 { "mtime", 0, RPMVERIFY_MTIME },
269 { "mode", 0, RPMVERIFY_MODE },
270 { "rdev", 0, RPMVERIFY_RDEV },
275 * Parse %verify and %defverify from file manifest.
276 * @param buf current spec file line
277 * @param fl package file tree walk data
278 * @return RPMRC_OK on success
280 static rpmRC parseForVerify(const char * buf, FileList fl)
284 rpmVerifyFlags *resultVerify;
286 rpmVerifyFlags verifyFlags;
287 specdFlags * specdFlags;
289 if ((p = strstr(buf, (name = "%verify"))) != NULL) {
290 resultVerify = &(fl->currentVerifyFlags);
291 specdFlags = &fl->currentSpecdFlags;
292 } else if ((p = strstr(buf, (name = "%defverify"))) != NULL) {
293 resultVerify = &(fl->defVerifyFlags);
294 specdFlags = &fl->defSpecdFlags;
298 for (pe = p; (pe-p) < strlen(name); pe++)
304 rpmlog(RPMLOG_ERR, _("Missing '(' in %s %s\n"), name, pe);
305 fl->processingFailed = 1;
309 /* Bracket %*verify args */
311 for (p = pe; *pe && *pe != ')'; pe++)
315 rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
316 fl->processingFailed = 1;
320 /* Localize. Erase parsed string */
321 q = alloca((pe-p) + 1);
328 verifyFlags = RPMVERIFY_NONE;
330 for (p = q; *p != '\0'; p = pe) {
340 for (vfa = verifyAttrs; vfa->attribute != NULL; vfa++) {
341 if (strcmp(p, vfa->attribute))
343 verifyFlags |= vfa->flag;
350 if (!strcmp(p, "not")) {
353 rpmlog(RPMLOG_ERR, _("Invalid %s token: %s\n"), name, p);
354 fl->processingFailed = 1;
359 *resultVerify = negated ? ~(verifyFlags) : verifyFlags;
360 *specdFlags |= SPECD_VERIFY;
365 #define isAttrDefault(_ars) ((_ars)[0] == '-' && (_ars)[1] == '\0')
368 * Parse %dev from file manifest.
369 * @param buf current spec file line
370 * @param fl package file tree walk data
371 * @return RPMRC_OK on success
373 static rpmRC parseForDev(const char * buf, FileList fl)
376 const char * errstr = NULL;
378 int rc = RPMRC_FAIL; /* assume error */
380 if ((p = strstr(buf, (name = "%dev"))) == NULL)
383 for (pe = p; (pe-p) < strlen(name); pe++)
392 /* Bracket %dev args */
394 for (p = pe; *pe && *pe != ')'; pe++)
401 /* Localize. Erase parsed string */
402 q = alloca((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;
464 * Parse %attr and %defattr from file manifest.
465 * @param buf current spec file line
466 * @param fl package file tree walk data
467 * @return 0 on success
469 static int parseForAttr(const char * buf, FileList fl)
474 struct AttrRec_s arbuf;
475 AttrRec ar = &arbuf, ret_ar;
476 specdFlags * specdFlags;
478 if ((p = strstr(buf, (name = "%attr"))) != NULL) {
479 ret_ar = &(fl->cur_ar);
480 specdFlags = &fl->currentSpecdFlags;
481 } else if ((p = strstr(buf, (name = "%defattr"))) != NULL) {
482 ret_ar = &(fl->def_ar);
483 specdFlags = &fl->defSpecdFlags;
487 for (pe = p; (pe-p) < strlen(name); pe++)
493 rpmlog(RPMLOG_ERR, _("Missing '(' in %s %s\n"), name, pe);
494 fl->processingFailed = 1;
498 /* Bracket %*attr args */
500 for (p = pe; *pe && *pe != ')'; pe++)
503 if (ret_ar == &(fl->def_ar)) { /* %defattr */
509 _("Non-white space follows %s(): %s\n"), name, q);
510 fl->processingFailed = 1;
515 /* Localize. Erase parsed string */
516 q = alloca((pe-p) + 1);
526 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
528 p = pe; SKIPWHITE(p);
531 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
533 p = pe; SKIPWHITE(p);
536 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
538 p = pe; SKIPWHITE(p);
540 if (*p != '\0' && ret_ar == &(fl->def_ar)) { /* %defattr */
541 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
543 p = pe; SKIPWHITE(p);
546 if (!(ar->ar_fmodestr && ar->ar_user && ar->ar_group) || *p != '\0') {
547 rpmlog(RPMLOG_ERR, _("Bad syntax: %s(%s)\n"), name, q);
548 fl->processingFailed = 1;
552 /* Do a quick test on the mode argument and adjust for "-" */
553 if (ar->ar_fmodestr && !isAttrDefault(ar->ar_fmodestr)) {
555 x = sscanf(ar->ar_fmodestr, "%o", &ui);
556 if ((x == 0) || (ar->ar_fmode & ~MYALLPERMS)) {
557 rpmlog(RPMLOG_ERR, _("Bad mode spec: %s(%s)\n"), name, q);
558 fl->processingFailed = 1;
563 ar->ar_fmodestr = NULL;
565 if (ar->ar_dmodestr && !isAttrDefault(ar->ar_dmodestr)) {
567 x = sscanf(ar->ar_dmodestr, "%o", &ui);
568 if ((x == 0) || (ar->ar_dmode & ~MYALLPERMS)) {
569 rpmlog(RPMLOG_ERR, _("Bad dirmode spec: %s(%s)\n"), name, q);
570 fl->processingFailed = 1;
575 ar->ar_dmodestr = NULL;
577 if (!(ar->ar_user && !isAttrDefault(ar->ar_user)))
580 if (!(ar->ar_group && !isAttrDefault(ar->ar_group)))
583 dupAttrRec(ar, ret_ar);
585 /* XXX fix all this */
586 *specdFlags |= SPECD_UID | SPECD_GID | SPECD_FILEMODE | SPECD_DIRMODE;
592 * Parse %config from file manifest.
593 * @param buf current spec file line
594 * @param fl package file tree walk data
595 * @return RPMRC_OK on success
597 static rpmRC parseForConfig(const char * buf, FileList fl)
602 if ((p = strstr(buf, (name = "%config"))) == NULL)
605 fl->currentFlags |= RPMFILE_CONFIG;
607 /* Erase "%config" token. */
608 for (pe = p; (pe-p) < strlen(name); pe++)
614 /* Bracket %config args */
616 for (p = pe; *pe && *pe != ')'; pe++)
620 rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
621 fl->processingFailed = 1;
625 /* Localize. Erase parsed string. */
626 q = alloca((pe-p) + 1);
632 for (p = q; *p != '\0'; p = pe) {
640 if (!strcmp(p, "missingok")) {
641 fl->currentFlags |= RPMFILE_MISSINGOK;
642 } else if (!strcmp(p, "noreplace")) {
643 fl->currentFlags |= RPMFILE_NOREPLACE;
645 rpmlog(RPMLOG_ERR, _("Invalid %s token: %s\n"), name, p);
646 fl->processingFailed = 1;
656 static int langCmp(const void * ap, const void * bp)
658 return strcmp(*(const char **)ap, *(const char **)bp);
662 * Parse %lang from file manifest.
663 * @param buf current spec file line
664 * @param fl package file tree walk data
665 * @return RPMRC_OK on success
667 static int parseForLang(const char * buf, FileList fl)
672 while ((p = strstr(buf, (name = "%lang"))) != NULL) {
674 for (pe = p; (pe-p) < strlen(name); pe++)
679 rpmlog(RPMLOG_ERR, _("Missing '(' in %s %s\n"), name, pe);
680 fl->processingFailed = 1;
684 /* Bracket %lang args */
686 for (pe = p; *pe && *pe != ')'; pe++)
690 rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
691 fl->processingFailed = 1;
695 /* Localize. Erase parsed string. */
696 q = alloca((pe-p) + 1);
702 /* Parse multiple arguments from %lang */
703 for (p = q; *p != '\0'; p = pe) {
714 /* Sanity check on locale lengths */
715 if (np < 1 || (np == 1 && *p != 'C') || np >= 32) {
717 _("Unusual locale length: \"%.*s\" in %%lang(%s)\n"),
719 fl->processingFailed = 1;
723 /* Check for duplicate locales */
724 if (fl->currentLangs != NULL)
725 for (i = 0; i < fl->nLangs; i++) {
726 if (strncmp(fl->currentLangs[i], p, np))
728 rpmlog(RPMLOG_ERR, _("Duplicate locale %.*s in %%lang(%s)\n"),
730 fl->processingFailed = 1;
735 fl->currentLangs = xrealloc(fl->currentLangs,
736 (fl->nLangs + 1) * sizeof(*fl->currentLangs));
737 newp = xmalloc( np+1 );
738 strncpy(newp, p, np);
740 fl->currentLangs[fl->nLangs++] = newp;
741 if (*pe == ',') pe++; /* skip , if present */
745 /* Insure that locales are sorted. */
746 if (fl->currentLangs)
747 qsort(fl->currentLangs, fl->nLangs, sizeof(*fl->currentLangs), langCmp);
754 static int parseForRegexLang(const char * fileName, char ** lang)
756 static int initialized = 0;
757 static int hasRegex = 0;
758 static regex_t compiledPatt;
759 static char buf[BUFSIZ];
761 regmatch_t matches[2];
765 char *patt = rpmExpand("%{?_langpatt}", NULL);
767 if (!(patt && *patt != '\0'))
769 else if (regcomp(&compiledPatt, patt, REG_EXTENDED))
778 memset(matches, 0, sizeof(matches));
779 if (! hasRegex || regexec(&compiledPatt, fileName, 2, matches, REG_NOTEOL))
783 s = fileName + matches[1].rm_eo - 1;
784 x = matches[1].rm_eo - matches[1].rm_so;
796 VFA_t virtualFileAttributes[] = {
797 { "%dir", 0, 0 }, /* XXX why not RPMFILE_DIR? */
798 { "%doc", 0, RPMFILE_DOC },
799 { "%ghost", 0, RPMFILE_GHOST },
800 { "%exclude", 0, RPMFILE_EXCLUDE },
801 { "%readme", 0, RPMFILE_README },
802 { "%license", 0, RPMFILE_LICENSE },
803 { "%pubkey", 0, RPMFILE_PUBKEY },
804 { "%policy", 0, RPMFILE_POLICY },
807 { "%icon", 0, RPMFILE_ICON },
808 { "%spec", 0, RPMFILE_SPEC },
809 { "%config", 0, RPMFILE_CONFIG },
810 { "%missingok", 0, RPMFILE_CONFIG|RPMFILE_MISSINGOK },
811 { "%noreplace", 0, RPMFILE_CONFIG|RPMFILE_NOREPLACE },
818 * Parse simple attributes (e.g. %dir) from file manifest.
821 * @param buf current spec file line
822 * @param fl package file tree walk data
823 * @retval *fileName file name
824 * @return RPMRC_OK on success
826 static rpmRC parseForSimple(rpmSpec spec, Package pkg, char * buf,
827 FileList fl, const char ** fileName)
830 int res, specialDoc = 0;
831 char specialDocBuf[BUFSIZ];
833 specialDocBuf[0] = '\0';
838 while ((s = strtokWithQuotes(t, " \t\n")) != NULL) {
840 if (!strcmp(s, "%docdir")) {
841 s = strtokWithQuotes(NULL, " \t\n");
842 if (fl->docDirCount == MAXDOCDIR) {
843 rpmlog(RPMLOG_ERR, _("Hit limit for %%docdir\n"));
844 fl->processingFailed = 1;
849 fl->docDirs[fl->docDirCount++] = xstrdup(s);
850 if (s == NULL || strtokWithQuotes(NULL, " \t\n")) {
851 rpmlog(RPMLOG_ERR, _("Only one arg for %%docdir\n"));
852 fl->processingFailed = 1;
858 /* Set flags for virtual file attributes */
860 for (vfa = virtualFileAttributes; vfa->attribute != NULL; vfa++) {
861 if (strcmp(s, vfa->attribute))
864 if (!strcmp(s, "%dir"))
865 fl->isDir = 1; /* XXX why not RPMFILE_DIR? */
868 fl->currentFlags &= ~vfa->flag;
870 fl->currentFlags |= vfa->flag;
875 /* if we got an attribute, continue with next token */
876 if (vfa->attribute != NULL)
881 /* We already got a file -- error */
882 rpmlog(RPMLOG_ERR, _("Two files on one line: %s\n"),
884 fl->processingFailed = 1;
889 if (fl->currentFlags & RPMFILE_DOC) {
891 strcat(specialDocBuf, " ");
892 strcat(specialDocBuf, s);
894 if (fl->currentFlags & (RPMFILE_POLICY|RPMFILE_PUBKEY|RPMFILE_ICON))
898 /* not in %doc, does not begin with / -- error */
900 _("File must begin with \"/\": %s\n"), s);
901 fl->processingFailed = 1;
910 if (*fileName || (fl->currentFlags & ~(RPMFILE_DOC))) {
912 _("Can't mix special %%doc with other forms: %s\n"),
913 (*fileName ? *fileName : ""));
914 fl->processingFailed = 1;
917 /* XXX WATCHOUT: buf is an arg */
919 static const char *_docdir_fmt = NULL;
920 static int oneshot = 0;
924 _docdir_fmt = rpmExpand("%{?_docdir_fmt}", NULL);
925 if (!_docdir_fmt || !*_docdir_fmt)
926 _docdir_fmt = "%{NAME}-%{VERSION}";
929 fmt = headerSprintf(pkg->header, _docdir_fmt, rpmTagTable, rpmHeaderFormats, &errstr);
931 rpmlog(RPMLOG_ERR, _("illegal _docdir_fmt: %s\n"), errstr);
932 fl->processingFailed = 1;
935 ddir = rpmGetPath("%{_docdir}/", fmt, NULL);
940 /* XXX FIXME: this is easy to do as macro expansion */
942 if (! fl->passedSpecialDoc) {
943 pkg->specialDoc = newStringBuf();
944 appendStringBuf(pkg->specialDoc, "DOCDIR=$RPM_BUILD_ROOT");
945 appendLineStringBuf(pkg->specialDoc, buf);
946 appendLineStringBuf(pkg->specialDoc, "export DOCDIR");
947 appendLineStringBuf(pkg->specialDoc, "rm -rf $DOCDIR");
948 appendLineStringBuf(pkg->specialDoc, RPM_MKDIR_P " $DOCDIR");
951 fl->passedSpecialDoc = 1;
952 fl->isSpecialDoc = 1;
955 appendStringBuf(pkg->specialDoc, "cp -pr ");
956 appendStringBuf(pkg->specialDoc, specialDocBuf);
957 appendLineStringBuf(pkg->specialDoc, " $DOCDIR");
966 static int compareFileListRecs(const void * ap, const void * bp)
968 const char *a = ((FileListRec)ap)->fileURL;
969 const char *b = ((FileListRec)bp)->fileURL;
974 * Test if file is located in a %docdir.
975 * @param fl package file tree walk data
976 * @param fileName file path
977 * @return 1 if doc file, 0 if not
979 static int isDoc(FileList fl, const char * fileName)
981 int x = fl->docDirCount;
984 k = strlen(fileName);
986 l = strlen(fl->docDirs[x]);
987 if (l < k && strncmp(fileName, fl->docDirs[x], l) == 0 && fileName[l] == '/')
994 * Verify that file attributes scope over hardlinks correctly.
995 * If partial hardlink sets are possible, then add tracking dependency.
996 * @param fl package file tree walk data
997 * @return 1 if partial hardlink sets can exist, 0 otherwise.
999 static int checkHardLinks(FileList fl)
1001 FileListRec ilp, jlp;
1004 for (i = 0; i < fl->fileListRecsUsed; i++) {
1005 ilp = fl->fileList + i;
1006 if (!(S_ISREG(ilp->fl_mode) && ilp->fl_nlink > 1))
1009 for (j = i + 1; j < fl->fileListRecsUsed; j++) {
1010 jlp = fl->fileList + j;
1011 if (!S_ISREG(jlp->fl_mode))
1013 if (ilp->fl_nlink != jlp->fl_nlink)
1015 if (ilp->fl_ino != jlp->fl_ino)
1017 if (ilp->fl_dev != jlp->fl_dev)
1026 * Add file entries to header.
1027 * @todo Should directories have %doc/%config attributes? (#14531)
1028 * @todo Remove RPMTAG_OLDFILENAMES, add dirname/basename instead.
1029 * @param fl package file tree walk data
1030 * @retval *fip file info for package
1034 static void genCpioListAndHeader(FileList fl,
1035 rpmfi * fip, Header h, int isSrc)
1037 int _addDotSlash = !(isSrc || rpmExpandNumeric("%{_noPayloadPrefix}"));
1038 size_t apathlen = 0;
1039 size_t dpathlen = 0;
1046 /* Sort the big list */
1047 qsort(fl->fileList, fl->fileListRecsUsed,
1048 sizeof(*(fl->fileList)), compareFileListRecs);
1050 /* Generate the header. */
1054 skipLen += strlen(fl->prefix);
1057 for (i = 0, flp = fl->fileList; i < fl->fileListRecsUsed; i++, flp++) {
1060 /* Merge duplicate entries. */
1061 while (i < (fl->fileListRecsUsed - 1) &&
1062 !strcmp(flp->fileURL, flp[1].fileURL)) {
1064 /* Two entries for the same file found, merge the entries. */
1065 /* Note that an %exclude is a duplication of a file reference */
1068 flp[1].flags |= flp->flags;
1070 if (!(flp[1].flags & RPMFILE_EXCLUDE))
1071 rpmlog(RPMLOG_WARNING, _("File listed twice: %s\n"),
1075 if (S_ISDIR(flp->fl_mode)) {
1076 if ((flp[1].specdFlags & (SPECD_DIRMODE | SPECD_DEFDIRMODE)) <
1077 (flp->specdFlags & (SPECD_DIRMODE | SPECD_DEFDIRMODE)))
1078 flp[1].fl_mode = flp->fl_mode;
1080 if ((flp[1].specdFlags & (SPECD_FILEMODE | SPECD_DEFFILEMODE)) <
1081 (flp->specdFlags & (SPECD_FILEMODE | SPECD_DEFFILEMODE)))
1082 flp[1].fl_mode = flp->fl_mode;
1086 if ((flp[1].specdFlags & (SPECD_UID | SPECD_DEFUID)) <
1087 (flp->specdFlags & (SPECD_UID | SPECD_DEFUID)))
1089 flp[1].fl_uid = flp->fl_uid;
1090 flp[1].uname = flp->uname;
1094 if ((flp[1].specdFlags & (SPECD_GID | SPECD_DEFGID)) <
1095 (flp->specdFlags & (SPECD_GID | SPECD_DEFGID)))
1097 flp[1].fl_gid = flp->fl_gid;
1098 flp[1].gname = flp->gname;
1102 if ((flp[1].specdFlags & (SPECD_VERIFY | SPECD_DEFVERIFY)) <
1103 (flp->specdFlags & (SPECD_VERIFY | SPECD_DEFVERIFY)))
1104 flp[1].verifyFlags = flp->verifyFlags;
1106 /* XXX to-do: language */
1111 /* Skip files that were marked with %exclude. */
1112 if (flp->flags & RPMFILE_EXCLUDE) continue;
1114 /* Omit '/' and/or URL prefix, leave room for "./" prefix */
1115 apathlen += (strlen(flp->fileURL) - skipLen + (_addDotSlash ? 3 : 1));
1117 /* Leave room for both dirname and basename NUL's */
1118 dpathlen += (strlen(flp->diskURL) + 2);
1121 * Make the header, the OLDFILENAMES will get converted to a
1122 * compressed file list write before we write the actual package to
1125 (void) headerAddOrAppendEntry(h, RPMTAG_OLDFILENAMES, RPM_STRING_ARRAY_TYPE,
1126 &(flp->fileURL), 1);
1128 if (sizeof(flp->fl_size) != sizeof(rpm_off_t)) {
1129 rpm_off_t psize = (rpm_off_t)flp->fl_size;
1130 (void) headerAddOrAppendEntry(h, RPMTAG_FILESIZES, RPM_INT32_TYPE,
1133 (void) headerAddOrAppendEntry(h, RPMTAG_FILESIZES, RPM_INT32_TYPE,
1134 &(flp->fl_size), 1);
1136 (void) headerAddOrAppendEntry(h, RPMTAG_FILEUSERNAME, RPM_STRING_ARRAY_TYPE,
1138 (void) headerAddOrAppendEntry(h, RPMTAG_FILEGROUPNAME, RPM_STRING_ARRAY_TYPE,
1140 if (sizeof(flp->fl_mtime) != sizeof(rpm_time_t)) {
1141 rpm_time_t mtime = (rpm_time_t)flp->fl_mtime;
1142 (void) headerAddOrAppendEntry(h, RPMTAG_FILEMTIMES, RPM_INT32_TYPE,
1145 (void) headerAddOrAppendEntry(h, RPMTAG_FILEMTIMES, RPM_INT32_TYPE,
1146 &(flp->fl_mtime), 1);
1148 if (sizeof(flp->fl_mode) != sizeof(rpm_mode_t)) {
1149 rpm_mode_t pmode = (rpm_mode_t)flp->fl_mode;
1150 (void) headerAddOrAppendEntry(h, RPMTAG_FILEMODES, RPM_INT16_TYPE,
1153 (void) headerAddOrAppendEntry(h, RPMTAG_FILEMODES, RPM_INT16_TYPE,
1154 &(flp->fl_mode), 1);
1156 if (sizeof(flp->fl_rdev) != sizeof(rpm_rdev_t)) {
1157 rpm_rdev_t prdev = (rpm_rdev_t)flp->fl_rdev;
1158 (void) headerAddOrAppendEntry(h, RPMTAG_FILERDEVS, RPM_INT16_TYPE,
1161 (void) headerAddOrAppendEntry(h, RPMTAG_FILERDEVS, RPM_INT16_TYPE,
1162 &(flp->fl_rdev), 1);
1164 if (sizeof(flp->fl_dev) != sizeof(rpm_dev_t)) {
1165 rpm_dev_t pdevice = (rpm_dev_t)flp->fl_dev;
1166 (void) headerAddOrAppendEntry(h, RPMTAG_FILEDEVICES, RPM_INT32_TYPE,
1169 (void) headerAddOrAppendEntry(h, RPMTAG_FILEDEVICES, RPM_INT32_TYPE,
1172 if (sizeof(flp->fl_ino) != sizeof(rpm_ino_t)) {
1173 rpm_ino_t ino = (rpm_ino_t)flp->fl_ino;
1174 (void) headerAddOrAppendEntry(h, RPMTAG_FILEINODES, RPM_INT32_TYPE,
1177 (void) headerAddOrAppendEntry(h, RPMTAG_FILEINODES, RPM_INT32_TYPE,
1181 (void) headerAddOrAppendEntry(h, RPMTAG_FILELANGS, RPM_STRING_ARRAY_TYPE,
1184 /* We used to add these, but they should not be needed */
1185 /* (void) headerAddOrAppendEntry(h, RPMTAG_FILEUIDS,
1186 * RPM_INT32_TYPE, &(flp->fl_uid), 1);
1187 * (void) headerAddOrAppendEntry(h, RPMTAG_FILEGIDS,
1188 * RPM_INT32_TYPE, &(flp->fl_gid), 1);
1192 if (S_ISREG(flp->fl_mode))
1193 (void) rpmDoDigest(PGPHASHALGO_MD5, flp->diskURL, 1,
1194 (unsigned char *)buf, NULL);
1196 (void) headerAddOrAppendEntry(h, RPMTAG_FILEMD5S, RPM_STRING_ARRAY_TYPE,
1200 if (S_ISLNK(flp->fl_mode)) {
1201 buf[readlink(flp->diskURL, buf, BUFSIZ)] = '\0';
1202 if (fl->buildRootURL) {
1203 const char * buildRoot;
1204 (void) urlPath(fl->buildRootURL, &buildRoot);
1206 if (buf[0] == '/' && strcmp(buildRoot, "/") &&
1207 !strncmp(buf, buildRoot, strlen(buildRoot))) {
1209 _("Symlink points to BuildRoot: %s -> %s\n"),
1211 fl->processingFailed = 1;
1216 (void) headerAddOrAppendEntry(h, RPMTAG_FILELINKTOS, RPM_STRING_ARRAY_TYPE,
1219 if (flp->flags & RPMFILE_GHOST) {
1220 flp->verifyFlags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE |
1221 RPMVERIFY_LINKTO | RPMVERIFY_MTIME);
1223 (void) headerAddOrAppendEntry(h, RPMTAG_FILEVERIFYFLAGS, RPM_INT32_TYPE,
1224 &(flp->verifyFlags), 1);
1226 if (!isSrc && isDoc(fl, flp->fileURL))
1227 flp->flags |= RPMFILE_DOC;
1228 /* XXX Should directories have %doc/%config attributes? (#14531) */
1229 if (S_ISDIR(flp->fl_mode))
1230 flp->flags &= ~(RPMFILE_CONFIG|RPMFILE_DOC);
1232 (void) headerAddOrAppendEntry(h, RPMTAG_FILEFLAGS, RPM_INT32_TYPE,
1237 (void) headerAddEntry(h, RPMTAG_SIZE, RPM_INT32_TYPE,
1238 &(fl->totalFileSize), 1);
1241 (void) rpmlibNeedsFeature(h, "PayloadFilesHavePrefix", "4.0-1");
1243 /* Choose how filenames are represented. */
1247 compressFilelist(h);
1248 /* Binary packages with dirNames cannot be installed by legacy rpm. */
1249 (void) rpmlibNeedsFeature(h, "CompressedFileNames", "3.0.4-1");
1253 rpmts ts = NULL; /* XXX FIXME drill rpmts ts all the way down here */
1254 rpmfi fi = rpmfiNew(ts, h, RPMTAG_BASENAMES, scareMem);
1257 if (fi == NULL) return; /* XXX can't happen */
1259 fi->te = xcalloc(1, sizeof(*fi->te));
1260 fi->te->type = TR_ADDED;
1262 fi->dnl = _free(fi->dnl);
1263 fi->bnl = _free(fi->bnl);
1264 if (!scareMem) fi->dil = _free(fi->dil);
1266 fi->dnl = xmalloc(fi->fc * sizeof(*fi->dnl) + dpathlen);
1267 d = (char *)(fi->dnl + fi->fc);
1270 fi->bnl = xmalloc(fi->fc * (sizeof(*fi->bnl) + sizeof(*fi->dil)));
1271 /* FIX: artifact of spoofing headerGetEntry */
1272 fi->dil = (!scareMem)
1273 ? xcalloc(sizeof(*fi->dil), fi->fc)
1274 : (int *)(fi->bnl + fi->fc);
1276 fi->apath = xmalloc(fi->fc * sizeof(*fi->apath) + apathlen + 1);
1277 a = (char *)(fi->apath + fi->fc);
1280 fi->actions = xcalloc(sizeof(*fi->actions), fi->fc);
1281 fi->fmapflags = xcalloc(sizeof(*fi->fmapflags), fi->fc);
1283 if (fl->buildRootURL)
1284 fi->astriplen = strlen(fl->buildRootURL);
1289 /* Make the cpio list */
1290 if (fi->dil != NULL) /* XXX can't happen */
1291 for (i = 0, flp = fl->fileList; i < fi->fc; i++, flp++) {
1294 /* Skip (possible) duplicate file entries, use last entry info. */
1295 while (((flp - fl->fileList) < (fl->fileListRecsUsed - 1)) &&
1296 !strcmp(flp->fileURL, flp[1].fileURL))
1299 if (flp->flags & RPMFILE_EXCLUDE) {
1304 if ((fnlen = strlen(flp->diskURL) + 1) > fi->fnlen)
1307 /* Create disk directory and base name. */
1309 /* FIX: artifact of spoofing headerGetEntry */
1310 fi->dnl[fi->dil[i]] = d;
1311 d = stpcpy(d, flp->diskURL);
1313 /* Make room for the dirName NUL, find start of baseName. */
1314 for (b = d; b > fi->dnl[fi->dil[i]] && *b != '/'; b--)
1316 b++; /* dirname's end in '/' */
1317 *b++ = '\0'; /* terminate dirname, b points to basename */
1319 d += 2; /* skip both dirname and basename NUL's */
1321 /* Create archive path, normally adding "./" */
1324 a = stpcpy(a, "./");
1325 a = stpcpy(a, (flp->fileURL + skipLen));
1326 a++; /* skip apath NUL */
1328 if (flp->flags & RPMFILE_GHOST) {
1329 fi->actions[i] = FA_SKIP;
1332 fi->actions[i] = FA_COPYOUT;
1333 fi->fmapflags[i] = CPIO_MAP_PATH |
1334 CPIO_MAP_TYPE | CPIO_MAP_MODE | CPIO_MAP_UID | CPIO_MAP_GID;
1336 fi->fmapflags[i] |= CPIO_FOLLOW_SYMLINKS;
1348 static FileListRec freeFileList(FileListRec fileList,
1352 fileList[count].diskURL = _free(fileList[count].diskURL);
1353 fileList[count].fileURL = _free(fileList[count].fileURL);
1354 fileList[count].langs = _free(fileList[count].langs);
1356 fileList = _free(fileList);
1361 static rpmRC recurseDir(FileList fl, const char * diskURL);
1364 * Add a file to the package manifest.
1365 * @param fl package file tree walk data
1366 * @param diskURL path to file
1367 * @param statp file stat (possibly NULL)
1368 * @return RPMRC_OK on success
1370 static rpmRC addFile(FileList fl, const char * diskURL,
1371 struct stat * statp)
1373 const char *fileURL = diskURL;
1374 struct stat statbuf;
1378 const char *fileUname;
1379 const char *fileGname;
1382 /* Path may have prepended buildRootURL, so locate the original filename. */
1384 * XXX There are 3 types of entry into addFile:
1386 * From diskUrl statp
1387 * =====================================================
1388 * processBinaryFile path NULL
1389 * processBinaryFile glob result path NULL
1393 { const char *fileName;
1394 (void) urlPath(fileURL, &fileName);
1395 if (fl->buildRootURL && strcmp(fl->buildRootURL, "/"))
1396 fileURL += strlen(fl->buildRootURL);
1399 /* XXX make sure '/' can be packaged also */
1400 if (*fileURL == '\0')
1403 /* If we are using a prefix, validate the file */
1404 if (!fl->inFtw && fl->prefix) {
1405 const char *prefixTest;
1406 const char *prefixPtr = fl->prefix;
1408 (void) urlPath(fileURL, &prefixTest);
1409 while (*prefixPtr && *prefixTest && (*prefixTest == *prefixPtr)) {
1413 if (*prefixPtr || (*prefixTest && *prefixTest != '/')) {
1414 rpmlog(RPMLOG_ERR, _("File doesn't match prefix (%s): %s\n"),
1415 fl->prefix, fileURL);
1416 fl->processingFailed = 1;
1421 if (statp == NULL) {
1423 memset(statp, 0, sizeof(*statp));
1425 time_t now = time(NULL);
1427 /* XXX hack up a stat structure for a %dev(...) directive. */
1428 statp->st_nlink = 1;
1430 ((fl->devmajor & 0xff) << 8) | (fl->devminor & 0xff);
1431 statp->st_dev = statp->st_rdev;
1432 statp->st_mode = (fl->devtype == 'b' ? S_IFBLK : S_IFCHR);
1433 statp->st_mode |= (fl->cur_ar.ar_fmode & 0777);
1434 statp->st_atime = now;
1435 statp->st_mtime = now;
1436 statp->st_ctime = now;
1437 } else if (lstat(diskURL, statp)) {
1438 rpmlog(RPMLOG_ERR, _("File not found: %s\n"), diskURL);
1439 fl->processingFailed = 1;
1444 if ((! fl->isDir) && S_ISDIR(statp->st_mode)) {
1445 /* FIX: fl->buildRootURL may be NULL */
1446 return recurseDir(fl, diskURL);
1449 fileMode = statp->st_mode;
1450 fileUid = statp->st_uid;
1451 fileGid = statp->st_gid;
1453 if (S_ISDIR(fileMode) && fl->cur_ar.ar_dmodestr) {
1455 fileMode |= fl->cur_ar.ar_dmode;
1456 } else if (fl->cur_ar.ar_fmodestr != NULL) {
1458 fileMode |= fl->cur_ar.ar_fmode;
1460 if (fl->cur_ar.ar_user) {
1461 fileUname = getUnameS(fl->cur_ar.ar_user);
1463 fileUname = getUname(fileUid);
1465 if (fl->cur_ar.ar_group) {
1466 fileGname = getGnameS(fl->cur_ar.ar_group);
1468 fileGname = getGname(fileGid);
1471 /* Default user/group to builder's user/group */
1472 if (fileUname == NULL)
1473 fileUname = getUname(getuid());
1474 if (fileGname == NULL)
1475 fileGname = getGname(getgid());
1477 /* S_XXX macro must be consistent with type in find call at check-files script */
1478 if (check_fileList && (S_ISREG(fileMode) || S_ISLNK(fileMode))) {
1479 appendStringBuf(check_fileList, diskURL);
1480 appendStringBuf(check_fileList, "\n");
1483 /* Add to the file list */
1484 if (fl->fileListRecsUsed == fl->fileListRecsAlloced) {
1485 fl->fileListRecsAlloced += 128;
1486 fl->fileList = xrealloc(fl->fileList,
1487 fl->fileListRecsAlloced * sizeof(*(fl->fileList)));
1490 { FileListRec flp = &fl->fileList[fl->fileListRecsUsed];
1493 flp->fl_st = *statp; /* structure assignment */
1494 flp->fl_mode = fileMode;
1495 flp->fl_uid = fileUid;
1496 flp->fl_gid = fileGid;
1498 flp->fileURL = xstrdup(fileURL);
1499 flp->diskURL = xstrdup(diskURL);
1500 flp->uname = fileUname;
1501 flp->gname = fileGname;
1503 if (fl->currentLangs && fl->nLangs > 0) {
1507 for (i = 0; i < fl->nLangs; i++)
1508 nl += strlen(fl->currentLangs[i]) + 1;
1510 flp->langs = ncl = xmalloc(nl);
1511 for (i = 0; i < fl->nLangs; i++) {
1513 if (i) *ncl++ = '|';
1514 for (ocl = fl->currentLangs[i]; *ocl != '\0'; ocl++)
1518 } else if (! parseForRegexLang(fileURL, &lang)) {
1519 flp->langs = xstrdup(lang);
1521 flp->langs = xstrdup("");
1524 flp->flags = fl->currentFlags;
1525 flp->specdFlags = fl->currentSpecdFlags;
1526 flp->verifyFlags = fl->currentVerifyFlags;
1528 /* Hard links need be counted only once. */
1529 if (S_ISREG(flp->fl_mode) && flp->fl_nlink > 1) {
1531 for (i = 0; i < fl->fileListRecsUsed; i++) {
1532 ilp = fl->fileList + i;
1533 if (!S_ISREG(ilp->fl_mode))
1535 if (flp->fl_nlink != ilp->fl_nlink)
1537 if (flp->fl_ino != ilp->fl_ino)
1539 if (flp->fl_dev != ilp->fl_dev)
1544 i = fl->fileListRecsUsed;
1546 if (!(flp->flags & RPMFILE_EXCLUDE) && S_ISREG(flp->fl_mode) && i >= fl->fileListRecsUsed)
1547 fl->totalFileSize += flp->fl_size;
1550 fl->fileListRecsUsed++;
1557 * Add directory (and all of its files) to the package manifest.
1558 * @param fl package file tree walk data
1559 * @param diskURL path to file
1560 * @return RPMRC_OK on success
1562 static rpmRC recurseDir(FileList fl, const char * diskURL)
1567 int myFtsOpts = (FTS_COMFOLLOW | FTS_NOCHDIR | FTS_PHYSICAL);
1568 int rc = RPMRC_FAIL;
1570 fl->inFtw = 1; /* Flag to indicate file has buildRootURL prefixed */
1571 fl->isDir = 1; /* Keep it from following myftw() again */
1573 ftsSet[0] = (char *) diskURL;
1575 ftsp = Fts_open(ftsSet, myFtsOpts, NULL);
1576 while ((fts = Fts_read(ftsp)) != NULL) {
1577 switch (fts->fts_info) {
1578 case FTS_D: /* preorder directory */
1579 case FTS_F: /* regular file */
1580 case FTS_SL: /* symbolic link */
1581 case FTS_SLNONE: /* symbolic link without target */
1582 case FTS_DEFAULT: /* none of the above */
1583 rc = addFile(fl, fts->fts_accpath, fts->fts_statp);
1585 case FTS_DOT: /* dot or dot-dot */
1586 case FTS_DP: /* postorder directory */
1589 case FTS_NS: /* stat(2) failed */
1590 case FTS_DNR: /* unreadable directory */
1591 case FTS_ERR: /* error; errno is set */
1592 case FTS_DC: /* directory that causes cycles */
1593 case FTS_NSOK: /* no stat(2) requested */
1594 case FTS_INIT: /* initialized only */
1595 case FTS_W: /* whiteout object */
1603 (void) Fts_close(ftsp);
1612 * Add a pubkey/policy/icon to a binary package.
1614 * @param fl package file tree walk data
1615 * @param fileURL path to file, relative is builddir, absolute buildroot.
1616 * @param tag tag to add
1617 * @return RPMRC_OK on success
1619 static rpmRC processMetadataFile(Package pkg, FileList fl, const char * fileURL,
1622 const char * buildURL = "%{_builddir}/%{?buildsubdir}/";
1623 const char * urlfn = NULL;
1626 uint8_t * pkt = NULL;
1629 int rc = RPMRC_FAIL;
1632 (void) urlPath(fileURL, &urlfn);
1633 if (*urlfn == '/') {
1634 fn = rpmGenPath(fl->buildRootURL, NULL, urlfn);
1637 fn = rpmGenPath(buildURL, NULL, urlfn);
1641 rpmlog(RPMLOG_ERR, _("%s: can't load unknown tag (%d).\n"),
1645 case RPMTAG_PUBKEYS: {
1646 if ((xx = pgpReadPkts(fn, &pkt, (size_t *)&pktlen)) <= 0) {
1648 rpmlog(RPMLOG_ERR, _("%s: public key read failed.\n"), fn);
1651 if (xx != PGPARMOR_PUBKEY) {
1653 rpmlog(RPMLOG_ERR, _("%s: not an armored public key.\n"), fn);
1656 apkt = pgpArmorWrap(PGPARMOR_PUBKEY, pkt, pktlen);
1659 case RPMTAG_POLICIES:
1660 if ((xx = rpmioSlurp(fn, &pkt, &pktlen)) != 0) {
1662 rpmlog(RPMLOG_ERR, _("%s: *.te policy read failed.\n"), fn);
1665 apkt = (char *) pkt; /* XXX unsigned char */
1670 xx = headerAddOrAppendEntry(pkg->header, tag,
1671 RPM_STRING_ARRAY_TYPE, &apkt, 1);
1675 rc = addFile(fl, fn, NULL);
1682 fl->processingFailed = 1;
1689 * Add a file to a binary package.
1691 * @param fl package file tree walk data
1693 * @return RPMRC_OK on success
1695 static rpmRC processBinaryFile(Package pkg, FileList fl,
1696 const char * fileURL)
1698 int quote = 1; /* XXX permit quoted glob characters. */
1700 char *diskURL = NULL;
1703 doGlob = glob_pattern_p(fileURL, quote);
1705 /* Check that file starts with leading "/" */
1706 { const char * fileName;
1707 (void) urlPath(fileURL, &fileName);
1708 if (*fileName != '/') {
1709 rpmlog(RPMLOG_ERR, _("File needs leading \"/\": %s\n"),
1716 /* Copy file name or glob pattern removing multiple "/" chars. */
1718 * Note: rpmGetPath should guarantee a "canonical" path. That means
1719 * that the following pathologies should be weeded out:
1722 * /.././../usr/../bin//./sh
1724 diskURL = rpmGenPath(fl->buildRootURL, NULL, fileURL);
1727 char ** argv = NULL;
1731 /* XXX for %dev marker in file manifest only */
1733 rpmlog(RPMLOG_ERR, _("Glob not permitted: %s\n"),
1739 rc = rpmGlob(diskURL, &argc, &argv);
1740 if (rc == 0 && argc >= 1) {
1741 for (i = 0; i < argc; i++) {
1742 rc = addFile(fl, argv[i], NULL);
1743 argv[i] = _free(argv[i]);
1747 rpmlog(RPMLOG_ERR, _("File not found by glob: %s\n"),
1753 rc = addFile(fl, diskURL, NULL);
1757 diskURL = _free(diskURL);
1759 fl->processingFailed = 1;
1767 static rpmRC processPackageFiles(rpmSpec spec, Package pkg,
1768 int installSpecialDoc, int test)
1770 HGE_t hge = (HGE_t)headerGetEntryMinMemory;
1771 struct FileList_s fl;
1772 char *s, **files, **fp;
1773 const char *fileName;
1775 struct AttrRec_s arbuf;
1776 AttrRec specialDocAttrRec = &arbuf;
1777 char *specialDoc = NULL;
1779 nullAttrRec(specialDocAttrRec);
1780 pkg->cpioList = NULL;
1782 if (pkg->fileFile) {
1787 /* XXX W2DO? urlPath might be useful here. */
1788 if (*pkg->fileFile == '/') {
1789 ffn = rpmGetPath(pkg->fileFile, NULL);
1791 /* XXX FIXME: add %{buildsubdir} */
1792 ffn = rpmGetPath("%{_builddir}/",
1793 (spec->buildSubdir ? spec->buildSubdir : "") ,
1794 "/", pkg->fileFile, NULL);
1796 fd = Fopen(ffn, "r.fpio");
1798 if (fd == NULL || Ferror(fd)) {
1800 _("Could not open %%files file %s: %s\n"),
1801 ffn, Fstrerror(fd));
1808 while (fgets(buf, sizeof(buf), f)) {
1809 handleComments(buf);
1810 if (expandMacros(spec, spec->macros, buf, sizeof(buf))) {
1811 rpmlog(RPMLOG_ERR, _("line: %s\n"), buf);
1814 appendStringBuf(pkg->fileList, buf);
1819 /* Init the file list structure */
1820 memset(&fl, 0, sizeof(fl));
1822 /* XXX spec->buildRootURL == NULL, then xstrdup("") is returned */
1823 fl.buildRootURL = rpmGenPath(spec->rootURL, spec->buildRootURL, NULL);
1825 if (hge(pkg->header, RPMTAG_DEFAULTPREFIX, NULL, (rpm_data_t *)&fl.prefix, NULL))
1826 fl.prefix = xstrdup(fl.prefix);
1831 fl.totalFileSize = 0;
1832 fl.processingFailed = 0;
1834 fl.passedSpecialDoc = 0;
1835 fl.isSpecialDoc = 0;
1839 fl.currentFlags = 0;
1840 fl.currentVerifyFlags = 0;
1847 nullAttrRec(&fl.cur_ar);
1848 nullAttrRec(&fl.def_ar);
1849 dupAttrRec(&root_ar, &fl.def_ar); /* XXX assume %defattr(-,root,root) */
1851 fl.defVerifyFlags = RPMVERIFY_ALL;
1853 fl.currentLangs = NULL;
1855 fl.currentSpecdFlags = 0;
1856 fl.defSpecdFlags = 0;
1859 fl.docDirs[fl.docDirCount++] = xstrdup("/usr/doc");
1860 fl.docDirs[fl.docDirCount++] = xstrdup("/usr/man");
1861 fl.docDirs[fl.docDirCount++] = xstrdup("/usr/info");
1862 fl.docDirs[fl.docDirCount++] = xstrdup("/usr/X11R6/man");
1863 fl.docDirs[fl.docDirCount++] = xstrdup("/usr/share/doc");
1864 fl.docDirs[fl.docDirCount++] = xstrdup("/usr/share/man");
1865 fl.docDirs[fl.docDirCount++] = xstrdup("/usr/share/info");
1866 fl.docDirs[fl.docDirCount++] = xstrdup("/usr/share/gtk-doc/html");
1867 fl.docDirs[fl.docDirCount++] = rpmGetPath("%{_docdir}", NULL);
1868 fl.docDirs[fl.docDirCount++] = rpmGetPath("%{_mandir}", NULL);
1869 fl.docDirs[fl.docDirCount++] = rpmGetPath("%{_infodir}", NULL);
1870 fl.docDirs[fl.docDirCount++] = rpmGetPath("%{_javadocdir}", NULL);
1873 fl.fileListRecsAlloced = 0;
1874 fl.fileListRecsUsed = 0;
1876 s = getStringBuf(pkg->fileList);
1877 files = splitString(s, strlen(s), '\n');
1879 for (fp = files; *fp != NULL; fp++) {
1887 /* Reset for a new line in %files */
1890 fl.currentFlags = 0;
1891 /* turn explicit flags into %def'd ones (gosh this is hacky...) */
1892 fl.currentSpecdFlags = ((unsigned)fl.defSpecdFlags) >> 8;
1893 fl.currentVerifyFlags = fl.defVerifyFlags;
1894 fl.isSpecialDoc = 0;
1901 /* XXX should reset to %deflang value */
1902 if (fl.currentLangs) {
1904 for (i = 0; i < fl.nLangs; i++)
1905 fl.currentLangs[i] = _free(fl.currentLangs[i]);
1906 fl.currentLangs = _free(fl.currentLangs);
1910 dupAttrRec(&fl.def_ar, &fl.cur_ar);
1912 if (parseForVerify(buf, &fl))
1914 if (parseForAttr(buf, &fl))
1916 if (parseForDev(buf, &fl))
1918 if (parseForConfig(buf, &fl))
1920 if (parseForLang(buf, &fl))
1922 if (parseForSimple(spec, pkg, buf, &fl, &fileName))
1924 if (fileName == NULL)
1927 if (fl.isSpecialDoc) {
1928 /* Save this stuff for last */
1929 specialDoc = _free(specialDoc);
1930 specialDoc = xstrdup(fileName);
1931 dupAttrRec(&fl.cur_ar, specialDocAttrRec);
1932 } else if (fl.currentFlags & RPMFILE_PUBKEY) {
1933 (void) processMetadataFile(pkg, &fl, fileName, RPMTAG_PUBKEYS);
1934 } else if (fl.currentFlags & RPMFILE_POLICY) {
1935 (void) processMetadataFile(pkg, &fl, fileName, RPMTAG_POLICIES);
1937 (void) processBinaryFile(pkg, &fl, fileName);
1941 /* Now process special doc, if there is one */
1943 if (installSpecialDoc) {
1944 int _missing_doc_files_terminate_build =
1945 rpmExpandNumeric("%{?_missing_doc_files_terminate_build}");
1948 rc = doScript(spec, RPMBUILD_STRINGBUF, "%doc", pkg->specialDoc, test);
1949 if (rc != RPMRC_OK && _missing_doc_files_terminate_build)
1950 fl.processingFailed = 1;
1953 /* Reset for %doc */
1956 fl.currentFlags = 0;
1957 fl.currentVerifyFlags = fl.defVerifyFlags;
1964 /* XXX should reset to %deflang value */
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);
1973 dupAttrRec(specialDocAttrRec, &fl.cur_ar);
1974 freeAttrRec(specialDocAttrRec);
1976 (void) processBinaryFile(pkg, &fl, specialDoc);
1978 specialDoc = _free(specialDoc);
1981 freeSplitString(files);
1983 if (fl.processingFailed)
1986 /* Verify that file attributes scope over hardlinks correctly. */
1987 if (checkHardLinks(&fl))
1988 (void) rpmlibNeedsFeature(pkg->header,
1989 "PartialHardlinkSets", "4.0.4-1");
1991 genCpioListAndHeader(&fl, &pkg->cpioList, pkg->header, 0);
1993 if (spec->timeCheck)
1994 timeCheck(spec->timeCheck, pkg->header);
1997 fl.buildRootURL = _free(fl.buildRootURL);
1998 fl.prefix = _free(fl.prefix);
2000 freeAttrRec(&fl.cur_ar);
2001 freeAttrRec(&fl.def_ar);
2003 if (fl.currentLangs) {
2005 for (i = 0; i < fl.nLangs; i++)
2006 fl.currentLangs[i] = _free(fl.currentLangs[i]);
2007 fl.currentLangs = _free(fl.currentLangs);
2010 fl.fileList = freeFileList(fl.fileList, fl.fileListRecsUsed);
2011 while (fl.docDirCount--)
2012 fl.docDirs[fl.docDirCount] = _free(fl.docDirs[fl.docDirCount]);
2013 return fl.processingFailed;
2016 void initSourceHeader(rpmSpec spec)
2024 spec->sourceHeader = headerNew();
2025 /* Only specific tags are added to the source package header */
2026 for (hi = headerInitIterator(spec->packages->header);
2027 headerNextIterator(hi, &tag, &type, &ptr, &count);
2028 ptr = headerFreeData(ptr, type))
2032 case RPMTAG_VERSION:
2033 case RPMTAG_RELEASE:
2035 case RPMTAG_SUMMARY:
2036 case RPMTAG_DESCRIPTION:
2037 case RPMTAG_PACKAGER:
2038 case RPMTAG_DISTRIBUTION:
2039 case RPMTAG_DISTURL:
2041 case RPMTAG_LICENSE:
2045 case RPMTAG_CHANGELOGTIME:
2046 case RPMTAG_CHANGELOGNAME:
2047 case RPMTAG_CHANGELOGTEXT:
2049 case HEADER_I18NTABLE:
2051 (void)headerAddEntry(spec->sourceHeader, tag, type, ptr, count);
2058 hi = headerFreeIterator(hi);
2060 /* Add the build restrictions */
2061 for (hi = headerInitIterator(spec->buildRestrictions);
2062 headerNextIterator(hi, &tag, &type, &ptr, &count);
2063 ptr = headerFreeData(ptr, type))
2066 (void) headerAddEntry(spec->sourceHeader, tag, type, ptr, count);
2068 hi = headerFreeIterator(hi);
2070 if (spec->BANames && spec->BACount > 0) {
2071 (void) headerAddEntry(spec->sourceHeader, RPMTAG_BUILDARCHS,
2072 RPM_STRING_ARRAY_TYPE,
2073 spec->BANames, (rpm_count_t) spec->BACount);
2077 int processSourceFiles(rpmSpec spec)
2079 struct Source *srcPtr;
2080 StringBuf sourceFiles;
2082 struct FileList_s fl;
2083 char *s, **files, **fp;
2085 static char *_srcdefattr;
2089 _srcdefattr = rpmExpand("%{?_srcdefattr}", NULL);
2090 if (_srcdefattr && !*_srcdefattr)
2091 _srcdefattr = _free(_srcdefattr);
2094 sourceFiles = newStringBuf();
2097 * XXX This is where the source header for noarch packages needs
2098 * XXX to be initialized.
2100 if (spec->sourceHeader == NULL)
2101 initSourceHeader(spec);
2103 /* Construct the file list and source entries */
2104 appendLineStringBuf(sourceFiles, spec->specFile);
2105 if (spec->sourceHeader != NULL)
2106 for (srcPtr = spec->sources; srcPtr != NULL; srcPtr = srcPtr->next) {
2107 if (srcPtr->flags & RPMBUILD_ISSOURCE) {
2108 (void) headerAddOrAppendEntry(spec->sourceHeader, RPMTAG_SOURCE,
2109 RPM_STRING_ARRAY_TYPE, &srcPtr->source, 1);
2110 if (srcPtr->flags & RPMBUILD_ISNO) {
2111 (void) headerAddOrAppendEntry(spec->sourceHeader, RPMTAG_NOSOURCE,
2112 RPM_INT32_TYPE, &srcPtr->num, 1);
2115 if (srcPtr->flags & RPMBUILD_ISPATCH) {
2116 (void) headerAddOrAppendEntry(spec->sourceHeader, RPMTAG_PATCH,
2117 RPM_STRING_ARRAY_TYPE, &srcPtr->source, 1);
2118 if (srcPtr->flags & RPMBUILD_ISNO) {
2119 (void) headerAddOrAppendEntry(spec->sourceHeader, RPMTAG_NOPATCH,
2120 RPM_INT32_TYPE, &srcPtr->num, 1);
2125 sfn = rpmGetPath( ((srcPtr->flags & RPMBUILD_ISNO) ? "!" : ""),
2126 "%{_sourcedir}/", srcPtr->source, NULL);
2127 appendLineStringBuf(sourceFiles, sfn);
2132 for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
2133 for (srcPtr = pkg->icon; srcPtr != NULL; srcPtr = srcPtr->next) {
2135 sfn = rpmGetPath( ((srcPtr->flags & RPMBUILD_ISNO) ? "!" : ""),
2136 "%{_sourcedir}/", srcPtr->source, NULL);
2137 appendLineStringBuf(sourceFiles, sfn);
2142 spec->sourceCpioList = NULL;
2144 /* Init the file list structure */
2145 memset(&fl, 0, sizeof(fl));
2147 char *a = xmalloc(strlen(_srcdefattr) + 9 + 1);
2148 strcpy(a, "%defattr ");
2149 strcpy(a + 9, _srcdefattr);
2150 parseForAttr(a, &fl);
2153 fl.fileList = xcalloc((spec->numSources + 1), sizeof(*fl.fileList));
2154 fl.processingFailed = 0;
2155 fl.fileListRecsUsed = 0;
2156 fl.totalFileSize = 0;
2158 fl.buildRootURL = NULL;
2160 s = getStringBuf(sourceFiles);
2161 files = splitString(s, strlen(s), '\n');
2163 /* The first source file is the spec file */
2165 for (fp = files; *fp != NULL; fp++) {
2166 const char * diskURL, *diskPath;
2174 flp = &fl.fileList[x];
2176 flp->flags = isSpec ? RPMFILE_SPECFILE : 0;
2177 /* files with leading ! are no source files */
2178 if (*diskURL == '!') {
2179 flp->flags |= RPMFILE_GHOST;
2183 (void) urlPath(diskURL, &diskPath);
2185 flp->diskURL = xstrdup(diskURL);
2186 diskPath = strrchr(diskPath, '/');
2192 flp->fileURL = xstrdup(diskPath);
2193 flp->verifyFlags = RPMVERIFY_ALL;
2195 if (stat(diskURL, &flp->fl_st)) {
2196 rpmlog(RPMLOG_ERR, _("Bad file: %s: %s\n"),
2197 diskURL, strerror(errno));
2198 fl.processingFailed = 1;
2201 if (fl.def_ar.ar_fmodestr) {
2202 flp->fl_mode &= S_IFMT;
2203 flp->fl_mode |= fl.def_ar.ar_fmode;
2205 if (fl.def_ar.ar_user) {
2206 flp->uname = getUnameS(fl.def_ar.ar_user);
2208 flp->uname = getUname(flp->fl_uid);
2210 if (fl.def_ar.ar_group) {
2211 flp->gname = getGnameS(fl.def_ar.ar_group);
2213 flp->gname = getGname(flp->fl_gid);
2215 flp->langs = xstrdup("");
2217 fl.totalFileSize += flp->fl_size;
2219 if (! (flp->uname && flp->gname)) {
2220 rpmlog(RPMLOG_ERR, _("Bad owner/group: %s\n"), diskURL);
2221 fl.processingFailed = 1;
2227 fl.fileListRecsUsed = x;
2228 freeSplitString(files);
2230 if (! fl.processingFailed) {
2231 if (spec->sourceHeader != NULL)
2232 genCpioListAndHeader(&fl, &spec->sourceCpioList,
2233 spec->sourceHeader, 1);
2236 sourceFiles = freeStringBuf(sourceFiles);
2237 fl.fileList = freeFileList(fl.fileList, fl.fileListRecsUsed);
2238 freeAttrRec(&fl.def_ar);
2239 return fl.processingFailed;
2243 * Check packaged file list against what's in the build root.
2244 * @param fileList packaged file list
2245 * @return -1 if skipped, 0 on OK, 1 on error
2247 static int checkFiles(StringBuf fileList)
2249 static const char * av_ckfile[] = { "%{?__check_files}", NULL };
2250 StringBuf sb_stdout = NULL;
2254 s = rpmExpand(av_ckfile[0], NULL);
2261 rpmlog(RPMLOG_NOTICE, _("Checking for unpackaged file(s): %s\n"), s);
2263 rc = rpmfcExec(av_ckfile, fileList, &sb_stdout, 0);
2268 int _unpackaged_files_terminate_build =
2269 rpmExpandNumeric("%{?_unpackaged_files_terminate_build}");
2272 t = getStringBuf(sb_stdout);
2273 if ((*t != '\0') && (*t != '\n')) {
2274 rc = (_unpackaged_files_terminate_build) ? 1 : 0;
2275 rpmlog((rc ? RPMLOG_ERR : RPMLOG_WARNING),
2276 _("Installed (but unpackaged) file(s) found:\n%s"), t);
2281 sb_stdout = freeStringBuf(sb_stdout);
2286 int processBinaryFiles(rpmSpec spec, int installSpecialDoc, int test)
2291 check_fileList = newStringBuf();
2293 for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
2294 const char *n, *v, *r;
2297 if (pkg->fileList == NULL)
2300 (void) headerNVR(pkg->header, &n, &v, &r);
2301 rpmlog(RPMLOG_NOTICE, _("Processing files: %s-%s-%s\n"), n, v, r);
2303 if ((rc = processPackageFiles(spec, pkg, installSpecialDoc, test)))
2306 if ((rc = rpmfcGenerateDepends(spec, pkg)))
2310 /* Now we have in fileList list of files from all packages.
2311 * We pass it to a script which does the work of finding missing
2312 * and duplicated files.
2316 if (checkFiles(check_fileList) > 0) {
2321 check_fileList = freeStringBuf(check_fileList);