3 * The post-build, pre-packaging file tree walk to assemble the package
9 #define MYALLPERMS 07777
14 #include <sys/capability.h>
17 #include <rpm/rpmpgp.h>
19 #include <rpm/rpmfc.h>
20 #include <rpm/rpmfileutil.h> /* rpmDoDigest() */
21 #include <rpm/rpmlog.h>
22 #include <rpm/rpmbase64.h>
28 #include "rpmio/rpmio_internal.h" /* XXX rpmioSlurp */
31 #include "lib/rpmfi_internal.h" /* XXX fi->apath */
32 #include "lib/rpmug.h"
33 #include "build/rpmbuild_internal.h"
34 #include "build/rpmbuild_misc.h"
39 #define SKIPSPACE(s) { while (*(s) && risspace(*(s))) (s)++; }
40 #define SKIPWHITE(_x) {while(*(_x) && (risspace(*_x) || *(_x) == ',')) (_x)++;}
41 #define SKIPNONWHITE(_x){while(*(_x) &&!(risspace(*_x) || *(_x) == ',')) (_x)++;}
46 SPECD_DEFFILEMODE = (1 << 0),
47 SPECD_DEFDIRMODE = (1 << 1),
48 SPECD_DEFUID = (1 << 2),
49 SPECD_DEFGID = (1 << 3),
50 SPECD_DEFVERIFY = (1 << 4),
52 SPECD_FILEMODE = (1 << 8),
53 SPECD_DIRMODE = (1 << 9),
54 SPECD_UID = (1 << 10),
55 SPECD_GID = (1 << 11),
56 SPECD_VERIFY = (1 << 12)
59 typedef rpmFlags specfFlags;
61 /* internal %files parsing state attributes */
63 RPMFILE_EXCLUDE = (1 << 16), /*!< from %%exclude */
64 RPMFILE_DOCDIR = (1 << 17), /*!< from %%docdir */
65 RPMFILE_DIR = (1 << 18), /*!< from %%dir */
66 RPMFILE_SPECIALDIR = (1 << 19), /*!< from special %%doc */
69 /* bits up to 15 (for now) reserved for exported rpmfileAttrs */
70 #define PARSEATTR_MASK 0x0000ffff
74 typedef struct FileListRec_s {
76 #define fl_dev fl_st.st_dev
77 #define fl_ino fl_st.st_ino
78 #define fl_mode fl_st.st_mode
79 #define fl_nlink fl_st.st_nlink
80 #define fl_uid fl_st.st_uid
81 #define fl_gid fl_st.st_gid
82 #define fl_rdev fl_st.st_rdev
83 #define fl_size fl_st.st_size
84 #define fl_mtime fl_st.st_mtime
86 char *diskPath; /* get file from here */
87 char *cpioPath; /* filename in cpio archive */
91 specfFlags specdFlags; /* which attributes have been explicitly specified. */
92 rpmVerifyFlags verifyFlags;
93 char *langs; /* XXX locales separated with | */
99 typedef struct AttrRec_s {
108 static struct AttrRec_s root_ar = { NULL, NULL, "root", "root", 0, 0 };
111 static StringBuf check_fileList = NULL;
113 typedef struct specialDir_s {
117 struct AttrRec_s def_ar;
121 typedef struct FileEntry_s {
122 rpmfileAttrs attrFlags;
123 specfFlags specdFlags;
124 rpmVerifyFlags verifyFlags;
130 /* these are only ever relevant for current entry */
137 typedef struct FileRecords_s {
144 * Package file tree walk data.
146 typedef struct FileList_s {
147 /* global filelist state */
149 int processingFailed;
153 rpmBuildPkgFlags pkgFlags;
155 /* actual file records */
156 struct FileRecords_s files;
158 /* active defaults */
159 struct FileEntry_s def;
161 /* current file-entry state */
162 struct FileEntry_s cur;
167 static void nullAttrRec(AttrRec ar)
169 ar->ar_fmodestr = NULL;
170 ar->ar_dmodestr = NULL;
179 static void freeAttrRec(AttrRec ar)
181 ar->ar_fmodestr = _free(ar->ar_fmodestr);
182 ar->ar_dmodestr = _free(ar->ar_dmodestr);
183 ar->ar_user = _free(ar->ar_user);
184 ar->ar_group = _free(ar->ar_group);
185 /* XXX doesn't free ar (yet) */
191 static void dupAttrRec(const AttrRec oar, AttrRec nar)
196 nar->ar_fmodestr = (oar->ar_fmodestr ? xstrdup(oar->ar_fmodestr) : NULL);
197 nar->ar_dmodestr = (oar->ar_dmodestr ? xstrdup(oar->ar_dmodestr) : NULL);
198 nar->ar_user = (oar->ar_user ? xstrdup(oar->ar_user) : NULL);
199 nar->ar_group = (oar->ar_group ? xstrdup(oar->ar_group) : NULL);
200 nar->ar_fmode = oar->ar_fmode;
201 nar->ar_dmode = oar->ar_dmode;
207 static void dumpAttrRec(const char * msg, AttrRec ar)
210 fprintf(stderr, "%s:\t", msg);
211 fprintf(stderr, "(%s, %s, %s, %s)\n",
219 static void FileEntryFree(FileEntry entry)
221 freeAttrRec(&(entry->ar));
222 argvFree(entry->langs);
223 memset(entry, 0, sizeof(*entry));
231 static char *strtokWithQuotes(char *s, const char *delim)
233 static char *olds = NULL;
241 /* Skip leading delimiters */
242 s += strspn(s, delim);
246 /* Find the end of the token. */
250 /* Find next " char */
251 s = strchr(token, '"');
253 s = strpbrk(token, delim);
258 /* This token finishes the string */
259 olds = strchr(token, '\0');
261 /* Terminate the token and make olds point past it */
271 typedef const struct VFA {
272 const char * attribute;
278 static VFA_t const verifyAttrs[] = {
279 { "md5", RPMVERIFY_FILEDIGEST },
280 { "filedigest", RPMVERIFY_FILEDIGEST },
281 { "size", RPMVERIFY_FILESIZE },
282 { "link", RPMVERIFY_LINKTO },
283 { "user", RPMVERIFY_USER },
284 { "owner", RPMVERIFY_USER },
285 { "group", RPMVERIFY_GROUP },
286 { "mtime", RPMVERIFY_MTIME },
287 { "mode", RPMVERIFY_MODE },
288 { "rdev", RPMVERIFY_RDEV },
289 { "caps", RPMVERIFY_CAPS },
294 * Add 'provides' information to debuginfo package
295 * @param h Header information from debuginfo package
298 static void addPackageProvides_for_debuginfo_pkg(Header h)
300 const char *arch, *name;
302 rpmsenseFlags pflags = RPMSENSE_EQUAL;
304 /* <name> = <evr> provide */
305 name = headerGetString(h, RPMTAG_NAME);
306 arch = headerGetString(h, RPMTAG_ARCH);
307 evr = headerGetAsString(h, RPMTAG_EVR);
308 headerPutString(h, RPMTAG_PROVIDENAME, name);
309 headerPutString(h, RPMTAG_PROVIDEVERSION, evr);
310 headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &pflags, 1);
313 * <name>(<isa>) = <evr> provide
314 * FIXME: noarch needs special casing for now as BuildArch: noarch doesn't
315 * cause reading in the noarch macros :-/
318 isaprov = rpmExpand(name, "%{?_isa}", NULL);
319 if (!rstreq(arch, "noarch") && !rstreq(name, isaprov)) {
320 headerPutString(h, RPMTAG_PROVIDENAME, isaprov);
321 headerPutString(h, RPMTAG_PROVIDEVERSION, evr);
322 headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &pflags, 1);
329 static rpmFlags vfaMatch(VFA_t *attrs, const char *token, rpmFlags *flags)
333 for (vfa = attrs; vfa->attribute != NULL; vfa++) {
334 if (rstreq(token, vfa->attribute)) {
343 * Parse %verify and %defverify from file manifest.
344 * @param buf current spec file line
345 * @param def parse for %defverify or %verify?
346 * @param entry file entry data (current or default)
347 * @return RPMRC_OK on success
349 static rpmRC parseForVerify(char * buf, int def, FileEntry entry)
351 char *p, *pe, *q = NULL;
352 const char *name = def ? "%defverify" : "%verify";
354 rpmVerifyFlags verifyFlags = RPMVERIFY_NONE;
355 rpmRC rc = RPMRC_FAIL;
357 if ((p = strstr(buf, name)) == NULL)
360 for (pe = p; (pe-p) < strlen(name); pe++)
366 rpmlog(RPMLOG_ERR, _("Missing '(' in %s %s\n"), name, pe);
370 /* Bracket %*verify args */
372 for (p = pe; *pe && *pe != ')'; pe++)
376 rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
380 /* Localize. Erase parsed string */
381 q = xmalloc((pe-p) + 1);
382 rstrlcpy(q, p, (pe-p) + 1);
386 for (p = q; *p != '\0'; p = pe) {
395 if (vfaMatch(verifyAttrs, p, &verifyFlags))
398 if (rstreq(p, "not")) {
401 rpmlog(RPMLOG_ERR, _("Invalid %s token: %s\n"), name, p);
406 entry->verifyFlags = negated ? ~(verifyFlags) : verifyFlags;
407 entry->specdFlags |= SPECD_VERIFY;
416 #define isAttrDefault(_ars) ((_ars)[0] == '-' && (_ars)[1] == '\0')
419 * Parse %dev from file manifest.
420 * @param buf current spec file line
421 * @param cur current file entry data
422 * @return RPMRC_OK on success
424 static rpmRC parseForDev(char * buf, FileEntry cur)
427 const char * errstr = NULL;
428 char *p, *pe, *q = NULL;
429 rpmRC rc = RPMRC_FAIL; /* assume error */
431 if ((p = strstr(buf, (name = "%dev"))) == NULL)
434 for (pe = p; (pe-p) < strlen(name); pe++)
443 /* Bracket %dev args */
445 for (p = pe; *pe && *pe != ')'; pe++)
452 /* Localize. Erase parsed string */
453 q = xmalloc((pe-p) + 1);
454 rstrlcpy(q, p, (pe-p) + 1);
459 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
469 p = pe; SKIPWHITE(p);
470 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe = '\0';
471 for (pe = p; *pe && risdigit(*pe); pe++)
474 cur->devmajor = atoi(p);
475 if (!(cur->devmajor >= 0 && cur->devmajor < 256)) {
485 p = pe; SKIPWHITE(p);
486 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe = '\0';
487 for (pe = p; *pe && risdigit(*pe); pe++)
490 cur->devminor = atoi(p);
491 if (!(cur->devminor >= 0 && cur->devminor < 256)) {
504 rpmlog(RPMLOG_ERR, _("Missing %s in %s %s\n"), errstr, name, p);
511 * Parse %attr and %defattr from file manifest.
512 * @param buf current spec file line
513 * @param def parse for %defattr or %attr?
514 * @param entry file entry data (current / default)
515 * @return 0 on success
517 static rpmRC parseForAttr(char * buf, int def, FileEntry entry)
519 const char *name = def ? "%defattr" : "%attr";
520 char *p, *pe, *q = NULL;
522 struct AttrRec_s arbuf;
524 rpmRC rc = RPMRC_FAIL;
526 if ((p = strstr(buf, name)) == NULL)
529 for (pe = p; (pe-p) < strlen(name); pe++)
535 rpmlog(RPMLOG_ERR, _("Missing '(' in %s %s\n"), name, pe);
539 /* Bracket %*attr args */
541 for (p = pe; *pe && *pe != ')'; pe++)
544 if (def) { /* %defattr */
550 _("Non-white space follows %s(): %s\n"), name, r);
555 /* Localize. Erase parsed string */
556 q = xmalloc((pe-p) + 1);
557 rstrlcpy(q, p, (pe-p) + 1);
565 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
567 p = pe; SKIPWHITE(p);
570 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
572 p = pe; SKIPWHITE(p);
575 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
577 p = pe; SKIPWHITE(p);
579 if (*p != '\0' && def) { /* %defattr */
580 pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0';
582 p = pe; SKIPWHITE(p);
585 if (!(ar->ar_fmodestr && ar->ar_user && ar->ar_group) || *p != '\0') {
586 rpmlog(RPMLOG_ERR, _("Bad syntax: %s(%s)\n"), name, q);
590 /* Do a quick test on the mode argument and adjust for "-" */
591 if (ar->ar_fmodestr && !isAttrDefault(ar->ar_fmodestr)) {
593 x = sscanf(ar->ar_fmodestr, "%o", &ui);
594 if ((x == 0) || (ar->ar_fmode & ~MYALLPERMS)) {
595 rpmlog(RPMLOG_ERR, _("Bad mode spec: %s(%s)\n"), name, q);
600 ar->ar_fmodestr = NULL;
603 if (ar->ar_dmodestr && !isAttrDefault(ar->ar_dmodestr)) {
605 x = sscanf(ar->ar_dmodestr, "%o", &ui);
606 if ((x == 0) || (ar->ar_dmode & ~MYALLPERMS)) {
607 rpmlog(RPMLOG_ERR, _("Bad dirmode spec: %s(%s)\n"), name, q);
612 ar->ar_dmodestr = NULL;
615 if (!(ar->ar_user && !isAttrDefault(ar->ar_user))) {
619 if (!(ar->ar_group && !isAttrDefault(ar->ar_group))) {
623 dupAttrRec(ar, &(entry->ar));
625 /* XXX fix all this */
626 entry->specdFlags |= SPECD_UID | SPECD_GID | SPECD_FILEMODE | SPECD_DIRMODE;
635 static VFA_t const configAttrs[] = {
636 { "missingok", RPMFILE_MISSINGOK },
637 { "noreplace", RPMFILE_NOREPLACE },
642 * Parse %config from file manifest.
643 * @param buf current spec file line
644 * @param cur current file entry data
645 * @return RPMRC_OK on success
647 static rpmRC parseForConfig(char * buf, FileEntry cur)
649 char *p, *pe, *q = NULL;
651 rpmRC rc = RPMRC_FAIL;
653 if ((p = strstr(buf, (name = "%config"))) == NULL)
656 cur->attrFlags |= RPMFILE_CONFIG;
658 /* Erase "%config" token. */
659 for (pe = p; (pe-p) < strlen(name); pe++)
665 /* Bracket %config args */
667 for (p = pe; *pe && *pe != ')'; pe++)
671 rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
675 /* Localize. Erase parsed string. */
676 q = xmalloc((pe-p) + 1);
677 rstrlcpy(q, p, (pe-p) + 1);
681 for (p = q; *p != '\0'; p = pe) {
689 if (!vfaMatch(configAttrs, p, &(cur->attrFlags))) {
690 rpmlog(RPMLOG_ERR, _("Invalid %s token: %s\n"), name, p);
702 static rpmRC addLang(ARGV_t *av, const char *lang, size_t n, const char *ent)
704 rpmRC rc = RPMRC_FAIL;
706 rstrlcpy(lbuf, lang, sizeof(lbuf));
709 /* Sanity check locale length */
710 if (n < 1 || (n == 1 && *lang != 'C') || n >= 32) {
711 rpmlog(RPMLOG_ERR, _("Unusual locale length: \"%s\" in %%lang(%s)\n"),
716 /* Check for duplicate locales */
717 if (argvSearch(*av, lbuf, NULL)) {
718 rpmlog(RPMLOG_WARNING, _("Duplicate locale %s in %%lang(%s)\n"),
731 * Parse %lang from file manifest.
732 * @param buf current spec file line
733 * @param cur current file entry data
734 * @return RPMRC_OK on success
736 static rpmRC parseForLang(char * buf, FileEntry cur)
738 char *p, *pe, *q = NULL;
740 rpmRC rc = RPMRC_FAIL;
742 while ((p = strstr(buf, (name = "%lang"))) != NULL) {
744 for (pe = p; (pe-p) < strlen(name); pe++)
749 rpmlog(RPMLOG_ERR, _("Missing '(' in %s %s\n"), name, pe);
753 /* Bracket %lang args */
755 for (pe = p; *pe && *pe != ')'; pe++)
759 rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
763 /* Localize. Erase parsed string. */
764 q = xmalloc((pe-p) + 1);
765 rstrlcpy(q, p, (pe-p) + 1);
769 /* Parse multiple arguments from %lang */
770 for (p = q; *p != '\0'; p = pe) {
775 if (addLang(&(cur->langs), p, (pe-p), q))
778 if (*pe == ',') pe++; /* skip , if present */
791 * Parse %caps from file manifest.
792 * @param buf current spec file line
793 * @param cur current file entry data
794 * @return RPMRC_OK on success
796 static rpmRC parseForCaps(char * buf, FileEntry cur)
798 char *p, *pe, *q = NULL;
800 rpmRC rc = RPMRC_FAIL;
802 if ((p = strstr(buf, (name = "%caps"))) == NULL)
805 /* Erase "%caps" token. */
806 for (pe = p; (pe-p) < strlen(name); pe++)
812 /* Bracket %caps args */
814 for (p = pe; *pe && *pe != ')'; pe++)
818 rpmlog(RPMLOG_ERR, _("Missing ')' in %s(%s\n"), name, p);
822 /* Localize. Erase parsed string. */
823 q = xmalloc((pe-p) + 1);
824 rstrlcpy(q, p, (pe-p) + 1);
831 cap_t fcaps = cap_from_text(q);
833 rpmlog(RPMLOG_ERR, _("Invalid capability: %s\n"), q);
836 /* run our string through cap_to_text() to get libcap presentation */
837 captxt = cap_to_text(fcaps, NULL);
838 cur->caps = xstrdup(captxt);
843 rpmlog(RPMLOG_ERR, _("File capability support not built in\n"));
856 static VFA_t const virtualAttrs[] = {
857 { "%dir", RPMFILE_DIR },
858 { "%docdir", RPMFILE_DOCDIR },
859 { "%doc", RPMFILE_DOC },
860 { "%ghost", RPMFILE_GHOST },
861 { "%exclude", RPMFILE_EXCLUDE },
862 { "%readme", RPMFILE_README },
863 { "%license", RPMFILE_LICENSE },
864 { "%pubkey", RPMFILE_PUBKEY },
865 { "%manifest", RPMFILE_SECMANIFEST },
870 * Parse simple attributes (e.g. %dir) from file manifest.
871 * @param buf current spec file line
872 * @param cur current file entry data
873 * @retval *fileNames file names
874 * @return RPMRC_OK on success
876 static rpmRC parseForSimple(char * buf, FileEntry cur, ARGV_t * fileNames)
879 rpmRC res = RPMRC_OK;
880 int allow_relative = (RPMFILE_PUBKEY|RPMFILE_DOC|RPMFILE_LICENSE|RPMFILE_SECMANIFEST);
883 while ((s = strtokWithQuotes(t, " \t\n")) != NULL) {
886 /* Set flags for virtual file attributes */
887 if (vfaMatch(virtualAttrs, s, &(cur->attrFlags)))
890 /* normally paths need to be absolute */
892 if (!(cur->attrFlags & allow_relative)) {
893 rpmlog(RPMLOG_ERR, _("File must begin with \"/\": %s\n"), s);
897 /* non-absolute %doc and %license paths are special */
898 if (cur->attrFlags & (RPMFILE_DOC | RPMFILE_LICENSE))
899 cur->attrFlags |= RPMFILE_SPECIALDIR;
901 argvAdd(fileNames, s);
909 static int compareFileListRecs(const void * ap, const void * bp)
911 const char *a = ((FileListRec)ap)->cpioPath;
912 const char *b = ((FileListRec)bp)->cpioPath;
917 * Test if file is located in a %docdir.
918 * @param docDirs doc dirs
919 * @param fileName file path
920 * @return 1 if doc file, 0 if not
922 static int isDoc(ARGV_const_t docDirs, const char * fileName)
926 k = strlen(fileName);
927 for (ARGV_const_t dd = docDirs; *dd; dd++) {
929 if (l < k && rstreqn(fileName, *dd, l) && fileName[l] == '/')
935 static int isHardLink(FileListRec flp, FileListRec tlp)
937 return ((S_ISREG(flp->fl_mode) && S_ISREG(tlp->fl_mode)) &&
938 ((flp->fl_nlink > 1) && (flp->fl_nlink == tlp->fl_nlink)) &&
939 (flp->fl_ino == tlp->fl_ino) &&
940 (flp->fl_dev == tlp->fl_dev));
944 * Verify that file attributes scope over hardlinks correctly.
945 * If partial hardlink sets are possible, then add tracking dependency.
946 * @param fl package file records
947 * @return 1 if partial hardlink sets can exist, 0 otherwise.
949 static int checkHardLinks(FileRecords files)
951 FileListRec ilp, jlp;
954 for (i = 0; i < files->used; i++) {
955 ilp = files->recs + i;
956 if (!(S_ISREG(ilp->fl_mode) && ilp->fl_nlink > 1))
959 for (j = i + 1; j < files->used; j++) {
960 jlp = files->recs + j;
961 if (isHardLink(ilp, jlp)) {
969 static int seenHardLink(FileRecords files, FileListRec flp, rpm_ino_t *fileid)
971 for (FileListRec ilp = files->recs; ilp < flp; ilp++) {
972 if (isHardLink(flp, ilp)) {
973 *fileid = ilp - files->recs;
981 * Add file entries to header.
982 * @todo Should directories have %doc/%config attributes? (#14531)
983 * @todo Remove RPMTAG_OLDFILENAMES, add dirname/basename instead.
984 * @param fl package file tree walk data
985 * @retval *fip file info for package
989 static void genCpioListAndHeader(FileList fl,
990 rpmfi * fip, Header h, int isSrc)
992 int _addDotSlash = !(isSrc || rpmExpandNumeric("%{_noPayloadPrefix}"));
999 uint32_t defaultalgo = PGPHASHALGO_MD5, digestalgo;
1000 rpm_loff_t totalFileSize = 0;
1003 * See if non-md5 file digest algorithm is requested. If not
1004 * specified, quietly assume md5. Otherwise check if supported type.
1006 digestalgo = rpmExpandNumeric(isSrc ? "%{_source_filedigest_algorithm}" :
1007 "%{_binary_filedigest_algorithm}");
1008 if (digestalgo == 0) {
1009 digestalgo = defaultalgo;
1012 if (rpmDigestLength(digestalgo) == 0) {
1013 rpmlog(RPMLOG_WARNING,
1014 _("Unknown file digest algorithm %u, falling back to MD5\n"),
1016 digestalgo = defaultalgo;
1019 /* Sort the big list */
1020 qsort(fl->files.recs, fl->files.used,
1021 sizeof(*(fl->files.recs)), compareFileListRecs);
1023 /* Generate the header. */
1028 for (i = 0, flp = fl->files.recs; i < fl->files.used; i++, flp++) {
1029 rpm_ino_t fileid = flp - fl->files.recs;
1031 /* Merge duplicate entries. */
1032 while (i < (fl->files.used - 1) &&
1033 rstreq(flp->cpioPath, flp[1].cpioPath)) {
1035 /* Two entries for the same file found, merge the entries. */
1036 /* Note that an %exclude is a duplication of a file reference */
1039 flp[1].flags |= flp->flags;
1041 if (!(flp[1].flags & RPMFILE_EXCLUDE))
1042 rpmlog(RPMLOG_WARNING, _("File listed twice: %s\n"),
1046 if (S_ISDIR(flp->fl_mode)) {
1047 if ((flp[1].specdFlags & (SPECD_DIRMODE | SPECD_DEFDIRMODE)) <
1048 (flp->specdFlags & (SPECD_DIRMODE | SPECD_DEFDIRMODE)))
1049 flp[1].fl_mode = flp->fl_mode;
1051 if ((flp[1].specdFlags & (SPECD_FILEMODE | SPECD_DEFFILEMODE)) <
1052 (flp->specdFlags & (SPECD_FILEMODE | SPECD_DEFFILEMODE)))
1053 flp[1].fl_mode = flp->fl_mode;
1057 if ((flp[1].specdFlags & (SPECD_UID | SPECD_DEFUID)) <
1058 (flp->specdFlags & (SPECD_UID | SPECD_DEFUID)))
1060 flp[1].fl_uid = flp->fl_uid;
1061 flp[1].uname = flp->uname;
1065 if ((flp[1].specdFlags & (SPECD_GID | SPECD_DEFGID)) <
1066 (flp->specdFlags & (SPECD_GID | SPECD_DEFGID)))
1068 flp[1].fl_gid = flp->fl_gid;
1069 flp[1].gname = flp->gname;
1073 if ((flp[1].specdFlags & (SPECD_VERIFY | SPECD_DEFVERIFY)) <
1074 (flp->specdFlags & (SPECD_VERIFY | SPECD_DEFVERIFY)))
1075 flp[1].verifyFlags = flp->verifyFlags;
1077 /* XXX to-do: language */
1082 /* Skip files that were marked with %exclude. */
1083 if (flp->flags & RPMFILE_EXCLUDE) continue;
1085 /* Omit '/' and/or URL prefix, leave room for "./" prefix */
1086 apathlen += (strlen(flp->cpioPath) - skipLen + (_addDotSlash ? 3 : 1));
1088 /* Leave room for both dirname and basename NUL's */
1089 dpathlen += (strlen(flp->diskPath) + 2);
1092 * Make the header. Store the on-disk path to OLDFILENAMES for
1093 * cpio list generation purposes for now, final path temporarily
1094 * to ORIGFILENAMES, to be swapped later into OLDFILENAMES.
1096 headerPutString(h, RPMTAG_OLDFILENAMES, flp->diskPath);
1097 headerPutString(h, RPMTAG_ORIGFILENAMES, flp->cpioPath);
1098 headerPutString(h, RPMTAG_FILEUSERNAME, flp->uname);
1099 headerPutString(h, RPMTAG_FILEGROUPNAME, flp->gname);
1101 /* Only use 64bit filesizes tag if required. */
1102 if (fl->largeFiles) {
1103 rpm_loff_t rsize64 = (rpm_loff_t)flp->fl_size;
1104 headerPutUint64(h, RPMTAG_LONGFILESIZES, &rsize64, 1);
1105 /* XXX TODO: add rpmlib() dependency for large files */
1107 rpm_off_t rsize32 = (rpm_off_t)flp->fl_size;
1108 headerPutUint32(h, RPMTAG_FILESIZES, &rsize32, 1);
1110 /* Excludes and dupes have been filtered out by now. */
1111 if (S_ISREG(flp->fl_mode)) {
1112 if (flp->fl_nlink == 1 || !seenHardLink(&fl->files, flp, &fileid)) {
1113 totalFileSize += flp->fl_size;
1118 * For items whose size varies between systems, always explicitly
1119 * cast to the header type before inserting.
1120 * TODO: check and warn if header type overflows for each case.
1122 { rpm_time_t rtime = (rpm_time_t) flp->fl_mtime;
1123 headerPutUint32(h, RPMTAG_FILEMTIMES, &rtime, 1);
1126 { rpm_mode_t rmode = (rpm_mode_t) flp->fl_mode;
1127 headerPutUint16(h, RPMTAG_FILEMODES, &rmode, 1);
1130 { rpm_rdev_t rrdev = (rpm_rdev_t) flp->fl_rdev;
1131 headerPutUint16(h, RPMTAG_FILERDEVS, &rrdev, 1);
1135 * To allow rpmbuild to work on filesystems with 64bit inodes numbers,
1136 * remap them into 32bit integers based on filelist index, just
1137 * preserving semantics for determining hardlinks.
1138 * Start at 1 as inode zero as that could be considered as an error.
1139 * Since we flatten all the inodes to appear within a single fs,
1140 * we also need to flatten the devices.
1142 { rpm_ino_t rino = fileid + 1;
1143 rpm_dev_t rdev = flp->fl_dev ? 1 : 0;
1144 headerPutUint32(h, RPMTAG_FILEINODES, &rino, 1);
1145 headerPutUint32(h, RPMTAG_FILEDEVICES, &rdev, 1);
1148 headerPutString(h, RPMTAG_FILELANGS, flp->langs);
1151 headerPutString(h, RPMTAG_FILECAPS, flp->caps);
1155 if (S_ISREG(flp->fl_mode))
1156 (void) rpmDoDigest(digestalgo, flp->diskPath, 1,
1157 (unsigned char *)buf, NULL);
1158 headerPutString(h, RPMTAG_FILEDIGESTS, buf);
1161 if (S_ISLNK(flp->fl_mode)) {
1162 ssize_t llen = readlink(flp->diskPath, buf, BUFSIZ-1);
1164 rpmlog(RPMLOG_ERR, _("reading symlink %s failed: %s\n"),
1165 flp->diskPath, strerror(errno));
1166 fl->processingFailed = 1;
1169 if (buf[0] == '/' && !rstreq(fl->buildRoot, "/") &&
1170 rstreqn(buf, fl->buildRoot, strlen(fl->buildRoot))) {
1172 _("Symlink points to BuildRoot: %s -> %s\n"),
1173 flp->cpioPath, buf);
1174 fl->processingFailed = 1;
1178 headerPutString(h, RPMTAG_FILELINKTOS, buf);
1180 if (flp->flags & RPMFILE_GHOST) {
1181 flp->verifyFlags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE |
1182 RPMVERIFY_LINKTO | RPMVERIFY_MTIME);
1184 headerPutUint32(h, RPMTAG_FILEVERIFYFLAGS, &(flp->verifyFlags),1);
1186 if (!isSrc && isDoc(fl->docDirs, flp->cpioPath))
1187 flp->flags |= RPMFILE_DOC;
1188 /* XXX Should directories have %doc/%config attributes? (#14531) */
1189 if (S_ISDIR(flp->fl_mode))
1190 flp->flags &= ~(RPMFILE_CONFIG|RPMFILE_DOC|RPMFILE_LICENSE);
1191 /* Strip internal parse data */
1192 flp->flags &= PARSEATTR_MASK;
1194 headerPutUint32(h, RPMTAG_FILEFLAGS, &(flp->flags) ,1);
1197 if (totalFileSize < UINT32_MAX) {
1198 rpm_off_t totalsize = totalFileSize;
1199 headerPutUint32(h, RPMTAG_SIZE, &totalsize, 1);
1201 rpm_loff_t totalsize = totalFileSize;
1202 headerPutUint64(h, RPMTAG_LONGSIZE, &totalsize, 1);
1205 if (digestalgo != defaultalgo) {
1206 headerPutUint32(h, RPMTAG_FILEDIGESTALGO, &digestalgo, 1);
1207 rpmlibNeedsFeature(h, "FileDigests", "4.6.0-1");
1211 rpmlibNeedsFeature(h, "FileCaps", "4.6.1-1");
1215 (void) rpmlibNeedsFeature(h, "PayloadFilesHavePrefix", "4.0-1");
1218 struct rpmtd_s filenames;
1219 rpmfiFlags flags = RPMFI_NOHEADER|RPMFI_NOFILEUSER|RPMFI_NOFILEGROUP;
1225 /* rpmfiNew() only groks compressed filelists */
1226 headerConvert(h, HEADERCONV_COMPRESSFILELIST);
1227 fi = rpmfiNew(NULL, h, RPMTAG_BASENAMES, flags);
1230 fl->processingFailed = 1;
1235 * Grab the real filenames from ORIGFILENAMES and put into OLDFILENAMES,
1236 * remove temporary cruft and side-effects from filelist compression
1239 headerGet(h, RPMTAG_ORIGFILENAMES, &filenames, HEADERGET_ALLOC);
1240 headerDel(h, RPMTAG_ORIGFILENAMES);
1241 headerDel(h, RPMTAG_BASENAMES);
1242 headerDel(h, RPMTAG_DIRNAMES);
1243 headerDel(h, RPMTAG_DIRINDEXES);
1244 rpmtdSetTag(&filenames, RPMTAG_OLDFILENAMES);
1245 headerPut(h, &filenames, HEADERPUT_DEFAULT);
1247 /* Create hge-style archive path array, normally adding "./" */
1248 fc = rpmtdCount(&filenames);
1249 apath = xmalloc(fc * sizeof(*apath) + apathlen + 1);
1250 a = (char *)(apath + fc);
1252 rpmtdInit(&filenames);
1253 for (int i = 0; (fn = rpmtdNextString(&filenames)); i++) {
1256 a = stpcpy(a, "./");
1257 a = stpcpy(a, (fn + skipLen));
1258 a++; /* skip apath NUL */
1262 rpmtdFreeData(&filenames);
1265 /* Compress filelist unless legacy format requested */
1266 if (!(fl->pkgFlags & RPMBUILD_PKG_NODIRTOKENS)) {
1267 headerConvert(h, HEADERCONV_COMPRESSFILELIST);
1268 /* Binary packages with dirNames cannot be installed by legacy rpm. */
1269 (void) rpmlibNeedsFeature(h, "CompressedFileNames", "3.0.4-1");
1273 static FileRecords FileRecordsFree(FileRecords files)
1275 for (int i = 0; i < files->used; i++) {
1276 free(files->recs[i].diskPath);
1277 free(files->recs[i].cpioPath);
1278 free(files->recs[i].langs);
1279 free(files->recs[i].caps);
1285 static void FileListFree(FileList fl)
1287 FileEntryFree(&(fl->cur));
1288 FileEntryFree(&(fl->def));
1289 FileRecordsFree(&(fl->files));
1290 free(fl->buildRoot);
1291 argvFree(fl->docDirs);
1295 static rpmRC recurseDir(FileList fl, const char * diskPath);
1297 /* Hack up a stat structure for a %dev or non-existing %ghost */
1298 static struct stat * fakeStat(FileEntry cur, struct stat * statp)
1300 time_t now = time(NULL);
1303 statp->st_rdev = ((cur->devmajor & 0xff) << 8) | (cur->devminor & 0xff);
1304 statp->st_dev = statp->st_rdev;
1305 statp->st_mode = (cur->devtype == 'b' ? S_IFBLK : S_IFCHR);
1307 /* non-existing %ghost file or directory */
1308 statp->st_mode = cur->isDir ? S_IFDIR : S_IFREG;
1309 /* can't recurse into non-existing directory */
1313 statp->st_mode |= (cur->ar.ar_fmode & 0777);
1314 statp->st_atime = now;
1315 statp->st_mtime = now;
1316 statp->st_ctime = now;
1317 statp->st_nlink = 1;
1322 * Add a file to the package manifest.
1323 * @param fl package file tree walk data
1324 * @param diskPath path to file
1325 * @param statp file stat (possibly NULL)
1326 * @return RPMRC_OK on success
1328 static rpmRC addFile(FileList fl, const char * diskPath,
1329 struct stat * statp)
1331 size_t plen = strlen(diskPath);
1333 const char *cpioPath;
1334 struct stat statbuf;
1338 const char *fileUname;
1339 const char *fileGname;
1340 rpmRC rc = RPMRC_FAIL; /* assume failure */
1342 /* Strip trailing slash. The special case of '/' path is handled below. */
1343 if (plen > 0 && diskPath[plen - 1] == '/') {
1344 diskPath = strcpy(buf, diskPath);
1345 buf[plen - 1] = '\0';
1347 cpioPath = 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 += strlen(fl->buildRoot);
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 /* Don't recurse into explicit %dir, don't double-recurse from fts */
1398 if ((fl->cur.isDir != 1) && (statp == &statbuf) && S_ISDIR(statp->st_mode)) {
1399 return recurseDir(fl, diskPath);
1402 fileMode = statp->st_mode;
1403 fileUid = statp->st_uid;
1404 fileGid = statp->st_gid;
1406 /* Explicit %attr() always wins */
1407 if (fl->cur.ar.ar_fmodestr != NULL) {
1409 fileMode |= fl->cur.ar.ar_fmode;
1411 /* ...but %defattr() for directories and files is different */
1412 if (S_ISDIR(fileMode)) {
1413 if (fl->def.ar.ar_dmodestr) {
1415 fileMode |= fl->def.ar.ar_dmode;
1417 } else if (fl->def.ar.ar_fmodestr) {
1419 fileMode |= fl->def.ar.ar_fmode;
1422 if (fl->cur.ar.ar_user) {
1423 fileUname = fl->cur.ar.ar_user;
1424 } else if (fl->def.ar.ar_user) {
1425 fileUname = fl->def.ar.ar_user;
1427 fileUname = rpmugUname(fileUid);
1429 if (fl->cur.ar.ar_group) {
1430 fileGname = fl->cur.ar.ar_group;
1431 } else if (fl->def.ar.ar_group) {
1432 fileGname = fl->def.ar.ar_group;
1434 fileGname = rpmugGname(fileGid);
1437 /* Default user/group to builder's user/group */
1438 if (fileUname == NULL)
1439 fileUname = rpmugUname(getuid());
1440 if (fileGname == NULL)
1441 fileGname = rpmugGname(getgid());
1443 /* S_XXX macro must be consistent with type in find call at check-files script */
1444 if (check_fileList && (S_ISREG(fileMode) || S_ISLNK(fileMode))) {
1445 appendStringBuf(check_fileList, diskPath);
1446 appendStringBuf(check_fileList, "\n");
1449 /* Add to the file list */
1450 if (fl->files.used == fl->files.alloced) {
1451 fl->files.alloced += 128;
1452 fl->files.recs = xrealloc(fl->files.recs,
1453 fl->files.alloced * sizeof(*(fl->files.recs)));
1456 { FileListRec flp = &fl->files.recs[fl->files.used];
1458 flp->fl_st = *statp; /* structure assignment */
1459 flp->fl_mode = fileMode;
1460 flp->fl_uid = fileUid;
1461 flp->fl_gid = fileGid;
1463 flp->cpioPath = xstrdup(cpioPath);
1464 flp->diskPath = xstrdup(diskPath);
1465 flp->uname = rpmugStashStr(fileUname);
1466 flp->gname = rpmugStashStr(fileGname);
1468 if (fl->cur.langs) {
1469 flp->langs = argvJoin(fl->cur.langs, "|");
1471 flp->langs = xstrdup("");
1475 flp->caps = fl->cur.caps;
1477 flp->caps = xstrdup("");
1480 flp->flags = fl->cur.attrFlags;
1481 flp->specdFlags = fl->cur.specdFlags;
1482 flp->verifyFlags = fl->cur.verifyFlags;
1484 if (!(flp->flags & RPMFILE_EXCLUDE) && S_ISREG(flp->fl_mode)) {
1485 if (flp->fl_size >= UINT32_MAX) {
1496 fl->processingFailed = 1;
1502 * Add directory (and all of its files) to the package manifest.
1503 * @param fl package file tree walk data
1504 * @param diskPath path to file
1505 * @return RPMRC_OK on success
1507 static rpmRC recurseDir(FileList fl, const char * diskPath)
1512 int myFtsOpts = (FTS_COMFOLLOW | FTS_NOCHDIR | FTS_PHYSICAL);
1513 rpmRC rc = RPMRC_FAIL;
1515 ftsSet[0] = (char *) diskPath;
1517 ftsp = Fts_open(ftsSet, myFtsOpts, NULL);
1518 while ((fts = Fts_read(ftsp)) != NULL) {
1519 switch (fts->fts_info) {
1520 case FTS_D: /* preorder directory */
1521 case FTS_F: /* regular file */
1522 case FTS_SL: /* symbolic link */
1523 case FTS_SLNONE: /* symbolic link without target */
1524 case FTS_DEFAULT: /* none of the above */
1525 rc = addFile(fl, fts->fts_accpath, fts->fts_statp);
1527 case FTS_DOT: /* dot or dot-dot */
1528 case FTS_DP: /* postorder directory */
1531 case FTS_NS: /* stat(2) failed */
1532 case FTS_DNR: /* unreadable directory */
1533 case FTS_ERR: /* error; errno is set */
1534 case FTS_DC: /* directory that causes cycles */
1535 case FTS_NSOK: /* no stat(2) requested */
1536 case FTS_INIT: /* initialized only */
1537 case FTS_W: /* whiteout object */
1545 (void) Fts_close(ftsp);
1551 * Add a pubkey/icon to a binary package.
1553 * @param fl package file tree walk data
1554 * @param fileName path to file, relative is builddir, absolute buildroot.
1555 * @param tag tag to add
1556 * @return RPMRC_OK on success
1558 static rpmRC processMetadataFile(Package pkg, FileList fl,
1559 const char * fileName, rpmTagVal tag)
1561 const char * buildDir = "%{_builddir}/%{?buildsubdir}/";
1564 uint8_t * pkt = NULL;
1567 rpmRC rc = RPMRC_FAIL;
1570 if (*fileName == '/') {
1571 fn = rpmGenPath(fl->buildRoot, NULL, fileName);
1574 fn = rpmGenPath(buildDir, NULL, fileName);
1578 rpmlog(RPMLOG_ERR, _("%s: can't load unknown tag (%d).\n"),
1582 case RPMTAG_PUBKEYS: {
1583 if ((xx = pgpReadPkts(fn, &pkt, (size_t *)&pktlen)) <= 0) {
1584 rpmlog(RPMLOG_ERR, _("%s: public key read failed.\n"), fn);
1587 if (xx != PGPARMOR_PUBKEY) {
1588 rpmlog(RPMLOG_ERR, _("%s: not an armored public key.\n"), fn);
1591 apkt = pgpArmorWrap(PGPARMOR_PUBKEY, pkt, pktlen);
1594 case RPMTAG_SECMANIFEST: {
1595 if ((xx = rpmioSlurp(fn, &pkt, &pktlen)) != 0 || pkt == NULL) {
1596 rpmlog(RPMLOG_ERR, _("%s: Security manifest file read failed.\n"), fn);
1599 apkt = rpmBase64Encode(pkt, pktlen, -1);
1600 rpmlog(RPMLOG_INFO, _("Aptk: %s\n"), apkt);
1606 rpmlog(RPMLOG_ERR, _("%s: failed to encode\n"), fn);
1610 headerPutString(pkg->header, tag, apkt);
1614 rc = addFile(fl, fn, NULL);
1621 fl->processingFailed = 1;
1628 * Add a file to a binary package.
1630 * @param fl package file tree walk data
1631 * @param fileName file to add
1632 * @return RPMRC_OK on success
1634 static rpmRC processBinaryFile(Package pkg, FileList fl, const char * fileName)
1636 int quote = 1; /* XXX permit quoted glob characters. */
1638 char *diskPath = NULL;
1639 rpmRC rc = RPMRC_OK;
1640 size_t fnlen = strlen(fileName);
1641 int trailing_slash = (fnlen > 0 && fileName[fnlen-1] == '/');
1643 /* XXX differentiate other directories from explicit %dir */
1644 if (trailing_slash && !fl->cur.isDir)
1647 doGlob = rpmIsGlob(fileName, quote);
1649 /* Check that file starts with leading "/" */
1650 if (*fileName != '/') {
1651 rpmlog(RPMLOG_ERR, _("File needs leading \"/\": %s\n"), fileName);
1656 /* Copy file name or glob pattern removing multiple "/" chars. */
1658 * Note: rpmGetPath should guarantee a "canonical" path. That means
1659 * that the following pathologies should be weeded out:
1662 * /.././../usr/../bin//./sh
1664 diskPath = rpmGenPath(fl->buildRoot, NULL, fileName);
1665 /* Arrange trailing slash on directories */
1667 diskPath = rstrcat(&diskPath, "/");
1674 if (fl->cur.devtype) {
1675 rpmlog(RPMLOG_ERR, _("%%dev glob not permitted: %s\n"), diskPath);
1680 if (rpmGlob(diskPath, &argc, &argv) == 0 && argc >= 1) {
1681 for (i = 0; i < argc; i++) {
1682 rc = addFile(fl, argv[i], NULL);
1686 int lvl = RPMLOG_WARNING;
1687 const char *msg = (fl->cur.isDir) ?
1688 _("Directory not found by glob: %s\n") :
1689 _("File not found by glob: %s\n");
1690 if (!(fl->cur.attrFlags & RPMFILE_EXCLUDE)) {
1694 rpmlog(lvl, msg, diskPath);
1698 rc = addFile(fl, diskPath, NULL);
1704 fl->processingFailed = 1;
1710 static rpmRC readFilesManifest(rpmSpec spec, Package pkg, const char *path)
1712 char *fn, buf[BUFSIZ];
1714 rpmRC rc = RPMRC_FAIL;
1717 fn = rpmGetPath(path, NULL);
1719 fn = rpmGetPath("%{_builddir}/",
1720 (spec->buildSubdir ? spec->buildSubdir : "") , "/", path, NULL);
1722 fd = fopen(fn, "r");
1725 rpmlog(RPMLOG_ERR, _("Could not open %%files file %s: %m\n"), fn);
1729 while (fgets(buf, sizeof(buf), fd)) {
1730 handleComments(buf);
1731 if (expandMacros(spec, spec->macros, buf, sizeof(buf))) {
1732 rpmlog(RPMLOG_ERR, _("line: %s\n"), buf);
1735 argvAdd(&(pkg->fileList), buf);
1739 rpmlog(RPMLOG_ERR, _("Error reading %%files file %s: %m\n"), fn);
1749 static char * getSpecialDocDir(Header h, rpmFlags sdtype)
1751 const char *errstr = NULL;
1752 const char *dirtype = (sdtype == RPMFILE_DOC) ? "docdir" : "licensedir";
1753 const char *fmt_default = "%{NAME}-%{VERSION}";
1754 char *fmt_macro = rpmExpand("%{?_docdir_fmt}", NULL);
1758 if (fmt_macro && strlen(fmt_macro) > 0) {
1759 fmt = headerFormat(h, fmt_macro, &errstr);
1761 rpmlog(RPMLOG_WARNING, _("illegal _docdir_fmt %s: %s\n"),
1767 fmt = headerFormat(h, fmt_default, &errstr);
1769 res = rpmGetPath("%{_", dirtype, "}/", fmt, NULL);
1776 static specialDir specialDirNew(Header h, rpmFlags sdtype,
1777 AttrRec ar, AttrRec def_ar)
1779 specialDir sd = xcalloc(1, sizeof(*sd));
1780 dupAttrRec(ar, &(sd->ar));
1781 dupAttrRec(def_ar, &(sd->def_ar));
1782 sd->dirname = getSpecialDocDir(h, sdtype);
1783 sd->sdtype = sdtype;
1787 static specialDir specialDirFree(specialDir sd)
1790 argvFree(sd->files);
1791 freeAttrRec(&(sd->ar));
1792 freeAttrRec(&(sd->def_ar));
1799 static void processSpecialDir(rpmSpec spec, Package pkg, FileList fl,
1800 specialDir sd, int install, int test)
1802 const char *sdenv = (sd->sdtype == RPMFILE_DOC) ? "DOCDIR" : "LICENSEDIR";
1803 const char *sdname = (sd->sdtype == RPMFILE_DOC) ? "%doc" : "%license";
1804 char *mkdocdir = rpmExpand("%{__mkdir_p} $", sdenv, NULL);
1805 StringBuf docScript = newStringBuf();
1807 appendStringBuf(docScript, sdenv);
1808 appendStringBuf(docScript, "=$RPM_BUILD_ROOT");
1809 appendLineStringBuf(docScript, sd->dirname);
1810 appendStringBuf(docScript, "export ");
1811 appendLineStringBuf(docScript, sdenv);
1812 appendLineStringBuf(docScript, mkdocdir);
1814 for (ARGV_const_t fn = sd->files; fn && *fn; fn++) {
1815 /* Quotes would break globs, escape spaces instead */
1816 char *efn = rpmEscapeSpaces(*fn);
1817 appendStringBuf(docScript, "cp -pr ");
1818 appendStringBuf(docScript, efn);
1819 appendStringBuf(docScript, " $");
1820 appendLineStringBuf(docScript, sdenv);
1825 rpmRC rc = doScript(spec, RPMBUILD_STRINGBUF, sdname,
1826 getStringBuf(docScript), test);
1828 if (rc && rpmExpandNumeric("%{?_missing_doc_files_terminate_build}"))
1829 fl->processingFailed = 1;
1832 /* Reset for %doc */
1833 FileEntryFree(&fl->cur);
1835 fl->cur.attrFlags |= sd->sdtype;
1836 fl->cur.verifyFlags = fl->def.verifyFlags;
1837 dupAttrRec(&(sd->ar), &(fl->cur.ar));
1838 dupAttrRec(&(sd->def_ar), &(fl->def.ar));
1840 (void) processBinaryFile(pkg, fl, sd->dirname);
1842 freeStringBuf(docScript);
1847 static rpmRC processPackageFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags,
1848 Package pkg, int installSpecialDoc, int test)
1850 struct FileList_s fl;
1851 ARGV_t fileNames = NULL;
1852 specialDir specialDoc = NULL;
1853 specialDir specialLic = NULL;
1855 pkg->cpioList = NULL;
1857 for (ARGV_const_t fp = pkg->fileFile; fp && *fp != NULL; fp++) {
1858 if (readFilesManifest(spec, pkg, *fp))
1861 /* Init the file list structure */
1862 memset(&fl, 0, sizeof(fl));
1864 /* XXX spec->buildRoot == NULL, then xstrdup("") is returned */
1865 fl.buildRoot = rpmGenPath(spec->rootDir, spec->buildRoot, NULL);
1867 dupAttrRec(&root_ar, &fl.def.ar); /* XXX assume %defattr(-,root,root) */
1868 fl.def.verifyFlags = RPMVERIFY_ALL;
1870 fl.pkgFlags = pkgFlags;
1872 { char *docs = rpmGetPath("%{?__docdir_path}", NULL);
1873 argvSplit(&fl.docDirs, docs, ":");
1877 for (ARGV_const_t fp = pkg->fileList; *fp != NULL; fp++) {
1878 char buf[strlen(*fp) + 1];
1879 const char *s = *fp;
1883 fileNames = argvFree(fileNames);
1884 rstrlcpy(buf, s, sizeof(buf));
1886 /* Reset for a new line in %files */
1887 FileEntryFree(&fl.cur);
1889 /* turn explicit flags into %def'd ones (gosh this is hacky...) */
1890 fl.cur.specdFlags = ((unsigned)fl.def.specdFlags) >> 8;
1891 fl.cur.verifyFlags = fl.def.verifyFlags;
1893 if (parseForVerify(buf, 0, &fl.cur) ||
1894 parseForVerify(buf, 1, &fl.def) ||
1895 parseForAttr(buf, 0, &fl.cur) ||
1896 parseForAttr(buf, 1, &fl.def) ||
1897 parseForDev(buf, &fl.cur) ||
1898 parseForConfig(buf, &fl.cur) ||
1899 parseForLang(buf, &fl.cur) ||
1900 parseForCaps(buf, &fl.cur) ||
1901 parseForSimple(buf, &fl.cur, &fileNames))
1903 fl.processingFailed = 1;
1907 for (ARGV_const_t fn = fileNames; fn && *fn; fn++) {
1908 if (fl.cur.attrFlags & RPMFILE_SPECIALDIR) {
1909 rpmFlags oattrs = (fl.cur.attrFlags & ~RPMFILE_SPECIALDIR);
1910 specialDir *sdp = NULL;
1911 if (oattrs == RPMFILE_DOC) {
1913 } else if (oattrs == RPMFILE_LICENSE) {
1917 if (sdp == NULL || **fn == '/') {
1919 _("Can't mix special %s with other forms: %s\n"),
1920 (oattrs & RPMFILE_DOC) ? "%doc" : "%license", *fn);
1921 fl.processingFailed = 1;
1925 /* save attributes on first special doc/license for later use */
1927 *sdp = specialDirNew(pkg->header, oattrs,
1928 &fl.cur.ar, &fl.def.ar);
1930 argvAdd(&(*sdp)->files, *fn);
1934 /* this is now an artificial limitation */
1935 if (fn != fileNames) {
1936 rpmlog(RPMLOG_ERR, _("More than one file on a line: %s\n"),*fn);
1937 fl.processingFailed = 1;
1941 if (fl.cur.attrFlags & RPMFILE_DOCDIR) {
1942 argvAdd(&(fl.docDirs), *fn);
1943 } else if (fl.cur.attrFlags & RPMFILE_PUBKEY) {
1944 (void) processMetadataFile(pkg, &fl, *fn, RPMTAG_PUBKEYS);
1945 } else if (fl.cur.attrFlags & RPMFILE_SECMANIFEST) {
1946 (void) processMetadataFile(pkg, &fl, *fn, RPMTAG_SECMANIFEST);
1948 if (fl.cur.attrFlags & RPMFILE_DIR)
1950 (void) processBinaryFile(pkg, &fl, *fn);
1958 /* Now process special docs and licenses if present */
1960 processSpecialDir(spec, pkg, &fl, specialDoc, installSpecialDoc, test);
1962 processSpecialDir(spec, pkg, &fl, specialLic, installSpecialDoc, test);
1964 if (fl.processingFailed)
1967 /* Verify that file attributes scope over hardlinks correctly. */
1968 if (checkHardLinks(&fl.files))
1969 (void) rpmlibNeedsFeature(pkg->header,
1970 "PartialHardlinkSets", "4.0.4-1");
1972 genCpioListAndHeader(&fl, &pkg->cpioList, pkg->header, 0);
1976 specialDirFree(specialDoc);
1977 specialDirFree(specialLic);
1978 return fl.processingFailed ? RPMRC_FAIL : RPMRC_OK;
1981 static void genSourceRpmName(rpmSpec spec)
1983 if (spec->sourceRpmName == NULL) {
1984 char *nvr = headerGetAsString(spec->packages->header, RPMTAG_NVR);
1985 rasprintf(&spec->sourceRpmName, "%s.%ssrc.rpm", nvr,
1986 spec->noSource ? "no" : "");
1991 rpmRC processSourceFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags)
1993 struct Source *srcPtr;
1994 struct FileList_s fl;
1995 ARGV_t files = NULL;
1997 static char *_srcdefattr;
2001 _srcdefattr = rpmExpand("%{?_srcdefattr}", NULL);
2002 if (_srcdefattr && !*_srcdefattr)
2003 _srcdefattr = _free(_srcdefattr);
2007 genSourceRpmName(spec);
2008 /* Construct the file list and source entries */
2009 argvAdd(&files, spec->specFile);
2010 for (srcPtr = spec->sources; srcPtr != NULL; srcPtr = srcPtr->next) {
2011 char * sfn = rpmGetPath( ((srcPtr->flags & RPMBUILD_ISNO) ? "!" : ""),
2012 "%{_sourcedir}/", srcPtr->source, NULL);
2013 argvAdd(&files, sfn);
2017 for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
2018 for (srcPtr = pkg->icon; srcPtr != NULL; srcPtr = srcPtr->next) {
2020 sfn = rpmGetPath( ((srcPtr->flags & RPMBUILD_ISNO) ? "!" : ""),
2021 "%{_sourcedir}/", srcPtr->source, NULL);
2022 argvAdd(&files, sfn);
2027 spec->sourceCpioList = NULL;
2029 /* Init the file list structure */
2030 memset(&fl, 0, sizeof(fl));
2032 char *a = rstrscat(NULL, "%defattr ", _srcdefattr, NULL);
2033 parseForAttr(a, 1, &fl.def);
2036 fl.files.alloced = spec->numSources + 1;
2037 fl.files.recs = xcalloc(fl.files.alloced, sizeof(*fl.files.recs));
2038 fl.pkgFlags = pkgFlags;
2040 for (ARGV_const_t fp = files; *fp != NULL; fp++) {
2041 const char *diskPath = *fp;
2045 SKIPSPACE(diskPath);
2049 flp = &fl.files.recs[fl.files.used];
2051 /* The first source file is the spec file */
2052 flp->flags = (fl.files.used == 0) ? RPMFILE_SPECFILE : 0;
2053 /* files with leading ! are no source files */
2054 if (*diskPath == '!') {
2055 flp->flags |= RPMFILE_GHOST;
2059 tmp = xstrdup(diskPath); /* basename() might modify */
2060 flp->diskPath = xstrdup(diskPath);
2061 flp->cpioPath = xstrdup(basename(tmp));
2062 flp->verifyFlags = RPMVERIFY_ALL;
2065 if (stat(diskPath, &flp->fl_st)) {
2066 rpmlog(RPMLOG_ERR, _("Bad file: %s: %s\n"),
2067 diskPath, strerror(errno));
2068 fl.processingFailed = 1;
2070 if (S_ISREG(flp->fl_mode) && flp->fl_size >= UINT32_MAX)
2074 if (fl.def.ar.ar_fmodestr) {
2075 flp->fl_mode &= S_IFMT;
2076 flp->fl_mode |= fl.def.ar.ar_fmode;
2078 if (fl.def.ar.ar_user) {
2079 flp->uname = rpmugStashStr(fl.def.ar.ar_user);
2081 flp->uname = rpmugStashStr(rpmugUname(flp->fl_uid));
2083 if (fl.def.ar.ar_group) {
2084 flp->gname = rpmugStashStr(fl.def.ar.ar_group);
2086 flp->gname = rpmugStashStr(rpmugGname(flp->fl_gid));
2088 flp->langs = xstrdup("");
2090 if (! (flp->uname && flp->gname)) {
2091 rpmlog(RPMLOG_ERR, _("Bad owner/group: %s\n"), diskPath);
2092 fl.processingFailed = 1;
2099 if (! fl.processingFailed) {
2100 if (spec->sourceHeader != NULL) {
2101 genCpioListAndHeader(&fl, &spec->sourceCpioList,
2102 spec->sourceHeader, 1);
2107 return fl.processingFailed ? RPMRC_FAIL : RPMRC_OK;
2111 * Check packaged file list against what's in the build root.
2112 * @param fileList packaged file list
2113 * @return -1 if skipped, 0 on OK, 1 on error
2115 static int checkFiles(const char *buildRoot, StringBuf fileList)
2117 static char * const av_ckfile[] = { "%{?__check_files}", NULL };
2118 StringBuf sb_stdout = NULL;
2120 char * s = rpmExpand(av_ckfile[0], NULL);
2125 rpmlog(RPMLOG_NOTICE, _("Checking for unpackaged file(s): %s\n"), s);
2127 rc = rpmfcExec(av_ckfile, fileList, &sb_stdout, 0, buildRoot);
2132 int _unpackaged_files_terminate_build =
2133 rpmExpandNumeric("%{?_unpackaged_files_terminate_build}");
2134 const char * t = getStringBuf(sb_stdout);
2135 if ((*t != '\0') && (*t != '\n')) {
2136 rc = (_unpackaged_files_terminate_build) ? 1 : 0;
2137 rpmlog((rc ? RPMLOG_ERR : RPMLOG_WARNING),
2138 _("Installed (but unpackaged) file(s) found:\n%s"), t);
2143 freeStringBuf(sb_stdout);
2148 #if HAVE_GELF_H && HAVE_LIBELF
2149 /* Query the build-id from the ELF file NAME and store it in the newly
2150 allocated *build_id array of size *build_id_size. Returns -1 on
2154 getELFBuildId (const char *name,
2155 unsigned char **id, size_t *id_size)
2160 Elf_Data *build_id = NULL;
2161 size_t build_id_offset = 0, build_id_size = 0;
2163 /* Now query the build-id of the file and add the
2164 corresponding links in the .build-id tree.
2165 The following code is based on tools/debugedit.c. */
2166 fd = open (name, O_RDONLY);
2169 elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
2172 fprintf (stderr, "cannot open ELF file: %s",
2177 if (elf_kind (elf) != ELF_K_ELF
2178 || gelf_getehdr (elf, &ehdr) == NULL
2179 || (ehdr.e_type != ET_DYN
2180 && ehdr.e_type != ET_EXEC
2181 && ehdr.e_type != ET_REL))
2187 for (i = 0; i < ehdr.e_shnum; ++i)
2189 Elf_Scn *s = elf_getscn (elf, i);
2195 .d_version = EV_CURRENT, .d_type = ELF_T_NHDR,
2196 .d_buf = &nh, .d_size = sizeof nh
2200 gelf_getshdr (s, &shdr);
2201 if (shdr.sh_type != SHT_NOTE
2202 || !(shdr.sh_flags & SHF_ALLOC))
2205 /* Look for a build-ID note here. */
2206 data = elf_rawdata (s, NULL);
2207 src.d_buf = data->d_buf;
2208 assert (sizeof (Elf32_Nhdr) == sizeof (Elf64_Nhdr));
2209 while (data->d_buf + data->d_size - src.d_buf > (int) sizeof nh
2210 && elf32_xlatetom (&dst, &src, ehdr.e_ident[EI_DATA]))
2212 Elf32_Word len = sizeof nh + nh.n_namesz;
2213 len = (len + 3) & ~3;
2215 if (nh.n_namesz == sizeof "GNU" && nh.n_type == 3
2216 && !memcmp (src.d_buf + sizeof nh, "GNU", sizeof "GNU"))
2219 build_id_offset = src.d_buf + len - data->d_buf;
2220 build_id_size = nh.n_descsz;
2225 len = (len + 3) & ~3;
2229 if (build_id != NULL)
2233 if (build_id == NULL)
2236 *id = malloc (build_id_size);
2237 *id_size = build_id_size;
2238 memcpy (*id, build_id->d_buf + build_id_offset, build_id_size);
2247 static rpmTag copyTagsForDebug[] = {
2253 RPMTAG_DISTRIBUTION,
2258 RPMTAG_CHANGELOGTIME,
2259 RPMTAG_CHANGELOGNAME,
2260 RPMTAG_CHANGELOGTEXT,
2270 static void addDebuginfoPackage(rpmSpec spec, Package pkg, char *buildroot)
2273 const char *ver, *rel;
2275 elf_version(EV_CURRENT);
2276 a = headerGetString(pkg->header, RPMTAG_ARCH);
2277 ver = headerGetAsString(pkg->header, RPMTAG_VERSION);
2278 rel = headerGetAsString(pkg->header, RPMTAG_RELEASE);
2280 if (strcmp(a, "noarch") != 0 && strcmp(a, "src") != 0 && strcmp(a, "nosrc") != 0)
2283 rpmfi fi = pkg->cpioList;
2286 ARGV_t files = NULL;
2287 int seen_build_id = 0;
2289 /* Check if the current package has files with debug info
2291 fi = rpmfiInit (fi, 0);
2292 while (rpmfiNext (fi) >= 0)
2296 unsigned char *build_id;
2297 size_t build_id_size = 0;
2300 name = rpmfiFN (fi);
2301 /* Skip leading buildroot. */
2302 base = name + strlen (buildroot);
2303 /* Pre-pend %buildroot/usr/lib/debug and append .debug. */
2304 snprintf (tmp, 1024, "%s/usr/lib/debug%s.debug",
2306 /* If that file exists we have debug information for it. */
2307 if (access (tmp, F_OK) != 0)
2310 /* Append the file list preamble. */
2313 argvAdd(&files, "%defattr(-,root,root)");
2314 argvAdd(&files, "%dir /usr/lib/debug");
2316 /* Add the files main debug-info file. */
2317 snprintf (tmp, 1024, "/usr/lib/debug/%s.debug", base);
2318 argvAdd(&files, tmp);
2320 /* Do not bother to check build-ids for symbolic links.
2321 We'll handle them for the link target. */
2322 if (lstat (name, &sbuf) == -1
2323 || S_ISLNK (sbuf.st_mode))
2326 /* Try to gather the build-id from the binary. */
2327 if (getELFBuildId (name, &build_id, &build_id_size) == -1)
2330 /* If we see build-id links for the first time add the
2333 argvAdd(&files, "%dir /usr/lib/debug/.build-id");
2335 /* From the build-id construct the two links pointing back
2336 to the debug information file and the binary. */
2337 snprintf (tmp, 1024, "/usr/lib/debug/.build-id/%02x/",
2339 for (i = 1; i < build_id_size; ++i)
2340 sprintf (tmp + strlen (tmp), "%02x", build_id[i]);
2341 argvAdd(&files, tmp);
2342 sprintf (tmp + strlen (tmp), ".debug");
2343 argvAdd(&files, tmp);
2348 /* If there are debuginfo files for this package add a
2349 new debuginfo package. */
2352 /* Add security manifest to set right SMACK labels */
2353 argvAdd(&files, "%manifest %{name}-debuginfo.manifest");
2355 dbg = newPackage (spec);
2356 headerNVR (pkg->header, &name, NULL, NULL);
2357 /* Set name, summary and group. */
2358 snprintf (tmp, 1024, "%s-debuginfo", name);
2359 headerPutString(dbg->header, RPMTAG_NAME, tmp);
2360 headerPutString(dbg->header, RPMTAG_VERSION, ver);
2361 headerPutString(dbg->header, RPMTAG_RELEASE, rel);
2362 headerPutString(dbg->header, RPMTAG_ARCH, a);
2363 snprintf (tmp, 1024, "Debug information for package %s", name);
2364 headerPutString(dbg->header, RPMTAG_SUMMARY, tmp);
2365 snprintf (tmp, 1024, "This package provides debug information for package %s.\n"
2366 "Debug information is useful when developing applications that use this\n"
2367 "package or when debugging this package.", name);
2368 headerPutString(dbg->header, RPMTAG_DESCRIPTION, tmp);
2369 headerPutString(dbg->header, RPMTAG_GROUP, "Development/Debug");
2371 /* Add 'provides' information to debuginfo package. */
2372 addPackageProvides_for_debuginfo_pkg(dbg->header);
2374 /* Inherit other tags from parent. */
2375 headerCopyTags (pkg->header, dbg->header, copyTagsForDebug);
2377 /* Build up the files list. */
2378 dbg->fileList = files;
2384 rpmRC processBinaryFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags,
2385 int installSpecialDoc, int test)
2388 rpmRC rc = RPMRC_OK;
2391 check_fileList = newStringBuf();
2392 buildroot = rpmGenPath(spec->rootDir, spec->buildRoot, NULL);
2393 genSourceRpmName(spec);
2395 for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
2401 if (pkg->fileList == NULL)
2404 headerPutString(pkg->header, RPMTAG_SOURCERPM, spec->sourceRpmName);
2406 nvr = headerGetAsString(pkg->header, RPMTAG_NVRA);
2407 rpmlog(RPMLOG_NOTICE, _("Processing files: %s\n"), nvr);
2410 if ((rc = processPackageFiles(spec, pkgFlags, pkg, installSpecialDoc, test)) != RPMRC_OK)
2412 #if HAVE_GELF_H && HAVE_LIBELF
2413 addDebuginfoPackage(spec, pkg, buildroot);
2415 if ((rc = rpmfcGenerateDepends(spec, pkg)) != RPMRC_OK)
2418 a = headerGetString(pkg->header, RPMTAG_ARCH);
2419 header_color = headerGetNumber(pkg->header, RPMTAG_HEADERCOLOR);
2420 if (!rstreq(a, "noarch")) {
2421 arch_color = rpmGetArchColor(a);
2422 if (arch_color > 0 && header_color > 0 &&
2423 !(arch_color & header_color)) {
2424 rpmlog(RPMLOG_WARNING,
2425 _("Binaries arch (%d) not matching the package arch (%d).\n"),
2426 header_color, arch_color);
2428 } else if (header_color != 0) {
2429 int terminate = rpmExpandNumeric("%{?_binaries_in_noarch_packages_terminate_build}");
2430 rpmlog(terminate ? RPMLOG_ERR : RPMLOG_WARNING,
2431 _("Arch dependent binaries in noarch package\n"));
2439 /* Now we have in fileList list of files from all packages.
2440 * We pass it to a script which does the work of finding missing
2441 * and duplicated files.
2445 if (checkFiles(spec->buildRoot, check_fileList) > 0) {
2449 check_fileList = freeStringBuf(check_fileList);