3 * The post-build, pre-packaging file tree walk to assemble the package
9 #define MYALLPERMS 07777
15 #include <sys/capability.h>
20 #include <elfutils/libdwelf.h>
23 #include <rpm/rpmpgp.h>
25 #include <rpm/rpmfc.h>
26 #include <rpm/rpmfileutil.h> /* rpmDoDigest() */
27 #include <rpm/rpmlog.h>
28 #include <rpm/rpmbase64.h>
30 #include "rpmio/rpmio_internal.h" /* XXX rpmioSlurp */
31 #include "misc/rpmfts.h"
32 #include "lib/rpmfi_internal.h" /* XXX fi->apath */
33 #include "lib/rpmug.h"
34 #include "build/rpmbuild_internal.h"
35 #include "build/rpmbuild_misc.h"
40 #define SKIPSPACE(s) { while (*(s) && risspace(*(s))) (s)++; }
41 #define SKIPWHITE(_x) {while (*(_x) && (risspace(*_x) || *(_x) == ',')) (_x)++;}
42 #define SKIPNONWHITE(_x){while (*(_x) &&!(risspace(*_x) || *(_x) == ',')) (_x)++;}
44 /* the following defines must be in sync with the equally hardcoded paths from
45 * scripts/find-debuginfo.sh
47 #define BUILD_ID_DIR "/usr/lib/.build-id"
48 #define DEBUG_SRC_DIR "/usr/src/debug"
49 #define DEBUG_LIB_DIR "/usr/lib/debug"
50 #define DEBUG_LIB_PREFIX "/usr/lib/debug/"
51 #define DEBUG_ID_DIR "/usr/lib/debug/.build-id"
52 #define DEBUG_DWZ_DIR "/usr/lib/debug/.dwz"
57 #define HASHTYPE fileRenameHash
58 #define HTKEYTYPE const char *
59 #define HTDATATYPE const char *
60 #include "lib/rpmhash.C"
68 SPECD_DEFFILEMODE = (1 << 0),
69 SPECD_DEFDIRMODE = (1 << 1),
70 SPECD_DEFUID = (1 << 2),
71 SPECD_DEFGID = (1 << 3),
72 SPECD_DEFVERIFY = (1 << 4),
74 SPECD_FILEMODE = (1 << 8),
75 SPECD_DIRMODE = (1 << 9),
76 SPECD_UID = (1 << 10),
77 SPECD_GID = (1 << 11),
78 SPECD_VERIFY = (1 << 12)
81 typedef rpmFlags specfFlags;
83 /* internal %files parsing state attributes */
85 RPMFILE_EXCLUDE = (1 << 16), /*!< from %%exclude */
86 RPMFILE_DOCDIR = (1 << 17), /*!< from %%docdir */
87 RPMFILE_DIR = (1 << 18), /*!< from %%dir */
88 RPMFILE_SPECIALDIR = (1 << 19), /*!< from special %%doc */
91 /* bits up to 15 (for now) reserved for exported rpmfileAttrs */
92 #define PARSEATTR_MASK 0x0000ffff
96 typedef struct FileListRec_s {
98 #define fl_dev fl_st.st_dev
99 #define fl_ino fl_st.st_ino
100 #define fl_mode fl_st.st_mode
101 #define fl_nlink fl_st.st_nlink
102 #define fl_uid fl_st.st_uid
103 #define fl_gid fl_st.st_gid
104 #define fl_rdev fl_st.st_rdev
105 #define fl_size fl_st.st_size
106 #define fl_mtime fl_st.st_mtime
108 char *diskPath; /* get file from here */
109 char *cpioPath; /* filename in cpio archive */
113 specfFlags specdFlags; /* which attributes have been explicitly specified. */
114 rpmVerifyFlags verifyFlags;
115 char *langs; /* XXX locales separated with | */
121 typedef struct AttrRec_s {
131 static StringBuf check_fileList = NULL;
133 typedef struct FileEntry_s {
134 rpmfileAttrs attrFlags;
135 specfFlags specdFlags;
136 rpmVerifyFlags verifyFlags;
142 /* these are only ever relevant for current entry */
149 typedef struct specialDir_s {
153 struct AttrRec_s def_ar;
160 struct FileEntry_s defEntry;
161 struct FileEntry_s curEntry;
166 typedef struct FileRecords_s {
173 * Package file tree walk data.
175 typedef struct FileList_s {
176 /* global filelist state */
179 int processingFailed;
183 rpmBuildPkgFlags pkgFlags;
186 /* actual file records */
187 struct FileRecords_s files;
189 /* active defaults */
190 struct FileEntry_s def;
192 /* current file-entry state */
193 struct FileEntry_s cur;
196 static void nullAttrRec(AttrRec ar)
198 memset(ar, 0, sizeof(*ar));
201 static void dupAttrRec(const AttrRec oar, AttrRec nar)
205 *nar = *oar; /* struct assignment */
208 /* Creates a default $defattr string. Can be used with argvAdd().
209 Caller owns the new string which needs to be freed when done. */
210 static char *mkattr(void)
213 rasprintf(&s, "%s(644,%s,%s,755)", "%defattr", UID_0_USER, GID_0_GROUP);
217 static void copyFileEntry(FileEntry src, FileEntry dest)
219 /* Copying struct makes just shallow copy */
222 /* Do also deep copying */
223 if (src->langs != NULL) {
224 dest->langs = argvNew();
225 argvAppend(&dest->langs, src->langs);
228 if (src->caps != NULL) {
229 dest->caps = xstrdup(src->caps);
233 static void FileEntryFree(FileEntry entry)
235 argvFree(entry->langs);
236 memset(entry, 0, sizeof(*entry));
244 static char *strtokWithQuotes(char *s, const char *delim)
246 static char *olds = NULL;
254 /* Skip leading delimiters */
255 s += strspn(s, delim);
259 /* Find the end of the token. */
263 /* Find next " char */
264 s = strchr(token, '"');
266 s = strpbrk(token, delim);
271 /* This token finishes the string */
272 olds = strchr(token, '\0');
274 /* Terminate the token and make olds point past it */
284 typedef const struct VFA {
285 const char * attribute;
291 static VFA_t const verifyAttrs[] = {
292 { "md5", RPMVERIFY_FILEDIGEST },
293 { "filedigest", RPMVERIFY_FILEDIGEST },
294 { "size", RPMVERIFY_FILESIZE },
295 { "link", RPMVERIFY_LINKTO },
296 { "user", RPMVERIFY_USER },
297 { "owner", RPMVERIFY_USER },
298 { "group", RPMVERIFY_GROUP },
299 { "mtime", RPMVERIFY_MTIME },
300 { "mode", RPMVERIFY_MODE },
301 { "rdev", RPMVERIFY_RDEV },
302 { "caps", RPMVERIFY_CAPS },
306 static rpmFlags vfaMatch(VFA_t *attrs, const char *token, rpmFlags *flags)
310 for (vfa = attrs; vfa->attribute != NULL; vfa++) {
311 if (rstreq(token, vfa->attribute)) {
320 * Parse %verify and %defverify from file manifest.
321 * @param buf current spec file line
322 * @param def parse for %defverify or %verify?
323 * @param entry file entry data (current or default)
324 * @return RPMRC_OK on success
326 static rpmRC parseForVerify(char * buf, int def, FileEntry entry)
328 char *p, *pe, *q = NULL;
329 const char *name = def ? "%defverify" : "%verify";
331 rpmVerifyFlags verifyFlags = RPMVERIFY_NONE;
332 rpmRC rc = RPMRC_FAIL;
334 if ((p = strstr(buf, name)) == NULL)
337 for (pe = p; (pe-p) < strlen(name); pe++)
343 rpmlog(RPMLOG_ERR, _("Missing '(' in %s %s\n"), name, pe);
347 /* Bracket %*verify args */
349 for (p = pe; *pe && *pe != ')'; pe++)
353 rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
357 /* Localize. Erase parsed string */
358 q = xmalloc((pe-p) + 1);
359 rstrlcpy(q, p, (pe-p) + 1);
363 for (p = q; *p != '\0'; p = pe) {
372 if (vfaMatch(verifyAttrs, p, &verifyFlags))
375 if (rstreq(p, "not")) {
378 rpmlog(RPMLOG_ERR, _("Invalid %s token: %s\n"), name, p);
383 entry->verifyFlags = negated ? ~(verifyFlags) : verifyFlags;
384 entry->specdFlags |= SPECD_VERIFY;
393 static int isAttrDefault(rpmstrPool pool, rpmsid arsid)
395 const char *ars = rpmstrPoolStr(pool, arsid);
396 return (ars && ars[0] == '-' && ars[1] == '\0');
400 * Parse %dev from file manifest.
401 * @param buf current spec file line
402 * @param cur current file entry data
403 * @return RPMRC_OK on success
405 static rpmRC parseForDev(char * buf, FileEntry cur)
408 const char * errstr = NULL;
409 char *p, *pe, *q = NULL;
410 rpmRC rc = RPMRC_FAIL; /* assume error */
411 char *attr_parameters = NULL;
413 if ((p = strstr(buf, (name = "%dev"))) == NULL)
416 for (pe = p; (pe-p) < strlen(name); pe++)
425 /* Bracket %dev args */
427 for (p = pe; *pe && *pe != ')'; pe++)
434 /* Localize. Erase parsed string */
435 q = xmalloc((pe-p) + 1);
436 rstrlcpy(q, p, (pe-p) + 1);
438 attr_parameters = xmalloc((pe-p) + 1);
439 rstrlcpy(attr_parameters, p, (pe-p) + 1);
445 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
455 p = pe; SKIPWHITE(p);
456 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe = '\0';
457 for (pe = p; *pe && risdigit(*pe); pe++)
460 cur->devmajor = atoi(p);
461 if (!(cur->devmajor >= 0 && cur->devmajor < 256)) {
471 p = pe; SKIPWHITE(p);
472 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe = '\0';
473 for (pe = p; *pe && risdigit(*pe); pe++)
476 cur->devminor = atoi(p);
477 if (!(cur->devminor >= 0 && cur->devminor < 256)) {
490 rpmlog(RPMLOG_ERR, _("Missing %s in %s(%s)\n"), errstr, name, attr_parameters);
492 free(attr_parameters);
498 * Parse %attr and %defattr from file manifest.
499 * @param pool string pool
500 * @param buf current spec file line
501 * @param def parse for %defattr or %attr?
502 * @param entry file entry data (current / default)
503 * @return 0 on success
505 static rpmRC parseForAttr(rpmstrPool pool, char * buf, int def, FileEntry entry)
507 const char *name = def ? "%defattr" : "%attr";
508 char *p, *pe, *q = NULL;
509 char *attr_parameters = NULL;
511 struct AttrRec_s arbuf;
513 rpmRC rc = RPMRC_FAIL;
515 if ((p = strstr(buf, name)) == NULL)
518 for (pe = p; (pe-p) < strlen(name); pe++)
524 rpmlog(RPMLOG_ERR, _("Missing '(' in %s %s\n"), name, pe);
528 /* Bracket %*attr args */
530 for (p = pe; *pe && *pe != ')'; pe++)
533 if (def) { /* %defattr */
539 _("Non-white space follows %s(): %s\n"), name, r);
544 /* Localize. Erase parsed string */
545 q = xmalloc((pe-p) + 1);
546 rstrlcpy(q, p, (pe-p) + 1);
548 attr_parameters = xmalloc((pe-p) + 1);
549 rstrlcpy(attr_parameters, p, (pe-p) + 1);
558 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
559 ar->ar_fmodestr = rpmstrPoolId(pool, p, 1);
560 p = pe; SKIPWHITE(p);
563 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
564 ar->ar_user = rpmstrPoolId(pool, p, 1);
565 p = pe; SKIPWHITE(p);
568 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
569 ar->ar_group = rpmstrPoolId(pool, p, 1);
570 p = pe; SKIPWHITE(p);
572 if (*p != '\0' && def) { /* %defattr */
573 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
574 ar->ar_dmodestr = rpmstrPoolId(pool, p, 1);
575 p = pe; SKIPWHITE(p);
578 if (!(ar->ar_fmodestr && ar->ar_user && ar->ar_group) || *p != '\0') {
579 rpmlog(RPMLOG_ERR, _("Bad syntax: %s(%s)\n"), name, attr_parameters);
583 /* Do a quick test on the mode argument and adjust for "-" */
584 if (ar->ar_fmodestr && !isAttrDefault(pool, ar->ar_fmodestr)) {
586 x = sscanf(rpmstrPoolStr(pool, ar->ar_fmodestr), "%o", &ui);
587 if ((x == 0) || (ar->ar_fmode & ~MYALLPERMS)) {
588 rpmlog(RPMLOG_ERR, _("Bad mode spec: %s(%s)\n"), name, attr_parameters);
596 if (ar->ar_dmodestr && !isAttrDefault(pool, ar->ar_dmodestr)) {
598 x = sscanf(rpmstrPoolStr(pool, ar->ar_dmodestr), "%o", &ui);
599 if ((x == 0) || (ar->ar_dmode & ~MYALLPERMS)) {
600 rpmlog(RPMLOG_ERR, _("Bad dirmode spec: %s(%s)\n"), name, attr_parameters);
608 if (!(ar->ar_user && !isAttrDefault(pool, ar->ar_user))) {
612 if (!(ar->ar_group && !isAttrDefault(pool, ar->ar_group))) {
616 dupAttrRec(ar, &(entry->ar));
618 /* XXX fix all this */
619 entry->specdFlags |= SPECD_UID | SPECD_GID | SPECD_FILEMODE | SPECD_DIRMODE;
624 free(attr_parameters);
629 static VFA_t const configAttrs[] = {
630 { "missingok", RPMFILE_MISSINGOK },
631 { "noreplace", RPMFILE_NOREPLACE },
636 * Parse %config from file manifest.
637 * @param buf current spec file line
638 * @param cur current file entry data
639 * @return RPMRC_OK on success
641 static rpmRC parseForConfig(char * buf, FileEntry cur)
643 char *p, *pe, *q = NULL;
645 rpmRC rc = RPMRC_FAIL;
647 if ((p = strstr(buf, (name = "%config"))) == NULL)
650 cur->attrFlags |= RPMFILE_CONFIG;
652 /* Erase "%config" token. */
653 for (pe = p; (pe-p) < strlen(name); pe++)
659 /* Bracket %config args */
661 for (p = pe; *pe && *pe != ')'; pe++)
665 rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
669 /* Localize. Erase parsed string. */
670 q = xmalloc((pe-p) + 1);
671 rstrlcpy(q, p, (pe-p) + 1);
675 for (p = q; *p != '\0'; p = pe) {
683 if (!vfaMatch(configAttrs, p, &(cur->attrFlags))) {
684 rpmlog(RPMLOG_ERR, _("Invalid %s token: %s\n"), name, p);
696 static rpmRC addLang(ARGV_t *av, const char *lang, size_t n, const char *ent)
698 rpmRC rc = RPMRC_FAIL;
700 rstrlcpy(lbuf, lang, sizeof(lbuf));
703 /* Sanity check locale length */
704 if (n < 1 || (n == 1 && *lang != 'C') || n >= 32) {
705 rpmlog(RPMLOG_ERR, _("Unusual locale length: \"%s\" in %%lang(%s)\n"),
710 /* Check for duplicate locales */
711 if (argvSearch(*av, lbuf, NULL)) {
712 rpmlog(RPMLOG_WARNING, _("Duplicate locale %s in %%lang(%s)\n"),
725 * Parse %lang from file manifest.
726 * @param buf current spec file line
727 * @param cur current file entry data
728 * @return RPMRC_OK on success
730 static rpmRC parseForLang(char * buf, FileEntry cur)
732 char *p, *pe, *q = NULL;
734 rpmRC rc = RPMRC_FAIL;
736 while ((p = strstr(buf, (name = "%lang"))) != NULL) {
738 for (pe = p; (pe-p) < strlen(name); pe++)
743 rpmlog(RPMLOG_ERR, _("Missing '(' in %s %s\n"), name, pe);
747 /* Bracket %lang args */
749 for (pe = p; *pe && *pe != ')'; pe++)
753 rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
757 /* Localize. Erase parsed string. */
758 q = xmalloc((pe-p) + 1);
759 rstrlcpy(q, p, (pe-p) + 1);
763 /* Parse multiple arguments from %lang */
764 for (p = q; *p != '\0'; p = pe) {
769 if (addLang(&(cur->langs), p, (pe-p), q))
772 if (*pe == ',') pe++; /* skip , if present */
785 * Parse %caps from file manifest.
786 * @param buf current spec file line
787 * @param cur current file entry data
788 * @return RPMRC_OK on success
790 static rpmRC parseForCaps(char * buf, FileEntry cur)
792 char *p, *pe, *q = NULL;
794 rpmRC rc = RPMRC_FAIL;
796 if ((p = strstr(buf, (name = "%caps"))) == NULL)
799 /* Erase "%caps" token. */
800 for (pe = p; (pe-p) < strlen(name); pe++)
806 /* Bracket %caps args */
808 for (p = pe; *pe && *pe != ')'; pe++)
812 rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
816 /* Localize. Erase parsed string. */
817 q = xmalloc((pe-p) + 1);
818 rstrlcpy(q, p, (pe-p) + 1);
825 cap_t fcaps = cap_from_text(q);
827 rpmlog(RPMLOG_ERR, _("Invalid capability: %s\n"), q);
830 /* run our string through cap_to_text() to get libcap presentation */
831 captxt = cap_to_text(fcaps, NULL);
832 cur->caps = xstrdup(captxt);
837 rpmlog(RPMLOG_ERR, _("File capability support not built in\n"));
850 static VFA_t const virtualAttrs[] = {
851 { "%dir", RPMFILE_DIR },
852 { "%docdir", RPMFILE_DOCDIR },
853 { "%doc", RPMFILE_DOC },
854 { "%ghost", RPMFILE_GHOST },
855 { "%exclude", RPMFILE_EXCLUDE },
856 { "%readme", RPMFILE_README },
857 { "%license", RPMFILE_LICENSE },
858 { "%pubkey", RPMFILE_PUBKEY },
859 { "%missingok", RPMFILE_MISSINGOK },
860 { "%artifact", RPMFILE_ARTIFACT },
865 * Parse simple attributes (e.g. %dir) from file manifest.
866 * @param buf current spec file line
867 * @param cur current file entry data
868 * @retval *fileNames file names
869 * @return RPMRC_OK on success
871 static rpmRC parseForSimple(char * buf, FileEntry cur, ARGV_t * fileNames)
874 rpmRC res = RPMRC_OK;
875 int allow_relative = (RPMFILE_PUBKEY|RPMFILE_DOC|RPMFILE_LICENSE);
878 while ((s = strtokWithQuotes(t, " \t\n")) != NULL) {
881 /* Set flags for virtual file attributes */
882 if (vfaMatch(virtualAttrs, s, &(cur->attrFlags)))
885 /* normally paths need to be absolute */
887 if (!(cur->attrFlags & allow_relative)) {
888 rpmlog(RPMLOG_ERR, _("File must begin with \"/\": %s\n"), s);
892 /* non-absolute %doc and %license paths are special */
893 if (cur->attrFlags & (RPMFILE_DOC | RPMFILE_LICENSE))
894 cur->attrFlags |= RPMFILE_SPECIALDIR;
896 argvAdd(fileNames, s);
904 static int compareFileListRecs(const void * ap, const void * bp)
906 const char *a = ((FileListRec)ap)->cpioPath;
907 const char *b = ((FileListRec)bp)->cpioPath;
912 * Test if file is located in a %docdir.
913 * @param docDirs doc dirs
914 * @param fileName file path
915 * @return 1 if doc file, 0 if not
917 static int isDoc(ARGV_const_t docDirs, const char * fileName)
921 k = strlen(fileName);
922 for (ARGV_const_t dd = docDirs; *dd; dd++) {
924 if (l < k && rstreqn(fileName, *dd, l) && fileName[l] == '/')
930 static int isHardLink(FileListRec flp, FileListRec tlp)
932 return ((S_ISREG(flp->fl_mode) && S_ISREG(tlp->fl_mode)) &&
933 ((flp->fl_nlink > 1) && (flp->fl_nlink == tlp->fl_nlink)) &&
934 (flp->fl_ino == tlp->fl_ino) &&
935 (flp->fl_dev == tlp->fl_dev));
939 * Verify that file attributes scope over hardlinks correctly.
940 * If partial hardlink sets are possible, then add tracking dependency.
941 * @param files package file records
942 * @return 1 if partial hardlink sets can exist, 0 otherwise.
944 static int checkHardLinks(FileRecords files)
946 FileListRec ilp, jlp;
949 for (i = 0; i < files->used; i++) {
950 ilp = files->recs + i;
951 if (!(S_ISREG(ilp->fl_mode) && ilp->fl_nlink > 1))
954 for (j = i + 1; j < files->used; j++) {
955 jlp = files->recs + j;
956 if (isHardLink(ilp, jlp)) {
964 static int seenHardLink(FileRecords files, FileListRec flp, rpm_ino_t *fileid)
966 for (FileListRec ilp = files->recs; ilp < flp; ilp++) {
967 if (isHardLink(flp, ilp)) {
968 *fileid = ilp - files->recs;
976 * Add file entries to header.
977 * @todo Should directories have %doc/%config attributes? (#14531)
978 * @todo Remove RPMTAG_OLDFILENAMES, add dirname/basename instead.
979 * @param fl package file tree walk data
980 * @param pkg (sub) package
981 * @param isSrc pass 1 for source packages 0 otherwise
983 static void genCpioListAndHeader(FileList fl, Package pkg, int isSrc)
988 uint32_t defaultalgo = PGPHASHALGO_MD5, digestalgo;
989 rpm_loff_t totalFileSize = 0;
990 Header h = pkg->header; /* just a shortcut */
991 int override_date = 0;
992 time_t source_date_epoch;
993 char *srcdate = getenv("SOURCE_DATE_EPOCH");
995 /* Limit the maximum date to SOURCE_DATE_EPOCH if defined
996 * similar to the tar --clamp-mtime option
997 * https://reproducible-builds.org/specs/source-date-epoch/
999 if (srcdate && rpmExpandNumeric("%{?clamp_mtime_to_source_date_epoch}")) {
1002 source_date_epoch = strtol(srcdate, &endptr, 10);
1003 if (srcdate == endptr || *endptr || errno != 0) {
1004 rpmlog(RPMLOG_ERR, _("unable to parse %s=%s\n"), "SOURCE_DATE_EPOCH", srcdate);
1011 * See if non-md5 file digest algorithm is requested. If not
1012 * specified, quietly assume md5. Otherwise check if supported type.
1014 digestalgo = rpmExpandNumeric(isSrc ? "%{_source_filedigest_algorithm}" :
1015 "%{_binary_filedigest_algorithm}");
1016 if (digestalgo == 0) {
1017 digestalgo = defaultalgo;
1020 if (rpmDigestLength(digestalgo) == 0) {
1021 rpmlog(RPMLOG_WARNING,
1022 _("Unknown file digest algorithm %u, falling back to MD5\n"),
1024 digestalgo = defaultalgo;
1027 /* Adjust paths if needed */
1028 if (!isSrc && pkg->removePostfixes) {
1029 pkg->fileRenameMap = fileRenameHashCreate(fl->files.used,
1031 (fileRenameHashFreeKey)rfree, (fileRenameHashFreeData)rfree);
1032 for (i = 0, flp = fl->files.recs; i < fl->files.used; i++, flp++) {
1033 char * cpiopath = flp->cpioPath;
1034 char * cpiopath_orig = xstrdup(cpiopath);
1036 for (ARGV_const_t postfix_p = pkg->removePostfixes; *postfix_p; postfix_p++) {
1037 int len = strlen(*postfix_p);
1038 int plen = strlen(cpiopath);
1039 if (len <= plen && !strncmp(cpiopath+plen-len, *postfix_p, len)) {
1040 cpiopath[plen-len] = '\0';
1041 if (plen-len > 0 && cpiopath[plen-len-1] == '/') {
1042 cpiopath[plen-len-1] = '\0';
1046 if (strcmp(cpiopath_orig, cpiopath))
1047 fileRenameHashAddEntry(pkg->fileRenameMap, xstrdup(cpiopath), cpiopath_orig);
1049 _free(cpiopath_orig);
1053 /* Sort the big list */
1054 qsort(fl->files.recs, fl->files.used,
1055 sizeof(*(fl->files.recs)), compareFileListRecs);
1057 pkg->dpaths = xmalloc((fl->files.used + 1) * sizeof(*pkg->dpaths));
1059 /* Generate the header. */
1060 for (i = 0, flp = fl->files.recs; i < fl->files.used; i++, flp++) {
1061 rpm_ino_t fileid = flp - fl->files.recs;
1063 /* Merge duplicate entries. */
1064 while (i < (fl->files.used - 1) &&
1065 rstreq(flp->cpioPath, flp[1].cpioPath)) {
1067 /* Two entries for the same file found, merge the entries. */
1068 /* Note that an %exclude is a duplication of a file reference */
1071 flp[1].flags |= flp->flags;
1073 if (!(flp[1].flags & RPMFILE_EXCLUDE))
1074 rpmlog(RPMLOG_WARNING, _("File listed twice: %s\n"),
1078 if (S_ISDIR(flp->fl_mode)) {
1079 if ((flp[1].specdFlags & (SPECD_DIRMODE | SPECD_DEFDIRMODE)) <
1080 (flp->specdFlags & (SPECD_DIRMODE | SPECD_DEFDIRMODE)))
1081 flp[1].fl_mode = flp->fl_mode;
1083 if ((flp[1].specdFlags & (SPECD_FILEMODE | SPECD_DEFFILEMODE)) <
1084 (flp->specdFlags & (SPECD_FILEMODE | SPECD_DEFFILEMODE)))
1085 flp[1].fl_mode = flp->fl_mode;
1089 if ((flp[1].specdFlags & (SPECD_UID | SPECD_DEFUID)) <
1090 (flp->specdFlags & (SPECD_UID | SPECD_DEFUID)))
1092 flp[1].fl_uid = flp->fl_uid;
1093 flp[1].uname = flp->uname;
1097 if ((flp[1].specdFlags & (SPECD_GID | SPECD_DEFGID)) <
1098 (flp->specdFlags & (SPECD_GID | SPECD_DEFGID)))
1100 flp[1].fl_gid = flp->fl_gid;
1101 flp[1].gname = flp->gname;
1105 if ((flp[1].specdFlags & (SPECD_VERIFY | SPECD_DEFVERIFY)) <
1106 (flp->specdFlags & (SPECD_VERIFY | SPECD_DEFVERIFY)))
1107 flp[1].verifyFlags = flp->verifyFlags;
1109 /* XXX to-do: language */
1114 /* Skip files that were marked with %exclude. */
1115 if (flp->flags & RPMFILE_EXCLUDE)
1117 argvAdd(&pkg->fileExcludeList, flp->cpioPath);
1121 /* Collect on-disk paths for archive creation */
1122 pkg->dpaths[npaths++] = xstrdup(flp->diskPath);
1124 headerPutString(h, RPMTAG_OLDFILENAMES, flp->cpioPath);
1125 headerPutString(h, RPMTAG_FILEUSERNAME,
1126 rpmstrPoolStr(fl->pool, flp->uname));
1127 headerPutString(h, RPMTAG_FILEGROUPNAME,
1128 rpmstrPoolStr(fl->pool, flp->gname));
1130 /* Only use 64bit filesizes tag if required. */
1131 if (fl->largeFiles) {
1132 rpm_loff_t rsize64 = (rpm_loff_t)flp->fl_size;
1133 headerPutUint64(h, RPMTAG_LONGFILESIZES, &rsize64, 1);
1134 (void) rpmlibNeedsFeature(pkg, "LargeFiles", "4.12.0-1");
1136 rpm_off_t rsize32 = (rpm_off_t)flp->fl_size;
1137 headerPutUint32(h, RPMTAG_FILESIZES, &rsize32, 1);
1139 /* Excludes and dupes have been filtered out by now. */
1140 if (S_ISREG(flp->fl_mode)) {
1141 if (flp->fl_nlink == 1 || !seenHardLink(&fl->files, flp, &fileid)) {
1142 totalFileSize += flp->fl_size;
1146 if (override_date && flp->fl_mtime > source_date_epoch) {
1147 flp->fl_mtime = source_date_epoch;
1150 * For items whose size varies between systems, always explicitly
1151 * cast to the header type before inserting.
1152 * TODO: check and warn if header type overflows for each case.
1154 { rpm_time_t rtime = (rpm_time_t) flp->fl_mtime;
1155 headerPutUint32(h, RPMTAG_FILEMTIMES, &rtime, 1);
1158 { rpm_mode_t rmode = (rpm_mode_t) flp->fl_mode;
1159 headerPutUint16(h, RPMTAG_FILEMODES, &rmode, 1);
1162 { rpm_rdev_t rrdev = (rpm_rdev_t) flp->fl_rdev;
1163 headerPutUint16(h, RPMTAG_FILERDEVS, &rrdev, 1);
1167 * To allow rpmbuild to work on filesystems with 64bit inodes numbers,
1168 * remap them into 32bit integers based on filelist index, just
1169 * preserving semantics for determining hardlinks.
1170 * Start at 1 as inode zero as that could be considered as an error.
1171 * Since we flatten all the inodes to appear within a single fs,
1172 * we also need to flatten the devices.
1174 { rpm_ino_t rino = fileid + 1;
1175 rpm_dev_t rdev = flp->fl_dev ? 1 : 0;
1176 headerPutUint32(h, RPMTAG_FILEINODES, &rino, 1);
1177 headerPutUint32(h, RPMTAG_FILEDEVICES, &rdev, 1);
1180 headerPutString(h, RPMTAG_FILELANGS, flp->langs);
1183 headerPutString(h, RPMTAG_FILECAPS, flp->caps);
1187 if (S_ISREG(flp->fl_mode) && !(flp->flags & RPMFILE_GHOST))
1188 (void) rpmDoDigest(digestalgo, flp->diskPath, 1,
1189 (unsigned char *)buf, NULL);
1190 headerPutString(h, RPMTAG_FILEDIGESTS, buf);
1193 if (S_ISLNK(flp->fl_mode)) {
1194 ssize_t llen = readlink(flp->diskPath, buf, BUFSIZ-1);
1196 rpmlog(RPMLOG_ERR, _("reading symlink %s failed: %s\n"),
1197 flp->diskPath, strerror(errno));
1198 fl->processingFailed = 1;
1201 if (buf[0] == '/' && !rstreq(fl->buildRoot, "/") &&
1202 rstreqn(buf, fl->buildRoot, fl->buildRootLen)) {
1204 _("Symlink points to BuildRoot: %s -> %s\n"),
1205 flp->cpioPath, buf);
1206 fl->processingFailed = 1;
1210 headerPutString(h, RPMTAG_FILELINKTOS, buf);
1212 if (flp->flags & RPMFILE_GHOST) {
1213 flp->verifyFlags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE |
1214 RPMVERIFY_LINKTO | RPMVERIFY_MTIME);
1216 headerPutUint32(h, RPMTAG_FILEVERIFYFLAGS, &(flp->verifyFlags),1);
1218 if (!isSrc && isDoc(fl->docDirs, flp->cpioPath))
1219 flp->flags |= RPMFILE_DOC;
1220 /* XXX Should directories have %doc/%config attributes? (#14531) */
1221 if (S_ISDIR(flp->fl_mode))
1222 flp->flags &= ~(RPMFILE_CONFIG|RPMFILE_DOC|RPMFILE_LICENSE);
1223 /* Strip internal parse data */
1224 flp->flags &= PARSEATTR_MASK;
1226 headerPutUint32(h, RPMTAG_FILEFLAGS, &(flp->flags) ,1);
1228 pkg->dpaths[npaths] = NULL;
1230 if (totalFileSize < UINT32_MAX) {
1231 rpm_off_t totalsize = totalFileSize;
1232 headerPutUint32(h, RPMTAG_SIZE, &totalsize, 1);
1234 rpm_loff_t totalsize = totalFileSize;
1235 headerPutUint64(h, RPMTAG_LONGSIZE, &totalsize, 1);
1238 if (digestalgo != defaultalgo) {
1239 headerPutUint32(h, RPMTAG_FILEDIGESTALGO, &digestalgo, 1);
1240 rpmlibNeedsFeature(pkg, "FileDigests", "4.6.0-1");
1244 rpmlibNeedsFeature(pkg, "FileCaps", "4.6.1-1");
1247 if (!isSrc && !rpmExpandNumeric("%{_noPayloadPrefix}"))
1248 (void) rpmlibNeedsFeature(pkg, "PayloadFilesHavePrefix", "4.0-1");
1250 /* rpmfiNew() only groks compressed filelists */
1251 headerConvert(h, HEADERCONV_COMPRESSFILELIST);
1252 pkg->cpioList = rpmfilesNew(NULL, h, RPMTAG_BASENAMES,
1253 (RPMFI_NOFILEUSER|RPMFI_NOFILEGROUP));
1255 if (pkg->cpioList == NULL || rpmfilesFC(pkg->cpioList) != npaths) {
1256 fl->processingFailed = 1;
1259 if (fl->pkgFlags & RPMBUILD_PKG_NODIRTOKENS) {
1260 /* Uncompress filelist if legacy format requested */
1261 headerConvert(h, HEADERCONV_EXPANDFILELIST);
1263 /* Binary packages with dirNames cannot be installed by legacy rpm. */
1264 (void) rpmlibNeedsFeature(pkg, "CompressedFileNames", "3.0.4-1");
1268 static FileRecords FileRecordsFree(FileRecords files)
1270 for (int i = 0; i < files->used; i++) {
1271 free(files->recs[i].diskPath);
1272 free(files->recs[i].cpioPath);
1273 free(files->recs[i].langs);
1274 free(files->recs[i].caps);
1280 static void FileListFree(FileList fl)
1282 FileEntryFree(&(fl->cur));
1283 FileEntryFree(&(fl->def));
1284 FileRecordsFree(&(fl->files));
1285 free(fl->buildRoot);
1286 argvFree(fl->docDirs);
1287 rpmstrPoolFree(fl->pool);
1291 static rpmRC recurseDir(FileList fl, const char * diskPath);
1293 /* Hack up a stat structure for a %dev or non-existing %ghost */
1294 static struct stat * fakeStat(FileEntry cur, struct stat * statp)
1296 time_t now = time(NULL);
1299 statp->st_rdev = ((cur->devmajor & 0xff) << 8) | (cur->devminor & 0xff);
1300 statp->st_dev = statp->st_rdev;
1301 statp->st_mode = (cur->devtype == 'b' ? S_IFBLK : S_IFCHR);
1303 /* non-existing %ghost file or directory */
1304 statp->st_mode = cur->isDir ? S_IFDIR : S_IFREG;
1305 /* can't recurse into non-existing directory */
1309 statp->st_mode |= (cur->ar.ar_fmode & 0777);
1310 statp->st_atime = now;
1311 statp->st_mtime = now;
1312 statp->st_ctime = now;
1313 statp->st_nlink = 1;
1318 * Add a file to the package manifest.
1319 * @param fl package file tree walk data
1320 * @param diskPath path to file
1321 * @param statp file stat (possibly NULL)
1322 * @return RPMRC_OK on success
1324 static rpmRC addFile(FileList fl, const char * diskPath,
1325 struct stat * statp)
1327 size_t plen = strlen(diskPath);
1329 const char *cpioPath;
1330 struct stat statbuf;
1334 const char *fileUname;
1335 const char *fileGname;
1336 rpmRC rc = RPMRC_FAIL; /* assume failure */
1338 /* Strip trailing slash. The special case of '/' path is handled below. */
1339 if (plen > 0 && diskPath[plen - 1] == '/') {
1340 diskPath = strcpy(buf, diskPath);
1341 buf[plen - 1] = '\0';
1343 cpioPath = diskPath;
1345 if (strncmp(diskPath, fl->buildRoot, fl->buildRootLen)) {
1346 rpmlog(RPMLOG_ERR, _("Path is outside buildroot: %s\n"), diskPath);
1350 /* Path may have prepended buildRoot, so locate the original filename. */
1352 * XXX There are 3 types of entry into addFile:
1354 * From diskUrl statp
1355 * =====================================================
1356 * processBinaryFile path NULL
1357 * processBinaryFile glob result path NULL
1361 if (fl->buildRoot && !rstreq(fl->buildRoot, "/"))
1362 cpioPath += fl->buildRootLen;
1364 /* XXX make sure '/' can be packaged also */
1365 if (*cpioPath == '\0')
1369 * Unless recursing, we dont have stat() info at hand. Handle the
1370 * various cases, preserving historical behavior wrt %dev():
1371 * - for %dev() entries we fake it up whether the file exists or not
1372 * - otherwise try to grab the data by lstat()
1373 * - %ghost entries might not exist, fake it up
1375 if (statp == NULL) {
1376 memset(&statbuf, 0, sizeof(statbuf));
1378 if (fl->cur.devtype) {
1379 statp = fakeStat(&(fl->cur), &statbuf);
1380 } else if (lstat(diskPath, &statbuf) == 0) {
1382 } else if (fl->cur.attrFlags & RPMFILE_GHOST) {
1383 statp = fakeStat(&(fl->cur), &statbuf);
1385 int lvl = RPMLOG_ERR;
1386 const char *msg = fl->cur.isDir ? _("Directory not found: %s\n") :
1387 _("File not found: %s\n");
1388 if (fl->cur.attrFlags & RPMFILE_EXCLUDE) {
1389 lvl = RPMLOG_WARNING;
1392 rpmlog(lvl, msg, diskPath);
1397 /* Error out when a non-directory is specified as one in spec */
1398 if (fl->cur.isDir && (statp == &statbuf) && !S_ISDIR(statp->st_mode)) {
1399 rpmlog(RPMLOG_ERR, _("Not a directory: %s\n"), diskPath);
1403 /* Don't recurse into explicit %dir, don't double-recurse from fts */
1404 if ((fl->cur.isDir != 1) && (statp == &statbuf) && S_ISDIR(statp->st_mode)) {
1405 return recurseDir(fl, diskPath);
1408 fileMode = statp->st_mode;
1409 fileUid = statp->st_uid;
1410 fileGid = statp->st_gid;
1412 /* Explicit %attr() always wins */
1413 if (fl->cur.ar.ar_fmodestr) {
1414 if (S_ISLNK(fileMode)) {
1415 rpmlog(RPMLOG_WARNING,
1416 "Explicit %%attr() mode not applicable to symlink: %s\n",
1420 fileMode |= fl->cur.ar.ar_fmode;
1423 /* ...but %defattr() for directories and files is different */
1424 if (S_ISDIR(fileMode)) {
1425 if (fl->def.ar.ar_dmodestr) {
1427 fileMode |= fl->def.ar.ar_dmode;
1429 } else if (!S_ISLNK(fileMode) && fl->def.ar.ar_fmodestr) {
1431 fileMode |= fl->def.ar.ar_fmode;
1434 if (fl->cur.ar.ar_user) {
1435 fileUname = rpmstrPoolStr(fl->pool, fl->cur.ar.ar_user);
1436 } else if (fl->def.ar.ar_user) {
1437 fileUname = rpmstrPoolStr(fl->pool, fl->def.ar.ar_user);
1439 fileUname = rpmugUname(fileUid);
1441 if (fl->cur.ar.ar_group) {
1442 fileGname = rpmstrPoolStr(fl->pool, fl->cur.ar.ar_group);
1443 } else if (fl->def.ar.ar_group) {
1444 fileGname = rpmstrPoolStr(fl->pool, fl->def.ar.ar_group);
1446 fileGname = rpmugGname(fileGid);
1449 /* Default user/group to builder's user/group */
1450 if (fileUname == NULL)
1451 fileUname = rpmugUname(getuid());
1452 if (fileGname == NULL)
1453 fileGname = rpmugGname(getgid());
1455 /* S_XXX macro must be consistent with type in find call at check-files script */
1456 if (check_fileList && (S_ISREG(fileMode) || S_ISLNK(fileMode))) {
1457 appendStringBuf(check_fileList, diskPath);
1458 appendStringBuf(check_fileList, "\n");
1461 /* Add to the file list */
1462 if (fl->files.used == fl->files.alloced) {
1463 fl->files.alloced += 128;
1464 fl->files.recs = xrealloc(fl->files.recs,
1465 fl->files.alloced * sizeof(*(fl->files.recs)));
1468 { FileListRec flp = &fl->files.recs[fl->files.used];
1470 flp->fl_st = *statp; /* structure assignment */
1471 flp->fl_mode = fileMode;
1472 flp->fl_uid = fileUid;
1473 flp->fl_gid = fileGid;
1474 if (S_ISDIR(fileMode))
1477 flp->cpioPath = xstrdup(cpioPath);
1478 flp->diskPath = xstrdup(diskPath);
1479 flp->uname = rpmstrPoolId(fl->pool, fileUname, 1);
1480 flp->gname = rpmstrPoolId(fl->pool, fileGname, 1);
1482 if (fl->cur.langs) {
1483 flp->langs = argvJoin(fl->cur.langs, "|");
1485 flp->langs = xstrdup("");
1489 flp->caps = xstrdup(fl->cur.caps);
1491 flp->caps = xstrdup("");
1494 flp->flags = fl->cur.attrFlags;
1495 flp->specdFlags = fl->cur.specdFlags;
1496 flp->verifyFlags = fl->cur.verifyFlags;
1498 if (!(flp->flags & RPMFILE_EXCLUDE) && S_ISREG(flp->fl_mode)) {
1499 if (flp->fl_size >= UINT32_MAX) {
1510 fl->processingFailed = 1;
1516 * Add directory (and all of its files) to the package manifest.
1517 * @param fl package file tree walk data
1518 * @param diskPath path to file
1519 * @return RPMRC_OK on success
1521 static rpmRC recurseDir(FileList fl, const char * diskPath)
1526 int myFtsOpts = (FTS_COMFOLLOW | FTS_NOCHDIR | FTS_PHYSICAL);
1527 rpmRC rc = RPMRC_FAIL;
1529 ftsSet[0] = (char *) diskPath;
1531 ftsp = Fts_open(ftsSet, myFtsOpts, NULL);
1532 while ((fts = Fts_read(ftsp)) != NULL) {
1533 switch (fts->fts_info) {
1534 case FTS_D: /* preorder directory */
1535 case FTS_F: /* regular file */
1536 case FTS_SL: /* symbolic link */
1537 case FTS_SLNONE: /* symbolic link without target */
1538 case FTS_DEFAULT: /* none of the above */
1539 rc = addFile(fl, fts->fts_accpath, fts->fts_statp);
1541 case FTS_DOT: /* dot or dot-dot */
1542 case FTS_DP: /* postorder directory */
1545 case FTS_NS: /* stat(2) failed */
1546 case FTS_DNR: /* unreadable directory */
1547 case FTS_ERR: /* error; errno is set */
1548 case FTS_DC: /* directory that causes cycles */
1549 case FTS_NSOK: /* no stat(2) requested */
1550 case FTS_INIT: /* initialized only */
1551 case FTS_W: /* whiteout object */
1559 (void) Fts_close(ftsp);
1565 * Add a pubkey/icon to a binary package.
1567 * @param fl package file tree walk data
1568 * @param fileName path to file, relative is builddir, absolute buildroot.
1569 * @param tag tag to add
1570 * @return RPMRC_OK on success
1572 static rpmRC processMetadataFile(Package pkg, FileList fl,
1573 const char * fileName, rpmTagVal tag)
1575 const char * buildDir = "%{_builddir}/%{?buildsubdir}/";
1578 uint8_t * pkt = NULL;
1581 rpmRC rc = RPMRC_FAIL;
1584 if (*fileName == '/') {
1585 fn = rpmGenPath(fl->buildRoot, NULL, fileName);
1588 fn = rpmGenPath(buildDir, NULL, fileName);
1592 rpmlog(RPMLOG_ERR, _("%s: can't load unknown tag (%d).\n"),
1596 case RPMTAG_PUBKEYS: {
1597 if ((xx = pgpReadPkts(fn, &pkt, (size_t *)&pktlen)) <= 0) {
1598 rpmlog(RPMLOG_ERR, _("%s: public key read failed.\n"), fn);
1601 if (xx != PGPARMOR_PUBKEY) {
1602 rpmlog(RPMLOG_ERR, _("%s: not an armored public key.\n"), fn);
1605 apkt = pgpArmorWrap(PGPARMOR_PUBKEY, pkt, pktlen);
1611 rpmlog(RPMLOG_ERR, _("%s: failed to encode\n"), fn);
1615 headerPutString(pkg->header, tag, apkt);
1619 rc = addFile(fl, fn, NULL);
1626 fl->processingFailed = 1;
1632 /* add a file with possible virtual attributes to the file list */
1633 static void argvAddAttr(ARGV_t *filesp, rpmfileAttrs attrs, const char *path)
1637 for (VFA_t *vfa = virtualAttrs; vfa->attribute != NULL; vfa++) {
1638 if (vfa->flag & attrs)
1639 line = rstrscat(&line, vfa->attribute, " ", NULL);
1641 line = rstrscat(&line, path, NULL);
1642 argvAdd(filesp, line);
1647 /* How build id links are generated. See macros.in for description. */
1648 #define BUILD_IDS_NONE 0
1649 #define BUILD_IDS_ALLDEBUG 1
1650 #define BUILD_IDS_SEPARATE 2
1651 #define BUILD_IDS_COMPAT 3
1653 static int addNewIDSymlink(ARGV_t *files,
1654 char *targetpath, char *idlinkpath,
1655 int isDbg, int *dups)
1657 const char *linkerr = _("failed symlink");
1661 char *origpath, *linkpath;
1664 rasprintf(&linkpath, "%s.debug", idlinkpath);
1666 linkpath = idlinkpath;
1667 origpath = linkpath;
1669 while (faccessat(AT_FDCWD, linkpath, F_OK, AT_SYMLINK_NOFOLLOW) == 0) {
1670 /* We don't care about finding dups for compat links, they are
1671 OK as is. Otherwise we will need to double check if
1672 existing link points to the correct target. */
1679 char ltarget[PATH_MAX];
1681 /* In short-circuited builds the link might already exist */
1682 if ((llen = readlink(linkpath, ltarget, sizeof(ltarget)-1)) != -1) {
1683 ltarget[llen] = '\0';
1684 if (rstreq(ltarget, targetpath)) {
1693 rasprintf(&linkpath, "%s.%d%s", idlinkpath, nr,
1694 isDbg ? ".debug" : "");
1697 if (!exists && symlink(targetpath, linkpath) < 0) {
1699 rpmlog(RPMLOG_ERR, "%s: %s -> %s: %m\n",
1700 linkerr, linkpath, targetpath);
1702 argvAddAttr(files, RPMFILE_ARTIFACT, linkpath);
1706 /* Lets see why there are multiple build-ids. If the original
1707 targets are hard linked, then it is OK, otherwise warn
1708 something fishy is going on. Would be nice to call
1709 something like eu-elfcmp to see if they are really the same
1711 struct stat st1, st2;
1712 if (stat (origpath, &st1) != 0) {
1713 rpmlog(RPMLOG_WARNING, _("Duplicate build-id, stat %s: %m\n"),
1715 } else if (stat (linkpath, &st2) != 0) {
1716 rpmlog(RPMLOG_WARNING, _("Duplicate build-id, stat %s: %m\n"),
1718 } else if (!(S_ISREG(st1.st_mode) && S_ISREG(st2.st_mode)
1719 && st1.st_nlink > 1 && st2.st_nlink == st1.st_nlink
1720 && st1.st_ino == st2.st_ino && st1.st_dev == st2.st_dev)) {
1721 char *rpath1 = realpath(origpath, NULL);
1722 char *rpath2 = realpath(linkpath, NULL);
1723 rpmlog(RPMLOG_WARNING, _("Duplicate build-ids %s and %s\n"),
1740 static int generateBuildIDs(FileList fl, ARGV_t *files)
1746 char **paths = NULL;
1747 size_t nr_ids, allocated;
1748 nr_ids = allocated = 0;
1750 /* How are we supposed to create the build-id links? */
1751 char *build_id_links_macro = rpmExpand("%{?_build_id_links}", NULL);
1753 if (*build_id_links_macro == '\0') {
1754 rpmlog(RPMLOG_WARNING,
1755 _("_build_id_links macro not set, assuming 'compat'\n"));
1756 build_id_links = BUILD_IDS_COMPAT;
1757 } else if (strcmp(build_id_links_macro, "none") == 0) {
1758 build_id_links = BUILD_IDS_NONE;
1759 } else if (strcmp(build_id_links_macro, "alldebug") == 0) {
1760 build_id_links = BUILD_IDS_ALLDEBUG;
1761 } else if (strcmp(build_id_links_macro, "separate") == 0) {
1762 build_id_links = BUILD_IDS_SEPARATE;
1763 } else if (strcmp(build_id_links_macro, "compat") == 0) {
1764 build_id_links = BUILD_IDS_COMPAT;
1768 _("_build_id_links macro set to unknown value '%s'\n"),
1769 build_id_links_macro);
1770 build_id_links = BUILD_IDS_NONE;
1772 free(build_id_links_macro);
1774 if (build_id_links == BUILD_IDS_NONE || rc != 0)
1777 /* Historically we have only checked build_ids when __debug_package
1778 was defined. So don't terminate the build if __debug_package is
1779 unset, even when _missing_build_ids_terminate_build is. */
1780 int terminate = (rpmExpandNumeric("%{?_missing_build_ids_terminate_build}")
1781 && rpmExpandNumeric("%{?__debug_package}"));
1783 /* Collect and check all build-ids for ELF files in this package. */
1786 for (i = 0, flp = fl->files.recs; i < fl->files.used; i++, flp++) {
1788 if (lstat(flp->diskPath, &sbuf) == 0 && S_ISREG (sbuf.st_mode)) {
1789 /* We determine whether this is a main or
1790 debug ELF based on path. */
1791 int isDbg = strncmp (flp->cpioPath,
1792 DEBUG_LIB_PREFIX, strlen (DEBUG_LIB_PREFIX)) == 0;
1794 /* For the main package files mimic what find-debuginfo.sh does.
1795 Only check build-ids for executable files. Debug files are
1796 always non-executable. */
1798 && (sbuf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0)
1801 int fd = open (flp->diskPath, O_RDONLY);
1803 /* Only real ELF files, that are ET_EXEC, ET_DYN or
1804 kernel modules (ET_REL files with names ending in .ko)
1805 should have build-ids. */
1807 Elf *elf = elf_begin (fd, ELF_C_READ, NULL);
1808 if (elf != NULL && elf_kind(elf) == ELF_K_ELF
1809 && gelf_getehdr(elf, &ehdr) != NULL
1810 && (ehdr.e_type == ET_EXEC || ehdr.e_type == ET_DYN
1811 || (ehdr.e_type == ET_REL
1812 && rpmFileHasSuffix (flp->diskPath, ".ko")))) {
1813 const void *build_id;
1814 ssize_t len = dwelf_elf_gnu_build_id (elf, &build_id);
1815 /* len == -1 means error. Zero means no
1816 build-id. We want at least a length of 2 so we
1817 have at least a xx/yy (hex) dir/file. But
1818 reasonable build-ids are between 16 bytes (md5
1819 is 128 bits) and 64 bytes (largest sha3 is 512
1820 bits), common is 20 bytes (sha1 is 160 bits). */
1821 if (len >= 16 && len <= 64) {
1827 else if (build_id_links != BUILD_IDS_ALLDEBUG) {
1832 const unsigned char *p = build_id;
1833 const unsigned char *end = p + len;
1835 if (allocated <= nr_ids) {
1837 paths = xrealloc (paths,
1838 allocated * sizeof(char *));
1839 ids = xrealloc (ids,
1840 allocated * sizeof(char *));
1843 paths[nr_ids] = xstrdup(flp->cpioPath);
1844 id_str = ids[nr_ids] = xmalloc(2 * len + 1);
1846 id_str += sprintf(id_str, "%02x",
1853 rpmlog(terminate ? RPMLOG_ERR : RPMLOG_WARNING,
1854 _("error reading build-id in %s: %s\n"),
1855 flp->diskPath, elf_errmsg (-1));
1856 } else if (len == 0) {
1857 rpmlog(terminate ? RPMLOG_ERR : RPMLOG_WARNING,
1858 _("Missing build-id in %s\n"),
1861 rpmlog(terminate ? RPMLOG_ERR : RPMLOG_WARNING,
1863 ? _("build-id found in %s too small\n")
1864 : _("build-id found in %s too large\n")),
1877 /* Process and clean up all build-ids. */
1879 const char *errdir = _("failed to create directory");
1880 char *mainiddir = NULL;
1881 char *debugiddir = NULL;
1884 /* Add .build-id directories to hold the subdirs/symlinks. */
1886 mainiddir = rpmGetPath(fl->buildRoot, BUILD_ID_DIR, NULL);
1887 debugiddir = rpmGetPath(fl->buildRoot, DEBUG_ID_DIR, NULL);
1889 /* Make sure to reset all file flags to defaults. */
1891 argvAdd(files, attrstr);
1894 /* Supported, but questionable. */
1895 if (needMain && needDbg)
1896 rpmlog(RPMLOG_WARNING,
1897 _("Mixing main ELF and debug files in package"));
1900 if ((rc = rpmioMkpath(mainiddir, 0755, -1, -1)) != 0) {
1901 rpmlog(RPMLOG_ERR, "%s %s: %m\n", errdir, mainiddir);
1903 argvAddAttr(files, RPMFILE_DIR|RPMFILE_ARTIFACT, mainiddir);
1907 if (rc == 0 && needDbg) {
1908 if ((rc = rpmioMkpath(debugiddir, 0755, -1, -1)) != 0) {
1909 rpmlog(RPMLOG_ERR, "%s %s: %m\n", errdir, debugiddir);
1911 argvAddAttr(files, RPMFILE_DIR|RPMFILE_ARTIFACT, debugiddir);
1916 /* In case we need ALLDEBUG links we might need the vra as
1917 tagged onto the .debug file name. */
1919 if (rc == 0 && needDbg && build_id_links == BUILD_IDS_ALLDEBUG) {
1920 int unique_debug_names =
1921 rpmExpandNumeric("%{?_unique_debug_names}");
1922 if (unique_debug_names == 1)
1923 vra = rpmExpand("-%{VERSION}-%{RELEASE}.%{_arch}", NULL);
1926 /* Now add a subdir and symlink for each buildid found. */
1927 for (i = 0; i < nr_ids; i++) {
1928 /* Don't add anything more when an error occurred. But do
1931 int isDbg = strncmp (paths[i], DEBUG_LIB_PREFIX,
1932 strlen (DEBUG_LIB_PREFIX)) == 0;
1934 char *buildidsubdir;
1937 subdir[1] = ids[i][0];
1938 subdir[2] = ids[i][1];
1941 buildidsubdir = rpmGetPath(debugiddir, subdir, NULL);
1943 buildidsubdir = rpmGetPath(mainiddir, subdir, NULL);
1944 /* We only need to create and add the subdir once. */
1945 int addsubdir = access (buildidsubdir, F_OK) == -1;
1947 && (rc = rpmioMkpath(buildidsubdir, 0755, -1, -1)) != 0) {
1948 rpmlog(RPMLOG_ERR, "%s %s: %m\n", errdir, buildidsubdir);
1951 argvAddAttr(files, RPMFILE_DIR|RPMFILE_ARTIFACT, buildidsubdir);
1953 char *linkpattern, *targetpattern;
1954 char *linkpath, *targetpath;
1957 linkpattern = "%s/%s";
1958 targetpattern = "../../../../..%s";
1960 linkpattern = "%s/%s";
1961 targetpattern = "../../../..%s";
1963 rasprintf(&linkpath, linkpattern,
1964 buildidsubdir, &ids[i][2]);
1965 rasprintf(&targetpath, targetpattern, paths[i]);
1966 rc = addNewIDSymlink(files, targetpath, linkpath,
1969 /* We might want to have a link from the debug
1970 build_ids dir to the main one. We create it
1971 when we are creating compat links or doing
1972 an old style alldebug build-ids package. In
1973 the first case things are simple since we
1974 just link to the main build-id symlink. The
1975 second case is a bit tricky, since we
1976 cannot be 100% sure the file names in the
1977 main and debug package match. Currently
1978 they do, but when creating parallel
1979 installable debuginfo packages they might
1980 not (in that case we might have to also
1981 strip the nvr from the debug name).
1983 In general either method is discouraged
1984 since it might create dangling symlinks if
1985 the package versions get out of sync. */
1986 if (rc == 0 && isDbg
1987 && build_id_links == BUILD_IDS_COMPAT) {
1988 /* buildidsubdir already points to the
1989 debug buildid. We just need to setup
1990 the symlink to the main one. There
1991 might be duplicate IDs, those are found
1992 by the addNewIDSymlink above. Target
1993 the last found duplicate, if any. */
1998 rasprintf(&linkpath, "%s/%s",
1999 buildidsubdir, &ids[i][2]);
2000 rasprintf(&targetpath,
2001 "../../../.build-id%s/%s",
2002 subdir, &ids[i][2]);
2006 rasprintf(&linkpath, "%s/%s.%d",
2007 buildidsubdir, &ids[i][2], dups);
2008 rasprintf(&targetpath,
2009 "../../../.build-id%s/%s.%d",
2010 subdir, &ids[i][2], dups);
2012 rc = addNewIDSymlink(files, targetpath, linkpath,
2016 if (rc == 0 && isDbg
2017 && build_id_links == BUILD_IDS_ALLDEBUG) {
2018 /* buildidsubdir already points to the
2019 debug buildid. We do have to figure out
2020 the main ELF file though (which is most
2021 likely not in this package). Guess we
2022 can find it by stripping the
2023 /usr/lib/debug path and .debug
2024 prefix. Which might not really be
2025 correct if there was a more involved
2026 transformation (for example for
2027 parallel installable debuginfo
2028 packages), but then we shouldn't be
2029 using ALLDEBUG in the first place.
2030 Also ignore things like .dwz multifiles
2031 which don't end in ".debug". */
2032 int pathlen = strlen(paths[i]);
2033 int debuglen = strlen(".debug");
2034 int prefixlen = strlen(DEBUG_LIB_DIR);
2035 int vralen = vra == NULL ? 0 : strlen(vra);
2036 if (pathlen > prefixlen + debuglen + vralen
2037 && strcmp ((paths[i] + pathlen - debuglen),
2041 char *targetstr = xstrdup (paths[i]
2043 int targetlen = pathlen - prefixlen;
2044 int targetend = targetlen - debuglen - vralen;
2045 targetstr[targetend] = '\0';
2046 rasprintf(&linkpath, "%s/%s",
2047 buildidsubdir, &ids[i][2]);
2048 rasprintf(&targetpath, "../../../../..%s",
2050 rc = addNewIDSymlink(files, targetpath,
2051 linkpath, 0, &dups);
2059 free(buildidsubdir);
2075 * Add a file to a binary package.
2077 * @param fl package file tree walk data
2078 * @param fileName file to add
2079 * @return RPMRC_OK on success
2081 static rpmRC processBinaryFile(Package pkg, FileList fl, const char * fileName)
2083 int quote = 1; /* XXX permit quoted glob characters. */
2085 char *diskPath = NULL;
2086 rpmRC rc = RPMRC_OK;
2087 size_t fnlen = strlen(fileName);
2088 int trailing_slash = (fnlen > 0 && fileName[fnlen-1] == '/');
2090 /* XXX differentiate other directories from explicit %dir */
2091 if (trailing_slash && !fl->cur.isDir)
2094 doGlob = rpmIsGlob(fileName, quote);
2096 /* Check that file starts with leading "/" */
2097 if (*fileName != '/') {
2098 rpmlog(RPMLOG_ERR, _("File needs leading \"/\": %s\n"), fileName);
2103 /* Copy file name or glob pattern removing multiple "/" chars. */
2105 * Note: rpmGetPath should guarantee a "canonical" path. That means
2106 * that the following pathologies should be weeded out:
2109 * /.././../usr/../bin//./sh
2111 diskPath = rpmGenPath(fl->buildRoot, NULL, fileName);
2112 /* Arrange trailing slash on directories */
2114 diskPath = rstrcat(&diskPath, "/");
2121 if (fl->cur.devtype) {
2122 rpmlog(RPMLOG_ERR, _("%%dev glob not permitted: %s\n"), diskPath);
2127 if (rpmGlob(diskPath, &argc, &argv) == 0 && argc >= 1) {
2128 for (i = 0; i < argc; i++) {
2129 rc = addFile(fl, argv[i], NULL);
2133 const char *msg = (fl->cur.isDir) ?
2134 _("Directory not found by glob: %s. "
2135 "Trying without globbing.\n") :
2136 _("File not found by glob: %s. "
2137 "Trying without globbing.\n");
2138 rpmlog(RPMLOG_DEBUG, msg, diskPath);
2139 rc = addFile(fl, diskPath, NULL);
2142 rc = addFile(fl, diskPath, NULL);
2148 fl->processingFailed = 1;
2154 static rpmRC readFilesManifest(rpmSpec spec, Package pkg, const char *path)
2156 char *fn, buf[BUFSIZ];
2158 rpmRC rc = RPMRC_FAIL;
2159 unsigned int nlines = 0;
2163 fn = rpmGetPath(path, NULL);
2165 fn = rpmGetPath("%{_builddir}/",
2166 (spec->buildSubdir ? spec->buildSubdir : "") , "/", path, NULL);
2168 fd = fopen(fn, "r");
2171 rpmlog(RPMLOG_ERR, _("Could not open %%files file %s: %m\n"), fn);
2175 /* XXX unmask %license while parsing files manifest*/
2176 rpmPushMacro(spec->macros, "license", NULL, "%%license", RMIL_SPEC);
2178 while (fgets(buf, sizeof(buf), fd)) {
2179 if (handleComments(buf))
2181 if (rpmExpandMacros(spec->macros, buf, &expanded, 0) < 0) {
2182 rpmlog(RPMLOG_ERR, _("line: %s\n"), buf);
2185 argvAdd(&(pkg->fileList), expanded);
2192 rpmExpandNumeric("%{?_empty_manifest_terminate_build}");
2193 rpmlog(terminate ? RPMLOG_ERR : RPMLOG_WARNING,
2194 _("Empty %%files file %s\n"), fn);
2200 rpmlog(RPMLOG_ERR, _("Error reading %%files file %s: %m\n"), fn);
2205 rpmPopMacro(NULL, "license");
2211 static char * getSpecialDocDir(Header h, rpmFlags sdtype)
2213 const char *errstr = NULL;
2214 const char *dirtype = (sdtype == RPMFILE_DOC) ? "docdir" : "licensedir";
2215 const char *fmt_default = "%{NAME}-%{VERSION}";
2216 char *fmt_macro = rpmExpand("%{?_docdir_fmt}", NULL);
2220 if (fmt_macro && strlen(fmt_macro) > 0) {
2221 fmt = headerFormat(h, fmt_macro, &errstr);
2223 rpmlog(RPMLOG_WARNING, _("illegal _docdir_fmt %s: %s\n"),
2229 fmt = headerFormat(h, fmt_default, &errstr);
2231 res = rpmGetPath("%{_", dirtype, "}/", fmt, NULL);
2238 static specialDir specialDirNew(Header h, rpmFlags sdtype)
2240 specialDir sd = xcalloc(1, sizeof(*sd));
2242 sd->entriesCount = 0;
2243 sd->entriesAlloced = 10;
2244 sd->entries = xcalloc(sd->entriesAlloced, sizeof(sd->entries[0]));
2246 sd->dirname = getSpecialDocDir(h, sdtype);
2247 sd->sdtype = sdtype;
2251 static void addSpecialFile(specialDir sd, const char *path, FileEntry cur,
2254 argvAdd(&sd->files, path);
2256 if (sd->entriesCount >= sd->entriesAlloced) {
2257 sd->entriesAlloced <<= 1;
2258 sd->entries = xrealloc(sd->entries, sd->entriesAlloced *
2259 sizeof(sd->entries[0]));
2262 copyFileEntry(cur, &sd->entries[sd->entriesCount].curEntry);
2263 copyFileEntry(def, &sd->entries[sd->entriesCount].defEntry);
2267 static specialDir specialDirFree(specialDir sd)
2272 argvFree(sd->files);
2274 for (i = 0; i < sd->entriesCount; i++) {
2275 FileEntryFree(&sd->entries[i].curEntry);
2276 FileEntryFree(&sd->entries[i].defEntry);
2284 static void processSpecialDir(rpmSpec spec, Package pkg, FileList fl,
2285 specialDir sd, int install, int test)
2287 const char *sdenv = (sd->sdtype == RPMFILE_DOC) ? "DOCDIR" : "LICENSEDIR";
2288 const char *sdname = (sd->sdtype == RPMFILE_DOC) ? "%doc" : "%license";
2289 char *mkdocdir = rpmExpand("%{__mkdir_p} $", sdenv, NULL);
2290 StringBuf docScript = newStringBuf();
2291 char *basepath, **files;
2294 appendStringBuf(docScript, sdenv);
2295 appendStringBuf(docScript, "=$RPM_BUILD_ROOT");
2296 appendLineStringBuf(docScript, sd->dirname);
2297 appendLineStringBuf(docScript, "export LC_ALL=C");
2298 appendStringBuf(docScript, "export ");
2299 appendLineStringBuf(docScript, sdenv);
2300 appendLineStringBuf(docScript, mkdocdir);
2302 for (ARGV_const_t fn = sd->files; fn && *fn; fn++) {
2303 /* Quotes would break globs, escape spaces instead */
2304 char *efn = rpmEscapeSpaces(*fn);
2305 appendStringBuf(docScript, "cp -pr ");
2306 appendStringBuf(docScript, efn);
2307 appendStringBuf(docScript, " $");
2308 appendStringBuf(docScript, sdenv);
2309 appendLineStringBuf(docScript, " ||:");
2314 rpmRC rc = doScript(spec, RPMBUILD_STRINGBUF, sdname,
2315 getStringBuf(docScript), test);
2317 if (rc && rpmExpandNumeric("%{?_missing_doc_files_terminate_build}"))
2318 fl->processingFailed = 1;
2321 basepath = rpmGenPath(spec->rootDir, "%{_builddir}", spec->buildSubdir);
2324 while (*files != NULL) {
2325 char *origfile = rpmGenPath(basepath, *files, NULL);
2326 char *eorigfile = rpmEscapeSpaces(origfile);
2328 int globFilesCount, i;
2331 FileEntryFree(&fl->cur);
2332 FileEntryFree(&fl->def);
2333 copyFileEntry(&sd->entries[fi].curEntry, &fl->cur);
2334 copyFileEntry(&sd->entries[fi].defEntry, &fl->def);
2337 if (rpmGlob(eorigfile, &globFilesCount, &globFiles) == 0) {
2338 for (i = 0; i < globFilesCount; i++) {
2339 rasprintf(&newfile, "%s/%s", sd->dirname, basename(globFiles[i]));
2340 processBinaryFile(pkg, fl, newfile);
2343 argvFree(globFiles);
2345 rpmlog(RPMLOG_ERR, _("File not found by glob: %s\n"), eorigfile);
2346 fl->processingFailed = 1;
2354 FileEntryFree(&fl->cur);
2355 FileEntryFree(&fl->def);
2356 copyFileEntry(&sd->entries[0].defEntry, &fl->def);
2358 (void) processBinaryFile(pkg, fl, sd->dirname);
2360 freeStringBuf(docScript);
2365 /* Resets the default settings for files in the package list.
2366 Used in processPackageFiles whenever a new set of files is added. */
2367 static void resetPackageFilesDefaults (struct FileList_s *fl,
2368 rpmBuildPkgFlags pkgFlags)
2370 struct AttrRec_s root_ar = { 0, 0, 0, 0, 0, 0 };
2372 root_ar.ar_user = rpmstrPoolId(fl->pool, UID_0_USER, 1);
2373 root_ar.ar_group = rpmstrPoolId(fl->pool, GID_0_GROUP, 1);
2374 dupAttrRec(&root_ar, &fl->def.ar); /* XXX assume %defattr(-,root,root) */
2376 fl->def.verifyFlags = RPMVERIFY_ALL;
2378 fl->pkgFlags = pkgFlags;
2381 /* Adds the given fileList to the package. If fromSpecFileList is not zero
2382 then the specialDirs are also filled in and the files are sanitized
2383 through processBinaryFile(). Otherwise no special files are processed
2384 and the files are added directly through addFile(). */
2385 static void addPackageFileList (struct FileList_s *fl, Package pkg,
2387 specialDir *specialDoc, specialDir *specialLic,
2388 int fromSpecFileList)
2390 ARGV_t fileNames = NULL;
2391 for (ARGV_const_t fp = *fileList; *fp != NULL; fp++) {
2392 char buf[strlen(*fp) + 1];
2393 const char *s = *fp;
2397 fileNames = argvFree(fileNames);
2398 rstrlcpy(buf, s, sizeof(buf));
2400 /* Reset for a new line in %files */
2401 FileEntryFree(&fl->cur);
2403 /* turn explicit flags into %def'd ones (gosh this is hacky...) */
2404 fl->cur.specdFlags = ((unsigned)fl->def.specdFlags) >> 8;
2405 fl->cur.verifyFlags = fl->def.verifyFlags;
2407 if (parseForVerify(buf, 0, &fl->cur) ||
2408 parseForVerify(buf, 1, &fl->def) ||
2409 parseForAttr(fl->pool, buf, 0, &fl->cur) ||
2410 parseForAttr(fl->pool, buf, 1, &fl->def) ||
2411 parseForDev(buf, &fl->cur) ||
2412 parseForConfig(buf, &fl->cur) ||
2413 parseForLang(buf, &fl->cur) ||
2414 parseForCaps(buf, &fl->cur) ||
2415 parseForSimple(buf, &fl->cur, &fileNames))
2417 fl->processingFailed = 1;
2421 for (ARGV_const_t fn = fileNames; fn && *fn; fn++) {
2423 /* For file lists that don't come from a spec file list
2424 processing is easy. There are no special files and the
2425 file names don't need to be adjusted. */
2426 if (!fromSpecFileList) {
2427 if (fl->cur.attrFlags & RPMFILE_SPECIALDIR
2428 || fl->cur.attrFlags & RPMFILE_DOCDIR
2429 || fl->cur.attrFlags & RPMFILE_PUBKEY) {
2431 _("Special file in generated file list: %s\n"),
2433 fl->processingFailed = 1;
2436 if (fl->cur.attrFlags & RPMFILE_DIR)
2438 addFile(fl, *fn, NULL);
2442 /* File list does come from the spec, try to detect special
2443 files and adjust the actual file names. */
2444 if (fl->cur.attrFlags & RPMFILE_SPECIALDIR) {
2445 rpmFlags oattrs = (fl->cur.attrFlags & ~RPMFILE_SPECIALDIR);
2446 specialDir *sdp = NULL;
2447 if (oattrs == RPMFILE_DOC) {
2449 } else if (oattrs == RPMFILE_LICENSE) {
2453 if (sdp == NULL || **fn == '/') {
2455 _("Can't mix special %s with other forms: %s\n"),
2456 (oattrs & RPMFILE_DOC) ? "%doc" : "%license", *fn);
2457 fl->processingFailed = 1;
2461 /* save attributes on first special doc/license for later use */
2463 *sdp = specialDirNew(pkg->header, oattrs);
2465 addSpecialFile(*sdp, *fn, &fl->cur, &fl->def);
2469 /* this is now an artificial limitation */
2470 if (fn != fileNames) {
2471 rpmlog(RPMLOG_ERR, _("More than one file on a line: %s\n"),*fn);
2472 fl->processingFailed = 1;
2476 if (fl->cur.attrFlags & RPMFILE_DOCDIR) {
2477 argvAdd(&(fl->docDirs), *fn);
2478 } else if (fl->cur.attrFlags & RPMFILE_PUBKEY) {
2479 (void) processMetadataFile(pkg, fl, *fn, RPMTAG_PUBKEYS);
2481 if (fl->cur.attrFlags & RPMFILE_DIR)
2483 (void) processBinaryFile(pkg, fl, *fn);
2490 argvFree(fileNames);
2493 static rpmRC processPackageFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags,
2494 Package pkg, int didInstall, int test)
2496 struct FileList_s fl;
2497 specialDir specialDoc = NULL;
2498 specialDir specialLic = NULL;
2500 pkg->cpioList = NULL;
2502 for (ARGV_const_t fp = pkg->fileFile; fp && *fp != NULL; fp++) {
2503 if (readFilesManifest(spec, pkg, *fp))
2506 /* Init the file list structure */
2507 memset(&fl, 0, sizeof(fl));
2509 fl.pool = rpmstrPoolLink(spec->pool);
2510 /* XXX spec->buildRoot == NULL, then xstrdup("") is returned */
2511 fl.buildRoot = rpmGenPath(spec->rootDir, spec->buildRoot, NULL);
2512 fl.buildRootLen = strlen(fl.buildRoot);
2514 resetPackageFilesDefaults (&fl, pkgFlags);
2516 { char *docs = rpmGetPath("%{?__docdir_path}", NULL);
2517 argvSplit(&fl.docDirs, docs, ":");
2521 addPackageFileList (&fl, pkg, &pkg->fileList,
2522 &specialDoc, &specialLic, 1);
2524 /* Now process special docs and licenses if present */
2526 processSpecialDir(spec, pkg, &fl, specialDoc, didInstall, test);
2528 processSpecialDir(spec, pkg, &fl, specialLic, didInstall, test);
2530 if (fl.processingFailed)
2534 /* Check build-ids and add build-ids links for files to package list. */
2535 const char *arch = headerGetString(pkg->header, RPMTAG_ARCH);
2536 if (!rstreq(arch, "noarch")) {
2537 /* Go through the current package list and generate a files list. */
2538 ARGV_t idFiles = NULL;
2539 if (generateBuildIDs (&fl, &idFiles) != 0) {
2540 rpmlog(RPMLOG_ERR, _("Generating build-id links failed\n"));
2541 fl.processingFailed = 1;
2546 if (idFiles != NULL) {
2547 resetPackageFilesDefaults (&fl, pkgFlags);
2548 addPackageFileList (&fl, pkg, &idFiles, NULL, NULL, 0);
2552 if (fl.processingFailed)
2557 /* Verify that file attributes scope over hardlinks correctly. */
2558 if (checkHardLinks(&fl.files))
2559 (void) rpmlibNeedsFeature(pkg, "PartialHardlinkSets", "4.0.4-1");
2561 genCpioListAndHeader(&fl, pkg, 0);
2565 specialDirFree(specialDoc);
2566 specialDirFree(specialLic);
2567 return fl.processingFailed ? RPMRC_FAIL : RPMRC_OK;
2570 static void genSourceRpmName(rpmSpec spec)
2572 if (spec->sourceRpmName == NULL) {
2573 char *nvr = headerGetAsString(spec->packages->header, RPMTAG_NVR);
2574 rasprintf(&spec->sourceRpmName, "%s.%ssrc.rpm", nvr,
2575 spec->noSource ? "no" : "");
2580 rpmRC processSourceFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags)
2582 struct Source *srcPtr;
2583 struct FileList_s fl;
2584 ARGV_t files = NULL;
2586 Package sourcePkg = spec->sourcePackage;
2587 static char *_srcdefattr;
2591 _srcdefattr = rpmExpand("%{?_srcdefattr}", NULL);
2592 if (_srcdefattr && !*_srcdefattr)
2593 _srcdefattr = _free(_srcdefattr);
2597 genSourceRpmName(spec);
2598 /* Construct the file list and source entries */
2599 argvAdd(&files, spec->specFile);
2600 for (srcPtr = spec->sources; srcPtr != NULL; srcPtr = srcPtr->next) {
2601 char * sfn = rpmGetPath( ((srcPtr->flags & RPMBUILD_ISNO) ? "!" : ""),
2602 "%{_sourcedir}/", srcPtr->source, NULL);
2603 argvAdd(&files, sfn);
2607 for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
2608 for (srcPtr = pkg->icon; srcPtr != NULL; srcPtr = srcPtr->next) {
2610 sfn = rpmGetPath( ((srcPtr->flags & RPMBUILD_ISNO) ? "!" : ""),
2611 "%{_sourcedir}/", srcPtr->source, NULL);
2612 argvAdd(&files, sfn);
2617 sourcePkg->cpioList = NULL;
2619 /* Init the file list structure */
2620 memset(&fl, 0, sizeof(fl));
2621 fl.pool = rpmstrPoolLink(spec->pool);
2623 char *a = rstrscat(NULL, "%defattr ", _srcdefattr, NULL);
2624 parseForAttr(fl.pool, a, 1, &fl.def);
2627 fl.files.alloced = spec->numSources + 1;
2628 fl.files.recs = xcalloc(fl.files.alloced, sizeof(*fl.files.recs));
2629 fl.pkgFlags = pkgFlags;
2631 for (ARGV_const_t fp = files; *fp != NULL; fp++) {
2632 const char *diskPath = *fp;
2636 SKIPSPACE(diskPath);
2640 flp = &fl.files.recs[fl.files.used];
2642 /* The first source file is the spec file */
2643 flp->flags = (fl.files.used == 0) ? RPMFILE_SPECFILE : 0;
2644 /* files with leading ! are no source files */
2645 if (*diskPath == '!') {
2646 flp->flags |= RPMFILE_GHOST;
2650 tmp = xstrdup(diskPath); /* basename() might modify */
2651 flp->diskPath = xstrdup(diskPath);
2652 flp->cpioPath = xstrdup(basename(tmp));
2653 flp->verifyFlags = RPMVERIFY_ALL;
2656 if (stat(diskPath, &flp->fl_st)) {
2657 rpmlog(RPMLOG_ERR, _("Bad file: %s: %s\n"),
2658 diskPath, strerror(errno));
2659 fl.processingFailed = 1;
2661 if (S_ISREG(flp->fl_mode) && flp->fl_size >= UINT32_MAX)
2665 if (fl.def.ar.ar_fmodestr) {
2666 flp->fl_mode &= S_IFMT;
2667 flp->fl_mode |= fl.def.ar.ar_fmode;
2670 if (fl.def.ar.ar_user) {
2671 flp->uname = fl.def.ar.ar_user;
2673 flp->uname = rpmstrPoolId(fl.pool, rpmugUname(flp->fl_uid), 1);
2676 flp->uname = rpmstrPoolId(fl.pool, rpmugUname(getuid()), 1);
2679 flp->uname = rpmstrPoolId(fl.pool, UID_0_USER, 1);
2682 if (fl.def.ar.ar_group) {
2683 flp->gname = fl.def.ar.ar_group;
2685 flp->gname = rpmstrPoolId(fl.pool, rpmugGname(flp->fl_gid), 1);
2688 flp->gname = rpmstrPoolId(fl.pool, rpmugGname(getgid()), 1);
2691 flp->gname = rpmstrPoolId(fl.pool, GID_0_GROUP, 1);
2694 flp->langs = xstrdup("");
2699 if (! fl.processingFailed) {
2700 if (sourcePkg->header != NULL) {
2701 genCpioListAndHeader(&fl, sourcePkg, 1);
2706 return fl.processingFailed ? RPMRC_FAIL : RPMRC_OK;
2710 * Check packaged file list against what's in the build root.
2711 * @param buildRoot path of build root
2712 * @param fileList packaged file list
2713 * @return -1 if skipped, 0 on OK, 1 on error
2715 static int checkFiles(const char *buildRoot, StringBuf fileList)
2717 static char * const av_ckfile[] = { "%{?__check_files}", NULL };
2718 StringBuf sb_stdout = NULL;
2720 char * s = rpmExpand(av_ckfile[0], NULL);
2725 rpmlog(RPMLOG_NOTICE, _("Checking for unpackaged file(s): %s\n"), s);
2727 rc = rpmfcExec(av_ckfile, fileList, &sb_stdout, 0, buildRoot);
2732 int _unpackaged_files_terminate_build =
2733 rpmExpandNumeric("%{?_unpackaged_files_terminate_build}");
2734 const char * t = getStringBuf(sb_stdout);
2735 if ((*t != '\0') && (*t != '\n')) {
2736 rc = (_unpackaged_files_terminate_build) ? 1 : 0;
2737 rpmlog((rc ? RPMLOG_ERR : RPMLOG_WARNING),
2738 _("Installed (but unpackaged) file(s) found:\n%s"), t);
2743 freeStringBuf(sb_stdout);
2748 static rpmTag copyTagsFromMainDebug[] = {
2753 /* see addTargets */
2759 /* this is a hack: patch the summary and the description to include
2760 * the correct package name */
2761 static void patchDebugPackageString(Package dbg, rpmTag tag, Package pkg, Package mainpkg)
2763 const char *oldname, *newname, *old;
2764 char *oldsubst = NULL, *newsubst = NULL, *p;
2765 oldname = headerGetString(mainpkg->header, RPMTAG_NAME);
2766 newname = headerGetString(pkg->header, RPMTAG_NAME);
2767 rasprintf(&oldsubst, "package %s", oldname);
2768 rasprintf(&newsubst, "package %s", newname);
2769 old = headerGetString(dbg->header, tag);
2770 p = old ? strstr(old, oldsubst) : NULL;
2773 rasprintf(&new, "%.*s%s%s", (int)(p - old), old, newsubst, p + strlen(oldsubst));
2774 headerDel(dbg->header, tag);
2775 headerPutString(dbg->header, tag, new);
2782 /* Early prototype for use in filterDebuginfoPackage. */
2783 static void addPackageDeps(Package from, Package to, enum rpmTag_e tag);
2785 /* create a new debuginfo subpackage for package pkg from the
2786 * main debuginfo package */
2787 static Package cloneDebuginfoPackage(rpmSpec spec, Package pkg, Package maindbg)
2789 const char *name = headerGetString(pkg->header, RPMTAG_NAME);
2790 char *dbgname = NULL;
2793 rasprintf(&dbgname, "%s-%s", name, "debuginfo");
2794 dbg = newPackage(dbgname, spec->pool, &spec->packages);
2795 headerPutString(dbg->header, RPMTAG_NAME, dbgname);
2796 copyInheritedTags(dbg->header, pkg->header);
2797 headerDel(dbg->header, RPMTAG_GROUP);
2798 headerCopyTags(maindbg->header, dbg->header, copyTagsFromMainDebug);
2799 dbg->autoReq = maindbg->autoReq;
2800 dbg->autoProv = maindbg->autoProv;
2802 /* patch summary and description strings */
2803 patchDebugPackageString(dbg, RPMTAG_SUMMARY, pkg, spec->packages);
2804 patchDebugPackageString(dbg, RPMTAG_DESCRIPTION, pkg, spec->packages);
2806 /* Add self-provides (normally done by addTargets) */
2807 addPackageProvides(dbg);
2808 dbg->ds = rpmdsThis(dbg->header, RPMTAG_REQUIRENAME, RPMSENSE_EQUAL);
2814 /* collect the debug files for package pkg and put them into
2815 * a (possibly new) debuginfo subpackage */
2816 static void filterDebuginfoPackage(rpmSpec spec, Package pkg,
2817 Package maindbg, Package dbgsrc,
2818 char *buildroot, char *uniquearch)
2821 ARGV_t files = NULL;
2823 int lastdiridx = -1, dirsadded;
2824 char *path = NULL, *p, *pmin;
2825 size_t buildrootlen = strlen(buildroot);
2827 /* ignore noarch subpackages */
2828 if (rstreq(headerGetString(pkg->header, RPMTAG_ARCH), "noarch"))
2834 fi = rpmfilesIter(pkg->cpioList, RPMFI_ITER_FWD);
2835 /* Check if the current package has files with debug info
2836 and add them to the file list */
2837 fi = rpmfiInit(fi, 0);
2838 while (rpmfiNext(fi) >= 0) {
2839 const char *name = rpmfiFN(fi);
2840 int namel = strlen(name);
2842 /* strip trailing .debug like in find-debuginfo.sh */
2843 if (namel > 6 && !strcmp(name + namel - 6, ".debug"))
2846 /* fileRenameMap doesn't necessarily have to be initialized */
2847 if (pkg->fileRenameMap) {
2848 const char **names = NULL;
2850 fileRenameHashGetEntry(pkg->fileRenameMap, name, &names, &namec, NULL);
2853 rpmlog(RPMLOG_WARNING, _("%s was mapped to multiple filenames"), name);
2855 namel = strlen(name);
2860 rasprintf(&path, "%s%s%.*s%s.debug", buildroot, DEBUG_LIB_DIR, namel, name, uniquearch);
2862 /* If that file exists we have debug information for it */
2863 if (access(path, F_OK) == 0) {
2864 /* Append the file list preamble */
2866 char *attr = mkattr();
2867 argvAdd(&files, attr);
2868 argvAddAttr(&files, RPMFILE_DIR, DEBUG_LIB_DIR);
2872 /* Add the files main debug-info file */
2873 argvAdd(&files, path + buildrootlen);
2875 /* Add the dir(s) */
2877 pmin = path + buildrootlen + strlen(DEBUG_LIB_DIR);
2878 while ((p = strrchr(path + buildrootlen, '/')) != NULL && p > pmin) {
2880 if (lastdiridx >= 0 && !strcmp(dirs[lastdiridx], path + buildrootlen))
2881 break; /* already added this one */
2882 argvAdd(&dirs, path + buildrootlen);
2886 lastdiridx = argvCount(dirs) - dirsadded; /* remember longest dir */
2891 /* Exclude debug files for files which were excluded in respective non-debug package */
2892 for (ARGV_const_t excl = pkg->fileExcludeList; excl && *excl; excl++) {
2893 const char *name = *excl;
2896 rasprintf(&path, "%s%s%s%s.debug", buildroot, DEBUG_LIB_DIR, name, uniquearch);
2897 /* Exclude only debuginfo files which actually exist */
2898 if (access(path, F_OK) == 0) {
2900 rasprintf(&line, "%%exclude %s", path + buildrootlen);
2901 argvAdd(&files, line);
2907 /* add collected directories to file list */
2910 argvSort(dirs, NULL);
2911 for (i = 0; dirs[i]; i++) {
2912 if (!i || strcmp(dirs[i], dirs[i - 1]) != 0)
2913 argvAddAttr(&files, RPMFILE_DIR, dirs[i]);
2915 dirs = argvFree(dirs);
2919 /* we have collected some files. Now put them in a debuginfo
2920 * package. If this is not the main package, clone the main
2921 * debuginfo package */
2922 if (pkg == spec->packages)
2923 maindbg->fileList = files;
2925 Package dbg = cloneDebuginfoPackage(spec, pkg, maindbg);
2926 dbg->fileList = files;
2927 /* Recommend the debugsource package (or the main debuginfo). */
2928 addPackageDeps(dbg, dbgsrc ? dbgsrc : maindbg,
2929 RPMTAG_RECOMMENDNAME);
2934 /* add the debug dwz files to package pkg.
2935 * return 1 if something was added, 0 otherwise. */
2936 static int addDebugDwz(Package pkg, char *buildroot)
2942 rasprintf(&path, "%s%s", buildroot, DEBUG_DWZ_DIR);
2943 if (lstat(path, &sbuf) == 0 && S_ISDIR(sbuf.st_mode)) {
2944 if (!pkg->fileList) {
2945 char *attr = mkattr();
2946 argvAdd(&pkg->fileList, attr);
2947 argvAddAttr(&pkg->fileList, RPMFILE_DIR|RPMFILE_ARTIFACT, DEBUG_LIB_DIR);
2950 argvAddAttr(&pkg->fileList, RPMFILE_ARTIFACT, DEBUG_DWZ_DIR);
2957 /* add the debug source files to package pkg.
2958 * return 1 if something was added, 0 otherwise. */
2959 static int addDebugSrc(Package pkg, char *buildroot)
2966 /* not needed if we have an extra debugsource subpackage */
2967 if (rpmExpandNumeric("%{?_debugsource_packages}"))
2970 rasprintf(&path, "%s%s", buildroot, DEBUG_SRC_DIR);
2974 while ((de = readdir(d)) != NULL) {
2975 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
2977 rasprintf(&path, "%s/%s", DEBUG_SRC_DIR, de->d_name);
2978 if (!pkg->fileList) {
2979 char *attr = mkattr();
2980 argvAdd(&pkg->fileList, attr);
2983 argvAdd(&pkg->fileList, path);
2992 /* find the debugsource package, if it has been created.
2993 * We do this simply by searching for a package with the right name. */
2994 static Package findDebugsourcePackage(rpmSpec spec)
2997 if (lookupPackage(spec, "debugsource", PART_SUBNAME|PART_QUIET, &pkg))
2999 return pkg && pkg->fileList ? pkg : NULL;
3002 /* find the main debuginfo package. We do this simply by
3003 * searching for a package with the right name. */
3004 static Package findDebuginfoPackage(rpmSpec spec)
3007 if (lookupPackage(spec, "debuginfo", PART_SUBNAME|PART_QUIET, &pkg))
3009 return pkg && pkg->fileList ? pkg : NULL;
3012 /* add a dependency (e.g. RPMTAG_REQUIRENAME or RPMTAG_RECOMMENDNAME)
3013 for package "to" into package "from". */
3014 static void addPackageDeps(Package from, Package to, enum rpmTag_e tag)
3017 char *evr, *isaprov;
3018 name = headerGetString(to->header, RPMTAG_NAME);
3019 evr = headerGetAsString(to->header, RPMTAG_EVR);
3020 isaprov = rpmExpand(name, "%{?_isa}", NULL);
3021 addReqProv(from, tag, isaprov, evr, RPMSENSE_EQUAL, 0);
3026 rpmRC processBinaryFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags,
3027 int didInstall, int test)
3030 rpmRC rc = RPMRC_OK;
3032 char *uniquearch = NULL;
3033 Package maindbg = NULL; /* the (existing) main debuginfo package */
3034 Package deplink = NULL; /* create requires to this package */
3035 /* The debugsource package, if it exists, that the debuginfo package(s)
3036 should Recommend. */
3037 Package dbgsrcpkg = findDebugsourcePackage(spec);
3040 elf_version (EV_CURRENT);
3042 check_fileList = newStringBuf();
3043 genSourceRpmName(spec);
3044 buildroot = rpmGenPath(spec->rootDir, spec->buildRoot, NULL);
3046 if (rpmExpandNumeric("%{?_debuginfo_subpackages}")) {
3047 maindbg = findDebuginfoPackage(spec);
3049 /* move debuginfo package to back */
3050 if (maindbg->next) {
3053 for (pp = &spec->packages; *pp != maindbg; pp = &(*pp)->next)
3055 *pp = maindbg->next;
3057 /* enqueue at tail */
3058 for (; *pp; pp = &(*pp)->next)
3062 /* delete unsplit file list, we will re-add files back later */
3063 maindbg->fileFile = argvFree(maindbg->fileFile);
3064 maindbg->fileList = argvFree(maindbg->fileList);
3065 if (rpmExpandNumeric("%{?_unique_debug_names}"))
3066 uniquearch = rpmExpand("-%{VERSION}-%{RELEASE}.%{_arch}", NULL);
3068 } else if (dbgsrcpkg != NULL) {
3069 /* We have a debugsource package, but no debuginfo subpackages.
3070 The main debuginfo package should recommend the debugsource one. */
3071 Package dbgpkg = findDebuginfoPackage(spec);
3073 addPackageDeps(dbgpkg, dbgsrcpkg, RPMTAG_RECOMMENDNAME);
3076 for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
3082 if (pkg == maindbg) {
3083 /* if there is just one debuginfo package, we put our extra stuff
3084 * in it. Otherwise we put it in the main debug package */
3085 Package extradbg = !maindbg->fileList && maindbg->next && !maindbg->next->next ?
3086 maindbg->next : maindbg;
3087 if (addDebugDwz(extradbg, buildroot))
3089 if (addDebugSrc(extradbg, buildroot))
3091 if (dbgsrcpkg != NULL)
3092 addPackageDeps(extradbg, dbgsrcpkg, RPMTAG_RECOMMENDNAME);
3093 maindbg = NULL; /* all normal packages processed */
3096 if (pkg->fileList == NULL)
3099 headerPutString(pkg->header, RPMTAG_SOURCERPM, spec->sourceRpmName);
3101 nvr = headerGetAsString(pkg->header, RPMTAG_NVRA);
3102 rpmlog(RPMLOG_NOTICE, _("Processing files: %s\n"), nvr);
3105 if ((rc = processPackageFiles(spec, pkgFlags, pkg, didInstall, test)) != RPMRC_OK)
3109 filterDebuginfoPackage(spec, pkg, maindbg, dbgsrcpkg,
3110 buildroot, uniquearch);
3111 else if (deplink && pkg != deplink)
3112 addPackageDeps(pkg, deplink, RPMTAG_REQUIRENAME);
3114 if ((rc = rpmfcGenerateDepends(spec, pkg)) != RPMRC_OK)
3117 a = headerGetString(pkg->header, RPMTAG_ARCH);
3118 header_color = headerGetNumber(pkg->header, RPMTAG_HEADERCOLOR);
3119 if (!rstreq(a, "noarch")) {
3120 arch_color = rpmGetArchColor(a);
3121 if (arch_color > 0 && header_color > 0 &&
3122 !(arch_color & header_color)) {
3123 rpmlog(RPMLOG_WARNING,
3124 _("Binaries arch (%d) not matching the package arch (%d).\n"),
3125 header_color, arch_color);
3127 } else if (header_color != 0) {
3128 int terminate = rpmExpandNumeric("%{?_binaries_in_noarch_packages_terminate_build}");
3129 rpmlog(terminate ? RPMLOG_ERR : RPMLOG_WARNING,
3130 _("Arch dependent binaries in noarch package\n"));
3138 /* Now we have in fileList list of files from all packages.
3139 * We pass it to a script which does the work of finding missing
3140 * and duplicated files.
3144 if (checkFiles(spec->buildRoot, check_fileList) > 0) {
3148 check_fileList = freeStringBuf(check_fileList);