diff options
Diffstat (limited to 'build/files.c')
-rw-r--r-- | build/files.c | 1794 |
1 files changed, 1288 insertions, 506 deletions
diff --git a/build/files.c b/build/files.c index 71d12729d..c641528a4 100644 --- a/build/files.c +++ b/build/files.c @@ -9,11 +9,17 @@ #define MYALLPERMS 07777 #include <errno.h> +#include <stdlib.h> #include <regex.h> #if WITH_CAP #include <sys/capability.h> #endif +#if HAVE_LIBDW +#include <libelf.h> +#include <elfutils/libdwelf.h> +#endif + #include <rpm/rpmpgp.h> #include <rpm/argv.h> #include <rpm/rpmfc.h> @@ -21,13 +27,8 @@ #include <rpm/rpmlog.h> #include <rpm/rpmbase64.h> -#if HAVE_GELF_H -#include <gelf.h> -#endif - #include "rpmio/rpmio_internal.h" /* XXX rpmioSlurp */ #include "misc/rpmfts.h" -#include "lib/cpio.h" #include "lib/rpmfi_internal.h" /* XXX fi->apath */ #include "lib/rpmug.h" #include "build/rpmbuild_internal.h" @@ -37,8 +38,29 @@ #include <libgen.h> #define SKIPSPACE(s) { while (*(s) && risspace(*(s))) (s)++; } -#define SKIPWHITE(_x) {while(*(_x) && (risspace(*_x) || *(_x) == ',')) (_x)++;} -#define SKIPNONWHITE(_x){while(*(_x) &&!(risspace(*_x) || *(_x) == ',')) (_x)++;} +#define SKIPWHITE(_x) {while (*(_x) && (risspace(*_x) || *(_x) == ',')) (_x)++;} +#define SKIPNONWHITE(_x){while (*(_x) &&!(risspace(*_x) || *(_x) == ',')) (_x)++;} + +/* the following defines must be in sync with the equally hardcoded paths from + * scripts/find-debuginfo.sh + */ +#define BUILD_ID_DIR "/usr/lib/.build-id" +#define DEBUG_SRC_DIR "/usr/src/debug" +#define DEBUG_LIB_DIR "/usr/lib/debug" +#define DEBUG_LIB_PREFIX "/usr/lib/debug/" +#define DEBUG_ID_DIR "/usr/lib/debug/.build-id" +#define DEBUG_DWZ_DIR "/usr/lib/debug/.dwz" + +#undef HASHTYPE +#undef HTKEYTYPE +#undef HTDATATYPE +#define HASHTYPE fileRenameHash +#define HTKEYTYPE const char * +#define HTDATATYPE const char * +#include "lib/rpmhash.C" +#undef HASHTYPE +#undef HTKEYTYPE +#undef HTDATATYPE /** */ @@ -85,8 +107,8 @@ typedef struct FileListRec_s { char *diskPath; /* get file from here */ char *cpioPath; /* filename in cpio archive */ - const char *uname; - const char *gname; + rpmsid uname; + rpmsid gname; unsigned flags; specfFlags specdFlags; /* which attributes have been explicitly specified. */ rpmVerifyFlags verifyFlags; @@ -97,26 +119,19 @@ typedef struct FileListRec_s { /** */ typedef struct AttrRec_s { - char *ar_fmodestr; - char *ar_dmodestr; - char *ar_user; - char *ar_group; + rpmsid ar_fmodestr; + rpmsid ar_dmodestr; + rpmsid ar_user; + rpmsid ar_group; mode_t ar_fmode; mode_t ar_dmode; } * AttrRec; -static struct AttrRec_s root_ar = { NULL, NULL, "root", "root", 0, 0 }; - /* list of files */ static StringBuf check_fileList = NULL; -typedef struct specialDir_s { - char * dirname; - ARGV_t files; - struct AttrRec_s ar; - struct AttrRec_s def_ar; - rpmFlags sdtype; -} * specialDir; +/*backup of maindb->fileList*/ +ARGV_t maindb_fileList_bakup = NULL; typedef struct FileEntry_s { rpmfileAttrs attrFlags; @@ -134,6 +149,23 @@ typedef struct FileEntry_s { int isDir; } * FileEntry; +typedef struct specialDir_s { + char * dirname; + ARGV_t files; + struct AttrRec_s ar; + struct AttrRec_s def_ar; + rpmFlags sdtype; + + int entriesCount; + int entriesAlloced; + + struct { + struct FileEntry_s defEntry; + struct FileEntry_s curEntry; + } *entries; + +} * specialDir; + typedef struct FileRecords_s { FileListRec recs; int alloced; @@ -146,11 +178,13 @@ typedef struct FileRecords_s { typedef struct FileList_s { /* global filelist state */ char * buildRoot; + size_t buildRootLen; int processingFailed; int haveCaps; int largeFiles; ARGV_t docDirs; rpmBuildPkgFlags pkgFlags; + rpmstrPool pool; /* actual file records */ struct FileRecords_s files; @@ -162,63 +196,45 @@ typedef struct FileList_s { struct FileEntry_s cur; } * FileList; -/** - */ static void nullAttrRec(AttrRec ar) { - ar->ar_fmodestr = NULL; - ar->ar_dmodestr = NULL; - ar->ar_user = NULL; - ar->ar_group = NULL; - ar->ar_fmode = 0; - ar->ar_dmode = 0; + memset(ar, 0, sizeof(*ar)); } -/** - */ -static void freeAttrRec(AttrRec ar) -{ - ar->ar_fmodestr = _free(ar->ar_fmodestr); - ar->ar_dmodestr = _free(ar->ar_dmodestr); - ar->ar_user = _free(ar->ar_user); - ar->ar_group = _free(ar->ar_group); - /* XXX doesn't free ar (yet) */ - return; -} - -/** - */ static void dupAttrRec(const AttrRec oar, AttrRec nar) { if (oar == nar) return; - freeAttrRec(nar); - nar->ar_fmodestr = (oar->ar_fmodestr ? xstrdup(oar->ar_fmodestr) : NULL); - nar->ar_dmodestr = (oar->ar_dmodestr ? xstrdup(oar->ar_dmodestr) : NULL); - nar->ar_user = (oar->ar_user ? xstrdup(oar->ar_user) : NULL); - nar->ar_group = (oar->ar_group ? xstrdup(oar->ar_group) : NULL); - nar->ar_fmode = oar->ar_fmode; - nar->ar_dmode = oar->ar_dmode; + *nar = *oar; /* struct assignment */ } -#if 0 -/** - */ -static void dumpAttrRec(const char * msg, AttrRec ar) +/* Creates a default $defattr string. Can be used with argvAdd(). + Caller owns the new string which needs to be freed when done. */ +static char *mkattr(void) { - if (msg) - fprintf(stderr, "%s:\t", msg); - fprintf(stderr, "(%s, %s, %s, %s)\n", - ar->ar_fmodestr, - ar->ar_user, - ar->ar_group, - ar->ar_dmodestr); + char *s = NULL; + rasprintf(&s, "%s(644,%s,%s,755)", "%defattr", UID_0_USER, GID_0_GROUP); + return s; +} + +static void copyFileEntry(FileEntry src, FileEntry dest) +{ + /* Copying struct makes just shallow copy */ + *dest = *src; + + /* Do also deep copying */ + if (src->langs != NULL) { + dest->langs = argvNew(); + argvAppend(&dest->langs, src->langs); + } + + if (src->caps != NULL) { + dest->caps = xstrdup(src->caps); + } } -#endif static void FileEntryFree(FileEntry entry) { - freeAttrRec(&(entry->ar)); argvFree(entry->langs); memset(entry, 0, sizeof(*entry)); } @@ -377,7 +393,11 @@ exit: return rc; } -#define isAttrDefault(_ars) ((_ars)[0] == '-' && (_ars)[1] == '\0') +static int isAttrDefault(rpmstrPool pool, rpmsid arsid) +{ + const char *ars = rpmstrPoolStr(pool, arsid); + return (ars && ars[0] == '-' && ars[1] == '\0'); +} /** * Parse %dev from file manifest. @@ -391,6 +411,7 @@ static rpmRC parseForDev(char * buf, FileEntry cur) const char * errstr = NULL; char *p, *pe, *q = NULL; rpmRC rc = RPMRC_FAIL; /* assume error */ + char *attr_parameters = NULL; if ((p = strstr(buf, (name = "%dev"))) == NULL) return RPMRC_OK; @@ -416,6 +437,10 @@ static rpmRC parseForDev(char * buf, FileEntry cur) /* Localize. Erase parsed string */ q = xmalloc((pe-p) + 1); rstrlcpy(q, p, (pe-p) + 1); + + attr_parameters = xmalloc((pe-p) + 1); + rstrlcpy(attr_parameters, p, (pe-p) + 1); + while (p <= pe) *p++ = ' '; @@ -465,23 +490,26 @@ static rpmRC parseForDev(char * buf, FileEntry cur) exit: if (rc) { - rpmlog(RPMLOG_ERR, _("Missing %s in %s %s\n"), errstr, name, p); + rpmlog(RPMLOG_ERR, _("Missing %s in %s(%s)\n"), errstr, name, attr_parameters); } + free(attr_parameters); free(q); return rc; } /** * Parse %attr and %defattr from file manifest. + * @param pool string pool * @param buf current spec file line * @param def parse for %defattr or %attr? * @param entry file entry data (current / default) * @return 0 on success */ -static rpmRC parseForAttr(char * buf, int def, FileEntry entry) +static rpmRC parseForAttr(rpmstrPool pool, char * buf, int def, FileEntry entry) { const char *name = def ? "%defattr" : "%attr"; char *p, *pe, *q = NULL; + char *attr_parameters = NULL; int x; struct AttrRec_s arbuf; AttrRec ar = &arbuf; @@ -519,6 +547,10 @@ static rpmRC parseForAttr(char * buf, int def, FileEntry entry) /* Localize. Erase parsed string */ q = xmalloc((pe-p) + 1); rstrlcpy(q, p, (pe-p) + 1); + + attr_parameters = xmalloc((pe-p) + 1); + rstrlcpy(attr_parameters, p, (pe-p) + 1); + while (p <= pe) *p++ = ' '; @@ -527,61 +559,61 @@ static rpmRC parseForAttr(char * buf, int def, FileEntry entry) p = q; SKIPWHITE(p); if (*p != '\0') { pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0'; - ar->ar_fmodestr = p; + ar->ar_fmodestr = rpmstrPoolId(pool, p, 1); p = pe; SKIPWHITE(p); } if (*p != '\0') { pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0'; - ar->ar_user = p; + ar->ar_user = rpmstrPoolId(pool, p, 1); p = pe; SKIPWHITE(p); } if (*p != '\0') { pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0'; - ar->ar_group = p; + ar->ar_group = rpmstrPoolId(pool, p, 1); p = pe; SKIPWHITE(p); } if (*p != '\0' && def) { /* %defattr */ pe = p; SKIPNONWHITE(pe); if (*pe != '\0') *pe++ = '\0'; - ar->ar_dmodestr = p; + ar->ar_dmodestr = rpmstrPoolId(pool, p, 1); p = pe; SKIPWHITE(p); } if (!(ar->ar_fmodestr && ar->ar_user && ar->ar_group) || *p != '\0') { - rpmlog(RPMLOG_ERR, _("Bad syntax: %s(%s)\n"), name, q); + rpmlog(RPMLOG_ERR, _("Bad syntax: %s(%s)\n"), name, attr_parameters); goto exit; } /* Do a quick test on the mode argument and adjust for "-" */ - if (ar->ar_fmodestr && !isAttrDefault(ar->ar_fmodestr)) { + if (ar->ar_fmodestr && !isAttrDefault(pool, ar->ar_fmodestr)) { unsigned int ui; - x = sscanf(ar->ar_fmodestr, "%o", &ui); + x = sscanf(rpmstrPoolStr(pool, ar->ar_fmodestr), "%o", &ui); if ((x == 0) || (ar->ar_fmode & ~MYALLPERMS)) { - rpmlog(RPMLOG_ERR, _("Bad mode spec: %s(%s)\n"), name, q); + rpmlog(RPMLOG_ERR, _("Bad mode spec: %s(%s)\n"), name, attr_parameters); goto exit; } ar->ar_fmode = ui; } else { - ar->ar_fmodestr = NULL; + ar->ar_fmodestr = 0; } - if (ar->ar_dmodestr && !isAttrDefault(ar->ar_dmodestr)) { + if (ar->ar_dmodestr && !isAttrDefault(pool, ar->ar_dmodestr)) { unsigned int ui; - x = sscanf(ar->ar_dmodestr, "%o", &ui); + x = sscanf(rpmstrPoolStr(pool, ar->ar_dmodestr), "%o", &ui); if ((x == 0) || (ar->ar_dmode & ~MYALLPERMS)) { - rpmlog(RPMLOG_ERR, _("Bad dirmode spec: %s(%s)\n"), name, q); + rpmlog(RPMLOG_ERR, _("Bad dirmode spec: %s(%s)\n"), name, attr_parameters); goto exit; } ar->ar_dmode = ui; } else { - ar->ar_dmodestr = NULL; + ar->ar_dmodestr = 0; } - if (!(ar->ar_user && !isAttrDefault(ar->ar_user))) { - ar->ar_user = NULL; + if (!(ar->ar_user && !isAttrDefault(pool, ar->ar_user))) { + ar->ar_user = 0; } - if (!(ar->ar_group && !isAttrDefault(ar->ar_group))) { - ar->ar_group = NULL; + if (!(ar->ar_group && !isAttrDefault(pool, ar->ar_group))) { + ar->ar_group = 0; } dupAttrRec(ar, &(entry->ar)); @@ -592,6 +624,7 @@ static rpmRC parseForAttr(char * buf, int def, FileEntry entry) exit: free(q); + free(attr_parameters); return rc; } @@ -826,7 +859,9 @@ static VFA_t const virtualAttrs[] = { { "%readme", RPMFILE_README }, { "%license", RPMFILE_LICENSE }, { "%pubkey", RPMFILE_PUBKEY }, - { "%manifest", RPMFILE_SECMANIFEST }, + { "%missingok", RPMFILE_MISSINGOK }, + { "%artifact", RPMFILE_ARTIFACT }, + { "%manifest", RPMFILE_SECMANIFEST }, { NULL, 0 } }; @@ -907,7 +942,7 @@ static int isHardLink(FileListRec flp, FileListRec tlp) /** * Verify that file attributes scope over hardlinks correctly. * If partial hardlink sets are possible, then add tracking dependency. - * @param fl package file records + * @param files package file records * @return 1 if partial hardlink sets can exist, 0 otherwise. */ static int checkHardLinks(FileRecords files) @@ -946,22 +981,35 @@ static int seenHardLink(FileRecords files, FileListRec flp, rpm_ino_t *fileid) * @todo Should directories have %doc/%config attributes? (#14531) * @todo Remove RPMTAG_OLDFILENAMES, add dirname/basename instead. * @param fl package file tree walk data - * @retval *fip file info for package - * @param h - * @param isSrc + * @param pkg (sub) package + * @param isSrc pass 1 for source packages 0 otherwise */ -static void genCpioListAndHeader(FileList fl, - rpmfi * fip, Header h, int isSrc) +static void genCpioListAndHeader(FileList fl, Package pkg, int isSrc) { - int _addDotSlash = !(isSrc || rpmExpandNumeric("%{_noPayloadPrefix}")); - size_t apathlen = 0; - size_t dpathlen = 0; - size_t skipLen = 0; FileListRec flp; char buf[BUFSIZ]; - int i; + int i, npaths = 0; uint32_t defaultalgo = PGPHASHALGO_MD5, digestalgo; rpm_loff_t totalFileSize = 0; + Header h = pkg->header; /* just a shortcut */ + int override_date = 0; + time_t source_date_epoch; + char *srcdate = getenv("SOURCE_DATE_EPOCH"); + + /* Limit the maximum date to SOURCE_DATE_EPOCH if defined + * similar to the tar --clamp-mtime option + * https://reproducible-builds.org/specs/source-date-epoch/ + */ + if (srcdate && rpmExpandNumeric("%{?clamp_mtime_to_source_date_epoch}")) { + char *endptr; + errno = 0; + source_date_epoch = strtol(srcdate, &endptr, 10); + if (srcdate == endptr || *endptr || errno != 0) { + rpmlog(RPMLOG_ERR, _("unable to parse %s=%s\n"), "SOURCE_DATE_EPOCH", srcdate); + exit(28); + } + override_date = 1; + } /* * See if non-md5 file digest algorithm is requested. If not @@ -979,16 +1027,40 @@ static void genCpioListAndHeader(FileList fl, digestalgo); digestalgo = defaultalgo; } - + + /* Adjust paths if needed */ + if (!isSrc && pkg->removePostfixes) { + pkg->fileRenameMap = fileRenameHashCreate(fl->files.used, + rstrhash, strcmp, + (fileRenameHashFreeKey)rfree, (fileRenameHashFreeData)rfree); + for (i = 0, flp = fl->files.recs; i < fl->files.used; i++, flp++) { + char * cpiopath = flp->cpioPath; + char * cpiopath_orig = xstrdup(cpiopath); + + for (ARGV_const_t postfix_p = pkg->removePostfixes; *postfix_p; postfix_p++) { + int len = strlen(*postfix_p); + int plen = strlen(cpiopath); + if (len <= plen && !strncmp(cpiopath+plen-len, *postfix_p, len)) { + cpiopath[plen-len] = '\0'; + if (plen-len > 0 && cpiopath[plen-len-1] == '/') { + cpiopath[plen-len-1] = '\0'; + } + } + } + if (strcmp(cpiopath_orig, cpiopath)) + fileRenameHashAddEntry(pkg->fileRenameMap, xstrdup(cpiopath), cpiopath_orig); + else + _free(cpiopath_orig); + } + } + /* Sort the big list */ qsort(fl->files.recs, fl->files.used, sizeof(*(fl->files.recs)), compareFileListRecs); - /* Generate the header. */ - if (! isSrc) { - skipLen = 1; - } + pkg->dpaths = xmalloc((fl->files.used + 1) * sizeof(*pkg->dpaths)); + /* Generate the header. */ for (i = 0, flp = fl->files.recs; i < fl->files.used; i++, flp++) { rpm_ino_t fileid = flp - fl->files.recs; @@ -1044,29 +1116,26 @@ static void genCpioListAndHeader(FileList fl, } /* Skip files that were marked with %exclude. */ - if (flp->flags & RPMFILE_EXCLUDE) continue; - - /* Omit '/' and/or URL prefix, leave room for "./" prefix */ - apathlen += (strlen(flp->cpioPath) - skipLen + (_addDotSlash ? 3 : 1)); + if (flp->flags & RPMFILE_EXCLUDE) + { + argvAdd(&pkg->fileExcludeList, flp->cpioPath); + continue; + } - /* Leave room for both dirname and basename NUL's */ - dpathlen += (strlen(flp->diskPath) + 2); + /* Collect on-disk paths for archive creation */ + pkg->dpaths[npaths++] = xstrdup(flp->diskPath); - /* - * Make the header. Store the on-disk path to OLDFILENAMES for - * cpio list generation purposes for now, final path temporarily - * to ORIGFILENAMES, to be swapped later into OLDFILENAMES. - */ - headerPutString(h, RPMTAG_OLDFILENAMES, flp->diskPath); - headerPutString(h, RPMTAG_ORIGFILENAMES, flp->cpioPath); - headerPutString(h, RPMTAG_FILEUSERNAME, flp->uname); - headerPutString(h, RPMTAG_FILEGROUPNAME, flp->gname); + headerPutString(h, RPMTAG_OLDFILENAMES, flp->cpioPath); + headerPutString(h, RPMTAG_FILEUSERNAME, + rpmstrPoolStr(fl->pool, flp->uname)); + headerPutString(h, RPMTAG_FILEGROUPNAME, + rpmstrPoolStr(fl->pool, flp->gname)); /* Only use 64bit filesizes tag if required. */ if (fl->largeFiles) { rpm_loff_t rsize64 = (rpm_loff_t)flp->fl_size; headerPutUint64(h, RPMTAG_LONGFILESIZES, &rsize64, 1); - /* XXX TODO: add rpmlib() dependency for large files */ + (void) rpmlibNeedsFeature(pkg, "LargeFiles", "4.12.0-1"); } else { rpm_off_t rsize32 = (rpm_off_t)flp->fl_size; headerPutUint32(h, RPMTAG_FILESIZES, &rsize32, 1); @@ -1078,6 +1147,9 @@ static void genCpioListAndHeader(FileList fl, } } + if (override_date && flp->fl_mtime > source_date_epoch) { + flp->fl_mtime = source_date_epoch; + } /* * For items whose size varies between systems, always explicitly * cast to the header type before inserting. @@ -1116,7 +1188,7 @@ static void genCpioListAndHeader(FileList fl, } buf[0] = '\0'; - if (S_ISREG(flp->fl_mode)) + if (S_ISREG(flp->fl_mode) && !(flp->flags & RPMFILE_GHOST)) (void) rpmDoDigest(digestalgo, flp->diskPath, 1, (unsigned char *)buf, NULL); headerPutString(h, RPMTAG_FILEDIGESTS, buf); @@ -1131,7 +1203,7 @@ static void genCpioListAndHeader(FileList fl, } else { buf[llen] = '\0'; if (buf[0] == '/' && !rstreq(fl->buildRoot, "/") && - rstreqn(buf, fl->buildRoot, strlen(fl->buildRoot))) { + rstreqn(buf, fl->buildRoot, fl->buildRootLen)) { rpmlog(RPMLOG_ERR, _("Symlink points to BuildRoot: %s -> %s\n"), flp->cpioPath, buf); @@ -1157,6 +1229,7 @@ static void genCpioListAndHeader(FileList fl, headerPutUint32(h, RPMTAG_FILEFLAGS, &(flp->flags) ,1); } + pkg->dpaths[npaths] = NULL; if (totalFileSize < UINT32_MAX) { rpm_off_t totalsize = totalFileSize; @@ -1168,69 +1241,31 @@ static void genCpioListAndHeader(FileList fl, if (digestalgo != defaultalgo) { headerPutUint32(h, RPMTAG_FILEDIGESTALGO, &digestalgo, 1); - rpmlibNeedsFeature(h, "FileDigests", "4.6.0-1"); + rpmlibNeedsFeature(pkg, "FileDigests", "4.6.0-1"); } if (fl->haveCaps) { - rpmlibNeedsFeature(h, "FileCaps", "4.6.1-1"); + rpmlibNeedsFeature(pkg, "FileCaps", "4.6.1-1"); } - if (_addDotSlash) - (void) rpmlibNeedsFeature(h, "PayloadFilesHavePrefix", "4.0-1"); - - { - struct rpmtd_s filenames; - rpmfiFlags flags = RPMFI_NOHEADER|RPMFI_NOFILEUSER|RPMFI_NOFILEGROUP; - rpmfi fi; - int fc; - const char *fn; - char *a, **apath; + if (!isSrc && !rpmExpandNumeric("%{_noPayloadPrefix}")) + (void) rpmlibNeedsFeature(pkg, "PayloadFilesHavePrefix", "4.0-1"); /* rpmfiNew() only groks compressed filelists */ headerConvert(h, HEADERCONV_COMPRESSFILELIST); - fi = rpmfiNew(NULL, h, RPMTAG_BASENAMES, flags); + pkg->cpioList = rpmfilesNew(NULL, h, RPMTAG_BASENAMES, + (RPMFI_NOFILEUSER|RPMFI_NOFILEGROUP)); - if (fi == NULL) { + if (pkg->cpioList == NULL || rpmfilesFC(pkg->cpioList) != npaths) { fl->processingFailed = 1; - return; } - /* - * Grab the real filenames from ORIGFILENAMES and put into OLDFILENAMES, - * remove temporary cruft and side-effects from filelist compression - * for rpmfiNew(). - */ - headerGet(h, RPMTAG_ORIGFILENAMES, &filenames, HEADERGET_ALLOC); - headerDel(h, RPMTAG_ORIGFILENAMES); - headerDel(h, RPMTAG_BASENAMES); - headerDel(h, RPMTAG_DIRNAMES); - headerDel(h, RPMTAG_DIRINDEXES); - rpmtdSetTag(&filenames, RPMTAG_OLDFILENAMES); - headerPut(h, &filenames, HEADERPUT_DEFAULT); - - /* Create hge-style archive path array, normally adding "./" */ - fc = rpmtdCount(&filenames); - apath = xmalloc(fc * sizeof(*apath) + apathlen + 1); - a = (char *)(apath + fc); - *a = '\0'; - rpmtdInit(&filenames); - for (int i = 0; (fn = rpmtdNextString(&filenames)); i++) { - apath[i] = a; - if (_addDotSlash) - a = stpcpy(a, "./"); - a = stpcpy(a, (fn + skipLen)); - a++; /* skip apath NUL */ - } - fi->apath = apath; - *fip = fi; - rpmtdFreeData(&filenames); - } - - /* Compress filelist unless legacy format requested */ - if (!(fl->pkgFlags & RPMBUILD_PKG_NODIRTOKENS)) { - headerConvert(h, HEADERCONV_COMPRESSFILELIST); + if (fl->pkgFlags & RPMBUILD_PKG_NODIRTOKENS) { + /* Uncompress filelist if legacy format requested */ + headerConvert(h, HEADERCONV_EXPANDFILELIST); + } else { /* Binary packages with dirNames cannot be installed by legacy rpm. */ - (void) rpmlibNeedsFeature(h, "CompressedFileNames", "3.0.4-1"); + (void) rpmlibNeedsFeature(pkg, "CompressedFileNames", "3.0.4-1"); } } @@ -1253,6 +1288,7 @@ static void FileListFree(FileList fl) FileRecordsFree(&(fl->files)); free(fl->buildRoot); argvFree(fl->docDirs); + rpmstrPoolFree(fl->pool); } /* forward ref */ @@ -1310,6 +1346,10 @@ static rpmRC addFile(FileList fl, const char * diskPath, } cpioPath = diskPath; + if (strncmp(diskPath, fl->buildRoot, fl->buildRootLen)) { + rpmlog(RPMLOG_ERR, _("Path is outside buildroot: %s\n"), diskPath); + goto exit; + } /* Path may have prepended buildRoot, so locate the original filename. */ /* @@ -1323,7 +1363,7 @@ static rpmRC addFile(FileList fl, const char * diskPath, * */ if (fl->buildRoot && !rstreq(fl->buildRoot, "/")) - cpioPath += strlen(fl->buildRoot); + cpioPath += fl->buildRootLen; /* XXX make sure '/' can be packaged also */ if (*cpioPath == '\0') @@ -1358,6 +1398,12 @@ static rpmRC addFile(FileList fl, const char * diskPath, } } + /* Error out when a non-directory is specified as one in spec */ + if (fl->cur.isDir && (statp == &statbuf) && !S_ISDIR(statp->st_mode)) { + rpmlog(RPMLOG_ERR, _("Not a directory: %s\n"), diskPath); + goto exit; + } + /* Don't recurse into explicit %dir, don't double-recurse from fts */ if ((fl->cur.isDir != 1) && (statp == &statbuf) && S_ISDIR(statp->st_mode)) { return recurseDir(fl, diskPath); @@ -1368,9 +1414,15 @@ static rpmRC addFile(FileList fl, const char * diskPath, fileGid = statp->st_gid; /* Explicit %attr() always wins */ - if (fl->cur.ar.ar_fmodestr != NULL) { - fileMode &= S_IFMT; - fileMode |= fl->cur.ar.ar_fmode; + if (fl->cur.ar.ar_fmodestr) { + if (S_ISLNK(fileMode)) { + rpmlog(RPMLOG_WARNING, + "Explicit %%attr() mode not applicable to symlink: %s\n", + diskPath); + } else { + fileMode &= S_IFMT; + fileMode |= fl->cur.ar.ar_fmode; + } } else { /* ...but %defattr() for directories and files is different */ if (S_ISDIR(fileMode)) { @@ -1378,22 +1430,22 @@ static rpmRC addFile(FileList fl, const char * diskPath, fileMode &= S_IFMT; fileMode |= fl->def.ar.ar_dmode; } - } else if (fl->def.ar.ar_fmodestr) { + } else if (!S_ISLNK(fileMode) && fl->def.ar.ar_fmodestr) { fileMode &= S_IFMT; fileMode |= fl->def.ar.ar_fmode; } } if (fl->cur.ar.ar_user) { - fileUname = fl->cur.ar.ar_user; + fileUname = rpmstrPoolStr(fl->pool, fl->cur.ar.ar_user); } else if (fl->def.ar.ar_user) { - fileUname = fl->def.ar.ar_user; + fileUname = rpmstrPoolStr(fl->pool, fl->def.ar.ar_user); } else { fileUname = rpmugUname(fileUid); } if (fl->cur.ar.ar_group) { - fileGname = fl->cur.ar.ar_group; + fileGname = rpmstrPoolStr(fl->pool, fl->cur.ar.ar_group); } else if (fl->def.ar.ar_group) { - fileGname = fl->def.ar.ar_group; + fileGname = rpmstrPoolStr(fl->pool, fl->def.ar.ar_group); } else { fileGname = rpmugGname(fileGid); } @@ -1423,11 +1475,13 @@ static rpmRC addFile(FileList fl, const char * diskPath, flp->fl_mode = fileMode; flp->fl_uid = fileUid; flp->fl_gid = fileGid; + if (S_ISDIR(fileMode)) + flp->fl_size = 0; flp->cpioPath = xstrdup(cpioPath); flp->diskPath = xstrdup(diskPath); - flp->uname = rpmugStashStr(fileUname); - flp->gname = rpmugStashStr(fileGname); + flp->uname = rpmstrPoolId(fl->pool, fileUname, 1); + flp->gname = rpmstrPoolId(fl->pool, fileGname, 1); if (fl->cur.langs) { flp->langs = argvJoin(fl->cur.langs, "|"); @@ -1436,7 +1490,7 @@ static rpmRC addFile(FileList fl, const char * diskPath, } if (fl->cur.caps) { - flp->caps = fl->cur.caps; + flp->caps = xstrdup(fl->cur.caps); } else { flp->caps = xstrdup(""); } @@ -1555,15 +1609,15 @@ static rpmRC processMetadataFile(Package pkg, FileList fl, apkt = pgpArmorWrap(PGPARMOR_PUBKEY, pkt, pktlen); break; } - case RPMTAG_SECMANIFEST: { + case RPMTAG_SECMANIFEST: { if ((xx = rpmioSlurp(fn, &pkt, &pktlen)) != 0 || pkt == NULL) { - rpmlog(RPMLOG_ERR, _("%s: Security manifest file read failed.\n"), fn); - goto exit; + rpmlog(RPMLOG_ERR, _("%s: Security manifest file read failed.\n"), fn); + goto exit; + } + apkt = rpmBase64Encode(pkt, pktlen, -1); + rpmlog(RPMLOG_INFO, _("Aptk: %s\n"), apkt); + break; } - apkt = rpmBase64Encode(pkt, pktlen, -1); - rpmlog(RPMLOG_INFO, _("Aptk: %s\n"), apkt); - break; - } } if (!apkt) { @@ -1588,6 +1642,448 @@ exit: return rc; } +/* add a file with possible virtual attributes to the file list */ +static void argvAddAttr(ARGV_t *filesp, rpmfileAttrs attrs, const char *path) +{ + char *line = NULL; + + for (VFA_t *vfa = virtualAttrs; vfa->attribute != NULL; vfa++) { + if (vfa->flag & attrs) + line = rstrscat(&line, vfa->attribute, " ", NULL); + } + line = rstrscat(&line, path, NULL); + argvAdd(filesp, line); + free(line); +} + +#if HAVE_LIBDW +/* How build id links are generated. See macros.in for description. */ +#define BUILD_IDS_NONE 0 +#define BUILD_IDS_ALLDEBUG 1 +#define BUILD_IDS_SEPARATE 2 +#define BUILD_IDS_COMPAT 3 + +static int addNewIDSymlink(ARGV_t *files, + char *targetpath, char *idlinkpath, + int isDbg, int *dups) +{ + const char *linkerr = _("failed symlink"); + int rc = 0; + int nr = 0; + int exists = 0; + char *origpath, *linkpath; + + if (isDbg) + rasprintf(&linkpath, "%s.debug", idlinkpath); + else + linkpath = idlinkpath; + origpath = linkpath; + + while (faccessat(AT_FDCWD, linkpath, F_OK, AT_SYMLINK_NOFOLLOW) == 0) { + /* We don't care about finding dups for compat links, they are + OK as is. Otherwise we will need to double check if + existing link points to the correct target. */ + if (dups == NULL) + { + exists = 1; + break; + } + + char ltarget[PATH_MAX]; + ssize_t llen; + /* In short-circuited builds the link might already exist */ + if ((llen = readlink(linkpath, ltarget, sizeof(ltarget)-1)) != -1) { + ltarget[llen] = '\0'; + if (rstreq(ltarget, targetpath)) { + exists = 1; + break; + } + } + + if (nr > 0) + free(linkpath); + nr++; + rasprintf(&linkpath, "%s.%d%s", idlinkpath, nr, + isDbg ? ".debug" : ""); + } + + if (!exists && symlink(targetpath, linkpath) < 0) { + rc = 1; + rpmlog(RPMLOG_ERR, "%s: %s -> %s: %m\n", + linkerr, linkpath, targetpath); + } else { + argvAddAttr(files, RPMFILE_ARTIFACT, linkpath); + } + + if (nr > 0) { + /* Lets see why there are multiple build-ids. If the original + targets are hard linked, then it is OK, otherwise warn + something fishy is going on. Would be nice to call + something like eu-elfcmp to see if they are really the same + ELF file or not. */ + struct stat st1, st2; + if (stat (origpath, &st1) != 0) { + rpmlog(RPMLOG_WARNING, _("Duplicate build-id, stat %s: %m\n"), + origpath); + } else if (stat (linkpath, &st2) != 0) { + rpmlog(RPMLOG_WARNING, _("Duplicate build-id, stat %s: %m\n"), + linkpath); + } else if (!(S_ISREG(st1.st_mode) && S_ISREG(st2.st_mode) + && st1.st_nlink > 1 && st2.st_nlink == st1.st_nlink + && st1.st_ino == st2.st_ino && st1.st_dev == st2.st_dev)) { + char *rpath1 = realpath(origpath, NULL); + char *rpath2 = realpath(linkpath, NULL); + rpmlog(RPMLOG_WARNING, _("Duplicate build-ids %s and %s\n"), + rpath1, rpath2); + free(rpath1); + free(rpath2); + } + } + + if (isDbg) + free(origpath); + if (nr > 0) + free(linkpath); + if (dups != NULL) + *dups = nr; + + return rc; +} + +static int generateBuildIDs(FileList fl, ARGV_t *files) +{ + int rc = 0; + int i; + FileListRec flp; + char **ids = NULL; + char **paths = NULL; + size_t nr_ids, allocated; + nr_ids = allocated = 0; + + /* How are we supposed to create the build-id links? */ + char *build_id_links_macro = rpmExpand("%{?_build_id_links}", NULL); + int build_id_links; + if (*build_id_links_macro == '\0') { + rpmlog(RPMLOG_WARNING, + _("_build_id_links macro not set, assuming 'compat'\n")); + build_id_links = BUILD_IDS_COMPAT; + } else if (strcmp(build_id_links_macro, "none") == 0) { + build_id_links = BUILD_IDS_NONE; + } else if (strcmp(build_id_links_macro, "alldebug") == 0) { + build_id_links = BUILD_IDS_ALLDEBUG; + } else if (strcmp(build_id_links_macro, "separate") == 0) { + build_id_links = BUILD_IDS_SEPARATE; + } else if (strcmp(build_id_links_macro, "compat") == 0) { + build_id_links = BUILD_IDS_COMPAT; + } else { + rc = 1; + rpmlog(RPMLOG_ERR, + _("_build_id_links macro set to unknown value '%s'\n"), + build_id_links_macro); + build_id_links = BUILD_IDS_NONE; + } + free(build_id_links_macro); + + if (build_id_links == BUILD_IDS_NONE || rc != 0) + return rc; + + /* Historically we have only checked build_ids when __debug_package + was defined. So don't terminate the build if __debug_package is + unset, even when _missing_build_ids_terminate_build is. */ + int terminate = (rpmExpandNumeric("%{?_missing_build_ids_terminate_build}") + && rpmExpandNumeric("%{?__debug_package}")); + + /* Collect and check all build-ids for ELF files in this package. */ + int needMain = 0; + int needDbg = 0; + for (i = 0, flp = fl->files.recs; i < fl->files.used; i++, flp++) { + struct stat sbuf; + if (lstat(flp->diskPath, &sbuf) == 0 && S_ISREG (sbuf.st_mode)) { + /* We determine whether this is a main or + debug ELF based on path. */ + int isDbg = strncmp (flp->cpioPath, + DEBUG_LIB_PREFIX, strlen (DEBUG_LIB_PREFIX)) == 0; + + /* For the main package files mimic what find-debuginfo.sh does. + Only check build-ids for executable files. Debug files are + always non-executable. */ + if (!isDbg + && (sbuf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) + continue; + + int fd = open (flp->diskPath, O_RDONLY); + if (fd >= 0) { + /* Only real ELF files, that are ET_EXEC, ET_DYN or + kernel modules (ET_REL files with names ending in .ko) + should have build-ids. */ + GElf_Ehdr ehdr; + Elf *elf = elf_begin (fd, ELF_C_READ, NULL); + if (elf != NULL && elf_kind(elf) == ELF_K_ELF + && gelf_getehdr(elf, &ehdr) != NULL + && (ehdr.e_type == ET_EXEC || ehdr.e_type == ET_DYN + || (ehdr.e_type == ET_REL + && rpmFileHasSuffix (flp->diskPath, ".ko")))) { + const void *build_id; + ssize_t len = dwelf_elf_gnu_build_id (elf, &build_id); + /* len == -1 means error. Zero means no + build-id. We want at least a length of 2 so we + have at least a xx/yy (hex) dir/file. But + reasonable build-ids are between 16 bytes (md5 + is 128 bits) and 64 bytes (largest sha3 is 512 + bits), common is 20 bytes (sha1 is 160 bits). */ + if (len >= 16 && len <= 64) { + int addid = 0; + if (isDbg) { + needDbg = 1; + addid = 1; + } + else if (build_id_links != BUILD_IDS_ALLDEBUG) { + needMain = 1; + addid = 1; + } + if (addid) { + const unsigned char *p = build_id; + const unsigned char *end = p + len; + char *id_str; + if (allocated <= nr_ids) { + allocated += 16; + paths = xrealloc (paths, + allocated * sizeof(char *)); + ids = xrealloc (ids, + allocated * sizeof(char *)); + } + + paths[nr_ids] = xstrdup(flp->cpioPath); + id_str = ids[nr_ids] = xmalloc(2 * len + 1); + while (p < end) + id_str += sprintf(id_str, "%02x", + (unsigned)*p++); + *id_str = '\0'; + nr_ids++; + } + } else { + if (len < 0) { + rpmlog(terminate ? RPMLOG_ERR : RPMLOG_WARNING, + _("error reading build-id in %s: %s\n"), + flp->diskPath, elf_errmsg (-1)); + } else if (len == 0) { + rpmlog(terminate ? RPMLOG_ERR : RPMLOG_WARNING, + _("Missing build-id in %s\n"), + flp->diskPath); + } else { + rpmlog(terminate ? RPMLOG_ERR : RPMLOG_WARNING, + (len < 16 + ? _("build-id found in %s too small\n") + : _("build-id found in %s too large\n")), + flp->diskPath); + } + if (terminate) + rc = 1; + } + elf_end (elf); + } + close (fd); + } + } + } + + /* Process and clean up all build-ids. */ + if (nr_ids > 0) { + const char *errdir = _("failed to create directory"); + char *mainiddir = NULL; + char *debugiddir = NULL; + if (rc == 0) { + char *attrstr; + /* Add .build-id directories to hold the subdirs/symlinks. */ + + mainiddir = rpmGetPath(fl->buildRoot, BUILD_ID_DIR, NULL); + debugiddir = rpmGetPath(fl->buildRoot, DEBUG_ID_DIR, NULL); + + /* Make sure to reset all file flags to defaults. */ + attrstr = mkattr(); + argvAdd(files, attrstr); + free (attrstr); + + /* Supported, but questionable. */ + if (needMain && needDbg) + rpmlog(RPMLOG_WARNING, + _("Mixing main ELF and debug files in package")); + + if (needMain) { + if ((rc = rpmioMkpath(mainiddir, 0755, -1, -1)) != 0) { + rpmlog(RPMLOG_ERR, "%s %s: %m\n", errdir, mainiddir); + } else { + argvAddAttr(files, RPMFILE_DIR|RPMFILE_ARTIFACT, mainiddir); + } + } + + if (rc == 0 && needDbg) { + if ((rc = rpmioMkpath(debugiddir, 0755, -1, -1)) != 0) { + rpmlog(RPMLOG_ERR, "%s %s: %m\n", errdir, debugiddir); + } else { + argvAddAttr(files, RPMFILE_DIR|RPMFILE_ARTIFACT, debugiddir); + } + } + } + + /* In case we need ALLDEBUG links we might need the vra as + tagged onto the .debug file name. */ + char *vra = NULL; + if (rc == 0 && needDbg && build_id_links == BUILD_IDS_ALLDEBUG) { + int unique_debug_names = + rpmExpandNumeric("%{?_unique_debug_names}"); + if (unique_debug_names == 1) + vra = rpmExpand("-%{VERSION}-%{RELEASE}.%{_arch}", NULL); + } + + /* Now add a subdir and symlink for each buildid found. */ + for (i = 0; i < nr_ids; i++) { + /* Don't add anything more when an error occurred. But do + cleanup. */ + if (rc == 0) { + int isDbg = strncmp (paths[i], DEBUG_LIB_PREFIX, + strlen (DEBUG_LIB_PREFIX)) == 0; + + char *buildidsubdir; + char subdir[4]; + subdir[0] = '/'; + subdir[1] = ids[i][0]; + subdir[2] = ids[i][1]; + subdir[3] = '\0'; + if (isDbg) + buildidsubdir = rpmGetPath(debugiddir, subdir, NULL); + else + buildidsubdir = rpmGetPath(mainiddir, subdir, NULL); + /* We only need to create and add the subdir once. */ + int addsubdir = access (buildidsubdir, F_OK) == -1; + if (addsubdir + && (rc = rpmioMkpath(buildidsubdir, 0755, -1, -1)) != 0) { + rpmlog(RPMLOG_ERR, "%s %s: %m\n", errdir, buildidsubdir); + } else { + if (addsubdir) + argvAddAttr(files, RPMFILE_DIR|RPMFILE_ARTIFACT, buildidsubdir); + if (rc == 0) { + char *linkpattern, *targetpattern; + char *linkpath, *targetpath; + int dups = 0; + if (isDbg) { + linkpattern = "%s/%s"; + targetpattern = "../../../../..%s"; + } else { + linkpattern = "%s/%s"; + targetpattern = "../../../..%s"; + } + rasprintf(&linkpath, linkpattern, + buildidsubdir, &ids[i][2]); + rasprintf(&targetpath, targetpattern, paths[i]); + rc = addNewIDSymlink(files, targetpath, linkpath, + isDbg, &dups); + + /* We might want to have a link from the debug + build_ids dir to the main one. We create it + when we are creating compat links or doing + an old style alldebug build-ids package. In + the first case things are simple since we + just link to the main build-id symlink. The + second case is a bit tricky, since we + cannot be 100% sure the file names in the + main and debug package match. Currently + they do, but when creating parallel + installable debuginfo packages they might + not (in that case we might have to also + strip the nvr from the debug name). + + In general either method is discouraged + since it might create dangling symlinks if + the package versions get out of sync. */ + if (rc == 0 && isDbg + && build_id_links == BUILD_IDS_COMPAT) { + /* buildidsubdir already points to the + debug buildid. We just need to setup + the symlink to the main one. There + might be duplicate IDs, those are found + by the addNewIDSymlink above. Target + the last found duplicate, if any. */ + free(linkpath); + free(targetpath); + if (dups == 0) + { + rasprintf(&linkpath, "%s/%s", + buildidsubdir, &ids[i][2]); + rasprintf(&targetpath, + "../../../.build-id%s/%s", + subdir, &ids[i][2]); + } + else + { + rasprintf(&linkpath, "%s/%s.%d", + buildidsubdir, &ids[i][2], dups); + rasprintf(&targetpath, + "../../../.build-id%s/%s.%d", + subdir, &ids[i][2], dups); + } + rc = addNewIDSymlink(files, targetpath, linkpath, + 0, NULL); + } + + if (rc == 0 && isDbg + && build_id_links == BUILD_IDS_ALLDEBUG) { + /* buildidsubdir already points to the + debug buildid. We do have to figure out + the main ELF file though (which is most + likely not in this package). Guess we + can find it by stripping the + /usr/lib/debug path and .debug + prefix. Which might not really be + correct if there was a more involved + transformation (for example for + parallel installable debuginfo + packages), but then we shouldn't be + using ALLDEBUG in the first place. + Also ignore things like .dwz multifiles + which don't end in ".debug". */ + int pathlen = strlen(paths[i]); + int debuglen = strlen(".debug"); + int prefixlen = strlen(DEBUG_LIB_DIR); + int vralen = vra == NULL ? 0 : strlen(vra); + if (pathlen > prefixlen + debuglen + vralen + && strcmp ((paths[i] + pathlen - debuglen), + ".debug") == 0) { + free(linkpath); + free(targetpath); + char *targetstr = xstrdup (paths[i] + + prefixlen); + int targetlen = pathlen - prefixlen; + int targetend = targetlen - debuglen - vralen; + targetstr[targetend] = '\0'; + rasprintf(&linkpath, "%s/%s", + buildidsubdir, &ids[i][2]); + rasprintf(&targetpath, "../../../../..%s", + targetstr); + rc = addNewIDSymlink(files, targetpath, + linkpath, 0, &dups); + free(targetstr); + } + } + free(linkpath); + free(targetpath); + } + } + free(buildidsubdir); + } + free(paths[i]); + free(ids[i]); + } + free(mainiddir); + free(debugiddir); + free(vra); + free(paths); + free(ids); + } + return rc; +} +#endif + /** * Add a file to a binary package. * @param pkg @@ -1647,16 +2143,13 @@ static rpmRC processBinaryFile(Package pkg, FileList fl, const char * fileName) } argvFree(argv); } else { - int lvl = RPMLOG_WARNING; const char *msg = (fl->cur.isDir) ? - _("Directory not found by glob: %s\n") : - _("File not found by glob: %s\n"); - if (!(fl->cur.attrFlags & RPMFILE_EXCLUDE)) { - lvl = RPMLOG_ERR; - rc = RPMRC_FAIL; - } - rpmlog(lvl, msg, diskPath); - goto exit; + _("Directory not found by glob: %s. " + "Trying without globbing.\n") : + _("File not found by glob: %s. " + "Trying without globbing.\n"); + rpmlog(RPMLOG_DEBUG, msg, diskPath); + rc = addFile(fl, diskPath, NULL); } } else { rc = addFile(fl, diskPath, NULL); @@ -1676,6 +2169,8 @@ static rpmRC readFilesManifest(rpmSpec spec, Package pkg, const char *path) char *fn, buf[BUFSIZ]; FILE *fd = NULL; rpmRC rc = RPMRC_FAIL; + unsigned int nlines = 0; + char *expanded; if (*path == '/') { fn = rpmGetPath(path, NULL); @@ -1690,13 +2185,28 @@ static rpmRC readFilesManifest(rpmSpec spec, Package pkg, const char *path) goto exit; } + /* XXX unmask %license while parsing files manifest*/ + rpmPushMacro(spec->macros, "license", NULL, "%%license", RMIL_SPEC); + while (fgets(buf, sizeof(buf), fd)) { - handleComments(buf); - if (expandMacros(spec, spec->macros, buf, sizeof(buf))) { + if (handleComments(buf)) + continue; + if (rpmExpandMacros(spec->macros, buf, &expanded, 0) < 0) { rpmlog(RPMLOG_ERR, _("line: %s\n"), buf); goto exit; } - argvAdd(&(pkg->fileList), buf); + argvAdd(&(pkg->fileList), expanded); + free(expanded); + nlines++; + } + + if (nlines == 0) { + int terminate = + rpmExpandNumeric("%{?_empty_manifest_terminate_build}"); + rpmlog(terminate ? RPMLOG_ERR : RPMLOG_WARNING, + _("Empty %%files file %s\n"), fn); + if (terminate) + goto exit; } if (ferror(fd)) @@ -1705,6 +2215,7 @@ static rpmRC readFilesManifest(rpmSpec spec, Package pkg, const char *path) rc = RPMRC_OK; exit: + rpmPopMacro(NULL, "license"); if (fd) fclose(fd); free(fn); return rc; @@ -1737,24 +2248,47 @@ static char * getSpecialDocDir(Header h, rpmFlags sdtype) return res; } -static specialDir specialDirNew(Header h, rpmFlags sdtype, - AttrRec ar, AttrRec def_ar) +static specialDir specialDirNew(Header h, rpmFlags sdtype) { specialDir sd = xcalloc(1, sizeof(*sd)); - dupAttrRec(ar, &(sd->ar)); - dupAttrRec(def_ar, &(sd->def_ar)); + + sd->entriesCount = 0; + sd->entriesAlloced = 10; + sd->entries = xcalloc(sd->entriesAlloced, sizeof(sd->entries[0])); + sd->dirname = getSpecialDocDir(h, sdtype); sd->sdtype = sdtype; return sd; } +static void addSpecialFile(specialDir sd, const char *path, FileEntry cur, + FileEntry def) +{ + argvAdd(&sd->files, path); + + if (sd->entriesCount >= sd->entriesAlloced) { + sd->entriesAlloced <<= 1; + sd->entries = xrealloc(sd->entries, sd->entriesAlloced * + sizeof(sd->entries[0])); + } + + copyFileEntry(cur, &sd->entries[sd->entriesCount].curEntry); + copyFileEntry(def, &sd->entries[sd->entriesCount].defEntry); + sd->entriesCount++; +} + static specialDir specialDirFree(specialDir sd) { + int i = 0; + if (sd) { argvFree(sd->files); - freeAttrRec(&(sd->ar)); - freeAttrRec(&(sd->def_ar)); free(sd->dirname); + for (i = 0; i < sd->entriesCount; i++) { + FileEntryFree(&sd->entries[i].curEntry); + FileEntryFree(&sd->entries[i].defEntry); + } + free(sd->entries); free(sd); } return NULL; @@ -1767,10 +2301,13 @@ static void processSpecialDir(rpmSpec spec, Package pkg, FileList fl, const char *sdname = (sd->sdtype == RPMFILE_DOC) ? "%doc" : "%license"; char *mkdocdir = rpmExpand("%{__mkdir_p} $", sdenv, NULL); StringBuf docScript = newStringBuf(); + char *basepath, **files; + int fi; appendStringBuf(docScript, sdenv); appendStringBuf(docScript, "=$RPM_BUILD_ROOT"); appendLineStringBuf(docScript, sd->dirname); + appendLineStringBuf(docScript, "export LC_ALL=C"); appendStringBuf(docScript, "export "); appendLineStringBuf(docScript, sdenv); appendLineStringBuf(docScript, mkdocdir); @@ -1781,7 +2318,8 @@ static void processSpecialDir(rpmSpec spec, Package pkg, FileList fl, appendStringBuf(docScript, "cp -pr "); appendStringBuf(docScript, efn); appendStringBuf(docScript, " $"); - appendLineStringBuf(docScript, sdenv); + appendStringBuf(docScript, sdenv); + appendLineStringBuf(docScript, " ||:"); free(efn); } @@ -1793,52 +2331,77 @@ static void processSpecialDir(rpmSpec spec, Package pkg, FileList fl, fl->processingFailed = 1; } - /* Reset for %doc */ - FileEntryFree(&fl->cur); - - fl->cur.attrFlags |= sd->sdtype; - fl->cur.verifyFlags = fl->def.verifyFlags; - dupAttrRec(&(sd->ar), &(fl->cur.ar)); - dupAttrRec(&(sd->def_ar), &(fl->def.ar)); + basepath = rpmGenPath(spec->rootDir, "%{_builddir}", spec->buildSubdir); + files = sd->files; + fi = 0; + while (*files != NULL) { + char *origfile = rpmGenPath(basepath, *files, NULL); + char *eorigfile = rpmEscapeSpaces(origfile); + ARGV_t globFiles; + int globFilesCount, i; + char *newfile; + + FileEntryFree(&fl->cur); + FileEntryFree(&fl->def); + copyFileEntry(&sd->entries[fi].curEntry, &fl->cur); + copyFileEntry(&sd->entries[fi].defEntry, &fl->def); + fi++; + + if (rpmGlob(eorigfile, &globFilesCount, &globFiles) == 0) { + for (i = 0; i < globFilesCount; i++) { + rasprintf(&newfile, "%s/%s", sd->dirname, basename(globFiles[i])); + processBinaryFile(pkg, fl, newfile); + free(newfile); + } + argvFree(globFiles); + } else { + rpmlog(RPMLOG_ERR, _("File not found by glob: %s\n"), eorigfile); + fl->processingFailed = 1; + } + free(eorigfile); + free(origfile); + files++; + } + free(basepath); + FileEntryFree(&fl->cur); + FileEntryFree(&fl->def); + copyFileEntry(&sd->entries[0].defEntry, &fl->def); + fl->cur.isDir = 1; (void) processBinaryFile(pkg, fl, sd->dirname); freeStringBuf(docScript); free(mkdocdir); } - -static rpmRC processPackageFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags, - Package pkg, int installSpecialDoc, int test) + +/* Resets the default settings for files in the package list. + Used in processPackageFiles whenever a new set of files is added. */ +static void resetPackageFilesDefaults (struct FileList_s *fl, + rpmBuildPkgFlags pkgFlags) { - struct FileList_s fl; - ARGV_t fileNames = NULL; - specialDir specialDoc = NULL; - specialDir specialLic = NULL; + struct AttrRec_s root_ar = { 0, 0, 0, 0, 0, 0 }; - pkg->cpioList = NULL; + root_ar.ar_user = rpmstrPoolId(fl->pool, UID_0_USER, 1); + root_ar.ar_group = rpmstrPoolId(fl->pool, GID_0_GROUP, 1); + dupAttrRec(&root_ar, &fl->def.ar); /* XXX assume %defattr(-,root,root) */ - for (ARGV_const_t fp = pkg->fileFile; fp && *fp != NULL; fp++) { - if (readFilesManifest(spec, pkg, *fp)) - return RPMRC_FAIL; - } - /* Init the file list structure */ - memset(&fl, 0, sizeof(fl)); + fl->def.verifyFlags = RPMVERIFY_ALL; - /* XXX spec->buildRoot == NULL, then xstrdup("") is returned */ - fl.buildRoot = rpmGenPath(spec->rootDir, spec->buildRoot, NULL); - - dupAttrRec(&root_ar, &fl.def.ar); /* XXX assume %defattr(-,root,root) */ - fl.def.verifyFlags = RPMVERIFY_ALL; - - fl.pkgFlags = pkgFlags; + fl->pkgFlags = pkgFlags; +} - { char *docs = rpmGetPath("%{?__docdir_path}", NULL); - argvSplit(&fl.docDirs, docs, ":"); - free(docs); - } - - for (ARGV_const_t fp = pkg->fileList; *fp != NULL; fp++) { +/* Adds the given fileList to the package. If fromSpecFileList is not zero + then the specialDirs are also filled in and the files are sanitized + through processBinaryFile(). Otherwise no special files are processed + and the files are added directly through addFile(). */ +static void addPackageFileList (struct FileList_s *fl, Package pkg, + ARGV_t *fileList, + specialDir *specialDoc, specialDir *specialLic, + int fromSpecFileList) +{ + ARGV_t fileNames = NULL; + for (ARGV_const_t fp = *fileList; *fp != NULL; fp++) { char buf[strlen(*fp) + 1]; const char *s = *fp; SKIPSPACE(s); @@ -1848,92 +2411,169 @@ static rpmRC processPackageFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags, rstrlcpy(buf, s, sizeof(buf)); /* Reset for a new line in %files */ - FileEntryFree(&fl.cur); + FileEntryFree(&fl->cur); /* turn explicit flags into %def'd ones (gosh this is hacky...) */ - fl.cur.specdFlags = ((unsigned)fl.def.specdFlags) >> 8; - fl.cur.verifyFlags = fl.def.verifyFlags; - - if (parseForVerify(buf, 0, &fl.cur) || - parseForVerify(buf, 1, &fl.def) || - parseForAttr(buf, 0, &fl.cur) || - parseForAttr(buf, 1, &fl.def) || - parseForDev(buf, &fl.cur) || - parseForConfig(buf, &fl.cur) || - parseForLang(buf, &fl.cur) || - parseForCaps(buf, &fl.cur) || - parseForSimple(buf, &fl.cur, &fileNames)) + fl->cur.specdFlags = ((unsigned)fl->def.specdFlags) >> 8; + fl->cur.verifyFlags = fl->def.verifyFlags; + + if (parseForVerify(buf, 0, &fl->cur) || + parseForVerify(buf, 1, &fl->def) || + parseForAttr(fl->pool, buf, 0, &fl->cur) || + parseForAttr(fl->pool, buf, 1, &fl->def) || + parseForDev(buf, &fl->cur) || + parseForConfig(buf, &fl->cur) || + parseForLang(buf, &fl->cur) || + parseForCaps(buf, &fl->cur) || + parseForSimple(buf, &fl->cur, &fileNames)) { - fl.processingFailed = 1; + fl->processingFailed = 1; continue; } for (ARGV_const_t fn = fileNames; fn && *fn; fn++) { - if (fl.cur.attrFlags & RPMFILE_SPECIALDIR) { - rpmFlags oattrs = (fl.cur.attrFlags & ~RPMFILE_SPECIALDIR); + + /* For file lists that don't come from a spec file list + processing is easy. There are no special files and the + file names don't need to be adjusted. */ + if (!fromSpecFileList) { + if (fl->cur.attrFlags & RPMFILE_SPECIALDIR + || fl->cur.attrFlags & RPMFILE_DOCDIR + || fl->cur.attrFlags & RPMFILE_PUBKEY) { + rpmlog(RPMLOG_ERR, + _("Special file in generated file list: %s\n"), + *fn); + fl->processingFailed = 1; + continue; + } + if (fl->cur.attrFlags & RPMFILE_DIR) + fl->cur.isDir = 1; + addFile(fl, *fn, NULL); + continue; + } + + /* File list does come from the spec, try to detect special + files and adjust the actual file names. */ + if (fl->cur.attrFlags & RPMFILE_SPECIALDIR) { + rpmFlags oattrs = (fl->cur.attrFlags & ~RPMFILE_SPECIALDIR); specialDir *sdp = NULL; if (oattrs == RPMFILE_DOC) { - sdp = &specialDoc; + sdp = specialDoc; } else if (oattrs == RPMFILE_LICENSE) { - sdp = &specialLic; + sdp = specialLic; } if (sdp == NULL || **fn == '/') { rpmlog(RPMLOG_ERR, _("Can't mix special %s with other forms: %s\n"), (oattrs & RPMFILE_DOC) ? "%doc" : "%license", *fn); - fl.processingFailed = 1; + fl->processingFailed = 1; continue; } /* save attributes on first special doc/license for later use */ if (*sdp == NULL) { - *sdp = specialDirNew(pkg->header, oattrs, - &fl.cur.ar, &fl.def.ar); + *sdp = specialDirNew(pkg->header, oattrs); } - argvAdd(&(*sdp)->files, *fn); + addSpecialFile(*sdp, *fn, &fl->cur, &fl->def); continue; } /* this is now an artificial limitation */ if (fn != fileNames) { rpmlog(RPMLOG_ERR, _("More than one file on a line: %s\n"),*fn); - fl.processingFailed = 1; + fl->processingFailed = 1; continue; } - if (fl.cur.attrFlags & RPMFILE_DOCDIR) { - argvAdd(&(fl.docDirs), *fn); - } else if (fl.cur.attrFlags & RPMFILE_PUBKEY) { - (void) processMetadataFile(pkg, &fl, *fn, RPMTAG_PUBKEYS); - } else if (fl.cur.attrFlags & RPMFILE_SECMANIFEST) { - (void) processMetadataFile(pkg, &fl, *fn, RPMTAG_SECMANIFEST); - } else { - if (fl.cur.attrFlags & RPMFILE_DIR) - fl.cur.isDir = 1; - (void) processBinaryFile(pkg, &fl, *fn); + if (fl->cur.attrFlags & RPMFILE_DOCDIR) { + argvAdd(&(fl->docDirs), *fn); + } else if (fl->cur.attrFlags & RPMFILE_PUBKEY) { + (void) processMetadataFile(pkg, fl, *fn, RPMTAG_PUBKEYS); + } else if (fl->cur.attrFlags & RPMFILE_SECMANIFEST) { + (void) processMetadataFile(pkg, fl, *fn, RPMTAG_SECMANIFEST); + }else { + if (fl->cur.attrFlags & RPMFILE_DIR) + fl->cur.isDir = 1; + (void) processBinaryFile(pkg, fl, *fn); } } - if (fl.cur.caps) - fl.haveCaps = 1; + if (fl->cur.caps) + fl->haveCaps = 1; } + argvFree(fileNames); +} + +static rpmRC processPackageFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags, + Package pkg, int didInstall, int test) +{ + struct FileList_s fl; + specialDir specialDoc = NULL; + specialDir specialLic = NULL; + + pkg->cpioList = NULL; + + for (ARGV_const_t fp = pkg->fileFile; fp && *fp != NULL; fp++) { + if (readFilesManifest(spec, pkg, *fp)) + return RPMRC_FAIL; + } + /* Init the file list structure */ + memset(&fl, 0, sizeof(fl)); + + fl.pool = rpmstrPoolLink(spec->pool); + /* XXX spec->buildRoot == NULL, then xstrdup("") is returned */ + fl.buildRoot = rpmGenPath(spec->rootDir, spec->buildRoot, NULL); + fl.buildRootLen = strlen(fl.buildRoot); + + resetPackageFilesDefaults (&fl, pkgFlags); + + { char *docs = rpmGetPath("%{?__docdir_path}", NULL); + argvSplit(&fl.docDirs, docs, ":"); + free(docs); + } + + addPackageFileList (&fl, pkg, &pkg->fileList, + &specialDoc, &specialLic, 1); /* Now process special docs and licenses if present */ if (specialDoc) - processSpecialDir(spec, pkg, &fl, specialDoc, installSpecialDoc, test); + processSpecialDir(spec, pkg, &fl, specialDoc, didInstall, test); if (specialLic) - processSpecialDir(spec, pkg, &fl, specialLic, installSpecialDoc, test); + processSpecialDir(spec, pkg, &fl, specialLic, didInstall, test); if (fl.processingFailed) goto exit; +#if HAVE_LIBDW + /* Check build-ids and add build-ids links for files to package list. */ + const char *arch = headerGetString(pkg->header, RPMTAG_ARCH); + if (!rstreq(arch, "noarch")) { + /* Go through the current package list and generate a files list. */ + ARGV_t idFiles = NULL; + if (generateBuildIDs (&fl, &idFiles) != 0) { + rpmlog(RPMLOG_ERR, _("Generating build-id links failed\n")); + fl.processingFailed = 1; + argvFree(idFiles); + goto exit; + } + + if (idFiles != NULL) { + resetPackageFilesDefaults (&fl, pkgFlags); + addPackageFileList (&fl, pkg, &idFiles, NULL, NULL, 0); + } + argvFree(idFiles); + + if (fl.processingFailed) + goto exit; + } +#endif + /* Verify that file attributes scope over hardlinks correctly. */ if (checkHardLinks(&fl.files)) - (void) rpmlibNeedsFeature(pkg->header, - "PartialHardlinkSets", "4.0.4-1"); + (void) rpmlibNeedsFeature(pkg, "PartialHardlinkSets", "4.0.4-1"); - genCpioListAndHeader(&fl, &pkg->cpioList, pkg->header, 0); + genCpioListAndHeader(&fl, pkg, 0); exit: FileListFree(&fl); @@ -1958,6 +2598,7 @@ rpmRC processSourceFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags) struct FileList_s fl; ARGV_t files = NULL; Package pkg; + Package sourcePkg = spec->sourcePackage; static char *_srcdefattr; static int oneshot; @@ -1988,13 +2629,14 @@ rpmRC processSourceFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags) } } - spec->sourceCpioList = NULL; + sourcePkg->cpioList = NULL; /* Init the file list structure */ memset(&fl, 0, sizeof(fl)); + fl.pool = rpmstrPoolLink(spec->pool); if (_srcdefattr) { char *a = rstrscat(NULL, "%defattr ", _srcdefattr, NULL); - parseForAttr(a, 1, &fl.def); + parseForAttr(fl.pool, a, 1, &fl.def); free(a); } fl.files.alloced = spec->numSources + 1; @@ -2039,31 +2681,39 @@ rpmRC processSourceFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags) flp->fl_mode &= S_IFMT; flp->fl_mode |= fl.def.ar.ar_fmode; } + if (fl.def.ar.ar_user) { - flp->uname = rpmugStashStr(fl.def.ar.ar_user); + flp->uname = fl.def.ar.ar_user; } else { - flp->uname = rpmugStashStr(rpmugUname(flp->fl_uid)); + flp->uname = rpmstrPoolId(fl.pool, rpmugUname(flp->fl_uid), 1); } + if (! flp->uname) { + flp->uname = rpmstrPoolId(fl.pool, rpmugUname(getuid()), 1); + } + if (! flp->uname) { + flp->uname = rpmstrPoolId(fl.pool, UID_0_USER, 1); + } + if (fl.def.ar.ar_group) { - flp->gname = rpmugStashStr(fl.def.ar.ar_group); + flp->gname = fl.def.ar.ar_group; } else { - flp->gname = rpmugStashStr(rpmugGname(flp->fl_gid)); + flp->gname = rpmstrPoolId(fl.pool, rpmugGname(flp->fl_gid), 1); } - flp->langs = xstrdup(""); - - if (! (flp->uname && flp->gname)) { - rpmlog(RPMLOG_ERR, _("Bad owner/group: %s\n"), diskPath); - fl.processingFailed = 1; + if (! flp->gname) { + flp->gname = rpmstrPoolId(fl.pool, rpmugGname(getgid()), 1); + } + if (! flp->gname) { + flp->gname = rpmstrPoolId(fl.pool, GID_0_GROUP, 1); } + flp->langs = xstrdup(""); fl.files.used++; } argvFree(files); if (! fl.processingFailed) { - if (spec->sourceHeader != NULL) { - genCpioListAndHeader(&fl, &spec->sourceCpioList, - spec->sourceHeader, 1); + if (sourcePkg->header != NULL) { + genCpioListAndHeader(&fl, sourcePkg, 1); } } @@ -2073,6 +2723,7 @@ rpmRC processSourceFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags) /** * Check packaged file list against what's in the build root. + * @param buildRoot path of build root * @param fileList packaged file list * @return -1 if skipped, 0 on OK, 1 on error */ @@ -2109,245 +2760,370 @@ exit: return rc; } -#if HAVE_GELF_H && HAVE_LIBELF -/* Query the build-id from the ELF file NAME and store it in the newly - allocated *build_id array of size *build_id_size. Returns -1 on - error. */ +static rpmTag copyTagsFromMainDebug[] = { + RPMTAG_ARCH, + RPMTAG_SUMMARY, + RPMTAG_DESCRIPTION, + RPMTAG_GROUP, + /* see addTargets */ + RPMTAG_OS, + RPMTAG_PLATFORM, + RPMTAG_OPTFLAGS, +}; -int -getELFBuildId (const char *name, - unsigned char **id, size_t *id_size) +/* this is a hack: patch the summary and the description to include + * the correct package name */ +static void patchDebugPackageString(Package dbg, rpmTag tag, Package pkg, Package mainpkg) { - int fd, i; - Elf *elf; - GElf_Ehdr ehdr; - Elf_Data *build_id = NULL; - size_t build_id_offset = 0, build_id_size = 0; - - /* Now query the build-id of the file and add the - corresponding links in the .build-id tree. - The following code is based on tools/debugedit.c. */ - fd = open (name, O_RDONLY); - if (fd < 0) - return -1; - elf = elf_begin (fd, ELF_C_READ_MMAP, NULL); - if (elf == NULL) - { - fprintf (stderr, "cannot open ELF file: %s", - elf_errmsg (-1)); - close (fd); - return -1; - } - if (elf_kind (elf) != ELF_K_ELF - || gelf_getehdr (elf, &ehdr) == NULL - || (ehdr.e_type != ET_DYN - && ehdr.e_type != ET_EXEC - && ehdr.e_type != ET_REL)) - { - elf_end (elf); - close (fd); - return -1; - } - for (i = 0; i < ehdr.e_shnum; ++i) - { - Elf_Scn *s = elf_getscn (elf, i); - GElf_Shdr shdr; - Elf_Data *data; - Elf32_Nhdr nh; - Elf_Data dst = - { - .d_version = EV_CURRENT, .d_type = ELF_T_NHDR, - .d_buf = &nh, .d_size = sizeof nh - }; - Elf_Data src = dst; - - gelf_getshdr (s, &shdr); - if (shdr.sh_type != SHT_NOTE - || !(shdr.sh_flags & SHF_ALLOC)) - continue; - - /* Look for a build-ID note here. */ - data = elf_rawdata (s, NULL); - src.d_buf = data->d_buf; - assert (sizeof (Elf32_Nhdr) == sizeof (Elf64_Nhdr)); - while (data->d_buf + data->d_size - src.d_buf > (int) sizeof nh - && elf32_xlatetom (&dst, &src, ehdr.e_ident[EI_DATA])) - { - Elf32_Word len = sizeof nh + nh.n_namesz; - len = (len + 3) & ~3; + const char *oldname, *newname, *old; + char *oldsubst = NULL, *newsubst = NULL, *p; + oldname = headerGetString(mainpkg->header, RPMTAG_NAME); + newname = headerGetString(pkg->header, RPMTAG_NAME); + rasprintf(&oldsubst, "package %s", oldname); + rasprintf(&newsubst, "package %s", newname); + old = headerGetString(dbg->header, tag); + p = old ? strstr(old, oldsubst) : NULL; + if (p) { + char *new = NULL; + rasprintf(&new, "%.*s%s%s", (int)(p - old), old, newsubst, p + strlen(oldsubst)); + headerDel(dbg->header, tag); + headerPutString(dbg->header, tag, new); + _free(new); + } + _free(oldsubst); + _free(newsubst); +} - if (nh.n_namesz == sizeof "GNU" && nh.n_type == 3 - && !memcmp (src.d_buf + sizeof nh, "GNU", sizeof "GNU")) - { - build_id = data; - build_id_offset = src.d_buf + len - data->d_buf; - build_id_size = nh.n_descsz; - break; - } +/* Early prototype for use in filterDebuginfoPackage. */ +static void addPackageDeps(Package from, Package to, enum rpmTag_e tag); - len += nh.n_descsz; - len = (len + 3) & ~3; - src.d_buf += len; - } +/* create a new debuginfo subpackage for package pkg from the + * main debuginfo package */ +static Package cloneDebuginfoPackage(rpmSpec spec, Package pkg, Package maindbg) +{ + const char *name = headerGetString(pkg->header, RPMTAG_NAME); + char *dbgname = NULL; + Package dbg; + + rasprintf(&dbgname, "%s-%s", name, "debuginfo"); + dbg = newPackage(dbgname, spec->pool, &spec->packages); + headerPutString(dbg->header, RPMTAG_NAME, dbgname); + copyInheritedTags(dbg->header, pkg->header); + headerDel(dbg->header, RPMTAG_GROUP); + headerCopyTags(maindbg->header, dbg->header, copyTagsFromMainDebug); + dbg->autoReq = maindbg->autoReq; + dbg->autoProv = maindbg->autoProv; + + /* patch summary and description strings */ + patchDebugPackageString(dbg, RPMTAG_SUMMARY, pkg, spec->packages); + patchDebugPackageString(dbg, RPMTAG_DESCRIPTION, pkg, spec->packages); + + /* Add self-provides (normally done by addTargets) */ + addPackageProvides(dbg); + dbg->ds = rpmdsThis(dbg->header, RPMTAG_REQUIRENAME, RPMSENSE_EQUAL); + + _free(dbgname); + return dbg; +} - if (build_id != NULL) - break; - } +/* collect the debug files for package pkg and put them into + * a (possibly new) debuginfo subpackage */ +static void filterDebuginfoPackage(rpmSpec spec, Package pkg, + Package maindbg, Package dbgsrc, + char *buildroot, char *uniquearch) +{ + rpmfi fi; + ARGV_t files = NULL; + ARGV_t dirs = NULL; + int lastdiridx = -1, dirsadded; + char *path = NULL, *p, *pmin; + size_t buildrootlen = strlen(buildroot); - if (build_id == NULL) - return -1; + /* ignore noarch subpackages */ + if (rstreq(headerGetString(pkg->header, RPMTAG_ARCH), "noarch")) + return; - *id = malloc (build_id_size); - *id_size = build_id_size; - memcpy (*id, build_id->d_buf + build_id_offset, build_id_size); + if (!uniquearch) + uniquearch = ""; + + fi = rpmfilesIter(pkg->cpioList, RPMFI_ITER_FWD); + /* Check if the current package has files with debug info + and add them to the file list */ + fi = rpmfiInit(fi, 0); + while (rpmfiNext(fi) >= 0) { + const char *name = rpmfiFN(fi); + int namel = strlen(name); + + /* strip trailing .debug like in find-debuginfo.sh */ + if (namel > 6 && !strcmp(name + namel - 6, ".debug")) + namel -= 6; + + /* fileRenameMap doesn't necessarily have to be initialized */ + if (pkg->fileRenameMap) { + const char **names = NULL; + int namec = 0; + fileRenameHashGetEntry(pkg->fileRenameMap, name, &names, &namec, NULL); + if (namec) { + if (namec > 1) + rpmlog(RPMLOG_WARNING, _("%s was mapped to multiple filenames"), name); + name = *names; + namel = strlen(name); + } + } + + /* generate path */ + rasprintf(&path, "%s%s%.*s%s.debug", buildroot, DEBUG_LIB_DIR, namel, name, uniquearch); + + /* If that file exists we have debug information for it */ + if (access(path, F_OK) == 0) { + /* Append the file list preamble */ + if (!files) { + char *attr = mkattr(); + argvAdd(&files, attr); + argvAddAttr(&files, RPMFILE_DIR, DEBUG_LIB_DIR); + free(attr); + } - elf_end (elf); - close (fd); + /* Add the files main debug-info file */ + argvAdd(&files, path + buildrootlen); + + /* Add the dir(s) */ + dirsadded = 0; + pmin = path + buildrootlen + strlen(DEBUG_LIB_DIR); + while ((p = strrchr(path + buildrootlen, '/')) != NULL && p > pmin) { + *p = 0; + if (lastdiridx >= 0 && !strcmp(dirs[lastdiridx], path + buildrootlen)) + break; /* already added this one */ + argvAdd(&dirs, path + buildrootlen); + dirsadded++; + } + if (dirsadded) + lastdiridx = argvCount(dirs) - dirsadded; /* remember longest dir */ + } + path = _free(path); + } + rpmfiFree(fi); + /* Exclude debug files for files which were excluded in respective non-debug package */ + for (ARGV_const_t excl = pkg->fileExcludeList; excl && *excl; excl++) { + const char *name = *excl; + + /* generate path */ + rasprintf(&path, "%s%s%s%s.debug", buildroot, DEBUG_LIB_DIR, name, uniquearch); + /* Exclude only debuginfo files which actually exist */ + if (access(path, F_OK) == 0) { + char *line = NULL; + rasprintf(&line, "%%exclude %s", path + buildrootlen); + argvAdd(&files, line); + _free(line); + } + path = _free(path); + } - return 0; -} + /* add collected directories to file list */ + if (dirs) { + int i; + argvSort(dirs, NULL); + for (i = 0; dirs[i]; i++) { + if (!i || strcmp(dirs[i], dirs[i - 1]) != 0) + argvAddAttr(&files, RPMFILE_DIR, dirs[i]); + } + dirs = argvFree(dirs); + } + if (files) { + /* Add security manifest to set right SMACK labels */ + argvAdd(&files, "%manifest %{name}-debuginfo.manifest"); -static rpmTag copyTagsForDebug[] = { - RPMTAG_EPOCH, - RPMTAG_VERSION, - RPMTAG_RELEASE, - RPMTAG_LICENSE, - RPMTAG_PACKAGER, - RPMTAG_DISTRIBUTION, - RPMTAG_DISTURL, - RPMTAG_VENDOR, - RPMTAG_ICON, - RPMTAG_URL, - RPMTAG_CHANGELOGTIME, - RPMTAG_CHANGELOGNAME, - RPMTAG_CHANGELOGTEXT, - RPMTAG_PREFIXES, - RPMTAG_RHNPLATFORM, - RPMTAG_OS, - RPMTAG_DISTTAG, - RPMTAG_CVSID, - RPMTAG_ARCH, - 0 -}; + /* we have collected some files. Now put them in a debuginfo + * package. If this is not the main package, clone the main + * debuginfo package */ + if (pkg == spec->packages) { + maindbg->fileList = files; + /*free maindb_fileList_bakup and set to NULL*/ + maindb_fileList_bakup = argvFree(maindb_fileList_bakup); + } + else { + Package dbg = cloneDebuginfoPackage(spec, pkg, maindbg); + dbg->fileList = files; + /* Recommend the debugsource package (or the main debuginfo). */ + addPackageDeps(dbg, dbgsrc ? dbgsrc : maindbg, + RPMTAG_RECOMMENDNAME); + } + } else { + /*To allow generate debuginfo package which is written in spec file, even there is no .debug files*/ + if (pkg == spec->packages && headerGetString(maindbg->header,RPMTAG_MULTIFILELIST)) { + rpmlog(RPMLOG_WARNING, "user rewrite %%file debuginfo to pack something\n"); + maindbg->fileList = maindb_fileList_bakup; + maindb_fileList_bakup = NULL; + headerDel(maindbg->header,RPMTAG_MULTIFILELIST); + } + } +} -static void addDebuginfoPackage(rpmSpec spec, Package pkg, char *buildroot) +/* add the debug dwz files to package pkg. + * return 1 if something was added, 0 otherwise. */ +static int addDebugDwz(Package pkg, char *buildroot) { - const char *a; - - elf_version(EV_CURRENT); - a = headerGetString(pkg->header, RPMTAG_ARCH); - if (strcmp(a, "noarch") != 0 && strcmp(a, "src") != 0 && strcmp(a, "nosrc") != 0) - { - Package dbg; - rpmfi fi = pkg->cpioList; - char tmp[1024]; - const char *name; - ARGV_t files = NULL; - int seen_build_id = 0; - - /* Check if the current package has files with debug info - and record them. */ - fi = rpmfiInit (fi, 0); - while (rpmfiNext (fi) >= 0) - { - const char *base; - int i; - unsigned char *build_id; - size_t build_id_size = 0; - struct stat sbuf; - - name = rpmfiFN (fi); - /* Skip leading buildroot. */ - base = name + strlen (buildroot); - /* Pre-pend %buildroot/usr/lib/debug and append .debug. */ - snprintf (tmp, 1024, "%s/usr/lib/debug%s.debug", - buildroot, base); - /* If that file exists we have debug information for it. */ - if (access (tmp, F_OK) != 0) - continue; + int ret = 0; + char *path = NULL; + struct stat sbuf; + + rasprintf(&path, "%s%s", buildroot, DEBUG_DWZ_DIR); + if (lstat(path, &sbuf) == 0 && S_ISDIR(sbuf.st_mode)) { + if (!pkg->fileList) { + char *attr = mkattr(); + argvAdd(&pkg->fileList, attr); + argvAddAttr(&pkg->fileList, RPMFILE_DIR|RPMFILE_ARTIFACT, DEBUG_LIB_DIR); + free(attr); + } + argvAddAttr(&pkg->fileList, RPMFILE_ARTIFACT, DEBUG_DWZ_DIR); + ret = 1; + } + path = _free(path); + return ret; +} - /* Append the file list preamble. */ - if (!files) - { - argvAdd(&files, "%defattr(-,root,root)"); - argvAdd(&files, "%dir /usr/lib/debug"); - } - /* Add the files main debug-info file. */ - snprintf (tmp, 1024, "/usr/lib/debug/%s.debug", base); - argvAdd(&files, tmp); - - /* Do not bother to check build-ids for symbolic links. - We'll handle them for the link target. */ - if (lstat (name, &sbuf) == -1 - || S_ISLNK (sbuf.st_mode)) - continue; +/* add the debug source files to package pkg. + * return 1 if something was added, 0 otherwise. */ +static int addDebugSrc(Package pkg, char *buildroot) +{ + int ret = 0; + char *path = NULL; + DIR *d; + struct dirent *de; + + /* not needed if we have an extra debugsource subpackage */ + if (rpmExpandNumeric("%{?_debugsource_packages}")) + return 0; + + rasprintf(&path, "%s%s", buildroot, DEBUG_SRC_DIR); + d = opendir(path); + path = _free(path); + if (d) { + while ((de = readdir(d)) != NULL) { + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) + continue; + rasprintf(&path, "%s/%s", DEBUG_SRC_DIR, de->d_name); + if (!pkg->fileList) { + char *attr = mkattr(); + argvAdd(&pkg->fileList, attr); + free(attr); + } + argvAdd(&pkg->fileList, path); + path = _free(path); + ret = 1; + } + closedir(d); + } + return ret; +} - /* Try to gather the build-id from the binary. */ - if (getELFBuildId (name, &build_id, &build_id_size) == -1) - continue; +/* find the debugsource package, if it has been created. + * We do this simply by searching for a package with the right name. */ +static Package findDebugsourcePackage(rpmSpec spec) +{ + Package pkg = NULL; + if (lookupPackage(spec, "debugsource", PART_SUBNAME|PART_QUIET, &pkg)) + return NULL; + return pkg && pkg->fileList ? pkg : NULL; +} - /* If we see build-id links for the first time add the - directory. */ - if (!seen_build_id) - argvAdd(&files, "%dir /usr/lib/debug/.build-id"); - - /* From the build-id construct the two links pointing back - to the debug information file and the binary. */ - snprintf (tmp, 1024, "/usr/lib/debug/.build-id/%02x/", - build_id[0]); - for (i = 1; i < build_id_size; ++i) - sprintf (tmp + strlen (tmp), "%02x", build_id[i]); - argvAdd(&files, tmp); - sprintf (tmp + strlen (tmp), ".debug"); - argvAdd(&files, tmp); - - free (build_id); - } +/* find the main debuginfo package. We do this simply by + * searching for a package with the right name. */ +static Package findDebuginfoPackage(rpmSpec spec) +{ + Package pkg = NULL; + if (lookupPackage(spec, "debuginfo", PART_SUBNAME|PART_QUIET, &pkg)) + return NULL; + return pkg && pkg->fileList ? pkg : NULL; +} - /* If there are debuginfo files for this package add a - new debuginfo package. */ - if (files) - { - dbg = newPackage (spec); - headerNVR (pkg->header, &name, NULL, NULL); - /* Set name, summary and group. */ - snprintf (tmp, 1024, "%s-debuginfo", name); - headerPutString(dbg->header, RPMTAG_NAME, tmp); - snprintf (tmp, 1024, "Debug information for package %s", name); - headerPutString(dbg->header, RPMTAG_SUMMARY, tmp); - snprintf (tmp, 1024, "This package provides debug information for package %s.\n" - "Debug information is useful when developing applications that use this\n" - "package or when debugging this package.", name); - headerPutString(dbg->header, RPMTAG_DESCRIPTION, tmp); - headerPutString(dbg->header, RPMTAG_GROUP, "Development/Debug"); - /* Inherit other tags from parent. */ - headerCopyTags (pkg->header, dbg->header, copyTagsForDebug); - - /* Build up the files list. */ - dbg->fileList = files; - } - } +/* add a dependency (e.g. RPMTAG_REQUIRENAME or RPMTAG_RECOMMENDNAME) + for package "to" into package "from". */ +static void addPackageDeps(Package from, Package to, enum rpmTag_e tag) +{ + const char *name; + char *evr, *isaprov; + name = headerGetString(to->header, RPMTAG_NAME); + evr = headerGetAsString(to->header, RPMTAG_EVR); + isaprov = rpmExpand(name, "%{?_isa}", NULL); + addReqProv(from, tag, isaprov, evr, RPMSENSE_EQUAL, 0); + free(isaprov); + free(evr); } -#endif rpmRC processBinaryFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags, - int installSpecialDoc, int test) + int didInstall, int test) { Package pkg; rpmRC rc = RPMRC_OK; char *buildroot; + char *uniquearch = NULL; + Package maindbg = NULL; /* the (existing) main debuginfo package */ + Package deplink = NULL; /* create requires to this package */ + /* The debugsource package, if it exists, that the debuginfo package(s) + should Recommend. */ + Package dbgsrcpkg = findDebugsourcePackage(spec); +#if HAVE_LIBDW + elf_version (EV_CURRENT); +#endif check_fileList = newStringBuf(); - buildroot = rpmGenPath(spec->rootDir, spec->buildRoot, NULL); genSourceRpmName(spec); + buildroot = rpmGenPath(spec->rootDir, spec->buildRoot, NULL); + if (rpmExpandNumeric("%{?_debuginfo_subpackages}")) { + maindbg = findDebuginfoPackage(spec); + if (maindbg) { + /* move debuginfo package to back */ + if (maindbg->next) { + Package *pp; + /* dequeue */ + for (pp = &spec->packages; *pp != maindbg; pp = &(*pp)->next) + ; + *pp = maindbg->next; + maindbg->next = 0; + /* enqueue at tail */ + for (; *pp; pp = &(*pp)->next) + ; + *pp = maindbg; + } + /* delete unsplit file list, we will re-add files back later */ + /*backup maindb fileList before free*/ + maindb_fileList_bakup = maindbg->fileList; + maindbg->fileFile = argvFree(maindbg->fileFile); + maindbg->fileList = NULL; + if (rpmExpandNumeric("%{?_unique_debug_names}")) + uniquearch = rpmExpand("-%{VERSION}-%{RELEASE}.%{_arch}", NULL); + } + } else if (dbgsrcpkg != NULL) { + /* We have a debugsource package, but no debuginfo subpackages. + The main debuginfo package should recommend the debugsource one. */ + Package dbgpkg = findDebuginfoPackage(spec); + if (dbgpkg) + addPackageDeps(dbgpkg, dbgsrcpkg, RPMTAG_RECOMMENDNAME); + } + for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) { char *nvr; const char *a; int header_color; int arch_color; + if (pkg == maindbg) { + /* if there is just one debuginfo package, we put our extra stuff + * in it. Otherwise we put it in the main debug package */ + Package extradbg = !maindbg->fileList && maindbg->next && !maindbg->next->next ? + maindbg->next : maindbg; + if (addDebugDwz(extradbg, buildroot)) + deplink = extradbg; + if (addDebugSrc(extradbg, buildroot)) + deplink = extradbg; + if (dbgsrcpkg != NULL) + addPackageDeps(extradbg, dbgsrcpkg, RPMTAG_RECOMMENDNAME); + maindbg = NULL; /* all normal packages processed */ + } + if (pkg->fileList == NULL) continue; @@ -2356,13 +3132,17 @@ rpmRC processBinaryFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags, nvr = headerGetAsString(pkg->header, RPMTAG_NVRA); rpmlog(RPMLOG_NOTICE, _("Processing files: %s\n"), nvr); free(nvr); - - if ((rc = processPackageFiles(spec, pkgFlags, pkg, installSpecialDoc, test)) != RPMRC_OK) + + if ((rc = processPackageFiles(spec, pkgFlags, pkg, didInstall, test)) != RPMRC_OK) goto exit; -#if HAVE_GELF_H && HAVE_LIBELF - addDebuginfoPackage(spec, pkg, buildroot); -#endif - if ((rc = rpmfcGenerateDepends(spec, pkg)) != RPMRC_OK) + + if (maindbg) + filterDebuginfoPackage(spec, pkg, maindbg, dbgsrcpkg, + buildroot, uniquearch); + else if (deplink && pkg != deplink) + addPackageDeps(pkg, deplink, RPMTAG_REQUIRENAME); + + if ((rc = rpmfcGenerateDepends(spec, pkg)) != RPMRC_OK) goto exit; a = headerGetString(pkg->header, RPMTAG_ARCH); @@ -2397,6 +3177,8 @@ rpmRC processBinaryFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags, } exit: check_fileList = freeStringBuf(check_fileList); + _free(buildroot); + _free(uniquearch); return rc; } |