summaryrefslogtreecommitdiff
path: root/build/files.c
diff options
context:
space:
mode:
Diffstat (limited to 'build/files.c')
-rw-r--r--build/files.c2171
1 files changed, 2171 insertions, 0 deletions
diff --git a/build/files.c b/build/files.c
new file mode 100644
index 0000000..b4b893a
--- /dev/null
+++ b/build/files.c
@@ -0,0 +1,2171 @@
+/** \ingroup rpmbuild
+ * \file build/files.c
+ * The post-build, pre-packaging file tree walk to assemble the package
+ * manifest.
+ */
+
+#include "system.h"
+
+#define MYALLPERMS 07777
+
+#include <errno.h>
+#include <regex.h>
+#if WITH_CAP
+#include <sys/capability.h>
+#endif
+
+#include <rpm/rpmpgp.h>
+#include <rpm/argv.h>
+#include <rpm/rpmfc.h>
+#include <rpm/rpmfileutil.h> /* rpmDoDigest() */
+#include <rpm/rpmlog.h>
+
+#include "rpmio/rpmio_internal.h" /* XXX rpmioSlurp */
+#include "rpmio/base64.h"
+#include "misc/fts.h"
+#include "lib/cpio.h"
+#include "lib/rpmfi_internal.h" /* XXX fi->apath */
+#include "lib/rpmug.h"
+#include "build/rpmbuild_internal.h"
+#include "build/rpmbuild_misc.h"
+
+#include "debug.h"
+#include <libgen.h>
+
+#define SKIPSPACE(s) { while (*(s) && risspace(*(s))) (s)++; }
+#define SKIPWHITE(_x) {while(*(_x) && (risspace(*_x) || *(_x) == ',')) (_x)++;}
+#define SKIPNONWHITE(_x){while(*(_x) &&!(risspace(*_x) || *(_x) == ',')) (_x)++;}
+
+/**
+ */
+enum specfFlags_e {
+ SPECD_DEFFILEMODE = (1 << 0),
+ SPECD_DEFDIRMODE = (1 << 1),
+ SPECD_DEFUID = (1 << 2),
+ SPECD_DEFGID = (1 << 3),
+ SPECD_DEFVERIFY = (1 << 4),
+
+ SPECD_FILEMODE = (1 << 8),
+ SPECD_DIRMODE = (1 << 9),
+ SPECD_UID = (1 << 10),
+ SPECD_GID = (1 << 11),
+ SPECD_VERIFY = (1 << 12)
+};
+
+typedef rpmFlags specfFlags;
+
+/**
+ */
+typedef struct FileListRec_s {
+ struct stat fl_st;
+#define fl_dev fl_st.st_dev
+#define fl_ino fl_st.st_ino
+#define fl_mode fl_st.st_mode
+#define fl_nlink fl_st.st_nlink
+#define fl_uid fl_st.st_uid
+#define fl_gid fl_st.st_gid
+#define fl_rdev fl_st.st_rdev
+#define fl_size fl_st.st_size
+#define fl_mtime fl_st.st_mtime
+
+ char *diskPath; /* get file from here */
+ char *cpioPath; /* filename in cpio archive */
+ const char *uname;
+ const char *gname;
+ unsigned flags;
+ specfFlags specdFlags; /* which attributes have been explicitly specified. */
+ rpmVerifyFlags verifyFlags;
+ char *langs; /* XXX locales separated with | */
+ char *caps;
+} * FileListRec;
+
+/**
+ */
+typedef struct AttrRec_s {
+ char *ar_fmodestr;
+ char *ar_dmodestr;
+ char *ar_user;
+ char *ar_group;
+ mode_t ar_fmode;
+ mode_t ar_dmode;
+} * AttrRec;
+
+static struct AttrRec_s root_ar = { NULL, NULL, "root", "root", 0, 0 };
+
+/* list of files */
+static StringBuf check_fileList = NULL;
+
+/**
+ * Package file tree walk data.
+ */
+typedef struct FileList_s {
+ char * buildRoot;
+
+ int fileCount;
+ int processingFailed;
+
+ int passedSpecialDoc;
+ int isSpecialDoc;
+
+ int noGlob;
+ unsigned devtype;
+ unsigned devmajor;
+ int devminor;
+
+ int isDir;
+ rpmBuildPkgFlags pkgFlags;
+ rpmfileAttrs currentFlags;
+ specfFlags currentSpecdFlags;
+ rpmVerifyFlags currentVerifyFlags;
+ struct AttrRec_s cur_ar;
+ struct AttrRec_s def_ar;
+ specfFlags defSpecdFlags;
+ rpmVerifyFlags defVerifyFlags;
+ int nLangs;
+ char ** currentLangs;
+ int haveCaps;
+ char *currentCaps;
+ ARGV_t docDirs;
+
+ FileListRec fileList;
+ int fileListRecsAlloced;
+ int fileListRecsUsed;
+ int largeFiles;
+} * FileList;
+
+/**
+ */
+static void nullAttrRec(AttrRec ar)
+{
+ ar->ar_fmodestr = NULL;
+ ar->ar_dmodestr = NULL;
+ ar->ar_user = NULL;
+ ar->ar_group = NULL;
+ ar->ar_fmode = 0;
+ ar->ar_dmode = 0;
+}
+
+/**
+ */
+static void freeAttrRec(AttrRec ar)
+{
+ ar->ar_fmodestr = _free(ar->ar_fmodestr);
+ ar->ar_dmodestr = _free(ar->ar_dmodestr);
+ ar->ar_user = _free(ar->ar_user);
+ ar->ar_group = _free(ar->ar_group);
+ /* XXX doesn't free ar (yet) */
+ return;
+}
+
+/**
+ */
+static void dupAttrRec(const AttrRec oar, AttrRec nar)
+{
+ if (oar == nar)
+ return;
+ freeAttrRec(nar);
+ nar->ar_fmodestr = (oar->ar_fmodestr ? xstrdup(oar->ar_fmodestr) : NULL);
+ nar->ar_dmodestr = (oar->ar_dmodestr ? xstrdup(oar->ar_dmodestr) : NULL);
+ nar->ar_user = (oar->ar_user ? xstrdup(oar->ar_user) : NULL);
+ nar->ar_group = (oar->ar_group ? xstrdup(oar->ar_group) : NULL);
+ nar->ar_fmode = oar->ar_fmode;
+ nar->ar_dmode = oar->ar_dmode;
+}
+
+#if 0
+/**
+ */
+static void dumpAttrRec(const char * msg, AttrRec ar)
+{
+ if (msg)
+ fprintf(stderr, "%s:\t", msg);
+ fprintf(stderr, "(%s, %s, %s, %s)\n",
+ ar->ar_fmodestr,
+ ar->ar_user,
+ ar->ar_group,
+ ar->ar_dmodestr);
+}
+#endif
+
+/**
+ * strtokWithQuotes.
+ * @param s
+ * @param delim
+ */
+static char *strtokWithQuotes(char *s, const char *delim)
+{
+ static char *olds = NULL;
+ char *token;
+
+ if (s == NULL)
+ s = olds;
+ if (s == NULL)
+ return NULL;
+
+ /* Skip leading delimiters */
+ s += strspn(s, delim);
+ if (*s == '\0')
+ return NULL;
+
+ /* Find the end of the token. */
+ token = s;
+ if (*token == '"') {
+ token++;
+ /* Find next " char */
+ s = strchr(token, '"');
+ } else {
+ s = strpbrk(token, delim);
+ }
+
+ /* Terminate it */
+ if (s == NULL) {
+ /* This token finishes the string */
+ olds = strchr(token, '\0');
+ } else {
+ /* Terminate the token and make olds point past it */
+ *s = '\0';
+ olds = s+1;
+ }
+
+ return token;
+}
+
+/**
+ */
+typedef const struct VFA {
+ const char * attribute;
+ int neg; /* XXX unused */
+ int flag;
+} VFA_t;
+
+/**
+ */
+static VFA_t const verifyAttrs[] = {
+ { "md5", 0, RPMVERIFY_FILEDIGEST },
+ { "filedigest", 0, RPMVERIFY_FILEDIGEST },
+ { "size", 0, RPMVERIFY_FILESIZE },
+ { "link", 0, RPMVERIFY_LINKTO },
+ { "user", 0, RPMVERIFY_USER },
+ { "group", 0, RPMVERIFY_GROUP },
+ { "mtime", 0, RPMVERIFY_MTIME },
+ { "mode", 0, RPMVERIFY_MODE },
+ { "rdev", 0, RPMVERIFY_RDEV },
+ { "caps", 0, RPMVERIFY_CAPS },
+ { NULL, 0, 0 }
+};
+
+/**
+ * Parse %verify and %defverify from file manifest.
+ * @param buf current spec file line
+ * @param fl package file tree walk data
+ * @return RPMRC_OK on success
+ */
+static rpmRC parseForVerify(char * buf, FileList fl)
+{
+ char *p, *pe, *q = NULL;
+ const char *name;
+ rpmVerifyFlags *resultVerify;
+ int negated;
+ rpmVerifyFlags verifyFlags;
+ specfFlags * specdFlags;
+ rpmRC rc = RPMRC_FAIL;
+
+ if ((p = strstr(buf, (name = "%verify"))) != NULL) {
+ resultVerify = &(fl->currentVerifyFlags);
+ specdFlags = &fl->currentSpecdFlags;
+ } else if ((p = strstr(buf, (name = "%defverify"))) != NULL) {
+ resultVerify = &(fl->defVerifyFlags);
+ specdFlags = &fl->defSpecdFlags;
+ } else
+ return RPMRC_OK;
+
+ for (pe = p; (pe-p) < strlen(name); pe++)
+ *pe = ' ';
+
+ SKIPSPACE(pe);
+
+ if (*pe != '(') {
+ rpmlog(RPMLOG_ERR, _("Missing '(' in %s %s\n"), name, pe);
+ goto exit;
+ }
+
+ /* Bracket %*verify args */
+ *pe++ = ' ';
+ for (p = pe; *pe && *pe != ')'; pe++)
+ {};
+
+ if (*pe == '\0') {
+ rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
+ goto exit;
+ }
+
+ /* Localize. Erase parsed string */
+ q = xmalloc((pe-p) + 1);
+ rstrlcpy(q, p, (pe-p) + 1);
+ while (p <= pe)
+ *p++ = ' ';
+
+ negated = 0;
+ verifyFlags = RPMVERIFY_NONE;
+
+ for (p = q; *p != '\0'; p = pe) {
+ SKIPWHITE(p);
+ if (*p == '\0')
+ break;
+ pe = p;
+ SKIPNONWHITE(pe);
+ if (*pe != '\0')
+ *pe++ = '\0';
+
+ { VFA_t *vfa;
+ for (vfa = verifyAttrs; vfa->attribute != NULL; vfa++) {
+ if (!rstreq(p, vfa->attribute))
+ continue;
+ verifyFlags |= vfa->flag;
+ break;
+ }
+ if (vfa->attribute)
+ continue;
+ }
+
+ if (rstreq(p, "not")) {
+ negated ^= 1;
+ } else {
+ rpmlog(RPMLOG_ERR, _("Invalid %s token: %s\n"), name, p);
+ goto exit;
+ }
+ }
+
+ *resultVerify = negated ? ~(verifyFlags) : verifyFlags;
+ *specdFlags |= SPECD_VERIFY;
+ rc = RPMRC_OK;
+
+exit:
+ free(q);
+ if (rc != RPMRC_OK) {
+ fl->processingFailed = 1;
+ }
+
+ return rc;
+}
+
+#define isAttrDefault(_ars) ((_ars)[0] == '-' && (_ars)[1] == '\0')
+
+/**
+ * Parse %dev from file manifest.
+ * @param buf current spec file line
+ * @param fl package file tree walk data
+ * @return RPMRC_OK on success
+ */
+static rpmRC parseForDev(char * buf, FileList fl)
+{
+ const char * name;
+ const char * errstr = NULL;
+ char *p, *pe, *q = NULL;
+ rpmRC rc = RPMRC_FAIL; /* assume error */
+
+ if ((p = strstr(buf, (name = "%dev"))) == NULL)
+ return RPMRC_OK;
+
+ for (pe = p; (pe-p) < strlen(name); pe++)
+ *pe = ' ';
+ SKIPSPACE(pe);
+
+ if (*pe != '(') {
+ errstr = "'('";
+ goto exit;
+ }
+
+ /* Bracket %dev args */
+ *pe++ = ' ';
+ for (p = pe; *pe && *pe != ')'; pe++)
+ {};
+ if (*pe != ')') {
+ errstr = "')'";
+ goto exit;
+ }
+
+ /* Localize. Erase parsed string */
+ q = xmalloc((pe-p) + 1);
+ rstrlcpy(q, p, (pe-p) + 1);
+ while (p <= pe)
+ *p++ = ' ';
+
+ p = q; SKIPWHITE(p);
+ pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
+ if (*p == 'b')
+ fl->devtype = 'b';
+ else if (*p == 'c')
+ fl->devtype = 'c';
+ else {
+ errstr = "devtype";
+ goto exit;
+ }
+
+ p = pe; SKIPWHITE(p);
+ pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
+ for (pe = p; *pe && risdigit(*pe); pe++)
+ {} ;
+ if (*pe == '\0') {
+ fl->devmajor = atoi(p);
+ if (!(fl->devmajor >= 0 && fl->devmajor < 256)) {
+ errstr = "devmajor";
+ goto exit;
+ }
+ pe++;
+ } else {
+ errstr = "devmajor";
+ goto exit;
+ }
+
+ p = pe; SKIPWHITE(p);
+ pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
+ for (pe = p; *pe && risdigit(*pe); pe++)
+ {} ;
+ if (*pe == '\0') {
+ fl->devminor = atoi(p);
+ if (!(fl->devminor >= 0 && fl->devminor < 256)) {
+ errstr = "devminor";
+ goto exit;
+ }
+ pe++;
+ } else {
+ errstr = "devminor";
+ goto exit;
+ }
+
+ fl->noGlob = 1;
+
+ rc = RPMRC_OK;
+
+exit:
+ if (rc) {
+ rpmlog(RPMLOG_ERR, _("Missing %s in %s %s\n"), errstr, name, p);
+ fl->processingFailed = 1;
+ }
+ free(q);
+ return rc;
+}
+
+/**
+ * Parse %attr and %defattr from file manifest.
+ * @param buf current spec file line
+ * @param fl package file tree walk data
+ * @return 0 on success
+ */
+static rpmRC parseForAttr(char * buf, FileList fl)
+{
+ const char *name;
+ char *p, *pe, *q = NULL;
+ int x;
+ struct AttrRec_s arbuf;
+ AttrRec ar = &arbuf, ret_ar;
+ specfFlags * specdFlags;
+ rpmRC rc = RPMRC_FAIL;
+
+ if ((p = strstr(buf, (name = "%attr"))) != NULL) {
+ ret_ar = &(fl->cur_ar);
+ specdFlags = &fl->currentSpecdFlags;
+ } else if ((p = strstr(buf, (name = "%defattr"))) != NULL) {
+ ret_ar = &(fl->def_ar);
+ specdFlags = &fl->defSpecdFlags;
+ } else
+ return RPMRC_OK;
+
+ for (pe = p; (pe-p) < strlen(name); pe++)
+ *pe = ' ';
+
+ SKIPSPACE(pe);
+
+ if (*pe != '(') {
+ rpmlog(RPMLOG_ERR, _("Missing '(' in %s %s\n"), name, pe);
+ goto exit;
+ }
+
+ /* Bracket %*attr args */
+ *pe++ = ' ';
+ for (p = pe; *pe && *pe != ')'; pe++)
+ {};
+
+ if (ret_ar == &(fl->def_ar)) { /* %defattr */
+ char *r = pe;
+ r++;
+ SKIPSPACE(r);
+ if (*r != '\0') {
+ rpmlog(RPMLOG_ERR,
+ _("Non-white space follows %s(): %s\n"), name, r);
+ goto exit;
+ }
+ }
+
+ /* Localize. Erase parsed string */
+ q = xmalloc((pe-p) + 1);
+ rstrlcpy(q, p, (pe-p) + 1);
+ while (p <= pe)
+ *p++ = ' ';
+
+ nullAttrRec(ar);
+
+ p = q; SKIPWHITE(p);
+ if (*p != '\0') {
+ pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
+ ar->ar_fmodestr = p;
+ p = pe; SKIPWHITE(p);
+ }
+ if (*p != '\0') {
+ pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
+ ar->ar_user = p;
+ p = pe; SKIPWHITE(p);
+ }
+ if (*p != '\0') {
+ pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
+ ar->ar_group = p;
+ p = pe; SKIPWHITE(p);
+ }
+ if (*p != '\0' && ret_ar == &(fl->def_ar)) { /* %defattr */
+ pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
+ ar->ar_dmodestr = p;
+ p = pe; SKIPWHITE(p);
+ }
+
+ if (!(ar->ar_fmodestr && ar->ar_user && ar->ar_group) || *p != '\0') {
+ rpmlog(RPMLOG_ERR, _("Bad syntax: %s(%s)\n"), name, q);
+ goto exit;
+ }
+
+ /* Do a quick test on the mode argument and adjust for "-" */
+ if (ar->ar_fmodestr && !isAttrDefault(ar->ar_fmodestr)) {
+ unsigned int ui;
+ x = sscanf(ar->ar_fmodestr, "%o", &ui);
+ if ((x == 0) || (ar->ar_fmode & ~MYALLPERMS)) {
+ rpmlog(RPMLOG_ERR, _("Bad mode spec: %s(%s)\n"), name, q);
+ goto exit;
+ }
+ ar->ar_fmode = ui;
+ } else {
+ ar->ar_fmodestr = NULL;
+ }
+
+ if (ar->ar_dmodestr && !isAttrDefault(ar->ar_dmodestr)) {
+ unsigned int ui;
+ x = sscanf(ar->ar_dmodestr, "%o", &ui);
+ if ((x == 0) || (ar->ar_dmode & ~MYALLPERMS)) {
+ rpmlog(RPMLOG_ERR, _("Bad dirmode spec: %s(%s)\n"), name, q);
+ goto exit;
+ }
+ ar->ar_dmode = ui;
+ } else {
+ ar->ar_dmodestr = NULL;
+ }
+
+ if (!(ar->ar_user && !isAttrDefault(ar->ar_user))) {
+ ar->ar_user = NULL;
+ }
+
+ if (!(ar->ar_group && !isAttrDefault(ar->ar_group))) {
+ ar->ar_group = NULL;
+ }
+
+ dupAttrRec(ar, ret_ar);
+
+ /* XXX fix all this */
+ *specdFlags |= SPECD_UID | SPECD_GID | SPECD_FILEMODE | SPECD_DIRMODE;
+ rc = RPMRC_OK;
+
+exit:
+ free(q);
+ if (rc != RPMRC_OK) {
+ fl->processingFailed = 1;
+ }
+
+ return rc;
+}
+
+/**
+ * Parse %config from file manifest.
+ * @param buf current spec file line
+ * @param fl package file tree walk data
+ * @return RPMRC_OK on success
+ */
+static rpmRC parseForConfig(char * buf, FileList fl)
+{
+ char *p, *pe, *q = NULL;
+ const char *name;
+ rpmRC rc = RPMRC_FAIL;
+
+ if ((p = strstr(buf, (name = "%config"))) == NULL)
+ return RPMRC_OK;
+
+ fl->currentFlags |= RPMFILE_CONFIG;
+
+ /* Erase "%config" token. */
+ for (pe = p; (pe-p) < strlen(name); pe++)
+ *pe = ' ';
+ SKIPSPACE(pe);
+ if (*pe != '(')
+ return RPMRC_OK;
+
+ /* Bracket %config args */
+ *pe++ = ' ';
+ for (p = pe; *pe && *pe != ')'; pe++)
+ {};
+
+ if (*pe == '\0') {
+ rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
+ goto exit;
+ }
+
+ /* Localize. Erase parsed string. */
+ q = xmalloc((pe-p) + 1);
+ rstrlcpy(q, p, (pe-p) + 1);
+ while (p <= pe)
+ *p++ = ' ';
+
+ for (p = q; *p != '\0'; p = pe) {
+ SKIPWHITE(p);
+ if (*p == '\0')
+ break;
+ pe = p;
+ SKIPNONWHITE(pe);
+ if (*pe != '\0')
+ *pe++ = '\0';
+ if (rstreq(p, "missingok")) {
+ fl->currentFlags |= RPMFILE_MISSINGOK;
+ } else if (rstreq(p, "noreplace")) {
+ fl->currentFlags |= RPMFILE_NOREPLACE;
+ } else {
+ rpmlog(RPMLOG_ERR, _("Invalid %s token: %s\n"), name, p);
+ goto exit;
+ }
+ }
+ rc = RPMRC_OK;
+
+exit:
+ free(q);
+ if (rc != RPMRC_OK) {
+ fl->processingFailed = 1;
+ }
+
+ return rc;
+}
+
+/**
+ */
+static int langCmp(const void * ap, const void * bp)
+{
+ return strcmp(*(const char **)ap, *(const char **)bp);
+}
+
+/**
+ * Parse %lang from file manifest.
+ * @param buf current spec file line
+ * @param fl package file tree walk data
+ * @return RPMRC_OK on success
+ */
+static rpmRC parseForLang(char * buf, FileList fl)
+{
+ char *p, *pe, *q = NULL;
+ const char *name;
+ rpmRC rc = RPMRC_FAIL;
+
+ while ((p = strstr(buf, (name = "%lang"))) != NULL) {
+
+ for (pe = p; (pe-p) < strlen(name); pe++)
+ *pe = ' ';
+ SKIPSPACE(pe);
+
+ if (*pe != '(') {
+ rpmlog(RPMLOG_ERR, _("Missing '(' in %s %s\n"), name, pe);
+ goto exit;
+ }
+
+ /* Bracket %lang args */
+ *pe++ = ' ';
+ for (pe = p; *pe && *pe != ')'; pe++)
+ {};
+
+ if (*pe == '\0') {
+ rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
+ goto exit;
+ }
+
+ /* Localize. Erase parsed string. */
+ q = xmalloc((pe-p) + 1);
+ rstrlcpy(q, p, (pe-p) + 1);
+ while (p <= pe)
+ *p++ = ' ';
+
+ /* Parse multiple arguments from %lang */
+ for (p = q; *p != '\0'; p = pe) {
+ char *newp;
+ size_t np;
+ int i;
+
+ SKIPWHITE(p);
+ pe = p;
+ SKIPNONWHITE(pe);
+
+ np = pe - p;
+
+ /* Sanity check on locale lengths */
+ if (np < 1 || (np == 1 && *p != 'C') || np >= 32) {
+ rpmlog(RPMLOG_ERR,
+ _("Unusual locale length: \"%.*s\" in %%lang(%s)\n"),
+ (int)np, p, q);
+ goto exit;
+ }
+
+ /* Check for duplicate locales */
+ if (fl->currentLangs != NULL)
+ for (i = 0; i < fl->nLangs; i++) {
+ if (!rstreqn(fl->currentLangs[i], p, np))
+ continue;
+ rpmlog(RPMLOG_ERR, _("Duplicate locale %.*s in %%lang(%s)\n"),
+ (int)np, p, q);
+ goto exit;
+ }
+
+ /* Add new locale */
+ fl->currentLangs = xrealloc(fl->currentLangs,
+ (fl->nLangs + 1) * sizeof(*fl->currentLangs));
+ newp = xmalloc( np+1 );
+ rstrlcpy(newp, p, np + 1);
+ fl->currentLangs[fl->nLangs++] = newp;
+ if (*pe == ',') pe++; /* skip , if present */
+ }
+ }
+
+ /* Insure that locales are sorted. */
+ if (fl->currentLangs)
+ qsort(fl->currentLangs, fl->nLangs, sizeof(*fl->currentLangs), langCmp);
+ rc = RPMRC_OK;
+
+exit:
+ free(q);
+ if (rc != RPMRC_OK) {
+ fl->processingFailed = 1;
+ }
+
+ return rc;
+}
+
+/**
+ * Parse %caps from file manifest.
+ * @param buf current spec file line
+ * @param fl package file tree walk data
+ * @return RPMRC_OK on success
+ */
+static rpmRC parseForCaps(char * buf, FileList fl)
+{
+ char *p, *pe, *q = NULL;
+ const char *name;
+ rpmRC rc = RPMRC_FAIL;
+
+ if ((p = strstr(buf, (name = "%caps"))) == NULL)
+ return RPMRC_OK;
+
+ /* Erase "%caps" token. */
+ for (pe = p; (pe-p) < strlen(name); pe++)
+ *pe = ' ';
+ SKIPSPACE(pe);
+ if (*pe != '(')
+ return RPMRC_OK;
+
+ /* Bracket %caps args */
+ *pe++ = ' ';
+ for (p = pe; *pe && *pe != ')'; pe++)
+ {};
+
+ if (*pe == '\0') {
+ rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
+ goto exit;
+ }
+
+ /* Localize. Erase parsed string. */
+ q = xmalloc((pe-p) + 1);
+ rstrlcpy(q, p, (pe-p) + 1);
+ while (p <= pe)
+ *p++ = ' ';
+
+#if WITH_CAP
+ {
+ char *captxt = NULL;
+ cap_t fcaps = cap_from_text(q);
+ if (fcaps == NULL) {
+ rpmlog(RPMLOG_ERR, _("Invalid capability: %s\n"), q);
+ goto exit;
+ }
+ /* run our string through cap_to_text() to get libcap presentation */
+ captxt = cap_to_text(fcaps, NULL);
+ fl->currentCaps = xstrdup(captxt);
+ fl->haveCaps = 1;
+ cap_free(captxt);
+ cap_free(fcaps);
+ }
+#else
+ rpmlog(RPMLOG_ERR, _("File capability support not built in\n"));
+ goto exit;
+#endif
+
+ rc = RPMRC_OK;
+
+exit:
+ free(q);
+ if (rc != RPMRC_OK) {
+ fl->processingFailed = 1;
+ }
+
+ return rc;
+}
+/**
+ */
+static VFA_t virtualFileAttributes[] = {
+ { "%dir", 0, 0 }, /* XXX why not RPMFILE_DIR? */
+ { "%doc", 0, RPMFILE_DOC },
+ { "%ghost", 0, RPMFILE_GHOST },
+ { "%exclude", 0, RPMFILE_EXCLUDE },
+ { "%readme", 0, RPMFILE_README },
+ { "%license", 0, RPMFILE_LICENSE },
+ { "%pubkey", 0, RPMFILE_PUBKEY },
+ { NULL, 0, 0 }
+};
+
+/**
+ * Parse simple attributes (e.g. %dir) from file manifest.
+ * @param spec
+ * @param pkg
+ * @param buf current spec file line
+ * @param fl package file tree walk data
+ * @retval *fileName file name
+ * @return RPMRC_OK on success
+ */
+static rpmRC parseForSimple(rpmSpec spec, Package pkg, char * buf,
+ FileList fl, const char ** fileName)
+{
+ char *s, *t;
+ rpmRC res;
+ char *specialDocBuf = NULL;
+
+ *fileName = NULL;
+ res = RPMRC_OK;
+
+ t = buf;
+ while ((s = strtokWithQuotes(t, " \t\n")) != NULL) {
+ VFA_t *vfa;
+ t = NULL;
+ if (rstreq(s, "%docdir")) {
+ s = strtokWithQuotes(NULL, " \t\n");
+
+ if (s == NULL || strtokWithQuotes(NULL, " \t\n")) {
+ rpmlog(RPMLOG_ERR, _("Only one arg for %%docdir\n"));
+ res = RPMRC_FAIL;
+ } else {
+ argvAdd(&(fl->docDirs), s);
+ }
+ break;
+ }
+
+ /* Set flags for virtual file attributes */
+ for (vfa = virtualFileAttributes; vfa->attribute != NULL; vfa++) {
+ if (!rstreq(s, vfa->attribute))
+ continue;
+ if (!vfa->flag) {
+ if (rstreq(s, "%dir"))
+ fl->isDir = 1; /* XXX why not RPMFILE_DIR? */
+ } else {
+ if (vfa->neg)
+ fl->currentFlags &= ~vfa->flag;
+ else
+ fl->currentFlags |= vfa->flag;
+ }
+ break;
+ }
+ /* if we got an attribute, continue with next token */
+ if (vfa->attribute != NULL)
+ continue;
+
+ if (*fileName) {
+ /* We already got a file -- error */
+ rpmlog(RPMLOG_ERR, _("Two files on one line: %s\n"), *fileName);
+ res = RPMRC_FAIL;
+ }
+
+ if (*s != '/') {
+ if (fl->currentFlags & RPMFILE_DOC) {
+ rstrscat(&specialDocBuf, " ", s, NULL);
+ } else
+ if (fl->currentFlags & RPMFILE_PUBKEY)
+ {
+ *fileName = s;
+ } else {
+ /* not in %doc, does not begin with / -- error */
+ rpmlog(RPMLOG_ERR, _("File must begin with \"/\": %s\n"), s);
+ res = RPMRC_FAIL;
+ }
+ } else {
+ *fileName = s;
+ }
+ }
+
+ if (specialDocBuf) {
+ if (*fileName || (fl->currentFlags & ~(RPMFILE_DOC))) {
+ rpmlog(RPMLOG_ERR,
+ _("Can't mix special %%doc with other forms: %s\n"),
+ (*fileName ? *fileName : ""));
+ res = RPMRC_FAIL;
+ } else {
+ /* XXX FIXME: this is easy to do as macro expansion */
+ if (! fl->passedSpecialDoc) {
+ char *mkdocdir = rpmExpand("%{__mkdir_p} $DOCDIR", NULL);
+ pkg->specialDoc = newStringBuf();
+ appendStringBuf(pkg->specialDoc, "DOCDIR=$RPM_BUILD_ROOT");
+ appendLineStringBuf(pkg->specialDoc, pkg->specialDocDir);
+ appendLineStringBuf(pkg->specialDoc, "export DOCDIR");
+ appendLineStringBuf(pkg->specialDoc, mkdocdir);
+ free(mkdocdir);
+
+ *fileName = pkg->specialDocDir;
+ fl->passedSpecialDoc = 1;
+ fl->isSpecialDoc = 1;
+ }
+
+ appendStringBuf(pkg->specialDoc, "cp -pr ");
+ appendStringBuf(pkg->specialDoc, specialDocBuf);
+ appendLineStringBuf(pkg->specialDoc, " $DOCDIR");
+ }
+ free(specialDocBuf);
+ }
+
+ if (res != RPMRC_OK) {
+ fl->processingFailed = 1;
+ }
+
+ return res;
+}
+
+/**
+ */
+static int compareFileListRecs(const void * ap, const void * bp)
+{
+ const char *a = ((FileListRec)ap)->cpioPath;
+ const char *b = ((FileListRec)bp)->cpioPath;
+ return strcmp(a, b);
+}
+
+/**
+ * Test if file is located in a %docdir.
+ * @param fl package file tree walk data
+ * @param fileName file path
+ * @return 1 if doc file, 0 if not
+ */
+static int isDoc(FileList fl, const char * fileName)
+{
+ size_t k, l;
+ ARGV_const_t dd;
+
+ k = strlen(fileName);
+ for (dd = fl->docDirs; *dd; dd++) {
+ l = strlen(*dd);
+ if (l < k && rstreqn(fileName, *dd, l) && fileName[l] == '/')
+ return 1;
+ }
+ return 0;
+}
+
+static int isHardLink(FileListRec flp, FileListRec tlp)
+{
+ return ((S_ISREG(flp->fl_mode) && S_ISREG(tlp->fl_mode)) &&
+ ((flp->fl_nlink > 1) && (flp->fl_nlink == tlp->fl_nlink)) &&
+ (flp->fl_ino == tlp->fl_ino) &&
+ (flp->fl_dev == tlp->fl_dev));
+}
+
+/**
+ * Verify that file attributes scope over hardlinks correctly.
+ * If partial hardlink sets are possible, then add tracking dependency.
+ * @param fl package file tree walk data
+ * @return 1 if partial hardlink sets can exist, 0 otherwise.
+ */
+static int checkHardLinks(FileList fl)
+{
+ FileListRec ilp, jlp;
+ int i, j;
+
+ for (i = 0; i < fl->fileListRecsUsed; i++) {
+ ilp = fl->fileList + i;
+ if (!(S_ISREG(ilp->fl_mode) && ilp->fl_nlink > 1))
+ continue;
+
+ for (j = i + 1; j < fl->fileListRecsUsed; j++) {
+ jlp = fl->fileList + j;
+ if (isHardLink(ilp, jlp)) {
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+static int seenHardLink(FileList fl, FileListRec flp)
+{
+ for (FileListRec ilp = fl->fileList; ilp < flp; ilp++) {
+ if (isHardLink(flp, ilp)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/**
+ * Add file entries to header.
+ * @todo Should directories have %doc/%config attributes? (#14531)
+ * @todo Remove RPMTAG_OLDFILENAMES, add dirname/basename instead.
+ * @param fl package file tree walk data
+ * @retval *fip file info for package
+ * @param h
+ * @param isSrc
+ */
+static void genCpioListAndHeader(FileList fl,
+ rpmfi * fip, Header h, int isSrc)
+{
+ int _addDotSlash = !(isSrc || rpmExpandNumeric("%{_noPayloadPrefix}"));
+ size_t apathlen = 0;
+ size_t dpathlen = 0;
+ size_t skipLen = 0;
+ FileListRec flp;
+ char buf[BUFSIZ];
+ int i;
+ uint32_t defaultalgo = PGPHASHALGO_MD5, digestalgo;
+ rpm_loff_t totalFileSize = 0;
+
+ /*
+ * See if non-md5 file digest algorithm is requested. If not
+ * specified, quietly assume md5. Otherwise check if supported type.
+ */
+ digestalgo = rpmExpandNumeric(isSrc ? "%{_source_filedigest_algorithm}" :
+ "%{_binary_filedigest_algorithm}");
+ if (digestalgo == 0) {
+ digestalgo = defaultalgo;
+ }
+
+ if (rpmDigestLength(digestalgo) == 0) {
+ rpmlog(RPMLOG_WARNING,
+ _("Unknown file digest algorithm %u, falling back to MD5\n"),
+ digestalgo);
+ digestalgo = defaultalgo;
+ }
+
+ /* Sort the big list */
+ qsort(fl->fileList, fl->fileListRecsUsed,
+ sizeof(*(fl->fileList)), compareFileListRecs);
+
+ /* Generate the header. */
+ if (! isSrc) {
+ skipLen = 1;
+ }
+
+ for (i = 0, flp = fl->fileList; i < fl->fileListRecsUsed; i++, flp++) {
+ /* Merge duplicate entries. */
+ while (i < (fl->fileListRecsUsed - 1) &&
+ rstreq(flp->cpioPath, flp[1].cpioPath)) {
+
+ /* Two entries for the same file found, merge the entries. */
+ /* Note that an %exclude is a duplication of a file reference */
+
+ /* file flags */
+ flp[1].flags |= flp->flags;
+
+ if (!(flp[1].flags & RPMFILE_EXCLUDE))
+ rpmlog(RPMLOG_WARNING, _("File listed twice: %s\n"),
+ flp->cpioPath);
+
+ /* file mode */
+ if (S_ISDIR(flp->fl_mode)) {
+ if ((flp[1].specdFlags & (SPECD_DIRMODE | SPECD_DEFDIRMODE)) <
+ (flp->specdFlags & (SPECD_DIRMODE | SPECD_DEFDIRMODE)))
+ flp[1].fl_mode = flp->fl_mode;
+ } else {
+ if ((flp[1].specdFlags & (SPECD_FILEMODE | SPECD_DEFFILEMODE)) <
+ (flp->specdFlags & (SPECD_FILEMODE | SPECD_DEFFILEMODE)))
+ flp[1].fl_mode = flp->fl_mode;
+ }
+
+ /* uid */
+ if ((flp[1].specdFlags & (SPECD_UID | SPECD_DEFUID)) <
+ (flp->specdFlags & (SPECD_UID | SPECD_DEFUID)))
+ {
+ flp[1].fl_uid = flp->fl_uid;
+ flp[1].uname = flp->uname;
+ }
+
+ /* gid */
+ if ((flp[1].specdFlags & (SPECD_GID | SPECD_DEFGID)) <
+ (flp->specdFlags & (SPECD_GID | SPECD_DEFGID)))
+ {
+ flp[1].fl_gid = flp->fl_gid;
+ flp[1].gname = flp->gname;
+ }
+
+ /* verify flags */
+ if ((flp[1].specdFlags & (SPECD_VERIFY | SPECD_DEFVERIFY)) <
+ (flp->specdFlags & (SPECD_VERIFY | SPECD_DEFVERIFY)))
+ flp[1].verifyFlags = flp->verifyFlags;
+
+ /* XXX to-do: language */
+
+ flp++; i++;
+ }
+
+ /* Skip files that were marked with %exclude. */
+ if (flp->flags & RPMFILE_EXCLUDE) continue;
+
+ /* Omit '/' and/or URL prefix, leave room for "./" prefix */
+ apathlen += (strlen(flp->cpioPath) - skipLen + (_addDotSlash ? 3 : 1));
+
+ /* Leave room for both dirname and basename NUL's */
+ dpathlen += (strlen(flp->diskPath) + 2);
+
+ /*
+ * Make the header. Store the on-disk path to OLDFILENAMES for
+ * cpio list generation purposes for now, final path temporarily
+ * to ORIGFILENAMES, to be swapped later into OLDFILENAMES.
+ */
+ headerPutString(h, RPMTAG_OLDFILENAMES, flp->diskPath);
+ headerPutString(h, RPMTAG_ORIGFILENAMES, flp->cpioPath);
+ headerPutString(h, RPMTAG_FILEUSERNAME, flp->uname);
+ headerPutString(h, RPMTAG_FILEGROUPNAME, flp->gname);
+
+ /*
+ * Only use 64bit filesizes if file sizes require it.
+ * This is basically no-op for now as we error out in addFile() if
+ * any individual file size exceeds the cpio limit.
+ */
+ if (fl->largeFiles) {
+ rpm_loff_t rsize64 = (rpm_loff_t)flp->fl_size;
+ headerPutUint64(h, RPMTAG_LONGFILESIZES, &rsize64, 1);
+ /* XXX TODO: add rpmlib() dependency for large files */
+ } else {
+ rpm_off_t rsize32 = (rpm_off_t)flp->fl_size;
+ headerPutUint32(h, RPMTAG_FILESIZES, &rsize32, 1);
+ }
+ /* Excludes and dupes have been filtered out by now. */
+ if (S_ISREG(flp->fl_mode)) {
+ if (flp->fl_nlink == 1 || !seenHardLink(fl, flp)) {
+ totalFileSize += flp->fl_size;
+ }
+ }
+
+ /*
+ * For items whose size varies between systems, always explicitly
+ * cast to the header type before inserting.
+ * TODO: check and warn if header type overflows for each case.
+ */
+ { rpm_time_t rtime = (rpm_time_t) flp->fl_mtime;
+ headerPutUint32(h, RPMTAG_FILEMTIMES, &rtime, 1);
+ }
+
+ { rpm_mode_t rmode = (rpm_mode_t) flp->fl_mode;
+ headerPutUint16(h, RPMTAG_FILEMODES, &rmode, 1);
+ }
+
+ { rpm_rdev_t rrdev = (rpm_rdev_t) flp->fl_rdev;
+ headerPutUint16(h, RPMTAG_FILERDEVS, &rrdev, 1);
+ }
+
+ { rpm_dev_t rdev = (rpm_dev_t) flp->fl_dev;
+ headerPutUint32(h, RPMTAG_FILEDEVICES, &rdev, 1);
+ }
+
+ { rpm_ino_t rino = (rpm_ino_t) flp->fl_ino;
+ headerPutUint32(h, RPMTAG_FILEINODES, &rino, 1);
+ }
+
+ headerPutString(h, RPMTAG_FILELANGS, flp->langs);
+
+ if (fl->haveCaps) {
+ headerPutString(h, RPMTAG_FILECAPS, flp->caps);
+ }
+
+ buf[0] = '\0';
+ if (S_ISREG(flp->fl_mode))
+ (void) rpmDoDigest(digestalgo, flp->diskPath, 1,
+ (unsigned char *)buf, NULL);
+ headerPutString(h, RPMTAG_FILEDIGESTS, buf);
+
+ buf[0] = '\0';
+ if (S_ISLNK(flp->fl_mode)) {
+ ssize_t llen = readlink(flp->diskPath, buf, BUFSIZ-1);
+ if (llen == -1) {
+ rpmlog(RPMLOG_ERR, _("reading symlink %s failed: %s\n"),
+ flp->diskPath, strerror(errno));
+ fl->processingFailed = 1;
+ } else {
+ buf[llen] = '\0';
+ if (buf[0] == '/' && !rstreq(fl->buildRoot, "/") &&
+ rstreqn(buf, fl->buildRoot, strlen(fl->buildRoot))) {
+ rpmlog(RPMLOG_ERR,
+ _("Symlink points to BuildRoot: %s -> %s\n"),
+ flp->cpioPath, buf);
+ fl->processingFailed = 1;
+ }
+ }
+ }
+ headerPutString(h, RPMTAG_FILELINKTOS, buf);
+
+ if (flp->flags & RPMFILE_GHOST) {
+ flp->verifyFlags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE |
+ RPMVERIFY_LINKTO | RPMVERIFY_MTIME);
+ }
+ headerPutUint32(h, RPMTAG_FILEVERIFYFLAGS, &(flp->verifyFlags),1);
+
+ if (!isSrc && isDoc(fl, flp->cpioPath))
+ flp->flags |= RPMFILE_DOC;
+ /* XXX Should directories have %doc/%config attributes? (#14531) */
+ if (S_ISDIR(flp->fl_mode))
+ flp->flags &= ~(RPMFILE_CONFIG|RPMFILE_DOC);
+
+ headerPutUint32(h, RPMTAG_FILEFLAGS, &(flp->flags) ,1);
+ }
+
+ if (totalFileSize < UINT32_MAX) {
+ rpm_off_t totalsize = totalFileSize;
+ headerPutUint32(h, RPMTAG_SIZE, &totalsize, 1);
+ } else {
+ rpm_loff_t totalsize = totalFileSize;
+ headerPutUint64(h, RPMTAG_LONGSIZE, &totalsize, 1);
+ }
+
+ if (digestalgo != defaultalgo) {
+ headerPutUint32(h, RPMTAG_FILEDIGESTALGO, &digestalgo, 1);
+ rpmlibNeedsFeature(h, "FileDigests", "4.6.0-1");
+ }
+
+ if (fl->haveCaps) {
+ rpmlibNeedsFeature(h, "FileCaps", "4.6.1-1");
+ }
+
+ if (_addDotSlash)
+ (void) rpmlibNeedsFeature(h, "PayloadFilesHavePrefix", "4.0-1");
+
+ {
+ struct rpmtd_s filenames;
+ rpmfiFlags flags = RPMFI_NOHEADER|RPMFI_NOFILEUSER|RPMFI_NOFILEGROUP;
+ rpmfi fi;
+ int fc;
+ const char *fn;
+ char *a, **apath;
+
+ /* rpmfiNew() only groks compressed filelists */
+ headerConvert(h, HEADERCONV_COMPRESSFILELIST);
+ fi = rpmfiNew(NULL, h, RPMTAG_BASENAMES, flags);
+
+ /*
+ * Grab the real filenames from ORIGFILENAMES and put into OLDFILENAMES,
+ * remove temporary cruft and side-effects from filelist compression
+ * for rpmfiNew().
+ */
+ headerGet(h, RPMTAG_ORIGFILENAMES, &filenames, HEADERGET_ALLOC);
+ headerDel(h, RPMTAG_ORIGFILENAMES);
+ headerDel(h, RPMTAG_BASENAMES);
+ headerDel(h, RPMTAG_DIRNAMES);
+ headerDel(h, RPMTAG_DIRINDEXES);
+ rpmtdSetTag(&filenames, RPMTAG_OLDFILENAMES);
+ headerPut(h, &filenames, HEADERPUT_DEFAULT);
+
+ /* Create hge-style archive path array, normally adding "./" */
+ fc = rpmtdCount(&filenames);
+ apath = xmalloc(fc * sizeof(*apath) + apathlen + 1);
+ a = (char *)(apath + fc);
+ *a = '\0';
+ rpmtdInit(&filenames);
+ for (int i = 0; (fn = rpmtdNextString(&filenames)); i++) {
+ apath[i] = a;
+ if (_addDotSlash)
+ a = stpcpy(a, "./");
+ a = stpcpy(a, (fn + skipLen));
+ a++; /* skip apath NUL */
+ }
+ fi->apath = apath;
+ *fip = fi;
+ rpmtdFreeData(&filenames);
+ }
+
+ /* Compress filelist unless legacy format requested */
+ if (!(fl->pkgFlags & RPMBUILD_PKG_NODIRTOKENS)) {
+ headerConvert(h, HEADERCONV_COMPRESSFILELIST);
+ /* Binary packages with dirNames cannot be installed by legacy rpm. */
+ (void) rpmlibNeedsFeature(h, "CompressedFileNames", "3.0.4-1");
+ }
+}
+
+/**
+ */
+static FileListRec freeFileList(FileListRec fileList,
+ int count)
+{
+ while (count--) {
+ fileList[count].diskPath = _free(fileList[count].diskPath);
+ fileList[count].cpioPath = _free(fileList[count].cpioPath);
+ fileList[count].langs = _free(fileList[count].langs);
+ fileList[count].caps = _free(fileList[count].caps);
+ }
+ fileList = _free(fileList);
+ return NULL;
+}
+
+/* forward ref */
+static rpmRC recurseDir(FileList fl, const char * diskPath);
+
+/**
+ * Add a file to the package manifest.
+ * @param fl package file tree walk data
+ * @param diskPath path to file
+ * @param statp file stat (possibly NULL)
+ * @return RPMRC_OK on success
+ */
+static rpmRC addFile(FileList fl, const char * diskPath,
+ struct stat * statp)
+{
+ const char *cpioPath = diskPath;
+ struct stat statbuf;
+ mode_t fileMode;
+ uid_t fileUid;
+ gid_t fileGid;
+ const char *fileUname;
+ const char *fileGname;
+
+ /* Path may have prepended buildRoot, so locate the original filename. */
+ /*
+ * XXX There are 3 types of entry into addFile:
+ *
+ * From diskUrl statp
+ * =====================================================
+ * processBinaryFile path NULL
+ * processBinaryFile glob result path NULL
+ * myftw path stat
+ *
+ */
+ if (fl->buildRoot && !rstreq(fl->buildRoot, "/"))
+ cpioPath += strlen(fl->buildRoot);
+
+ /* XXX make sure '/' can be packaged also */
+ if (*cpioPath == '\0')
+ cpioPath = "/";
+
+ if (statp == NULL) {
+ time_t now = time(NULL);
+ statp = &statbuf;
+ memset(statp, 0, sizeof(*statp));
+ if (fl->devtype) {
+ /* XXX hack up a stat structure for a %dev(...) directive. */
+ statp->st_nlink = 1;
+ statp->st_rdev =
+ ((fl->devmajor & 0xff) << 8) | (fl->devminor & 0xff);
+ statp->st_dev = statp->st_rdev;
+ statp->st_mode = (fl->devtype == 'b' ? S_IFBLK : S_IFCHR);
+ statp->st_mode |= (fl->cur_ar.ar_fmode & 0777);
+ statp->st_atime = now;
+ statp->st_mtime = now;
+ statp->st_ctime = now;
+ } else {
+ int is_ghost = fl->currentFlags & RPMFILE_GHOST;
+
+ if (lstat(diskPath, statp)) {
+ if (is_ghost) { /* the file is %ghost missing from build root, assume regular file */
+ if (fl->cur_ar.ar_fmodestr != NULL) {
+ statp->st_mode = S_IFREG | (fl->cur_ar.ar_fmode & 0777);
+ } else {
+ rpmlog(RPMLOG_ERR, _("Explicit file attributes required in spec for: %s\n"), diskPath);
+ fl->processingFailed = 1;
+ return RPMRC_FAIL;
+ }
+ statp->st_atime = now;
+ statp->st_mtime = now;
+ statp->st_ctime = now;
+ } else {
+ const char *msg = fl->isDir ?
+ _("Directory not found: %s\n") :
+ _("File not found: %s\n");
+ rpmlog(RPMLOG_ERR, msg, diskPath);
+ fl->processingFailed = 1;
+ return RPMRC_FAIL;
+ }
+ }
+ }
+ }
+
+ if ((! fl->isDir) && S_ISDIR(statp->st_mode)) {
+/* FIX: fl->buildRoot may be NULL */
+ return recurseDir(fl, diskPath);
+ }
+
+ fileMode = statp->st_mode;
+ fileUid = statp->st_uid;
+ fileGid = statp->st_gid;
+
+ /* Explicit %attr() always wins */
+ if (fl->cur_ar.ar_fmodestr != NULL) {
+ fileMode &= S_IFMT;
+ fileMode |= fl->cur_ar.ar_fmode;
+ } else {
+ /* ...but %defattr() for directories and files is different */
+ if (S_ISDIR(fileMode)) {
+ if (fl->def_ar.ar_dmodestr) {
+ fileMode &= S_IFMT;
+ fileMode |= fl->def_ar.ar_dmode;
+ }
+ } else if (fl->def_ar.ar_fmodestr) {
+ fileMode &= S_IFMT;
+ fileMode |= fl->def_ar.ar_fmode;
+ }
+ }
+ if (fl->cur_ar.ar_user) {
+ fileUname = fl->cur_ar.ar_user;
+ } else if (fl->def_ar.ar_user) {
+ fileUname = fl->def_ar.ar_user;
+ } else {
+ fileUname = rpmugUname(fileUid);
+ }
+ if (fl->cur_ar.ar_group) {
+ fileGname = fl->cur_ar.ar_group;
+ } else if (fl->def_ar.ar_group) {
+ fileGname = fl->def_ar.ar_group;
+ } else {
+ fileGname = rpmugGname(fileGid);
+ }
+
+ /* Default user/group to builder's user/group */
+ if (fileUname == NULL)
+ fileUname = rpmugUname(getuid());
+ if (fileGname == NULL)
+ fileGname = rpmugGname(getgid());
+
+ /* S_XXX macro must be consistent with type in find call at check-files script */
+ if (check_fileList && (S_ISREG(fileMode) || S_ISLNK(fileMode))) {
+ appendStringBuf(check_fileList, diskPath);
+ appendStringBuf(check_fileList, "\n");
+ }
+
+ /* Add to the file list */
+ if (fl->fileListRecsUsed == fl->fileListRecsAlloced) {
+ fl->fileListRecsAlloced += 128;
+ fl->fileList = xrealloc(fl->fileList,
+ fl->fileListRecsAlloced * sizeof(*(fl->fileList)));
+ }
+
+ { FileListRec flp = &fl->fileList[fl->fileListRecsUsed];
+ int i;
+
+ flp->fl_st = *statp; /* structure assignment */
+ flp->fl_mode = fileMode;
+ flp->fl_uid = fileUid;
+ flp->fl_gid = fileGid;
+
+ flp->cpioPath = xstrdup(cpioPath);
+ flp->diskPath = xstrdup(diskPath);
+ flp->uname = rpmugStashStr(fileUname);
+ flp->gname = rpmugStashStr(fileGname);
+
+ if (fl->currentLangs && fl->nLangs > 0) {
+ char * ncl;
+ size_t nl = 0;
+
+ for (i = 0; i < fl->nLangs; i++)
+ nl += strlen(fl->currentLangs[i]) + 1;
+
+ flp->langs = ncl = xmalloc(nl);
+ for (i = 0; i < fl->nLangs; i++) {
+ const char *ocl;
+ if (i) *ncl++ = '|';
+ for (ocl = fl->currentLangs[i]; *ocl != '\0'; ocl++)
+ *ncl++ = *ocl;
+ *ncl = '\0';
+ }
+ } else {
+ flp->langs = xstrdup("");
+ }
+
+ if (fl->currentCaps) {
+ flp->caps = fl->currentCaps;
+ } else {
+ flp->caps = xstrdup("");
+ }
+
+ flp->flags = fl->currentFlags;
+ flp->specdFlags = fl->currentSpecdFlags;
+ flp->verifyFlags = fl->currentVerifyFlags;
+
+ if (!(flp->flags & RPMFILE_EXCLUDE) && S_ISREG(flp->fl_mode)) {
+ /*
+ * XXX Simple and stupid check for now, this needs to be per-payload
+ * format check once we have other payloads than good 'ole cpio.
+ */
+ if ((rpm_loff_t) flp->fl_size >= CPIO_FILESIZE_MAX) {
+ fl->largeFiles = 1;
+ rpmlog(RPMLOG_ERR, _("File %s too large for payload\n"),
+ flp->diskPath);
+ return RPMRC_FAIL;
+ }
+ }
+ }
+
+ fl->fileListRecsUsed++;
+ fl->fileCount++;
+
+ return RPMRC_OK;
+}
+
+/**
+ * Add directory (and all of its files) to the package manifest.
+ * @param fl package file tree walk data
+ * @param diskPath path to file
+ * @return RPMRC_OK on success
+ */
+static rpmRC recurseDir(FileList fl, const char * diskPath)
+{
+ char * ftsSet[2];
+ FTS * ftsp;
+ FTSENT * fts;
+ int myFtsOpts = (FTS_COMFOLLOW | FTS_NOCHDIR | FTS_PHYSICAL);
+ rpmRC rc = RPMRC_FAIL;
+
+ fl->isDir = 1; /* Keep it from following myftw() again */
+
+ ftsSet[0] = (char *) diskPath;
+ ftsSet[1] = NULL;
+ ftsp = Fts_open(ftsSet, myFtsOpts, NULL);
+ while ((fts = Fts_read(ftsp)) != NULL) {
+ switch (fts->fts_info) {
+ case FTS_D: /* preorder directory */
+ case FTS_F: /* regular file */
+ case FTS_SL: /* symbolic link */
+ case FTS_SLNONE: /* symbolic link without target */
+ case FTS_DEFAULT: /* none of the above */
+ rc = addFile(fl, fts->fts_accpath, fts->fts_statp);
+ break;
+ case FTS_DOT: /* dot or dot-dot */
+ case FTS_DP: /* postorder directory */
+ rc = RPMRC_OK;
+ break;
+ case FTS_NS: /* stat(2) failed */
+ case FTS_DNR: /* unreadable directory */
+ case FTS_ERR: /* error; errno is set */
+ case FTS_DC: /* directory that causes cycles */
+ case FTS_NSOK: /* no stat(2) requested */
+ case FTS_INIT: /* initialized only */
+ case FTS_W: /* whiteout object */
+ default:
+ rc = RPMRC_FAIL;
+ break;
+ }
+ if (rc)
+ break;
+ }
+ (void) Fts_close(ftsp);
+
+ fl->isDir = 0;
+
+ return rc;
+}
+
+/**
+ * Add a pubkey/icon to a binary package.
+ * @param pkg
+ * @param fl package file tree walk data
+ * @param fileName path to file, relative is builddir, absolute buildroot.
+ * @param tag tag to add
+ * @return RPMRC_OK on success
+ */
+static rpmRC processMetadataFile(Package pkg, FileList fl,
+ const char * fileName, rpmTagVal tag)
+{
+ const char * buildDir = "%{_builddir}/%{?buildsubdir}/";
+ char * fn = NULL;
+ char * apkt = NULL;
+ uint8_t * pkt = NULL;
+ ssize_t pktlen = 0;
+ int absolute = 0;
+ rpmRC rc = RPMRC_FAIL;
+ int xx;
+
+ if (*fileName == '/') {
+ fn = rpmGenPath(fl->buildRoot, NULL, fileName);
+ absolute = 1;
+ } else
+ fn = rpmGenPath(buildDir, NULL, fileName);
+
+ switch (tag) {
+ default:
+ rpmlog(RPMLOG_ERR, _("%s: can't load unknown tag (%d).\n"),
+ fn, tag);
+ goto exit;
+ break;
+ case RPMTAG_PUBKEYS: {
+ if ((xx = pgpReadPkts(fn, &pkt, (size_t *)&pktlen)) <= 0) {
+ rpmlog(RPMLOG_ERR, _("%s: public key read failed.\n"), fn);
+ goto exit;
+ }
+ if (xx != PGPARMOR_PUBKEY) {
+ rpmlog(RPMLOG_ERR, _("%s: not an armored public key.\n"), fn);
+ goto exit;
+ }
+ apkt = pgpArmorWrap(PGPARMOR_PUBKEY, pkt, pktlen);
+ break;
+ }
+ }
+
+ if (!apkt) {
+ rpmlog(RPMLOG_ERR, _("%s: failed to encode\n"), fn);
+ goto exit;
+ }
+
+ headerPutString(pkg->header, tag, apkt);
+ rc = RPMRC_OK;
+
+ if (absolute)
+ rc = addFile(fl, fn, NULL);
+
+exit:
+ apkt = _free(apkt);
+ pkt = _free(pkt);
+ fn = _free(fn);
+ if (rc) {
+ fl->processingFailed = 1;
+ rc = RPMRC_FAIL;
+ }
+ return rc;
+}
+
+/**
+ * Add a file to a binary package.
+ * @param pkg
+ * @param fl package file tree walk data
+ * @param fileName file to add
+ * @return RPMRC_OK on success
+ */
+static rpmRC processBinaryFile(Package pkg, FileList fl, const char * fileName)
+{
+ int quote = 1; /* XXX permit quoted glob characters. */
+ int doGlob;
+ char *diskPath = NULL;
+ rpmRC rc = RPMRC_OK;
+ size_t fnlen = strlen(fileName);
+ int trailing_slash = (fnlen > 0 && fileName[fnlen-1] == '/');
+
+ /* XXX differentiate other directories from explicit %dir */
+ if (trailing_slash && !fl->isDir)
+ fl->isDir = -1;
+
+ doGlob = glob_pattern_p(fileName, quote);
+
+ /* Check that file starts with leading "/" */
+ if (*fileName != '/') {
+ rpmlog(RPMLOG_ERR, _("File needs leading \"/\": %s\n"), fileName);
+ rc = RPMRC_FAIL;
+ goto exit;
+ }
+
+ /* Copy file name or glob pattern removing multiple "/" chars. */
+ /*
+ * Note: rpmGetPath should guarantee a "canonical" path. That means
+ * that the following pathologies should be weeded out:
+ * //bin//sh
+ * //usr//bin/
+ * /.././../usr/../bin//./sh
+ */
+ diskPath = rpmGenPath(fl->buildRoot, NULL, fileName);
+ /* Arrange trailing slash on directories */
+ if (fl->isDir)
+ diskPath = rstrcat(&diskPath, "/");
+
+ if (doGlob) {
+ ARGV_t argv = NULL;
+ int argc = 0;
+ int i;
+
+ /* XXX for %dev marker in file manifest only */
+ if (fl->noGlob) {
+ rpmlog(RPMLOG_ERR, _("Glob not permitted: %s\n"), diskPath);
+ rc = RPMRC_FAIL;
+ goto exit;
+ }
+
+ if (rpmGlob(diskPath, &argc, &argv) == 0 && argc >= 1) {
+ for (i = 0; i < argc; i++) {
+ rc = addFile(fl, argv[i], NULL);
+ }
+ argvFree(argv);
+ } else {
+ const char *msg = (fl->isDir) ?
+ _("Directory not found by glob: %s\n") :
+ _("File not found by glob: %s\n");
+ rpmlog(RPMLOG_ERR, msg, diskPath);
+ rc = RPMRC_FAIL;
+ goto exit;
+ }
+ } else {
+ rc = addFile(fl, diskPath, NULL);
+ }
+
+exit:
+ free(diskPath);
+ if (rc) {
+ fl->processingFailed = 1;
+ rc = RPMRC_FAIL;
+ }
+ return rc;
+}
+
+/**
+ */
+static rpmRC processPackageFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags,
+ Package pkg, int installSpecialDoc, int test)
+{
+ struct FileList_s fl;
+ const char *fileName;
+ char buf[BUFSIZ];
+ struct AttrRec_s arbuf;
+ AttrRec specialDocAttrRec = &arbuf;
+ char *specialDoc = NULL;
+
+ nullAttrRec(specialDocAttrRec);
+ pkg->cpioList = NULL;
+
+ if (pkg->fileFile) {
+ char *ffn;
+ FILE *fd;
+
+ for (ARGV_const_t fp = pkg->fileFile; *fp != NULL; fp++) {
+ if (**fp == '/') {
+ ffn = rpmGetPath(*fp, NULL);
+ } else {
+ ffn = rpmGetPath("%{_builddir}/",
+ (spec->buildSubdir ? spec->buildSubdir : "") ,
+ "/", *fp, NULL);
+ }
+ fd = fopen(ffn, "r");
+
+ if (fd == NULL || ferror(fd)) {
+ rpmlog(RPMLOG_ERR, _("Could not open %%files file %s: %m\n"), ffn);
+ return RPMRC_FAIL;
+ }
+ ffn = _free(ffn);
+
+ while (fgets(buf, sizeof(buf), fd)) {
+ handleComments(buf);
+ if (expandMacros(spec, spec->macros, buf, sizeof(buf))) {
+ rpmlog(RPMLOG_ERR, _("line: %s\n"), buf);
+ fclose(fd);
+ return RPMRC_FAIL;
+ }
+ argvAdd(&(pkg->fileList), buf);
+ }
+ (void) fclose(fd);
+ }
+ }
+
+ /* Init the file list structure */
+ memset(&fl, 0, sizeof(fl));
+
+ /* XXX spec->buildRoot == NULL, then xstrdup("") is returned */
+ fl.buildRoot = rpmGenPath(spec->rootDir, spec->buildRoot, NULL);
+
+ fl.fileCount = 0;
+ fl.processingFailed = 0;
+
+ fl.passedSpecialDoc = 0;
+ fl.isSpecialDoc = 0;
+
+ fl.isDir = 0;
+ fl.currentFlags = 0;
+ fl.currentVerifyFlags = 0;
+
+ fl.noGlob = 0;
+ fl.devtype = 0;
+ fl.devmajor = 0;
+ fl.devminor = 0;
+
+ nullAttrRec(&fl.cur_ar);
+ nullAttrRec(&fl.def_ar);
+ dupAttrRec(&root_ar, &fl.def_ar); /* XXX assume %defattr(-,root,root) */
+
+ fl.defVerifyFlags = RPMVERIFY_ALL;
+ fl.nLangs = 0;
+ fl.currentLangs = NULL;
+ fl.haveCaps = 0;
+ fl.currentCaps = NULL;
+
+ fl.currentSpecdFlags = 0;
+ fl.defSpecdFlags = 0;
+
+ fl.largeFiles = 0;
+ fl.pkgFlags = pkgFlags;
+
+ fl.docDirs = NULL;
+ { char *docs = rpmGetPath("%{?__docdir_path}", NULL);
+ argvSplit(&fl.docDirs, docs, ":");
+ free(docs);
+ }
+
+ fl.fileList = NULL;
+ fl.fileListRecsAlloced = 0;
+ fl.fileListRecsUsed = 0;
+
+ for (ARGV_const_t fp = pkg->fileList; *fp != NULL; fp++) {
+ const char *s = *fp;
+ SKIPSPACE(s);
+ if (*s == '\0')
+ continue;
+ fileName = NULL;
+ rstrlcpy(buf, s, sizeof(buf));
+
+ /* Reset for a new line in %files */
+ fl.isDir = 0;
+ fl.currentFlags = 0;
+ /* turn explicit flags into %def'd ones (gosh this is hacky...) */
+ fl.currentSpecdFlags = ((unsigned)fl.defSpecdFlags) >> 8;
+ fl.currentVerifyFlags = fl.defVerifyFlags;
+ fl.isSpecialDoc = 0;
+
+ fl.noGlob = 0;
+ fl.devtype = 0;
+ fl.devmajor = 0;
+ fl.devminor = 0;
+
+ /* XXX should reset to %deflang value */
+ if (fl.currentLangs) {
+ int i;
+ for (i = 0; i < fl.nLangs; i++)
+ fl.currentLangs[i] = _free(fl.currentLangs[i]);
+ fl.currentLangs = _free(fl.currentLangs);
+ }
+ fl.nLangs = 0;
+ fl.currentCaps = NULL;
+
+ freeAttrRec(&fl.cur_ar);
+
+ if (parseForVerify(buf, &fl))
+ continue;
+ if (parseForAttr(buf, &fl))
+ continue;
+ if (parseForDev(buf, &fl))
+ continue;
+ if (parseForConfig(buf, &fl))
+ continue;
+ if (parseForLang(buf, &fl))
+ continue;
+ if (parseForCaps(buf, &fl))
+ continue;
+ if (parseForSimple(spec, pkg, buf, &fl, &fileName))
+ continue;
+ if (fileName == NULL)
+ continue;
+
+ if (fl.isSpecialDoc) {
+ /* Save this stuff for last */
+ specialDoc = _free(specialDoc);
+ specialDoc = xstrdup(fileName);
+ dupAttrRec(&fl.cur_ar, specialDocAttrRec);
+ } else if (fl.currentFlags & RPMFILE_PUBKEY) {
+ (void) processMetadataFile(pkg, &fl, fileName, RPMTAG_PUBKEYS);
+ } else {
+ (void) processBinaryFile(pkg, &fl, fileName);
+ }
+ }
+
+ /* Now process special doc, if there is one */
+ if (specialDoc) {
+ if (installSpecialDoc) {
+ int _missing_doc_files_terminate_build =
+ rpmExpandNumeric("%{?_missing_doc_files_terminate_build}");
+ rpmRC rc;
+
+ rc = doScript(spec, RPMBUILD_STRINGBUF, "%doc",
+ getStringBuf(pkg->specialDoc), test);
+ if (rc != RPMRC_OK && _missing_doc_files_terminate_build)
+ fl.processingFailed = 1;
+ }
+
+ /* Reset for %doc */
+ fl.isDir = 0;
+ fl.currentFlags = 0;
+ fl.currentVerifyFlags = fl.defVerifyFlags;
+
+ fl.noGlob = 0;
+ fl.devtype = 0;
+ fl.devmajor = 0;
+ fl.devminor = 0;
+
+ /* XXX should reset to %deflang value */
+ if (fl.currentLangs) {
+ int i;
+ for (i = 0; i < fl.nLangs; i++)
+ fl.currentLangs[i] = _free(fl.currentLangs[i]);
+ fl.currentLangs = _free(fl.currentLangs);
+ }
+ fl.nLangs = 0;
+
+ dupAttrRec(specialDocAttrRec, &fl.cur_ar);
+ freeAttrRec(specialDocAttrRec);
+
+ (void) processBinaryFile(pkg, &fl, specialDoc);
+
+ specialDoc = _free(specialDoc);
+ }
+
+ if (fl.processingFailed)
+ goto exit;
+
+ /* Verify that file attributes scope over hardlinks correctly. */
+ if (checkHardLinks(&fl))
+ (void) rpmlibNeedsFeature(pkg->header,
+ "PartialHardlinkSets", "4.0.4-1");
+
+ genCpioListAndHeader(&fl, &pkg->cpioList, pkg->header, 0);
+
+exit:
+ fl.buildRoot = _free(fl.buildRoot);
+
+ freeAttrRec(&fl.cur_ar);
+ freeAttrRec(&fl.def_ar);
+
+ if (fl.currentLangs) {
+ int i;
+ for (i = 0; i < fl.nLangs; i++)
+ fl.currentLangs[i] = _free(fl.currentLangs[i]);
+ fl.currentLangs = _free(fl.currentLangs);
+ }
+
+ fl.fileList = freeFileList(fl.fileList, fl.fileListRecsUsed);
+ argvFree(fl.docDirs);
+ return fl.processingFailed ? RPMRC_FAIL : RPMRC_OK;
+}
+
+static void genSourceRpmName(rpmSpec spec)
+{
+ if (spec->sourceRpmName == NULL) {
+ char *nvr = headerGetAsString(spec->packages->header, RPMTAG_NVR);
+ rasprintf(&spec->sourceRpmName, "%s.%ssrc.rpm", nvr,
+ spec->noSource ? "no" : "");
+ free(nvr);
+ }
+}
+
+rpmRC processSourceFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags)
+{
+ struct Source *srcPtr;
+ int x, isSpec = 1;
+ struct FileList_s fl;
+ ARGV_t files = NULL;
+ Package pkg;
+ static char *_srcdefattr;
+ static int oneshot;
+
+ if (!oneshot) {
+ _srcdefattr = rpmExpand("%{?_srcdefattr}", NULL);
+ if (_srcdefattr && !*_srcdefattr)
+ _srcdefattr = _free(_srcdefattr);
+ oneshot = 1;
+ }
+
+ genSourceRpmName(spec);
+ /* Construct the file list and source entries */
+ argvAdd(&files, spec->specFile);
+ for (srcPtr = spec->sources; srcPtr != NULL; srcPtr = srcPtr->next) {
+ char * sfn = rpmGetPath( ((srcPtr->flags & RPMBUILD_ISNO) ? "!" : ""),
+ "%{_sourcedir}/", srcPtr->source, NULL);
+ argvAdd(&files, sfn);
+ sfn = _free(sfn);
+ }
+
+ for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
+ for (srcPtr = pkg->icon; srcPtr != NULL; srcPtr = srcPtr->next) {
+ char * sfn;
+ sfn = rpmGetPath( ((srcPtr->flags & RPMBUILD_ISNO) ? "!" : ""),
+ "%{_sourcedir}/", srcPtr->source, NULL);
+ argvAdd(&files, sfn);
+ sfn = _free(sfn);
+ }
+ }
+
+ spec->sourceCpioList = NULL;
+
+ /* Init the file list structure */
+ memset(&fl, 0, sizeof(fl));
+ if (_srcdefattr) {
+ char *a = rstrscat(NULL, "%defattr ", _srcdefattr, NULL);
+ parseForAttr(a, &fl);
+ free(a);
+ }
+ fl.fileList = xcalloc((spec->numSources + 1), sizeof(*fl.fileList));
+ fl.processingFailed = 0;
+ fl.fileListRecsUsed = 0;
+ fl.pkgFlags = pkgFlags;
+ fl.buildRoot = NULL;
+
+ /* The first source file is the spec file */
+ x = 0;
+ for (ARGV_const_t fp = files; *fp != NULL; fp++) {
+ const char *diskPath = *fp;
+ char *tmp;
+ FileListRec flp;
+
+ SKIPSPACE(diskPath);
+ if (! *diskPath)
+ continue;
+
+ flp = &fl.fileList[x];
+
+ flp->flags = isSpec ? RPMFILE_SPECFILE : 0;
+ /* files with leading ! are no source files */
+ if (*diskPath == '!') {
+ flp->flags |= RPMFILE_GHOST;
+ diskPath++;
+ }
+
+ tmp = xstrdup(diskPath); /* basename() might modify */
+ flp->diskPath = xstrdup(diskPath);
+ flp->cpioPath = xstrdup(basename(tmp));
+ flp->verifyFlags = RPMVERIFY_ALL;
+ free(tmp);
+
+ if (stat(diskPath, &flp->fl_st)) {
+ rpmlog(RPMLOG_ERR, _("Bad file: %s: %s\n"),
+ diskPath, strerror(errno));
+ fl.processingFailed = 1;
+ }
+
+ if (fl.def_ar.ar_fmodestr) {
+ flp->fl_mode &= S_IFMT;
+ flp->fl_mode |= fl.def_ar.ar_fmode;
+ }
+ if (fl.def_ar.ar_user) {
+ flp->uname = rpmugStashStr(fl.def_ar.ar_user);
+ } else {
+ flp->uname = rpmugStashStr(rpmugUname(flp->fl_uid));
+ }
+ if (fl.def_ar.ar_group) {
+ flp->gname = rpmugStashStr(fl.def_ar.ar_group);
+ } else {
+ flp->gname = rpmugStashStr(rpmugGname(flp->fl_gid));
+ }
+ flp->langs = xstrdup("");
+
+ if (! (flp->uname && flp->gname)) {
+ rpmlog(RPMLOG_ERR, _("Bad owner/group: %s\n"), diskPath);
+ fl.processingFailed = 1;
+ }
+
+ isSpec = 0;
+ x++;
+ }
+ fl.fileListRecsUsed = x;
+ argvFree(files);
+
+ if (! fl.processingFailed) {
+ if (spec->sourceHeader != NULL)
+ genCpioListAndHeader(&fl, &spec->sourceCpioList,
+ spec->sourceHeader, 1);
+ }
+
+ fl.fileList = freeFileList(fl.fileList, fl.fileListRecsUsed);
+ freeAttrRec(&fl.def_ar);
+ return fl.processingFailed ? RPMRC_FAIL : RPMRC_OK;
+}
+
+/**
+ * Check packaged file list against what's in the build root.
+ * @param fileList packaged file list
+ * @return -1 if skipped, 0 on OK, 1 on error
+ */
+static int checkFiles(const char *buildRoot, StringBuf fileList)
+{
+ static char * const av_ckfile[] = { "%{?__check_files}", NULL };
+ StringBuf sb_stdout = NULL;
+ char * s;
+ int rc;
+
+ s = rpmExpand(av_ckfile[0], NULL);
+ if (!(s && *s)) {
+ rc = -1;
+ goto exit;
+ }
+ rc = 0;
+
+ rpmlog(RPMLOG_NOTICE, _("Checking for unpackaged file(s): %s\n"), s);
+
+ rc = rpmfcExec(av_ckfile, fileList, &sb_stdout, 0, buildRoot);
+ if (rc < 0)
+ goto exit;
+
+ if (sb_stdout) {
+ int _unpackaged_files_terminate_build =
+ rpmExpandNumeric("%{?_unpackaged_files_terminate_build}");
+ const char * t;
+
+ t = getStringBuf(sb_stdout);
+ if ((*t != '\0') && (*t != '\n')) {
+ rc = (_unpackaged_files_terminate_build) ? 1 : 0;
+ rpmlog((rc ? RPMLOG_ERR : RPMLOG_WARNING),
+ _("Installed (but unpackaged) file(s) found:\n%s"), t);
+ }
+ }
+
+exit:
+ sb_stdout = freeStringBuf(sb_stdout);
+ s = _free(s);
+ return rc;
+}
+
+rpmRC processBinaryFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags,
+ int installSpecialDoc, int test)
+{
+ Package pkg;
+ rpmRC rc = RPMRC_OK;
+
+ check_fileList = newStringBuf();
+ genSourceRpmName(spec);
+
+ for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
+ char *nvr;
+ const char *a;
+
+ if (pkg->fileList == NULL)
+ continue;
+
+ headerPutString(pkg->header, RPMTAG_SOURCERPM, spec->sourceRpmName);
+
+ nvr = headerGetAsString(pkg->header, RPMTAG_NVRA);
+ rpmlog(RPMLOG_NOTICE, _("Processing files: %s\n"), nvr);
+ free(nvr);
+
+ if ((rc = processPackageFiles(spec, pkgFlags, pkg, installSpecialDoc, test)) != RPMRC_OK ||
+ (rc = rpmfcGenerateDepends(spec, pkg)) != RPMRC_OK)
+ goto exit;
+
+ a = headerGetString(pkg->header, RPMTAG_ARCH);
+ if (rstreq(a, "noarch") && headerGetNumber(pkg->header, RPMTAG_HEADERCOLOR) != 0) {
+ int terminate = rpmExpandNumeric("%{?_binaries_in_noarch_packages_terminate_build}");
+ rpmlog(terminate ? RPMLOG_ERR : RPMLOG_WARNING,
+ _("Arch dependent binaries in noarch package\n"));
+ if (terminate) {
+ rc = RPMRC_FAIL;
+ goto exit;
+ }
+ }
+ }
+
+ /* Now we have in fileList list of files from all packages.
+ * We pass it to a script which does the work of finding missing
+ * and duplicated files.
+ */
+
+
+ if (checkFiles(spec->buildRoot, check_fileList) > 0) {
+ rc = RPMRC_FAIL;
+ }
+exit:
+ check_fileList = freeStringBuf(check_fileList);
+
+ return rc;
+}